diff --git a/.npmignore b/.npmignore
index 7247df5..975d5f3 100644
--- a/.npmignore
+++ b/.npmignore
@@ -2,7 +2,6 @@
.github
.gitignore
.idea
-Mini_Docs
node_modules
pnpm-lock.yaml
src
diff --git a/Mini_Docs/add.md b/Mini_Docs/add.md
deleted file mode 100644
index 5a9accc..0000000
--- a/Mini_Docs/add.md
+++ /dev/null
@@ -1,53 +0,0 @@
-## Functions
-
-
-- addLevel(userId, guildId, level, username) ⇒
Promise.<UserResult>
-Add XP to a user
-
-- addXP(userId, guildId, xpData, username) ⇒
Promise.<XPResult>
-Add XP to a user.
-
-
-
-
-
-## addLevel(userId, guildId, level, username) ⇒ Promise.<UserResult>
-
-Add XP to a user
-
-**Kind**: global function
-**Returns**: Promise.<UserResult>
- - Object of user data on success
-**Throws**:
-
-- XpFatal
- If parameters are not provided correctly
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/addlevel
-
-| Param | Type | Description |
-|----------|---------------------|-------------------------------------------|
-| userId | string
| |
-| guildId | string
| |
-| level | number
| |
-| username | string
| Username to use if auto_create is enabled |
-
-
-
-## addXP(userId, guildId, xpData, username) ⇒ Promise.<XPResult>
-
-Add XP to a user.
-
-**Kind**: global function
-**Returns**: Promise.<XPResult>
- - Object of user data on success.
-**Throws**:
-
-- XpFatal
- If parameters are not provided correctly.
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/addxp
-
-| Param | Type | Description |
-|----------|--------------------------------------------|--------------------------------------------------------------------------|
-| userId | string
| The ID of the user. |
-| guildId | string
| The ID of the guild. |
-| xpData | number
\| Object
| The XP to add, can be a number or an object with min and max properties. |
-| username | string
| Username to use if auto_create is enabled. |
-
diff --git a/Mini_Docs/cards.md b/Mini_Docs/cards.md
deleted file mode 100644
index ecacc83..0000000
--- a/Mini_Docs/cards.md
+++ /dev/null
@@ -1,51 +0,0 @@
-## Functions
-
-
-- rankCard(guild, user, options, locales) ⇒
Promise.<{attachment: Buffer, description: string, name: string}>
-Generate a simple user rank card
-
-- leaderboardCard(data, options, guildInfo, locales) ⇒
Promise.<{attachment: Buffer, description: string, name: string}>
-Generate a simple leaderboard card
-
-
-
-
-
-## rankCard(guild, user, options, locales) ⇒ Promise.<{attachment: Buffer, description: string, name: string}>
-
-Generate a simple user rank card
-
-**Kind**: global function
-**Throws**:
-
-- XpFatal
- If parameters are not provided correctly
-
-**Link**: [Documentation](https://simplyxp.js.org/docs/rankCard)
-
-| Param | Type | Description |
-|---------|------------------------------|-----------------------------------------------------|
-| guild | Object
| (id, name) |
-| user | UserOptions
| (id, username, avatarURL) |
-| options | RankCardOptions
| (background, color, legacy, lvlbar, lvlbarBg, font) |
-| locales | rankLocales
| [BETA] Translate the rank card |
-
-
-
-## leaderboardCard(data, options, guildInfo, locales) ⇒ Promise.<{attachment: Buffer, description: string, name: string}>
-
-Generate a simple leaderboard card
-
-**Kind**: global function
-**Throws**:
-
-- XpFatal
- If parameters are not provided correctly
-
-**Link**: [Documentation](https://simplyxp.js.org/docs/leaderboard)
-
-| Param | Type | Description |
-|-----------|---------------------------------|-------------------------------------|
-| data | Array.<User>
| Array of user data |
-| options | LeaderboardOptions
| (artworkColor, artworkImage, light) |
-| guildInfo | Object
| Guild info |
-| locales | LeaderboardLocales
| Locales |
-
diff --git a/Mini_Docs/charts.md b/Mini_Docs/charts.md
deleted file mode 100644
index a0faa93..0000000
--- a/Mini_Docs/charts.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-## charts(guildId, options) ⇒ Promise.<{attachment: Buffer, description: string, name: string}>
-
-Creates a chart
-
-**Kind**: global function
-**Returns**: Promise.<{attachment: Buffer, description: string, name: string}>
- Chart attachment
-**Throws**:
-
-- XpFatal
If invalid parameters are provided, or if there are not enough users to create a chart
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/charts
-
-| Param | Type |
-|---------|---------------------------|
-| guildId | string
|
-| options | ChartOptions
|
-
diff --git a/Mini_Docs/connect.md b/Mini_Docs/connect.md
deleted file mode 100644
index 0ab4e35..0000000
--- a/Mini_Docs/connect.md
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-## connect(uri, options) ⇒ Promise.<boolean>
-
-Connect to a database (MongoDB, SQLite)
-
-**Kind**: global function
-**Throws**:
-
-- XpFatal
If an invalid type is provided or if the value is not provided.
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/connect
-
-| Param | Type |
-|---------|--------------------------------|
-| uri | string
|
-| options | ConnectionOptions
|
-
diff --git a/Mini_Docs/create.md b/Mini_Docs/create.md
deleted file mode 100644
index 70a420f..0000000
--- a/Mini_Docs/create.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-## create(userId, guildId, username) ⇒ Promise.<UserResult>
-
-Create a new user in the database
-
-**Kind**: global function
-**Throws**:
-
-- XpFatal
If invalid parameters are provided
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/create
-
-| Param | Type |
-|----------|---------------------|
-| userId | string
|
-| guildId | string
|
-| username | string
|
-
diff --git a/Mini_Docs/deprecated/rank.md b/Mini_Docs/deprecated/rank.md
deleted file mode 100644
index 9324591..0000000
--- a/Mini_Docs/deprecated/rank.md
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-## ~~rank(message, userId, _guildId, options) ⇒ Promise.<{attachment: Buffer, description: string, name:
-string}>
~~
-
-***Deprecated***
-
-**Kind**: global function
-**Throws**:
-
-- XpFatal
- If parameters are not provided correctly
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/deprecated/rank
-
-| Param | Type | Description |
-|----------|------------------------------|-------------|
-| message | Message
| (DISCORD) |
-| userId | string
| |
-| _guildId | string
| |
-| options | RankCardOptions
| |
-
diff --git a/Mini_Docs/fetch.md b/Mini_Docs/fetch.md
deleted file mode 100644
index dcc7034..0000000
--- a/Mini_Docs/fetch.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-## fetch(userId, guildId, username) ⇒ Promise.<{name: (string\|null), user: string, guild: string, level: number, position: number, xp: number}>
-
-Fetch user data
-
-**Kind**: global function
-**Throws**:
-
-- XpFatal
If invalid parameters are provided, or if the user data is not found.
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/fetch
-
-| Param | Type | Description |
-|----------|---------------------|-------------------------------------------|
-| userId | string
| |
-| guildId | string
| |
-| username | string
| Username to use if auto_create is enabled |
-
diff --git a/Mini_Docs/functions/database.md b/Mini_Docs/functions/database.md
deleted file mode 100644
index e3425ed..0000000
--- a/Mini_Docs/functions/database.md
+++ /dev/null
@@ -1,134 +0,0 @@
-
-
-## db
-
-**Kind**: global class
-
-* [db](#db)
- * [new db()](#new_db_new)
- * [.getCollection(collection)](#db.getCollection) ⇒ Collection
- * [.createOne(query)](#db.createOne) ⇒ Promise.<(UserResult\|LevelRoleResult)>
- * [.deleteOne(query)](#db.deleteOne) ⇒ Promise.<boolean>
- * [.findOne(query)](#db.findOne) ⇒ Promise.<(UserResult\|LevelRoleResult)>
- * [.find(query)](#db.find) ⇒ Promise.<(Array.<UserResult>\|Array.<LevelRoleResult>)>
- * [.updateOne(filter, update, [options])](#db.updateOne) ⇒ Promise.<(UserResult\|LevelRoleResult)
- >
-
-
-
-### new db()
-
-Database class providing methods to interact with the database.
-
-
-
-### db.getCollection(collection) ⇒ Collection
-
-Gets a collection from the database.
-
-**Kind**: static method of [db
](#db)
-**Returns**: Collection
- The collection.
-**Throws**:
-
-- XpFatal
Throws an error if there is no database connection, or database type is invalid.
-
-**Link**: https://simplyxp.js.org/docs/handlers/database#getCollection Documentation
-
-| Param | Type | Description |
-|------------|-------------------------|------------------------|
-| collection | collection
| The collection to get. |
-
-
-
-### db.createOne(query) ⇒ Promise.<(UserResult\|LevelRoleResult)>
-
-Creates one document in the database.
-
-**Kind**: static method of [db
](#db)
-**Returns**: Promise.<(UserResult\|LevelRoleResult)>
- The created document.
-**Throws**:
-
-- XpFatal
Throws an error if there is no database connection.
-
-**Link**: https://simplyxp.js.org/docs/handlers/database#createOne Documentation
-
-| Param | Type | Description |
-|-------|-----------------------------------------------------------|-------------------------|
-| query | UserOptions
\| LevelRoleOptions
| The document to create. |
-
-
-
-### db.deleteOne(query) ⇒ Promise.<boolean>
-
-Deletes one document from the database.
-
-**Kind**: static method of [db
](#db)
-**Returns**: Promise.<boolean>
- `true` if the document was successfully deleted,
-otherwise `false`.
-**Throws**:
-
-- XpFatal
Throws an error if there is no database connection.
-
-**Link**: https://simplyxp.js.org/docs/handlers/database#deleteOne Documentation
-
-| Param | Type | Description |
-|-------|-----------------------------------------------------------|-------------------------|
-| query | UserOptions
\| LevelRoleOptions
| The document to delete. |
-
-
-
-### db.findOne(query) ⇒ Promise.<(UserResult\|LevelRoleResult)>
-
-Finds one document in the database.
-
-**Kind**: static method of [db
](#db)
-**Returns**: Promise.<(UserResult\|LevelRoleResult)>
- The found document.
-**Throws**:
-
-- XpFatal
Throws an error if there is no database connection.
-
-**Link**: https://simplyxp.js.org/docs/handlers/database#findOne Documentation
-
-| Param | Type | Description |
-|-------|-----------------------------------------------------------|---------------------------------------|
-| query | UserOptions
\| LevelRoleOptions
| The query to search for the document. |
-
-
-
-### db.find(query) ⇒ Promise.<(Array.<UserResult>\|Array.<LevelRoleResult>)>
-
-Finds multiple documents in the database.
-
-**Kind**: static method of [db
](#db)
-**Returns**: Promise.<(Array.<UserResult>\|Array.<LevelRoleResult>)>
- An array of found
-documents.
-**Throws**:
-
-- XpFatal
Throws an error if there is no database connection.
-
-**Link**: https://simplyxp.js.org/docs/handlers/database#find Documentation
-
-| Param | Type | Description |
-|-------|-----------------------------------------------------------|---------------------------------------------|
-| query | UserOptions
\| LevelRoleOptions
| The query to search for multiple documents. |
-
-
-
-### db.updateOne(filter, update, [options]) ⇒ Promise.<(UserResult\|LevelRoleResult)>
-
-Updates one document in the database.
-
-**Kind**: static method of [db
](#db)
-**Returns**: Promise.<(UserResult\|LevelRoleResult)>
- The updated document.
-**Throws**:
-
-- XpFatal
Throws an error if there is no database connection.
-
-**Link**: https://simplyxp.js.org/docs/handlers/database#updateOne Documentation
-
-| Param | Type | Description |
-|-----------|-----------------------------------------------------------|--------------------------------------------|
-| filter | UserOptions
\| LevelRoleOptions
| The document to update. |
-| update | UserOptions
\| LevelRoleOptions
| The document update data. |
-| [options] | object
| MongoDB options for updating the document. |
-
diff --git a/Mini_Docs/functions/utilities.md b/Mini_Docs/functions/utilities.md
deleted file mode 100644
index 84bd782..0000000
--- a/Mini_Docs/functions/utilities.md
+++ /dev/null
@@ -1,48 +0,0 @@
-## Functions
-
-
-- convertFrom(type) ⇒
number
-Convert XP to level and vice versa.
-
-- updateOptions(clientOptions) ⇒
void
-Updates the options of the XP client.
-
-
-
-
-
-## convertFrom(type) ⇒ number
-
-Convert XP to level and vice versa.
-
-**Kind**: global function
-**Returns**: number
- - The converted value. (XP to level or level to XP)
-**Throws**:
-
-- XpFatal
If an invalid type is provided or if the value is not provided.
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/utilities/convert
-
-| Param | Type | Description |
-|--------|---------------------------------------------------------------|----------------------------------------|
-| value. | number
| |
-| type | "xp"
\| "level"
| Type to convert from (Default: level). |
-
-
-
-## updateOptions(clientOptions) ⇒ void
-
-Updates the options of the XP client.
-
-**Kind**: global function
-**Returns**: void
- - Nothing.
-**Throws**:
-
-- XpFatal
If an invalid option is provided.
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/utilities/updateOptions
-
-| Param | Type | Description |
-|---------------|-------------------------------|----------------------------|
-| clientOptions | NewClientOptions
| The new options to update. |
-
diff --git a/Mini_Docs/functions/xplogs.md b/Mini_Docs/functions/xplogs.md
deleted file mode 100644
index d37f344..0000000
--- a/Mini_Docs/functions/xplogs.md
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-## XpFatal
-
-**Kind**: global class
-
-* [XpFatal](#XpFatal)
- * [new XpFatal()](#new_XpFatal_new)
- * [new XpFatal(options)](#new_XpFatal_new)
-
-
-
-### new XpFatal()
-
-Emits a fatal error message
-
-
-
-### new XpFatal(options)
-
-Emits a simple error message
-
-| Param | Type |
-|---------|-------------------------|
-| options | errOptions
|
-
diff --git a/Mini_Docs/leaderboard.md b/Mini_Docs/leaderboard.md
deleted file mode 100644
index b8812d9..0000000
--- a/Mini_Docs/leaderboard.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-## leaderboard(guildId, limit) ⇒ Promise.<Array.<User>>
-
-Get array of all users in the leaderboard
-
-**Kind**: global function
-**Returns**: Promise.<Array.<User>>
- Array of all users in the leaderboard
-**Throws**:
-
-- XpFatal
If guild ID is not provided or limit is less than 1
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/leaderboard
-
-| Param | Type | Description |
-|---------|---------------------|--------------------------|
-| guildId | string
| Guild ID |
-| limit | number
| Limit of users to return |
-
diff --git a/Mini_Docs/migrate.md b/Mini_Docs/migrate.md
deleted file mode 100644
index 4549460..0000000
--- a/Mini_Docs/migrate.md
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-## migrate
-
-**Kind**: global class
-
-* [migrate](#migrate)
- * [new migrate()](#new_migrate_new)
- * [.discord_xp(deleteOld)](#migrate.discord_xp) ⇒ Promise.<boolean>
- * [.fromDB(dbType, connection)](#migrate.fromDB) ⇒ Promise.<boolean>
-
-
-
-### new migrate()
-
-Migration functions
-
-
-
-### migrate.discord\_xp(deleteOld) ⇒ Promise.<boolean>
-
-Effortlessly migrate from discord-xp to simply-xp.
-
-**Kind**: static method of [migrate
](#migrate)
-**Returns**: Promise.<boolean>
- - Returns true if migration is successful
-**Throws**:
-
-- XpLog.err
- If migration fails.
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/migrate/discord_xp
-
-| Param | Type | Default | Description |
-|-----------|----------------------|--------------------|---------------------------------|
-| deleteOld | boolean
| false
| Delete old data after migration |
-
-
-
-### migrate.fromDB(dbType, connection) ⇒ Promise.<boolean>
-
-Effortlessly migrate from MongoDB to SQLite. (or vice versa)
-
-**Kind**: static method of [migrate
](#migrate)
-**Returns**: Promise.<boolean>
- - Returns true if migration is successful
-**Throws**:
-
-- XpFatal
- If parameters are not provided correctly
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/migrate/database
-
-| Param | Type |
-|------------|---------------------------------------------------------------------|
-| dbType | "mongodb"
\| "sqlite"
|
-| connection | Database
\| MongoClient
|
-
diff --git a/Mini_Docs/reset.md b/Mini_Docs/reset.md
deleted file mode 100644
index 8a7cdca..0000000
--- a/Mini_Docs/reset.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-## reset(userId, guildId, erase, username) ⇒ Promise.<boolean>
-
-Reset user levels to 0 in a guild
-
-**Kind**: global function
-**Throws**:
-
-- XpFatal
If an invalid type is provided or if the value is not provided.
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/reset
-
-| Param | Type | Default | Description |
-|----------|----------------------|--------------------|-------------------------------------------|
-| userId | string
| | |
-| guildId | string
| | |
-| erase | boolean
| false
| Erase user entry from the database |
-| username | string
| | Username to use if auto_create is enabled |
-
diff --git a/Mini_Docs/roleSetup.md b/Mini_Docs/roleSetup.md
deleted file mode 100644
index df2c9ac..0000000
--- a/Mini_Docs/roleSetup.md
+++ /dev/null
@@ -1,75 +0,0 @@
-
-
-## roleSetup
-
-**Kind**: global class
-
-* [roleSetup](#roleSetup)
- * [new roleSetup()](#new_roleSetup_new)
- * [.add(guildId, options)](#roleSetup.add) ⇒ Promise.<boolean>
- * [.find(guildId, levelNumber)](#roleSetup.find) ⇒ Promise.<RoleSetupObject>
- * [.remove(guildId, levelNumber)](#roleSetup.remove) ⇒ Promise.<boolean>
-
-
-
-### new roleSetup()
-
-Setup roles for levels
-
-
-
-### roleSetup.add(guildId, options) ⇒ Promise.<boolean>
-
-Add a role to the role setup
-
-**Kind**: static method of [roleSetup
](#roleSetup)
-**Returns**: Promise.<boolean>
- - True if successful
-**Throws**:
-
-- XpFatal
If an invalid type is provided or value is not provided.
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/roleSetup/add
-
-| Param | Type | Description |
-|---------|------------------------------|--------------------|
-| guildId | string
| The guild ID |
-| options | RoleSetupObject
| Level/role options |
-
-
-
-### roleSetup.find(guildId, levelNumber) ⇒ Promise.<RoleSetupObject>
-
-Find a role in roleSetup
-
-**Kind**: static method of [roleSetup
](#roleSetup)
-**Returns**: Promise.<RoleSetupObject>
- - The level role object
-**Throws**:
-
-- XpFatal
If an invalid type is provided or value is not provided.
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/roleSetup/find
-
-| Param | Type | Description |
-|-------------|---------------------|------------------|
-| guildId | string
| The guild ID |
-| levelNumber | number
| The level number |
-
-
-
-### roleSetup.remove(guildId, levelNumber) ⇒ Promise.<boolean>
-
-Remove a level from the role setup
-
-**Kind**: static method of [roleSetup
](#roleSetup)
-**Returns**: Promise.<boolean>
- - True if successful
-**Throws**:
-
-- XpFatal
If an invalid type is provided or value is not provided.
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/roleSetup/remove
-
-| Param | Type | Description |
-|-------------|---------------------|------------------|
-| guildId | string
| The guild ID |
-| levelNumber | number
| The level number |
-
diff --git a/Mini_Docs/set.md b/Mini_Docs/set.md
deleted file mode 100644
index 6538448..0000000
--- a/Mini_Docs/set.md
+++ /dev/null
@@ -1,53 +0,0 @@
-## Functions
-
-
-- setLevel(userId, guildId, level, username) ⇒
Promise.<UserResult>
-Set user level
-
-- setXP(userId, guildId, xpData, username) ⇒
Promise.<XPResult>
-Set user XP
-
-
-
-
-
-## setLevel(userId, guildId, level, username) ⇒ Promise.<UserResult>
-
-Set user level
-
-**Kind**: global function
-**Returns**: Promise.<UserResult>
- - Object of user data on success
-**Throws**:
-
-- XpFatal
- If parameters are not provided correctly
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/setlevel
-
-| Param | Type | Description |
-|----------|---------------------|-------------------------------------------|
-| userId | string
| |
-| guildId | string
| |
-| level | number
| |
-| username | string
| Username to use if auto_create is enabled |
-
-
-
-## setXP(userId, guildId, xpData, username) ⇒ Promise.<XPResult>
-
-Set user XP
-
-**Kind**: global function
-**Returns**: Promise.<XPResult>
- - Object of user data on success
-**Throws**:
-
-- XpFatal
- If parameters are not provided correctly
-
-**Link**: `Documentation:` https://simplyxp.js.org/docs/setxp
-
-| Param | Type | Description |
-|----------|---------------------|-------------------------------------------|
-| userId | string
| |
-| guildId | string
| |
-| xpData | number
| |
-| username | string
| Username to use if auto_create is enabled |
-
diff --git a/Mini_Docs/types/ChartOptions.md b/Mini_Docs/types/ChartOptions.md
deleted file mode 100644
index 5d14cff..0000000
--- a/Mini_Docs/types/ChartOptions.md
+++ /dev/null
@@ -1,19 +0,0 @@
-## ChartOptions
-
-### Properties
-
-- font `(string)`: The font to be used in the chart.
-- limit `(2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10)`: The maximum number of users to be displayed in the chart.
-- theme `("blue" | "dark" | "discord" | "green" | "orange" | "red" | "space" | "yellow")`: The theme to be used in the chart.
-- type `("bar" | "doughnut" | "pie")`: The type of chart to be created.
-
-### Example
-
-```typescript
-export interface ChartOptions {
- font?: string;
- limit?: 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
- theme?: "blue" | "dark" | "discord" | "green" | "orange" | "red" | "space" | "yellow";
- type?: "bar" | "doughnut" | "pie";
-}
-```
\ No newline at end of file
diff --git a/Mini_Docs/types/ConnectionOptions.md b/Mini_Docs/types/ConnectionOptions.md
deleted file mode 100644
index c196af6..0000000
--- a/Mini_Docs/types/ConnectionOptions.md
+++ /dev/null
@@ -1,25 +0,0 @@
-## ConnectionOptions
-
-### Description
-
-An interface representing the options for a database connection.
-
-### Properties
-
-- `type` (required, string): The type of the database. It can be one of the following values: "mongodb", "mysql", or "
- sqlite".
-- `autopurge` (boolean, optional): Indicates whether to automatically purge data of users/servers that are no longer
- relevant. **(REQUIRES DISCORD/GUILDED)**
-- `notify` (boolean, optional): Indicates whether to enable logging with simply-xp.
-- `debug` (boolean, optional): Indicates whether to enable debugging with simply-xp.
-
-### Example
-
-```typescript
-export type ConnectionOptions = {
- type: "mongodb" | "sqlite" | undefined;
- auto_purge?: false | unknown;
- notify?: boolean;
- debug?: boolean;
-}
-```
\ No newline at end of file
diff --git a/Mini_Docs/types/DatabaseOptions.md b/Mini_Docs/types/DatabaseOptions.md
deleted file mode 100644
index 163801e..0000000
--- a/Mini_Docs/types/DatabaseOptions.md
+++ /dev/null
@@ -1,57 +0,0 @@
-## UserOptions
-
-### Description
-
-An interface representing the options for many Database methods.
-
-### Properties
-
-- collection `(string)`: The name of the collection to search in.
-- data `(object)`: The data to be inserted into the database.
-- guild `(string)`: The ID of the guild for whom the document is related to.
-- user `(string)`: The ID of the user for whom the document is related to.
-- name `(string)`: The name of the user for whom the document is related to.
-- level `(number)`: The level of the user in the guild.
-- xp `(number)`: The XP of the user in the guild.
-
-### Example
-
-```typescript
-export type UserOptions = {
- collection: "simply-xps";
- data: {
- guild: string;
- user?: string;
- name?: string;
- level?: number;
- xp?: number;
- };
-}
-```
-
-## LevelRoleOptions
-
-### Description
-
-An interface representing the options for many Database methods.
-
-### Properties
-
-- collection `(string)`: The name of the collection to search in.
-- data `(object)`: The data to be inserted into the database.
-- guild `(string)`: The ID of the guild for whom the document is related to.
-- level `(number)`: The level of the user in the guild.
-- roles `(string | Array)`: The role(s) to be assigned to the user when they reach the specified level.
-
-### Example
-
-```typescript
-export type LevelRoleOptions = {
- collection: "simply-xp-levelroles";
- data: {
- guild: string;
- level?: number;
- roles?: string | Array;
- };
-}
-```
\ No newline at end of file
diff --git a/Mini_Docs/types/HexColor.md b/Mini_Docs/types/HexColor.md
deleted file mode 100644
index 4fea51e..0000000
--- a/Mini_Docs/types/HexColor.md
+++ /dev/null
@@ -1,11 +0,0 @@
-## HexColor
-
-### Properties
-
-- `HexColor` (string): The hex color.
-
-### Example
-
-```typescript
-type HexColor = `#${string}` | `0x${string}`;
-```
\ No newline at end of file
diff --git a/Mini_Docs/types/LevelRoleResult.md b/Mini_Docs/types/LevelRoleResult.md
deleted file mode 100644
index e185aec..0000000
--- a/Mini_Docs/types/LevelRoleResult.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# LevelRoleResult
-
-### Properties
-
-- `_id` (string, optional): The ID of the document.
-- `guild` (string): The guild ID of the level role.
-- `level` (number): The level of the level role.
-- `roles` (string[] | string): The roles of the level role.
-- `timestamp` (string): The timestamp of the level role.
-
-### Example
-
-```typescript
-export type LevelRoleResult = {
- _id?: string,
- guild: string;
- level: number;
- roles: Array;
- timestamp: string;
-}
-```
\ No newline at end of file
diff --git a/Mini_Docs/types/RankCardOptions.md b/Mini_Docs/types/RankCardOptions.md
deleted file mode 100644
index 8e2a4dd..0000000
--- a/Mini_Docs/types/RankCardOptions.md
+++ /dev/null
@@ -1,63 +0,0 @@
-## RankCardOptions
-
-### Description
-
-An interface representing the options for a rank card.
-
-### Properties
-
-- `background` (string, optional): The background image of the rank card.
-- `color` (string, optional): The color of the rank card.
-- `legacy` (boolean, optional): Indicates whether to use the legacy rank card.
-- `lvlbar` (HexColor, optional): The color of the level bar.
-- `lvlbarBg` (HexColor, optional): The background color of the level bar.
-- `font` (string, optional): The font of the rank card.
-
-### Example
-
-```typescript
-export type RankCardOptions = {
- background?: URL;
- color?: HexColor;
- legacy?: boolean;
- lvlbar?: HexColor;
- lvlbarBg?: HexColor;
- font?: string;
-};
-```
-
-## rankLocales
-
-### Properties
-
-- `level` (string, default: `"Level"`): The text to be displayed for the level.
-- `next_level` (string, default: `"Next Level"`): The text to be displayed for the next level.
-- `xp` (string, default: `"XP"`): The text to be displayed for the XP.
-
-### Example
-
-```typescript
-export type rankLocales = {
- level?: string;
- next_level?: string;
- xp?: string;
-}
-```
-
-## UserOptions
-
-### Properties
-
-- `id` (string): The ID of the user.
-- `username` (string): The username of the user.
-- `avatarURL` (string): The avatar URL of the user.
-
-### Example
-
-```typescript
-export type UserOptions = {
- id: string;
- username: string;
- avatarURL: string;
-}
-```
\ No newline at end of file
diff --git a/Mini_Docs/types/UserResult.md b/Mini_Docs/types/UserResult.md
deleted file mode 100644
index 3f31f1b..0000000
--- a/Mini_Docs/types/UserResult.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# UserResult
-
-### Properties
-
-- `_id` (string, optional): The ID of the document.
-- `user` (string): The ID of the user.
-- `name` (string, optional): The name of the user.
-- `guild` (string): The ID of the guild.
-- `level` (number): The level of the user in the guild.
-- `xp` (number): The XP of the user in the guild.
-
-### Example
-
-```typescript
-export interface UserResult {
- _id?: string,
- user: string;
- name?: string;
- guild: string;
- level: number;
- xp: number;
-}
-```
\ No newline at end of file
diff --git a/Mini_Docs/types/XPResult.md b/Mini_Docs/types/XPResult.md
deleted file mode 100644
index 92213b1..0000000
--- a/Mini_Docs/types/XPResult.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# XPResult
-
-### Properties
-
-- `_id` (string, optional): The ID of the document.
-- `user` (string): The ID of the user.
-- `name` (string, optional): The name of the user.
-- `guild` (string): The ID of the guild.
-- `level` (number): The level of the user in the guild.
-- `xp` (number): The XP of the user in the guild.
-- `hasLevelledUp` (boolean): Whether or not the user has levelled up.
-
-### Example
-
-```typescript
-interface XPResult extends UserResult {
- hasLevelledUp: boolean;
-}
-```
\ No newline at end of file
diff --git a/Mini_Docs/types/clientOptions.md b/Mini_Docs/types/clientOptions.md
deleted file mode 100644
index 828575f..0000000
--- a/Mini_Docs/types/clientOptions.md
+++ /dev/null
@@ -1,21 +0,0 @@
-## ClientOptions
-
-### Properties
-
-- `auto_create` (boolean, optional): Whether to automatically create a database if it doesn't exist.
-- `auto_purge` (boolean, optional): Whether to automatically purge the database if it exists.
-- `dbOptions` (object, required): The database options.
-- `notify` (boolean, optional): Whether to notify the user when they level up.
-- `debug` (boolean, optional): Whether to enable debug mode.
-
-### Example
-
-```typescript
-interface NewClientOptions {
- auto_create: boolean;
- auto_purge: boolean;
- dbOptions: { type: "mongodb" | "sqlite"; database: MongoClient | Database; };
- notify: boolean;
- debug: boolean;
-}
-```
\ No newline at end of file
diff --git a/Mini_Docs/types/leaderboardCardOptions.md b/Mini_Docs/types/leaderboardCardOptions.md
deleted file mode 100644
index 6e66c37..0000000
--- a/Mini_Docs/types/leaderboardCardOptions.md
+++ /dev/null
@@ -1,45 +0,0 @@
-## LeaderboardCardOptions
-
-### Description
-
-An interface representing the options for a leaderboard card.
-
-### Properties
-
-- `artworkColors` (Array, optional): The colors of the artwork.
-- `artworkImage` (URL, optional): The image of the artwork.
-- `borderColors` (Array, optional): The colors of the border.
-- `backgroundColor` (HexColor, optional): The background color of the leaderboard.
-- `backgroundImage` (URL, optional): The background image of the leaderboard.
-- `font` (string, optional): The font of the leaderboard.
-- `light` (boolean, optional): Indicates whether to use the light theme.
-
-### Example
-
-```typescript
-export type LeaderboardOptions = {
- artworkColors?: [HexColor, HexColor];
- artworkImage?: URL;
- borderColors?: [HexColor, HexColor];
- backgroundColor?: HexColor;
- backgroundImage?: URL;
- font?: string;
- light?: boolean;
-}
-```
-
-## LeaderboardLocales
-
-### Properties
-
-- `level` (string, default: `"LEVEL"`): The text to be displayed for the level.
-- `members` (string, default: `"Members"`): The text to be displayed for the members.
-
-### Example
-
-```typescript
-export type LeaderboardLocales = {
- level?: string;
- members?: string;
-}
-```
\ No newline at end of file
diff --git a/Mini_Docs/types/roleSetupObject.md b/Mini_Docs/types/roleSetupObject.md
deleted file mode 100644
index 8da0902..0000000
--- a/Mini_Docs/types/roleSetupObject.md
+++ /dev/null
@@ -1,17 +0,0 @@
-## RoleSetupObject
-
-### Properties
-
-- `guild` (string, optional): The guild ID of the role setup.
-- `level` (number): The level of the role setup.
-- `roles` (string[] | string): The roles of the role setup.
-
-### Example
-
-```typescript
-export type RoleSetupObject = {
- guild?: string;
- level: number;
- roles: string[] | string;
-}
-```
\ No newline at end of file
diff --git a/README.md b/README.md
index 971f6d4..7e6a6f7 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
[![Version](https://img.shields.io/npm/v/simply-xp.svg?style=for-the-badge)](https://www.npmjs.com/package/simply-xp)
[![CodeFactor](https://www.codefactor.io/repository/github/abadima/simply-xp/badge?style=for-the-badge)](https://www.codefactor.io/repository/github/abadima/simply-xp)
-[![Documentation](https://img.shields.io/badge/SimplyXP-Documentation-6b46d4?style=for-the-badge)](https://simplyxp.js.org)
+[![Documentation](https://img.shields.io/badge/SimplyXP-Documentation-6b46d4?style=for-the-badge)](https://simplyxp.js.org/docs/next/intro/)
[![Support](https://img.shields.io/badge/Discord-Support-5865F2?style=for-the-badge&logo=discord)](https://discord.gg/hjhnjYJNHX)
@@ -45,6 +45,7 @@ yarn add simply-xp@dev
- Added `db` class for extended database functionality
- Added `leaderboardCard()` function
- Added `convertFrom()` function
+- Added `compareCard` function
- Added `migrate` class
# 🎉 V2 Changes 🎉
@@ -63,8 +64,7 @@ yarn add simply-xp@dev
# ⚠️ V2 Breaking Changes ⚠️
- `create()` Now requires `username` argument.
-- `charts()` Requires new arguments.
-- `rank()` is **deprecated**, use `rankCard()` instead.
-- `rankCard()` Requires completely new arguments.
+- `charts()` Requires new arguments, and is revamped.
+- `rank()` is **deprecated**, use `rankCard()` instead. (REQUIRES NEW ARGUMENTS)
- `roleSetup()` functions loses `client` argument.
- `leaderboard()` loses `client` argument, and returns `user` instead of `userID`
\ No newline at end of file
diff --git a/Tests/charts.png b/Tests/charts.png
index ed52f59..510cb75 100644
Binary files a/Tests/charts.png and b/Tests/charts.png differ
diff --git a/Tests/clean.mjs b/Tests/clean.mjs
index 6c63bd3..c2de185 100644
--- a/Tests/clean.mjs
+++ b/Tests/clean.mjs
@@ -1,3 +1,5 @@
+// noinspection SpellCheckingInspection
+
import {readdir, readFileSync, writeFileSync} from "fs";
import {exec} from "child_process";
import {minify} from "uglify-js";
diff --git a/Tests/compareCard.png b/Tests/compareCard.png
new file mode 100644
index 0000000..0ece956
Binary files /dev/null and b/Tests/compareCard.png differ
diff --git a/Tests/jsdocs.cjs b/Tests/jsdocs.cjs
index 50de6f4..de5aea9 100644
--- a/Tests/jsdocs.cjs
+++ b/Tests/jsdocs.cjs
@@ -1,4 +1,3 @@
-const jsdoc2md = require('jsdoc-to-markdown');
const { readdirSync } = require('fs');
const { resolve } = require('path');
const {execSync} = require("child_process");
diff --git a/Tests/leaderboard.png b/Tests/leaderboard.png
index caa65a0..9faa795 100644
Binary files a/Tests/leaderboard.png and b/Tests/leaderboard.png differ
diff --git a/Tests/rankCard.png b/Tests/rankCard.png
new file mode 100644
index 0000000..72bd7a2
Binary files /dev/null and b/Tests/rankCard.png differ
diff --git a/Tests/rankcard.png b/Tests/rankcard.png
deleted file mode 100644
index 67c1f6e..0000000
Binary files a/Tests/rankcard.png and /dev/null differ
diff --git a/Tests/test.cjs b/Tests/test.cjs
index b4c00f1..cbbf959 100644
--- a/Tests/test.cjs
+++ b/Tests/test.cjs
@@ -31,27 +31,47 @@ async function test(dbType) {
await xp.create("1234567894", "0987654321", "Snowball");*/
- await xp.setLevel("1234567890", "0987654321", 25, "Abadima");
+ await xp.setLevel("326815959358898189", "0987654321", 69, "Abadima");
- await xp.setLevel("1234567893", "0987654321", 20, "Rahul");
+ await xp.addXP("326815959358898189", "0987654321", 13899, "Abadima");
- await xp.setLevel("1234567894", "0987654321", 15, "Jena");
+ await xp.setLevel("1234567893", "0987654321", 69, "Rahul");
- await xp.addLevel("1234567892", "0987654321", 10, "Ash");
+ await xp.setLevel("1234567894", "0987654321", 20, "Sammy");
+
+ await xp.setLevel("1234567895", "0987654321", 15, "Jena");
+
+ await xp.addLevel("1234567892", "0987654321", 10, "Jeremy");
await xp.setLevel("1234567891", "0987654321", 5, "Elizabeth");
+ // await xp.leaderboard("0987654321").then(console.log);
+
await xp.rankCard(
{id: "0987654321", name: "SimplyTests"},
{
- avatarURL: "https://cdn.discordapp.com/avatars/326815959358898189/67f99af24216f6d98d8d61a3b127d160.webp",
- id: "1234567890", username: "Abadima"
+ avatarURL: "https://avatarfiles.alphacoders.com/280/280594.jpg",
+ id: "326815959358898189", username: "Abadima"
},
- {background: "https://img.freepik.com/free-vector/gradient-wavy-purple-background_23-2149117433.jpg"}
- ).then(results => {
- require("fs").writeFileSync("Tests/rankcard.png", results.attachment);
+ {light: false}).then(results => {
+ require("fs").writeFileSync("Tests/rankCard.png", results.attachment);
});
+ await xp.compareCard(
+ {id: "0987654321", name: "SimplyTests"},
+ {
+ avatarURL: "https://avatarfiles.alphacoders.com/280/280594.jpg",
+ id: "326815959358898189", username: "Abadima"
+ },
+ {
+ avatarURL: "https://rahuletto.thedev.id/assets/logo.webp",
+ id: "1234567893", username: "Rahuletto"
+ },
+ {light: false}).then(results => {
+ require("fs").writeFileSync("Tests/compareCard.png", results.attachment);
+ }
+ );
+
await xp.leaderboard("0987654321").then(async (users) => {
await xp.leaderboardCard(users, {
// artworkImage: "https://th.bing.com/th/id/R.8cd8594560bd9cf4b042833a4acefaa5?rik=A6B1qYN%2b5GQAcA&riu=http%3a%2f%2fwallpaperswide.com%2fdownload%2fdesert_sky-wallpaper-2560x720.jpg&ehk=rE5VYZy8njd5ZeNT2p4sP7C5psjSf%2bxLZmV%2bvlQCffs%3d&risl=&pid=ImgRaw&r=0",
@@ -67,21 +87,16 @@ async function test(dbType) {
});
await xp.charts("0987654321", {
- theme: "space",
- type: "doughnut"
+ theme: "discord", type: "doughnut"
}).then(results => {
require("fs").writeFileSync("Tests/charts.png", results.attachment);
});
- await xp.reset("1234567890", "0987654321", true);
-
- await xp.reset("1234567893", "0987654321", true);
-
- await xp.reset("1234567894", "0987654321", true);
-
- await xp.reset("1234567892", "0987654321", true);
-
- await xp.reset("1234567891", "0987654321", true);
+ await xp.db.deleteMany({
+ collection: "simply-xps", data: {
+ guild: "0987654321"
+ }
+ });
}
-test("mongodb");
\ No newline at end of file
+test("sqlite");
\ No newline at end of file
diff --git a/Tests/test.db b/Tests/test.db
index c57fb6e..33caab2 100644
Binary files a/Tests/test.db and b/Tests/test.db differ
diff --git a/UPDATES@DEV.md b/UPDATES@DEV.md
index 217e0f9..8cc5226 100644
--- a/UPDATES@DEV.md
+++ b/UPDATES@DEV.md
@@ -1,5 +1,35 @@
# VERSION 2@DEV CHANGELOGS
+## [DEV 5](https://github.com/Abadima/simply-xp/releases/tag/v2.0.0-dev.5)
+
+
+
+### Additions
+
+- Added `better-sqlite3` V9 Support
+- Added `compareCard()` function, to compare two users.
+- `db.deleteMany()` is now added, to make deleting multiple users easier.
+- `rankCard()` now supports **Modern Design**, you can still override by passing `legacy: true` in options.
+
+### Changes
+
+- `connect()` will now install `mongodb v6` by default, versions down to `v4` are still supported.
+
+### Bug Fixes
+
+- Fix `charts()` handling invalid `type` & `theme` parameters.
+- Missing `await` in `migrate.fromDB()` function.
+- SQLite deleting username on `updateOne()`.
+
+### Improvements
+
+- Remove duplicate code in `charts()`.
+- Updated colours in `charts()` themes, to make them more accurate and easier to differentiate.
+- `leaderboard()` removes loop, and replaces with an `Asyncronous` method to improve performance.
+- Tweaked `checkPackageVersion()`, which also updates `migrate` class, and `updateOptions()` function.
+- Optimized font file (again), significantly lowers package size.
+- Update `JSDocs` to feature our updated documentation urls.
+
## [DEV 4](https://github.com/Abadima/simply-xp/releases/tag/v2.0.0-dev.4)
### Additions
@@ -34,6 +64,7 @@
## [DEV 3](https://github.com/Abadima/simply-xp/releases/tag/v2.0.0-dev.3)
### Additions
+
- Added `db.getCollection()` feature, useful for custom database implementations.
- Implemented `migrate.fromDB()`, from MongoDB to SQLite. (Support for SQLite to MongoDB coming soon)
diff --git a/package.json b/package.json
index 5c14d14..939e6fd 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "simply-xp",
- "version": "2.0.0-dev.4",
+ "version": "2.0.0-dev.5",
"description": "The easiest way to implement xp system",
"main": "lib/xp.js",
"scripts": {
@@ -14,7 +14,10 @@
"author": "Abadima",
"keywords": [
"amaribot",
+ "canvacord",
"charts",
+ "compare",
+ "compete",
"discord",
"discord.js",
"discord-xp",
@@ -22,14 +25,15 @@
"fun",
"guilded",
"leaderboard",
- "level role",
- "level up",
+ "level",
+ "leveling",
"mee6",
"mongodb",
"package",
"simply",
"simply-djs",
"simplydjs",
+ "slack",
"sqlite",
"system",
"xp"
@@ -37,33 +41,32 @@
"license": "Apache-2.0",
"repository": {
"type": "git",
- "url": "git+https://github.com/Rahuletto/simply-xp.git"
+ "url": "git+https://github.com/Abadima/simply-xp.git"
},
"dependencies": {
"@napi-rs/canvas": "^0.1.44"
},
"devDependencies": {
- "@types/better-sqlite3": "^7.6.4",
- "@types/node": "^20.5.4",
- "@typescript-eslint/eslint-plugin": "^6.4.1",
- "@typescript-eslint/parser": "^6.4.1",
- "better-sqlite3": "8.5.1",
+ "@types/better-sqlite3": "^7.6.5",
+ "@types/node": "^20.8.6",
+ "@typescript-eslint/eslint-plugin": "^6.7.5",
+ "@typescript-eslint/parser": "^6.7.5",
+ "better-sqlite3": "^9.0.0",
"discord.js": "^14.13.0",
- "eslint": "^8.47.0",
+ "eslint": "^8.51.0",
"jsdoc-to-markdown": "^8.0.0",
- "mongodb": "^5.8.1",
- "typescript": "^5.1.6",
+ "mongodb": "^6.1.0",
+ "typescript": "^5.2.2",
"uglify-js": "^3.17.4"
},
"engines": {
"node": ">=15.5.0"
},
"bugs": {
- "url": "https://github.com/Rahuletto/simply-xp/issues"
+ "url": "https://github.com/Abadima/simply-xp/issues"
},
"homepage": "https://simplyxp.js.org",
"directories": {
- "doc": "Mini_Docs",
"lib": "lib"
}
}
diff --git a/src/Fonts/BalooBhaijaan-Regular.otf b/src/Fonts/BalooBhaijaan-Regular.otf
deleted file mode 100644
index a395563..0000000
Binary files a/src/Fonts/BalooBhaijaan-Regular.otf and /dev/null differ
diff --git a/src/Fonts/BalooBhaijaan-Regular.woff2 b/src/Fonts/BalooBhaijaan-Regular.woff2
new file mode 100644
index 0000000..aa3b883
Binary files /dev/null and b/src/Fonts/BalooBhaijaan-Regular.woff2 differ
diff --git a/src/add.ts b/src/add.ts
index 43a2683..045af13 100644
--- a/src/add.ts
+++ b/src/add.ts
@@ -10,7 +10,7 @@ import {xp} from "../xp";
* @param {string} guildId
* @param {number} level
* @param {string} username - Username to use if auto_create is enabled
- * @link `Documentation:` https://simplyxp.js.org/docs/addlevel
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/functions/addlevel
* @returns {Promise} - Object of user data on success
* @throws {XpFatal} - If parameters are not provided correctly
*/
@@ -62,7 +62,7 @@ interface XPResult extends UserResult {
* @param {string} guildId - The ID of the guild.
* @param {number | {min: number, max: number}} xpData - The XP to add, can be a number or an object with min and max properties.
* @param {string} username - Username to use if auto_create is enabled.
- * @link `Documentation:` https://simplyxp.js.org/docs/addxp
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/functions/addxp
* @returns {Promise} - Object of user data on success.
* @throws {XpFatal} - If parameters are not provided correctly.
*/
diff --git a/src/cards.ts b/src/cards.ts
index 04aef9d..a029386 100644
--- a/src/cards.ts
+++ b/src/cards.ts
@@ -14,6 +14,33 @@ let cachedLeaderboardArtwork: Image | null,
cachedLeaderboardContext: SKRSContext2D | null,
cachedLeaderboardImage: Image | null;
+export type CompareCardLocales = {
+ level?: string;
+ versus?: string;
+}
+
+/**
+ * @property {URL} background - Background image URL
+ * @property {HexColor} color - Avatar border color
+ * @property {HexColor} centerBar - Center bar color
+ * @property {HexColor} centerBarBg - Center bar background color
+ * @property {string} font - ABSOLUTE FILE PATH
+ * @property {boolean} light - Use light theme
+ */
+export interface CompareCardOptions {
+ background?: URL;
+ color?: HexColor;
+ centerBar?: HexColor;
+ centerBarBg?: HexColor;
+ font?: string;
+ light?: boolean;
+}
+
+export type LeaderboardCardLocales = {
+ level?: string;
+ members?: string;
+}
+
/**
* @property {[HexColor, HexColor]} artworkColors - Gradient colors
* @property {URL} artworkImage
@@ -23,7 +50,7 @@ let cachedLeaderboardArtwork: Image | null,
* @property {string} font - ABSOLUTE FILE PATH
* @property {boolean} light - Use light theme
*/
-export interface LeaderboardOptions {
+export interface LeaderboardCardOptions {
artworkColors?: [HexColor, HexColor];
artworkImage?: URL;
borderColors?: [HexColor, HexColor];
@@ -33,9 +60,15 @@ export interface LeaderboardOptions {
light?: boolean;
}
+export type RankCardLocales = {
+ level?: string;
+ next_level?: string;
+ xp?: string;
+}
+
/**
* @property {URL} background - Background image URL
- * @property {HexColor} color
+ * @property {HexColor} color - Avatar border color
* @property {boolean} legacy - Use legacy card design
* @property {HexColor} lvlbar
* @property {HexColor} lvlbarBg
@@ -45,250 +78,174 @@ export interface RankCardOptions {
background?: URL;
color?: HexColor;
legacy?: boolean;
+ light?: boolean;
lvlbar?: HexColor;
lvlbarBg?: HexColor;
font?: string;
}
-export type UserOptions = {
+export type CardUserOptions = {
id: string;
username: string;
avatarURL: string;
}
-export type LeaderboardLocales = {
- level?: string;
- members?: string;
-}
-
-export type rankLocales = {
- level?: string;
- next_level?: string;
- xp?: string;
-}
-
/**
- * Generate a simple user rank card
+ * Generate a simple comparison card
* @async
* @param {{id: string, name: string}} guild - (id, name)
- * @param {UserOptions} user - (id, username, avatarURL)
- * @param {RankCardOptions?} options - (background, color, legacy, lvlbar, lvlbarBg, font)
- * @param {rankLocales?} locales - [BETA] Translate the rank card
- * @link [Documentation](https://simplyxp.js.org/docs/rankCard)
+ * @param {CardUserOptions} user1 - User 1
+ * @param {CardUserOptions} user2 - User 2
+ * @param {CompareCardOptions?} options - (background, color, legacy, lvlbar, lvlbarBg, font)
+ * @param {CompareCardLocales?} locales - [BETA] Translate the rank card
+ * @link `Documentation` https://simplyxp.js.org/docs/next/functions/compareCard
* @returns {Promise<{attachment: Buffer, description: string, name: string}>}
- * @throws {XpFatal} - If parameters are not provided correctly
+ * @throws {XpFatal} - If parameters are not provided correctly or if the user is not found in the database
*/
-export async function rankCard(guild: {
- id: string,
- name: string
-}, user: UserOptions, options: RankCardOptions = {}, locales: rankLocales = {}): Promise<{
+export async function compareCard(guild: {
+ id: string, name: string
+}, user1: CardUserOptions, user2: CardUserOptions, options: CompareCardOptions = {}, locales: CompareCardLocales = {}): Promise<{
attachment: Buffer;
description: string;
name: string;
}> {
- if (!guild) throw new XpFatal({function: "rankCard()", message: "No Guild Provided"});
- if (!user) throw new XpFatal({function: "rankCard()", message: "No User Provided"});
-
- // TODO: Add support for modern mode
- options.legacy = true;
- let canvas: Canvas, context: SKRSContext2D;
-
- if (!locales?.level) locales.level = "Level";
- if (!locales?.next_level) locales.next_level = "Next Level";
- if (!locales?.xp) locales.xp = "XP";
-
- XpLog.debug("rankCard()", "LEGACY MODE ENABLED");
- XpLog.info("rankCard()", "Modern RankCard is coming in dev.5");
+ if (!guild?.id || !guild?.name) throw new XpFatal({function: "compareCard()", message: "Please provide a guild"});
+ if (!user1?.id || !user1?.username || !user2?.id || !user2?.username) throw new XpFatal({
+ function: "compareCard()",
+ message: "Please provide two valid users!"
+ });
- if (!user?.avatarURL.endsWith(".png") && !user.avatarURL.endsWith(".jpg") && !user.avatarURL.endsWith(".webp")) {
+ if (!user1?.avatarURL.endsWith(".png") && !user1.avatarURL.endsWith(".jpg") && !user1.avatarURL.endsWith(".webp")) {
throw new XpFatal({
- function: "rankCard()", message: "Invalid avatar image, avatar image must be a png, jpg, or webp"
+ function: "compareCard()", message: "[USER 1] Avatar image must be a png, jpg, or webp"
});
}
-
- if (!user || !user.id || !user.username) {
+ if (!user2?.avatarURL.endsWith(".png") && !user2.avatarURL.endsWith(".jpg") && !user2.avatarURL.endsWith(".webp")) {
throw new XpFatal({
- function: "rankCard()", message: "Invalid User Provided, user must contain id, username, and avatarURL."
+ function: "compareCard()", message: "[USER 2] Avatar image, avatar image must be a png, jpg, or webp"
});
}
- GlobalFonts.registerFromPath(options?.font || join(__dirname, "Fonts", "BalooBhaijaan-Regular.otf"), "Sans Serif");
+ GlobalFonts.registerFromPath(options?.font || join(__dirname, "Fonts", "BalooBhaijaan-Regular.woff2"), "Sans Serif");
+
+ if (!locales?.level) locales.level = "Level";
+ if (!locales?.versus) locales.versus = "vs";
- if (!cachedRankImage) cachedRankImage = await loadImage(options?.background || "https://i.ibb.co/dck2Tnt/rank-card.webp").catch(() => {
+ const compareImage = await loadImage(options?.background || "https://i.ibb.co/WnfXZjc/clouds.jpg").catch(() => {
throw new XpFatal({
- function: "rankCard()", message: "Unable to load background image, is it valid?"
+ function: "compareCard()", message: "Unable to load background image, is it valid?"
});
});
- const avatarURL = await loadImage(user.avatarURL).catch(() => {
+ const avatarURL1 = await loadImage(user1.avatarURL).catch(() => {
throw new XpFatal({
- function: "rankCard()", message: "Unable to load user's AvatarURL, is it reachable?"
+ function: "compareCard()", message: "[USER 1] Unable to load user's AvatarURL, is it reachable?"
});
});
- let dbUser = await db.findOne({collection: "simply-xps", data: {guild: guild.id, user: user.id}}) as User;
- if (!dbUser) {
- if (xp.auto_create) dbUser = await create(user.id, guild.id, user.username) as User;
- else throw new XpFatal({function: "rankCard()", message: "User not found in database"});
- }
-
- const users = await db.find({collection: "simply-xps", data: {guild: guild.id}}) as User[];
-
- dbUser.position = users.sort((a, b) => b.xp - a.xp).findIndex((u) => u.user === user.id) + 1;
-
- if (options?.legacy) {
- const Username = user.username.replace(/[\u007f-\uffff]/g, ""),
- BoxColor = options?.color || "#9900ff",
- LevelBarFill = options?.lvlbar || "#ffffff",
- LevelBarBackground = options?.lvlbarBg || "#ffffff",
- TextEXP = shortener(dbUser.xp) + ` ${locales.xp}`,
- LvlText = locales.level + ` ${shortener(dbUser.level)}`,
- TextXpNeeded = "{current} / {needed}",
- nextLevelXP = convertFrom(dbUser.level + 1),
- currentLevelXP = convertFrom(dbUser.level),
- progress = (((100 * (dbUser.xp - currentLevelXP)) / (nextLevelXP - currentLevelXP)) * 660) / 100;
-
-
- if (!cachedRankContext || !cachedRankCanvas) {
- canvas = createCanvas(1080, 400);
- context = canvas.getContext("2d");
-
- RoundedBox(context, 0, 0, canvas.width, canvas.height, 50);
- context.clip();
-
- context.fillStyle = "#000000";
- context.fillRect(0, 0, 1080, 400);
-
- context.globalAlpha = 0.7;
- context.drawImage(cachedRankImage, -5, 0, 1090, 400);
- context.restore();
+ const avatarURL2 = await loadImage(user2.avatarURL).catch(() => {
+ throw new XpFatal({
+ function: "compareCard()", message: "[USER 2] Unable to load user's AvatarURL, is it reachable?"
+ });
+ });
- context.fillStyle = "#000000";
- context.globalAlpha = 0.4;
- context.fillRect(40, 0, 240, canvas.height);
- context.globalAlpha = 1;
- } else {
- canvas = cachedRankCanvas;
- context = cachedRankContext;
- }
+ let dbUser1 = await db.findOne({collection: "simply-xps", data: {guild: guild.id, user: user1.id}}) as User;
+ if (!dbUser1) {
+ if (xp.auto_create && user2?.username) dbUser1 = await create(user1.id, guild.id, user1.username) as User;
+ else throw new XpFatal({function: "compareCard()", message: "[USER 1] User not found in database"});
+ }
- context.save();
- RoundedBox(context, 70, 30, 180, 180, 50);
- context.strokeStyle = BoxColor;
- context.lineWidth = 15;
- context.stroke();
- context.clip();
- context.drawImage(avatarURL, 70, 30, 180, 180);
- context.restore();
+ let dbUser2 = await db.findOne({collection: "simply-xps", data: {guild: guild.id, user: user2.id}}) as User;
+ if (!dbUser2) {
+ if (xp.auto_create && user2?.username) dbUser2 = await create(user2.id, guild.id, user2.username) as User;
+ else throw new XpFatal({function: "compareCard()", message: "[USER 2] User not found in database"});
+ }
- context.save();
- RoundedBox(context, 70, 240 + 50 + 30, 180, 50, 20);
- context.strokeStyle = "#BFC85A22";
- context.stroke();
- context.clip();
- context.fillStyle = BoxColor;
- context.globalAlpha = 1;
- context.fillRect(70, 320, 180, 50);
- context.globalAlpha = 1;
- context.fillStyle = "#ffffff";
- context.textAlign = "center";
- dynamicFont(context, TextEXP, 160, 358, 160, 32);
- context.restore();
+ const canvas = createCanvas(1080, 400);
+ const context = canvas.getContext("2d");
- context.save();
- RoundedBox(context, 70, 240, 180, 50, 20);
- context.strokeStyle = "#BFC85A22";
- context.stroke();
- context.clip();
- context.fillStyle = BoxColor;
- context.globalAlpha = 1;
- context.fillRect(70, 240, 180, 50);
- context.globalAlpha = 1;
- context.fillStyle = "#ffffff";
- context.textAlign = "center";
- dynamicFont(context, LvlText, 160, 278, 160, 32);
- context.restore();
+ RoundedBox(context, 0, 0, canvas.width, canvas.height, 25);
+ context.clip();
- context.save();
- context.textAlign = "left";
- context.fillStyle = "#ffffff";
- context.shadowColor = "#000000";
- context.shadowBlur = 15;
- context.shadowOffsetX = 1;
- context.shadowOffsetY = 1;
- context.font = "39px \"Sans Serif\"";
- context.fillText(Username, 390, 80);
- context.restore();
+ context.globalAlpha = 0.2;
- context.save();
- context.textAlign = "right";
- context.fillStyle = "#ffffff";
- context.shadowColor = "#000000";
- context.shadowBlur = 15;
- context.shadowOffsetX = 1;
- context.shadowOffsetY = 1;
- context.font = "55px \"Sans Serif\"";
- context.fillText("#" + dbUser.position, canvas.width - 55, 80);
- context.restore();
+ context.fillStyle = options?.light ? "#ffffff" : "#000000";
+ context.fill();
- context.save();
- RoundedBox(context, 390, 305, 660, 70, Number(20));
- context.strokeStyle = "#BFC85A22";
- context.stroke();
- context.clip();
- context.fillStyle = "#ffffff";
- context.textAlign = "center";
- dynamicFont(context, guild.name, 720, 355, 700, 45);
- context.globalAlpha = 0.2;
- context.fillRect(390, 305, 660, 70);
- context.restore();
+ context.globalAlpha = 0.5;
- context.save();
- RoundedBox(context, 390, 145, 660, 50, 20);
- context.strokeStyle = "#BFC85A22";
- context.stroke();
- context.clip();
- context.fillStyle = LevelBarBackground;
- context.globalAlpha = 0.2;
- context.fillRect(390, 145, 660, 50);
- context.restore();
+ context.drawImage(compareImage, -5, 0, 1090, 400);
+ context.restore();
- context.save();
- RoundedBox(context, 390, 145, progress, 50, 20);
- context.strokeStyle = "#BFC85A22";
- context.stroke();
- context.clip();
- context.fillStyle = LevelBarFill;
- context.globalAlpha = 0.5;
- context.fillRect(390, 145, progress, 50);
- context.restore();
+ context.globalAlpha = 1;
- context.save();
- context.textAlign = "left";
- context.fillStyle = "#ffffff";
- context.globalAlpha = 0.8;
- context.font = "30px \"Sans Serif\"";
- context.fillText(`${locales.next_level}: ` + shortener(nextLevelXP) + ` ${locales.xp}`, 390, 230);
- context.restore();
+ const Username1 = user1.username.replace(/[\u007f-\uffff]/g, ""),
+ Username2 = user2.username.replace(/[\u007f-\uffff]/g, ""),
+ cardBoxColor = options?.color || "rgba(255,255,255,0.5)",
+ CenterBarBackground = options?.centerBarBg || options?.light ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)",
+ LvlText1 = locales.level + ` ${shortener(dbUser1.level)}`,
+ LvlText2 = locales.level + ` ${shortener(dbUser2.level)}`;
- const textXPEdited = TextXpNeeded.replace(/{needed}/g, shortener(nextLevelXP)).replace(/{current}/g, shortener(dbUser.xp));
- context.textAlign = "center";
- context.fillStyle = "#474747";
- context.globalAlpha = 1;
- context.font = "30px \"Sans Serif\"";
- context.fillText(textXPEdited, 730, 180);
+ // Add Usernames
+ context.save();
+ context.textAlign = "center";
+ context.fillStyle = "#ffffff";
+ context.shadowColor = "#000000";
+ context.shadowBlur = 6;
+ context.shadowOffsetX = 1;
+ context.shadowOffsetY = 1;
+ context.font = "39px \"Sans Serif\"";
+ context.fillText(`${Username1} ${locales.versus} ${Username2}`, 540, 60);
+ context.restore();
+
+ // Add User 1 Avatar
+ context.save();
+ context.beginPath();
+ context.arc(160, 200, 100, 0, Math.PI * 2, true);
+ context.closePath();
+ context.strokeStyle = cardBoxColor;
+ context.lineWidth = 15;
+ context.stroke();
+ context.clip();
+ context.drawImage(avatarURL1, 50, 90, 220, 220);
+ context.restore();
+
+ // Add User 2 Avatar
+ context.save();
+ context.beginPath();
+ context.arc(920, 200, 100, 0, Math.PI * 2, true);
+ context.closePath();
+ context.strokeStyle = cardBoxColor;
+ context.lineWidth = 15;
+ context.stroke();
+ context.clip();
+ context.drawImage(avatarURL2, 810, 90, 220, 220);
+ context.restore();
+
+ // Add Level Texts
+ context.save();
+ context.globalAlpha = 1;
+ context.fillStyle = "#ffffff";
+ context.textAlign = "center";
+ context.font = "25px \"Sans Serif\"";
+ context.fillText(LvlText1, 160, 350);
- } else {
- // TODO: Add support for modern mode
+ context.fillText(LvlText2, 920, 350);
+ context.restore();
- canvas = createCanvas(1080, 360);
- }
+ // Add sleek center bar
+ context.save();
+ context.globalAlpha = 1;
+ RoundedBox(context, 265, 330, 540, 25, 10);
+ context.clip();
+ context.fillStyle = CenterBarBackground;
+ context.fill();
return {
attachment: canvas.toBuffer("image/png"),
- description: "Simply-XP Rank Card",
- name: "rank.png"
+ description: "Simply-XP Comparison Card",
+ name: "compareCard.png"
};
}
@@ -296,18 +253,18 @@ export async function rankCard(guild: {
* Generate a simple leaderboard card
* @async
* @param {Array} data - Array of user data
- * @param {LeaderboardOptions?} options - (artworkColor, artworkImage, light)
+ * @param {LeaderboardCardOptions?} options - (artworkColor, artworkImage, light)
* @param {{name: string, imageURL: string, memberCount: number}?} guildInfo - Guild info
- * @param {LeaderboardLocales} locales - Locales
- * @link [Documentation](https://simplyxp.js.org/docs/leaderboard)
+ * @param {LeaderboardCardLocales?} locales - Locales
+ * @link `Documentation` https://simplyxp.js.org/docs/next/functions/leaderboard
* @returns {Promise<{attachment: Buffer, description: string, name: string}>}
* @throws {XpFatal} - If parameters are not provided correctly
*/
-export async function leaderboardCard(data: Array, options: LeaderboardOptions = {}, guildInfo?: {
+export async function leaderboardCard(data: Array, options: LeaderboardCardOptions = {}, guildInfo?: {
name: string,
imageURL: string,
memberCount: number
-}, locales: LeaderboardLocales = {}): Promise<{ attachment: Buffer; description: string; name: string; }> {
+}, locales: LeaderboardCardLocales = {}): Promise<{ attachment: Buffer; description: string; name: string; }> {
if (!data || data.length < 1) throw new XpFatal({
function: "leaderboardCard()",
message: "There must be at least 1 user in the data array"
@@ -325,7 +282,7 @@ export async function leaderboardCard(data: Array, options: LeaderboardOpt
});
});
- GlobalFonts.registerFromPath(options?.font || join(__dirname, "Fonts", "BalooBhaijaan-Regular.otf"), "Sans Serif");
+ GlobalFonts.registerFromPath(options?.font || join(__dirname, "Fonts", "BalooBhaijaan-Regular.woff2"), "Sans Serif");
if (!locales.level) locales.level = "LEVEL";
if (!locales.members) locales.members = "Members";
@@ -430,10 +387,10 @@ export async function leaderboardCard(data: Array, options: LeaderboardOpt
RoundedBox(context, 30, cardY, 1290, 90, 20);
} else {
if (i === 0) {
- RoundedBox(context, 30, cardY, 1290, 90, 20, {top: true, bottom: false});
+ RoundedBox(context, 30, cardY, 1290, 90, 20, undefined, {top: true, bottom: false});
} else if (i === data.length - 1) {
- RoundedBox(context, 30, cardY, 1290, 90, 20, {top: false, bottom: true});
+ RoundedBox(context, 30, cardY, 1290, 90, 20, undefined, {top: false, bottom: true});
} else {
RoundedBox(context, 30, cardY, 1290, 90, 0);
}
@@ -473,20 +430,320 @@ export async function leaderboardCard(data: Array, options: LeaderboardOpt
};
}
+/**
+ * Generate a simple user rank card
+ * @async
+ * @param {{id: string, name: string}} guild - (id, name)
+ * @param {CardUserOptions} user - (id, username, avatarURL)
+ * @param {RankCardOptions?} options - (background, color, legacy, lvlbar, lvlbarBg, font)
+ * @param {RankCardLocales?} locales - [BETA] Translate the rank card
+ * @link `Documentation` https://simplyxp.js.org/docs/next/functions/rankCard
+ * @returns {Promise<{attachment: Buffer, description: string, name: string}>}
+ * @throws {XpFatal} - If parameters are not provided correctly
+ */
+export async function rankCard(guild: {
+ id: string, name: string
+}, user: CardUserOptions, options: RankCardOptions = {}, locales: RankCardLocales = {}): Promise<{
+ attachment: Buffer; description: string; name: string;
+}> {
+ if (!guild) throw new XpFatal({function: "rankCard()", message: "No Guild Provided"});
+ if (!user) throw new XpFatal({function: "rankCard()", message: "No User Provided"});
+
+ let canvas: Canvas, context: SKRSContext2D;
+
+ if (!locales?.level) locales.level = "Level";
+ if (!locales?.next_level) locales.next_level = "Next Level";
+ if (!locales?.xp) locales.xp = "XP";
+
+ XpLog.debug("rankCard()", `${options?.legacy ? "LEGACY" : "MODERN"} ENABLED`);
+
+ if (!user?.avatarURL.endsWith(".png") && !user.avatarURL.endsWith(".jpg") && !user.avatarURL.endsWith(".webp")) {
+ throw new XpFatal({
+ function: "rankCard()", message: "Invalid avatar image, avatar image must be a png, jpg, or webp"
+ });
+ }
+
+
+ if (!user || !user.id || !user.username) {
+ throw new XpFatal({
+ function: "rankCard()", message: "Invalid User Provided, user must contain id, username, and avatarURL."
+ });
+ }
+
+ GlobalFonts.registerFromPath(options?.font || join(__dirname, "Fonts", "BalooBhaijaan-Regular.woff2"), "Sans Serif");
+
+ if (!cachedRankImage) cachedRankImage = await loadImage(options?.background || (options?.legacy ? "https://i.ibb.co/dck2Tnt/rank-card.webp" : "https://i.ibb.co/WnfXZjc/clouds.jpg")).catch(() => {
+ throw new XpFatal({
+ function: "rankCard()", message: "Unable to load background image, is it valid?"
+ });
+ });
+
+ const avatarURL = await loadImage(user.avatarURL).catch(() => {
+ throw new XpFatal({
+ function: "rankCard()", message: "Unable to load user's AvatarURL, is it reachable?"
+ });
+ });
+
+ let dbUser = await db.findOne({collection: "simply-xps", data: {guild: guild.id, user: user.id}}) as User;
+ if (!dbUser) {
+ if (xp.auto_create) dbUser = await create(user.id, guild.id, user.username) as User;
+ else throw new XpFatal({function: "rankCard()", message: "User not found in database"});
+ }
+
+ const users = await db.find({collection: "simply-xps", data: {guild: guild.id}}) as User[];
+
+ dbUser.position = users.sort((a, b) => b.xp - a.xp).findIndex((u) => u.user === user.id) + 1;
+
+ if (!cachedRankContext || !cachedRankCanvas) {
+ canvas = createCanvas(1080, 400);
+ context = canvas.getContext("2d");
+
+ RoundedBox(context, 0, 0, canvas.width, canvas.height, 25);
+ context.clip();
+
+ if (options?.legacy) {
+ context.globalAlpha = 1;
+ context.fillStyle = "#000000";
+ context.fill();
+ } else {
+ context.globalAlpha = 0.2;
+ context.fillStyle = options?.light ? "#ffffff" : "#000000";
+ context.fill();
+
+ context.globalAlpha = 0.5;
+ }
+
+ context.drawImage(cachedRankImage, -5, 0, 1090, 400);
+ context.restore();
+ } else {
+ canvas = cachedRankCanvas;
+ context = cachedRankContext;
+ }
+
+ const Username = user.username.replace(/[\u007f-\uffff]/g, ""),
+ rankBoxColor = options?.color || (options?.legacy ? "#9900ff" : "rgba(255,255,255,0.5)"),
+ LevelBarFill = options?.lvlbar || "#ffffff",
+ LevelBarBackground = options?.lvlbarBg || options?.legacy ? "#FFFFFF" : (options?.light ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)"),
+ TextEXP = shortener(dbUser.xp) + ` ${locales.xp}`,
+ LvlText = locales.level + ` ${shortener(dbUser.level)}`,
+ TextXpNeeded = "{current} / {needed}",
+ nextLevelXP = convertFrom(dbUser.level + 1),
+ currentLevelXP = convertFrom(dbUser.level),
+ progress = (((100 * (dbUser.xp - currentLevelXP)) / (nextLevelXP - currentLevelXP)) * (options?.legacy ? 660 : 530)) / 100,
+ positionColour = dbUser.user === "326815959358898189" ? "#ade6d8" : dbUser.position === 1 ? "#ADD8E6" : dbUser.position === 2 ? "#C0C0C0" : dbUser.position === 3 ? "#CD7F32" : "#ffffff";
+
+
+ context.save();
+ if (!options?.legacy) {
+ context.globalAlpha = 1;
+
+ // Add Username
+ context.save();
+ context.textAlign = "center";
+ context.fillStyle = "#ffffff";
+ context.shadowColor = "#000000";
+ context.shadowBlur = 5;
+ context.shadowOffsetX = 1;
+ context.shadowOffsetY = 1;
+ context.font = "39px \"Sans Serif\"";
+ context.fillText(Username, 540, 80);
+ context.restore();
+
+ // Add Avatar
+ context.save();
+ context.beginPath();
+ context.arc(160, 200, 100, 0, Math.PI * 2, true);
+ context.closePath();
+ context.strokeStyle = rankBoxColor;
+ context.lineWidth = 15;
+ context.stroke();
+ context.clip();
+ context.drawImage(avatarURL, 50, 90, 220, 220);
+ context.restore();
+
+ // Add Position Badge
+ context.save();
+ context.beginPath();
+ context.arc(230, 130, 30, 0, Math.PI * 2, true);
+ context.closePath();
+ context.strokeStyle = rankBoxColor;
+ context.lineWidth = 5;
+ context.stroke();
+ context.clip();
+
+ context.beginPath();
+ context.arc(230, 130, 30, 0, Math.PI * 2, true);
+ context.closePath();
+ context.fillStyle = positionColour;
+ context.fill();
+ context.clip();
+
+ // Add Position Text
+ context.fillStyle = "#000000";
+ context.textAlign = "center";
+ context.font = "25px \"Sans Serif\"";
+ context.fillText(shortener(dbUser.position, true), 230, 138);
+ context.restore();
+
+ // Add Level Text
+ context.save();
+ context.fillStyle = "#ffffff";
+ context.font = "25px \"Sans Serif\"";
+ context.textAlign = "center";
+ context.globalAlpha = 1;
+ context.fillText(LvlText, 160, 350);
+ context.restore();
+
+ // Add sleek progress bar
+ context.save();
+ context.globalAlpha = 1;
+ RoundedBox(context, 265, 330, 540, 25, 10);
+ context.clip();
+ context.fillStyle = LevelBarBackground;
+ context.fill();
+
+ // now fill the progress bar
+ RoundedBox(context, 270, 335, progress, 15, 5);
+ context.fillStyle = LevelBarFill;
+ context.fill();
+ context.restore();
+
+ // Right in the middle, add the XP Text
+ context.save();
+ context.textAlign = "center";
+ context.fillStyle = "#ffffff";
+ context.globalAlpha = 0.6;
+ context.font = "20px \"Sans Serif\"";
+ context.fillText(TextXpNeeded.replace(/{needed}/g, shortener(nextLevelXP)).replace(/{current}/g, shortener(dbUser.xp)), 540, 320);
+
+ // Add Level Text (Next Level)
+ context.save();
+ context.fillStyle = "#ffffff";
+ context.textAlign = "center";
+ context.globalAlpha = 1;
+ context.font = "25px \"Sans Serif\"";
+ context.fillText(`${locales.level} ` + shortener(dbUser.level + 1), 920, 350);
+ context.restore();
+
+ } else {
+ context.fillStyle = "#000000";
+ context.globalAlpha = 0.4;
+ context.fillRect(40, 0, 240, canvas.height);
+ context.globalAlpha = 1;
+
+ RoundedBox(context, 70, 30, 180, 180, 50);
+ context.strokeStyle = rankBoxColor;
+ context.lineWidth = 15;
+ context.stroke();
+ context.clip();
+ context.drawImage(avatarURL, 70, 30, 180, 180);
+ context.restore();
+
+ context.save();
+ RoundedBox(context, 70, 240 + 50 + 30, 180, 50, 20, "#BFC85A22");
+ context.fillStyle = rankBoxColor;
+ context.globalAlpha = 1;
+ context.fillRect(70, 320, 180, 50);
+ context.globalAlpha = 1;
+ context.fillStyle = "#ffffff";
+ context.textAlign = "center";
+ dynamicFont(context, TextEXP, 160, 358, 160, 32);
+ context.restore();
+
+ context.save();
+ RoundedBox(context, 70, 240, 180, 50, 20, "#BFC85A22");
+ context.fillStyle = rankBoxColor;
+ context.globalAlpha = 1;
+ context.fillRect(70, 240, 180, 50);
+ context.globalAlpha = 1;
+ context.fillStyle = "#ffffff";
+ context.textAlign = "center";
+ dynamicFont(context, LvlText, 160, 278, 160, 32);
+ context.restore();
+
+ context.save();
+ context.textAlign = "left";
+ context.fillStyle = "#ffffff";
+ context.shadowColor = "#000000";
+ context.shadowBlur = 15;
+ context.shadowOffsetX = 1;
+ context.shadowOffsetY = 1;
+ context.font = "39px \"Sans Serif\"";
+ context.fillText(Username, 390, 80);
+ context.restore();
+
+ context.save();
+ context.textAlign = "right";
+ context.fillStyle = "#ffffff";
+ context.shadowColor = "#000000";
+ context.shadowBlur = 15;
+ context.shadowOffsetX = 1;
+ context.shadowOffsetY = 1;
+ context.font = "55px \"Sans Serif\"";
+ context.fillText("#" + dbUser.position, canvas.width - 55, 80);
+ context.restore();
+
+ context.save();
+ RoundedBox(context, 390, 305, 660, 70, 20, "#BFC85A22");
+ context.fillStyle = "#ffffff";
+ context.textAlign = "center";
+ dynamicFont(context, guild.name, 720, 355, 700, 45);
+ context.globalAlpha = 0.2;
+ context.fillRect(390, 305, 660, 70);
+ context.restore();
+
+ context.save();
+ RoundedBox(context, 390, 145, 660, 50, 20, "#BFC85A22");
+ context.fillStyle = LevelBarBackground;
+ context.globalAlpha = 0.2;
+ context.fillRect(390, 145, 660, 50);
+ context.restore();
+
+ context.save();
+ RoundedBox(context, 390, 145, progress, 50, 20, "#BFC85A22");
+ context.fillStyle = LevelBarFill;
+ context.globalAlpha = 0.5;
+ context.fillRect(390, 145, progress, 50);
+ context.restore();
+
+ context.save();
+ context.textAlign = "left";
+ context.fillStyle = "#ffffff";
+ context.globalAlpha = 0.8;
+ context.font = "30px \"Sans Serif\"";
+ context.fillText(`${locales.next_level}: ` + shortener(nextLevelXP) + ` ${locales.xp}`, 390, 230);
+ context.restore();
+
+ const textXPEdited = TextXpNeeded.replace(/{needed}/g, shortener(nextLevelXP)).replace(/{current}/g, shortener(dbUser.xp));
+ context.textAlign = "center";
+ context.fillStyle = "#474747";
+ context.globalAlpha = 1;
+ context.font = "30px \"Sans Serif\"";
+ context.fillText(textXPEdited, 730, 180);
+ }
+
+ return {
+ attachment: canvas.toBuffer("image/png"),
+ description: "Simply-XP Rank Card",
+ name: "rank.png"
+ };
+}
+
/**
* @constructor
* @private
*/
-export function RoundedBox(ctx: {
- beginPath: () => void;
- moveTo: (arg0: number, arg1: number) => void;
- lineTo: (arg0: number, arg1: number) => void;
- quadraticCurveTo: (arg0: number, arg1: number, arg2: number, arg3: number) => void;
- closePath: () => void;
-}, x: number, y: number, width: number, height: number, radius: number, roundCorners: {
- top?: boolean,
- bottom?: boolean
-} = {top: true, bottom: true}) {
+export function RoundedBox(
+ ctx: SKRSContext2D,
+ x: number,
+ y: number,
+ width: number,
+ height: number,
+ radius: number,
+ strokeColor?: string, roundCorners: {
+ top?: boolean,
+ bottom?: boolean
+ } = {top: true, bottom: true}) {
ctx.beginPath();
ctx.moveTo(x + (roundCorners.top ? radius : 0), y);
ctx.lineTo(x + width - (roundCorners.top ? radius : 0), y);
@@ -511,30 +768,56 @@ export function RoundedBox(ctx: {
ctx.quadraticCurveTo(x, y, x + radius, y);
}
ctx.closePath();
+
+ if (strokeColor) {
+ ctx.strokeStyle = strokeColor;
+ ctx.stroke();
+ ctx.clip();
+ }
}
-function shortener(count: number | undefined): string {
- const COUNT_ABBREVIATIONS = [
- "", // 0
- "k", // Thousands
- "M", // Millions
- "B", // Billions
- "T", // Trillions
- "Qa", // Quadrillions
- "Qi", // Quintillions
- "Sx", // Sextillions
- "Sp", // Septillions
- ];
+function shortener(count: number | undefined, roundedNumber?: boolean): string {
+ let abbreviation = "", i = 0;
+ const base = 1000;
if (!count || count === 0) return "0";
if (count === Infinity) return "∞";
- const i = Math.floor(Math.log(count) / Math.log(1000));
- if (i >= COUNT_ABBREVIATIONS.length) {
- return count.toFixed(0) + COUNT_ABBREVIATIONS[COUNT_ABBREVIATIONS.length - 1];
- } else {
- return (count / Math.pow(1000, i)).toFixed(i === 0 ? 0 : 2) + COUNT_ABBREVIATIONS[i];
+ while (count >= base && i < 8) {
+ count /= base;
+ i++;
}
+
+ switch (i) {
+ case 1:
+ abbreviation = "K"; // Thousands
+ break;
+ case 2:
+ abbreviation = "M"; // Millions
+ break;
+ case 3:
+ abbreviation = "B"; // Billions
+ break;
+ case 4:
+ abbreviation = "T"; // Trillions
+ break;
+ case 5:
+ abbreviation = "Qa"; // Quadrillions
+ break;
+ case 6:
+ abbreviation = "Qi"; // Quintillions
+ break;
+ case 7:
+ abbreviation = "Sx"; // Sextillions
+ break;
+ case 8:
+ abbreviation = "Sp"; // Septillions
+ break;
+ default:
+ break;
+ }
+
+ return `${count.toFixed(i === 0 ? 0 : (roundedNumber ? 0 : 2))}${abbreviation}`;
}
diff --git a/src/charts.ts b/src/charts.ts
index 4b1e938..a66162e 100644
--- a/src/charts.ts
+++ b/src/charts.ts
@@ -2,7 +2,7 @@ import {createCanvas, GlobalFonts} from "@napi-rs/canvas";
import {join} from "path";
import {leaderboard} from "./leaderboard";
import {RoundedBox} from "./cards";
-import {XpFatal} from "./functions/xplogs";
+import {XpFatal, XpLog} from "./functions/xplogs";
/**
* Chart options
@@ -23,7 +23,7 @@ export interface ChartOptions {
* @async
* @param {string} guildId
* @param {ChartOptions?} options
- * @link `Documentation:` https://simplyxp.js.org/docs/charts
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/functions/charts
* @returns {Promise<{attachment: Buffer, description: string, name: string}>} Chart attachment
* @throws {XpFatal} If invalid parameters are provided, or if there are not enough users to create a chart
*/
@@ -32,8 +32,14 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
}> {
if (!guildId) throw new XpFatal({function: "charts()", message: "No Guild ID Provided"});
if (!options) throw new XpFatal({function: "charts()", message: "No Options Provided"});
- if (!options.theme) options.theme = "blue";
- if (!options.type) options.type = "bar";
+ if (!options.theme || !["blue", "dark", "discord", "green", "orange", "red", "space", "yellow"].includes(options.theme)) {
+ XpLog.warn("charts()", "Invalid theme provided, defaulting to discord");
+ options.theme = "discord";
+ }
+ if (!options.type || !["bar", "doughnut", "pie"].includes(options.type)) {
+ XpLog.warn("charts()", "Invalid type provided, defaulting to bar chart");
+ options.type = "bar";
+ }
let colors = {
background: "#FFFFFF",
barColor: "#FFFFFF",
@@ -48,14 +54,14 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
if (users.length < 2) throw new XpFatal({function: "charts()", message: "Not enough users to create a chart"});
users.sort((a, b) => b.position - a.position);
- GlobalFonts.registerFromPath(options?.font || join(__dirname, "Fonts", "BalooBhaijaan-Regular.otf"), "Sans Serif");
+ GlobalFonts.registerFromPath(options?.font || join(__dirname, "Fonts", "BalooBhaijaan-Regular.woff2"), "Sans Serif");
switch (options.theme) {
case "blue":
colors = {
background: "#1e1e3c",
barColor: "#747fff",
- pieColors: ["#747fff", "#55b9f3", "#4dc7ec", "#3ad5e5", "#32e3dd", "#2cf2d4", "#26ffd2", "#30edb4", "#3cda96", "#48c878"],
+ pieColors: ["#747fff", "#2832C2", "#59788E", "#00d2e7", "#281E5D", "#a9f5ff", "#000e3f", "#30edc2", "#186c84", "#0098ff"],
textColor: "#FFFFFF"
};
break;
@@ -64,7 +70,7 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
colors = {
background: "#1e1e1e",
barColor: "#747474",
- pieColors: ["#747474", "#8f8f8f", "#a8a8a8", "#c1c1c1", "#dadada", "#f4f4f4", "#ffffff", "#ffffff", "#ffffff", "#ffffff"],
+ pieColors: ["#1B1D1F", "#454C53", "#72787F", "#999999", "#9EA4AA", "#CCCCCC", "#C9CDD2", "#DEDEDE", "#E8EBED", "#FFFFFF"],
textColor: "#FFFFFF"
};
break;
@@ -73,7 +79,7 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
colors = {
background: "#36393f",
barColor: "#5865F2",
- pieColors: ["#5865F2", "#57F287", "#FEE75C", "#ED4245", "#F47FFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF"],
+ pieColors: ["#5865F2", "#57F287", "#FEE75C", "#EB459E", "#ED4245", "#FFFFFF", "#000000", "#FAA61A", "#C04DF9", "#00AAFF"],
textColor: "#FFFFFF"
};
break;
@@ -82,7 +88,7 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
colors = {
background: "#1e321e",
barColor: "#74ff7f",
- pieColors: ["#74ff7f", "#55f3a0", "#4decb2", "#3dd5c3", "#32cdd5", "#2cc6e6", "#26bfee", "#30a8e6", "#3c91dd", "#487ad4"],
+ pieColors: ["#00FF00", "#008000", "#7FFF00", "#32CD32", "#228B22", "#006400", "#9ACD32", "#00FA9A", "#ADFF2F", "#7CFC00"],
textColor: "#FFFFFF"
};
break;
@@ -91,7 +97,7 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
colors = {
background: "#321e1e",
barColor: "#ff9f74",
- pieColors: ["#ff9f74", "#f3b055", "#ecbe4d", "#d5c63d", "#cdd532", "#c6e62c", "#bfe626", "#a8e630", "#91dd3c", "#7ad448"],
+ pieColors: ["#FF8C00", "#FF5E0E", "#FF4500", "#FF6347", "#E26310", "#F5761A", "#FD673A", "#FFA500", "#FF7F50", "#FFD700"],
textColor: "#FFFFFF"
};
break;
@@ -100,7 +106,7 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
colors = {
background: "#321e1e",
barColor: "#ff7474",
- pieColors: ["#ff7474", "#f35555", "#ec4d4d", "#d53d3d", "#cd3232", "#c62c2c", "#bf2626", "#a83030", "#913c3c", "#7a4848"],
+ pieColors: ["#FF0000", "#FF2400", "#FF4500", "#FF6347", "#FF7F50", "#FF8C00", "#FFA07A", "#FFA500", "#FFC0CB", "#FFD700"],
textColor: "#FFFFFF"
};
break;
@@ -118,7 +124,7 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
colors = {
background: "#32321e",
barColor: "#ffff74",
- pieColors: ["#ffff74", "#f3f355", "#ecf24d", "#d5eb3d", "#cde532", "#c6e02c", "#bfe626", "#a8df30", "#91d93c", "#7ad448"],
+ pieColors: ["#FFFD37", "#FFEF00", "#FDFF00", "#DAA520", "#F4C430", "#E4D00A", "#D2B55B", "#FFFFE0", "#FFFACD", "#F5DEB3"],
textColor: "#FFFFFF"
};
break;
@@ -191,13 +197,15 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
}
}
+ let chartAreaWidth = canvas.width - 20 * 2;
+ let chartAreaHeight = canvas.height - 20 * 2;
switch (options.type) {
case "bar": {
const maxValueLabelWidth = context.measureText(maxLevel.toString()).width;
- const chartAreaWidth = canvas.width - maxValueLabelWidth - 20 * 3 - 20 * 2;
- const chartAreaHeight = canvas.height - 100 - 20 * 2;
+ chartAreaWidth = canvas.width - maxValueLabelWidth - 20 * 3 - 20 * 2;
+ chartAreaHeight = canvas.height - 100 - 20 * 2;
const barWidth = chartAreaWidth / users.length - 20;
@@ -259,17 +267,12 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
break;
case "doughnut": {
- const chartAreaWidth = canvas.width - 20 * 2;
- const chartAreaHeight = canvas.height - 20 * 2;
const totalLevelSum = users.reduce((sum, user) => sum + user.level, 0);
-
- const centerX = canvas.width / 2;
- const centerY = canvas.height / 2;
-
const outerRadius = Math.min(chartAreaWidth, chartAreaHeight) / 3; // Adjust the divisor for a smaller outer radius
const innerRadius = outerRadius * 0.6; // Adjust the multiplier for the size of the hole
let startAngle = -Math.PI / 2;
+ const centerX = canvas.width / 2, centerY = canvas.height / 2;
await Promise.all(
users.map(async (user, index) => {
@@ -288,44 +291,15 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
startAngle = endAngle;
})
);
-
- // Render legend
- const legendX = 20; // Legend position from left
- const legendY = canvas.height - 20 - users.length * 20; // Legend position from bottom
- const legendSpacing = 20; // Vertical spacing between legend items
-
- context.fillStyle = "rgba(0,0,0,0.25)";
- context.fillRect(legendX - 5, legendY - 5, 200, users.length * legendSpacing + 5);
-
- context.font = "12px Sans Serif";
- users.forEach((user, index) => {
- const legendText = user?.name || user.user;
- const legendColor = colors.pieColors[index % colors.pieColors.length];
- const legendColorBoxX = legendX;
- const legendItemY = legendY + index * legendSpacing;
-
- // Place colored squares to the right and usernames to the left
- context.fillStyle = legendColor || "#FFFFFF";
- context.fillRect(legendColorBoxX, legendItemY, 15, 15);
-
- context.fillStyle = colors.textColor;
- context.fillText(legendText, legendColorBoxX + 20, legendItemY + 11.5);
- });
-
}
break;
case "pie": {
- const chartAreaWidth = canvas.width - 20 * 2;
- const chartAreaHeight = canvas.height - 20 * 2;
const totalLevelSum = users.reduce((sum, user) => sum + user.level, 0);
-
- const centerX = canvas.width / 2;
- const centerY = canvas.height / 2;
-
const radius = Math.min(chartAreaWidth, chartAreaHeight) / 3; // Adjust the divisor for a smaller radius
let startAngle = -Math.PI / 2;
+ const centerX = canvas.width / 2, centerY = canvas.height / 2;
await Promise.all(
users.map(async (user, index) => {
@@ -342,8 +316,14 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
startAngle = endAngle;
})
);
+ }
+ break;
- // Render legend
+ default:
+ throw new XpFatal({function: "charts()", message: "Invalid chart type provided"});
+ }
+
+ if (["doughnut", "pie"].includes(options.type)) { // Render legend
const legendX = 20; // Legend position from left
const legendY = canvas.height - 20 - users.length * 20; // Legend position from bottom
const legendSpacing = 20; // Vertical spacing between legend items
@@ -352,7 +332,7 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
context.fillRect(legendX - 5, legendY - 5, 200, users.length * legendSpacing + 5);
context.font = "12px Sans Serif";
- users.forEach((user, index) => {
+ await Promise.all(users.map(async (user, index) => {
const legendText = user?.name || user.user;
const legendColor = colors.pieColors[index % colors.pieColors.length];
const legendColorBoxX = legendX;
@@ -364,13 +344,8 @@ export async function charts(guildId: string, options: ChartOptions = {}): Promi
context.fillStyle = colors.textColor;
context.fillText(legendText, legendColorBoxX + 20, legendItemY + 11.5);
- });
+ }));
- }
- break;
-
- default:
- throw new XpFatal({function: "charts()", message: "Invalid chart type provided"});
}
return {
diff --git a/src/connect.ts b/src/connect.ts
index 8d5259e..2f03d20 100644
--- a/src/connect.ts
+++ b/src/connect.ts
@@ -16,7 +16,7 @@ export type ConnectionOptions = {
* @async
* @param {string} uri
* @param {ConnectionOptions} options
- * @link `Documentation:` https://simplyxp.js.org/docs/connect
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/functions/connect
* @returns {Promise}
* @throws {XpFatal} If an invalid type is provided or if the value is not provided.
*/
@@ -35,8 +35,8 @@ export async function connect(uri: string, options: ConnectionOptions = {type: u
switch (type) {
case "mongodb": {
- const {MongoClient} = await import("mongodb"), goodVersion = await checkPackageVersion("mongodb");
- if (!goodVersion) return XpLog.err("connect()", "MongoDB V4 or higher is required");
+ const {MongoClient} = await import("mongodb"), goodVersion = await checkPackageVersion("mongodb", 4, 6);
+ if (!goodVersion) return XpLog.err("connect()", "MongoDB Version 4 to 6 is required");
const client = await MongoClient.connect(uri).catch((error) => {
throw new XpFatal({function: "connect()", message: error.message});
@@ -49,10 +49,10 @@ export async function connect(uri: string, options: ConnectionOptions = {type: u
case "sqlite":
try {
const [betterSqlite3, goodVersion] = await Promise.all([
- import("better-sqlite3"), checkPackageVersion("sqlite")
+ import("better-sqlite3"), checkPackageVersion("better-sqlite3", 7, 9)
]);
- if (!goodVersion) return XpLog.err("connect()", "better-sqlite3 V7 or higher is required");
+ if (!goodVersion) return XpLog.err("connect()", "better-sqlite3 Version 7 to 9 is required");
xp.database = new betterSqlite3.default(uri);
xp.dbType = "sqlite";
@@ -61,7 +61,7 @@ export async function connect(uri: string, options: ConnectionOptions = {type: u
(
user TEXT NOT NULL,
guild TEXT NOT NULL,
- name TEXT DEFAULT "Unknown",
+ name TEXT DEFAULT user,
level INTEGER DEFAULT 0,
xp INTEGER DEFAULT 0
)`
@@ -103,7 +103,7 @@ async function getPackageManager(): Promise<"yarn" | "npm" | "pnpm"> {
if (foundLockfiles.length === 1) {
if (foundLockfiles[0] === "yarn.lock") {
- XpLog.debug("getPackageManager()", "Using Yarn");
+ XpLog.debug("getPackageManager()", "Using YARN");
return "yarn";
} else if (foundLockfiles[0] === "pnpm-lock.yaml" || foundLockfiles[0] === "pnpm-lock.json") {
XpLog.debug("getPackageManager()", "Using PNPM");
@@ -117,32 +117,20 @@ async function getPackageManager(): Promise<"yarn" | "npm" | "pnpm"> {
/**
* Check database package versions
* @private
- * @param {"mongodb" | "sqlite"} type
+ * @param {string} type - NPM Package Name (lowercase)
+ * @param {number} min - Minimum Major Version Number
+ * @param {number} max - Maximum Major Version Number (Optional)
* @returns {Promise}
* @throws {XpFatal} If the package version is not supported
*/
-export async function checkPackageVersion(type: "mongodb" | "sqlite"): Promise {
- switch (type) {
- case "mongodb":
- try {
- const mongoPackage = await import("mongodb/package.json");
- return parseInt(mongoPackage.version.substring(0, 1)) >= 4;
- } catch (_) {
- XpLog.info("checkPackageVersion()", "Installing MongoDB [5.x] | Please wait...");
- execSync(`${await getPackageManager()} add mongodb@5.x.x`);
- XpLog.warn("checkPackageVersion()", "Installed MongoDB. Please restart!");
- return process.exit(1);
- }
-
- case "sqlite":
- try {
- const sqlitePackage = await import("better-sqlite3/package.json");
- return parseInt(sqlitePackage.version.substring(0, 1)) >= 7;
- } catch (_) {
- XpLog.info("checkPackageVersion()", "Installing better-sqlite3 [V8] | Please wait...");
- execSync(`${await getPackageManager()} add better-sqlite3@8.x.x`);
- XpLog.warn("checkPackageVersion()", "Installed better-sqlite3. Please restart!");
- return process.exit(1);
- }
+export async function checkPackageVersion(type: string, min: number, max?: number): Promise {
+ try {
+ const chosenPackage = await import(`${type}/package.json`);
+ return parseInt(chosenPackage.version.substring(0, 1)) >= min && (max ? parseInt(chosenPackage.version.substring(0, 1)) <= max : true);
+ } catch (_) {
+ XpLog.info("checkPackageVersion()", `Installing ${type} [V${max || min}] | Please wait...`);
+ execSync(`${await getPackageManager()} add ${type}@${max || min}.x.x`);
+ XpLog.warn("checkPackageVersion()", `Installed ${type}. Please restart!`);
+ return process.exit(1);
}
}
\ No newline at end of file
diff --git a/src/create.ts b/src/create.ts
index 7811ac7..d1b8df2 100644
--- a/src/create.ts
+++ b/src/create.ts
@@ -7,7 +7,7 @@ import {UserResult} from "./functions/database";
* @param {string} userId
* @param {string} guildId
* @param {string} username
- * @link `Documentation:` https://simplyxp.js.org/docs/create
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/functions/create
* @returns {Promise}
* @throws {XpFatal} If invalid parameters are provided
*/
diff --git a/src/deprecated/rank.ts b/src/deprecated/rank.ts
index 5a00e54..abc8526 100644
--- a/src/deprecated/rank.ts
+++ b/src/deprecated/rank.ts
@@ -10,7 +10,7 @@ import {rankCard, RankCardOptions} from "../cards";
* @param {string} userId
* @param {string} _guildId
* @param {RankCardOptions?} options
- * @link `Documentation:` https://simplyxp.js.org/docs/deprecated/rank
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/deprecated/rank
* @returns {Promise<{attachment: Buffer, description: string, name: string}>}
* @throws {XpFatal} - If parameters are not provided correctly
*/
diff --git a/src/fetch.ts b/src/fetch.ts
index cc5c02c..6a10a9d 100644
--- a/src/fetch.ts
+++ b/src/fetch.ts
@@ -8,12 +8,12 @@ import {UserResult} from "./functions/database";
* @async
* @param {string} userId
* @param {string} guildId
- * @param {string} username - Username to use if auto_create is enabled
- * @link `Documentation:` https://simplyxp.js.org/docs/fetch
+ * @param {string?} username - Username to use if auto_create is enabled
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/functions/fetch
* @returns {Promise<{name: string | null, user: string, guild: string, level: number, position: number, xp: number}>}
* @throws {XpFatal} If invalid parameters are provided, or if the user data is not found.
*/
-export async function fetch(userId: string, guildId: string, username: string): Promise {
+export async function fetch(userId: string, guildId: string, username?: string): Promise {
if (!userId) throw new XpFatal({function: "create()", message: "User ID was not provided"});
if (!guildId) throw new XpFatal({function: "create()", message: "Guild ID was not provided"});
diff --git a/src/functions/database.ts b/src/functions/database.ts
index 43d031a..72dab38 100644
--- a/src/functions/database.ts
+++ b/src/functions/database.ts
@@ -78,7 +78,7 @@ export class db {
/**
* Gets a collection from the database.
* @param {collection} collection - The collection to get.
- * @link https://simplyxp.js.org/docs/handlers/database#getCollection Documentation
+ * @link https://simplyxp.js.org/docs/next/handlers/database#getCollection Documentation
* @returns {Collection} The collection.
* @throws {XpFatal} Throws an error if there is no database connection, or database type is invalid.
*/
@@ -93,7 +93,7 @@ export class db {
*
* @async
* @param {UserOptions | LevelRoleOptions} query - The document to create.
- * @link https://simplyxp.js.org/docs/handlers/database#createOne Documentation
+ * @link https://simplyxp.js.org/docs/next/handlers/database#createOne Documentation
* @returns {Promise} The created document.
* @throws {XpFatal} Throws an error if there is no database connection.
*/
@@ -115,12 +115,35 @@ export class db {
return result as UserResult | LevelRoleResult;
}
+ /**
+ * Deletes multiple documents from the database.
+ * @async
+ * @param {UserOptions | LevelRoleOptions} query - The documents to delete.
+ * @link https://simplyxp.js.org/docs/next/handlers/database#deleteMany Documentation
+ * @returns {Promise} `true` if the documents were successfully deleted, otherwise `false`.
+ * @throws {XpFatal} Throws an error if there is no database connection.
+ */
+ static async deleteMany(query: UserOptions | LevelRoleOptions): Promise {
+ if (!xp.database) throw new XpFatal({function: "deleteMany()", message: "No database connection"});
+ let result: Document;
+
+ switch (xp.dbType) {
+ case "mongodb":
+ result = (xp.database as MongoClient).db().collection(query.collection).deleteMany(query.data).catch(error => handleError(error, "deleteMany()")) as Document;
+ break;
+ case "sqlite":
+ if (query.collection === "simply-xps") result = (xp.database as Database).prepare("DELETE FROM \"simply-xps\" WHERE guild = ?").run(query.data.guild);
+ else result = (xp.database as Database).prepare("DELETE FROM \"simply-xp-levelroles\" WHERE guild = ?").run(query.data.guild);
+ }
+ return !!result;
+ }
+
/**
* Deletes one document from the database.
*
* @async
* @param {UserOptions | LevelRoleOptions} query - The document to delete.
- * @link https://simplyxp.js.org/docs/handlers/database#deleteOne Documentation
+ * @link https://simplyxp.js.org/docs/next/handlers/database#deleteOne Documentation
* @returns {Promise} `true` if the document was successfully deleted, otherwise `false`.
* @throws {XpFatal} Throws an error if there is no database connection.
*/
@@ -144,7 +167,7 @@ export class db {
*
* @async
* @param {UserOptions | LevelRoleOptions} query - The query to search for the document.
- * @link https://simplyxp.js.org/docs/handlers/database#findOne Documentation
+ * @link https://simplyxp.js.org/docs/next/handlers/database#findOne Documentation
* @returns {Promise} The found document.
* @throws {XpFatal} Throws an error if there is no database connection.
*/
@@ -170,7 +193,7 @@ export class db {
*
* @async
* @param {UserOptions | LevelRoleOptions} query - The query to search for multiple documents.
- * @link https://simplyxp.js.org/docs/handlers/database#find Documentation
+ * @link https://simplyxp.js.org/docs/next/handlers/database#find Documentation
* @returns {Promise} An array of found documents.
* @throws {XpFatal} Throws an error if there is no database connection.
*/
@@ -198,7 +221,7 @@ export class db {
* @param {UserOptions | LevelRoleOptions} filter - The document to update.
* @param {UserOptions | LevelRoleOptions} update - The document update data.
* @param {object} [options] - MongoDB options for updating the document.
- * @link https://simplyxp.js.org/docs/handlers/database#updateOne Documentation
+ * @link https://simplyxp.js.org/docs/next/handlers/database#updateOne Documentation
* @returns {Promise} The updated document.
* @throws {XpFatal} Throws an error if there is no database connection.
*/
@@ -211,7 +234,7 @@ export class db {
break;
case "sqlite":
- if (filter.collection === "simply-xps" && update.collection === "simply-xps") (xp.database as Database).prepare("UPDATE \"simply-xps\" SET name = ?, xp = ?, level = ? WHERE guild = ? AND user = ?").run(update.data?.name, update.data.xp, update.data.level, filter.data.guild, filter.data.user);
+ if (filter.collection === "simply-xps" && update.collection === "simply-xps") (xp.database as Database).prepare("UPDATE \"simply-xps\" SET xp = ?, level = ?"+ (update.data?.name ? ", name = ?" : "") +" WHERE guild = ? AND user = ?").run(update.data?.name ? [update.data.xp, update.data.level, update.data.name, filter.data.guild, filter.data.user] : [update.data.xp, update.data.level, filter.data.guild, filter.data.user]);
else if (filter.collection === "simply-xp-levelroles" && update.collection === "simply-xp-levelroles") (xp.database as Database).prepare("UPDATE \"simply-xp-levelroles\" SET role = ? WHERE guild = ? AND level = ?").run(update.data.roles, filter.data.guild, filter.data.level);
else throw new XpFatal({
function: "updateOne()", message: "Collection mismatch, expected same collection on both filter and update."
diff --git a/src/functions/utilities.ts b/src/functions/utilities.ts
index edc8dab..2c0331e 100644
--- a/src/functions/utilities.ts
+++ b/src/functions/utilities.ts
@@ -27,7 +27,7 @@ interface NewClientOptions {
*
* @param {number} value.
* @param {"xp" | "level"} type - Type to convert from (Default: level).
- * @link `Documentation:` https://simplyxp.js.org/docs/utilities/convert
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/utilities/convert
* @returns {number} - The converted value. (XP to level or level to XP)
* @throws {XpFatal} If an invalid type is provided or if the value is not provided.
*/
@@ -47,7 +47,7 @@ export function convertFrom(value: number, type: "xp" | "level" = "level"): numb
/**
* Updates the options of the XP client.
* @param {NewClientOptions} clientOptions - The new options to update.
- * @link `Documentation:` https://simplyxp.js.org/docs/utilities/updateOptions
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/utilities/updateOptions
* @returns {void} - Nothing.
* @throws {XpFatal} If an invalid option is provided.
*/
@@ -71,32 +71,32 @@ export function updateOptions(clientOptions: NewClientOptions): void {
if (database) {
xp.database = database;
+ const dbInfo = xp.dbType === "mongodb" ?
+ {name: "MongoDB", type: "mongodb", min: 4, max: 6} :
+ {name: "Better-SQLite3", type: "better-sqlite3", min: 7, max: 9};
- if (xp.dbType === "mongodb") {
- checkPackageVersion("mongodb").then((result) => {
- if (!result) throw new XpFatal({
- function: "updateOptions()", message: "MongoDB V4 or higher is required"
- });
+ checkPackageVersion(dbInfo.type, dbInfo.min, dbInfo.max).then((result) => {
+ if (!result) throw new XpFatal({
+ function: "updateOptions()", message: `${dbInfo.name} V${dbInfo.min} up to V${dbInfo.max} is required.`
+ });
+ switch (xp.dbType) {
+ case "mongodb":
(xp.database as MongoClient).db().command({ping: 1}).catch(() => {
xp.database = undefined;
throw new XpFatal({function: "updateOptions()", message: "Invalid MongoDB connection"});
});
- });
-
- } else if (xp.dbType === "sqlite") {
- checkPackageVersion("sqlite").then((result) => {
- if (!result) throw new XpFatal({
- function: "updateOptions()", message: "SQLite V7 or higher is required"
- });
+ break;
+ case "sqlite":
try {
(xp.database as Database).prepare("SELECT 1").get();
} catch (error) {
throw new XpFatal({function: "updateOptions()", message: "Invalid SQLite connection"});
}
- });
- }
+ break;
+ }
+ });
}
}
}
\ No newline at end of file
diff --git a/src/leaderboard.ts b/src/leaderboard.ts
index 6ee17bf..0dcb2f3 100644
--- a/src/leaderboard.ts
+++ b/src/leaderboard.ts
@@ -10,7 +10,7 @@ import {db} from "./functions/database";
* @property {number} level - User level
* @property {number} xp - User XP
*/
-export interface User {
+export interface User {
guild: string;
user: string;
name?: string | null;
@@ -24,17 +24,25 @@ export interface User {
* @async
* @param {string} guildId - Guild ID
* @param {number} limit - Limit of users to return
- * @link `Documentation:` https://simplyxp.js.org/docs/leaderboard
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/functions/leaderboard
* @returns {Promise} Array of all users in the leaderboard
* @throws {XpFatal} If guild ID is not provided or limit is less than 1
*/
export async function leaderboard(guildId: string, limit?: number): Promise {
if (!guildId) throw new XpFatal({function: "leaderboard()", message: "Guild ID was not provided"});
- if (limit && limit < 1) throw new XpFatal({function: "leaderboard()", message: "Limit must be greater than 0"});
+ if (limit && !(limit >= 1)) throw new XpFatal({
+ function: "leaderboard()", message: "Limit must be a number greater than 0"
+ });
- const users = (await db.find({collection: "simply-xps", data: {guild: guildId}}) as Array).sort((a, b) => b.xp - a.xp);
- users.forEach((user, index) => user.position = index + 1);
+ const users = (await db.find({
+ collection: "simply-xps", data: {guild: guildId}
+ }) as Array).sort((a, b) => b.xp - a.xp);
- if (limit) return users.slice(0, limit);
- return users;
+
+ await Promise.all(users.map(async (user, index) => {
+ user.position = index + 1;
+ return user;
+ }));
+
+ return users.slice(0, limit);
}
\ No newline at end of file
diff --git a/src/migrate.ts b/src/migrate.ts
index ce1269a..2739b1e 100644
--- a/src/migrate.ts
+++ b/src/migrate.ts
@@ -15,7 +15,7 @@ export class migrate {
* Effortlessly migrate from discord-xp to simply-xp.
* @async
* @param {boolean} deleteOld - Delete old data after migration
- * @link `Documentation:` https://simplyxp.js.org/docs/migrate/discord_xp
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/classes/migrate#migratediscord_xp
* @returns {Promise} - Returns true if migration is successful
* @throws {XpLog.err} - If migration fails.
*/
@@ -49,7 +49,7 @@ export class migrate {
* @async
* @param {"mongodb"|"sqlite"} dbType
* @param {Database | MongoClient} connection
- * @link `Documentation:` https://simplyxp.js.org/docs/migrate/database
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/classes/migrate#migratefromdb
* @returns {Promise} - Returns true if migration is successful
* @throws {XpFatal} - If parameters are not provided correctly
*/
@@ -65,10 +65,9 @@ export class migrate {
switch (dbType) {
case "mongodb":
try {
- if (!await checkPackageVersion("mongodb")) return XpLog.err("migrate.fromDB()", "MongoDB V4 or higher is required");
+ if (!await checkPackageVersion("mongodb", 4, 6)) return XpLog.err("migrate.fromDB()", "MongoDB V4 up to V6 is required");
- results = (connection as MongoClient).db().collection("simply-xps").find().toArray() as Document as UserResult[];
- XpLog.debug("migrate.fromDB()", `FOUND ${results.length} DOCUMENTS`);
+ results = await (connection as MongoClient).db().collection("simply-xps").find().toArray() as Document as UserResult[];
} catch (error) {
XpLog.err("migrate.fromDB()", error as string);
@@ -78,10 +77,8 @@ export class migrate {
case "sqlite":
try {
- if (!await checkPackageVersion("sqlite")) return XpLog.err("migrate.fromDB()", "better-sqlite3 V7 or higher is required");
-
+ if (!await checkPackageVersion("better-sqlite3", 7, 8)) return XpLog.err("migrate.fromDB()", "better-sqlite3 V7 up to V8 is required");
results = (connection as Database).prepare("SELECT * FROM `simply-xps`").all() as UserResult[];
- XpLog.debug("migrate.fromDB()", `FOUND ${results.length} ROWS`);
} catch (error) {
XpLog.err("migrate.fromDB()", error as string);
@@ -90,6 +87,8 @@ export class migrate {
break;
}
+ XpLog.debug("migrate.fromDB()", `FOUND ${results.length} RESULTS`);
+
await Promise.all(results.map(async (user) => {
if (!await db.findOne({collection: "simply-xps", data: {guild: user.guild, user: user.user}})) {
return db.createOne({
diff --git a/src/reset.ts b/src/reset.ts
index 90a8f1d..4549a98 100644
--- a/src/reset.ts
+++ b/src/reset.ts
@@ -9,7 +9,7 @@ import {db} from "./functions/database";
* @param {string} guildId
* @param {boolean?} erase - Erase user entry from the database
* @param {string?} username - Username to use if auto_create is enabled
- * @link `Documentation:` https://simplyxp.js.org/docs/reset
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/functions/reset
* @returns {Promise}
* @throws {XpFatal} If an invalid type is provided or if the value is not provided.
*/
diff --git a/src/roleSetup.ts b/src/roleSetup.ts
index 326e0a9..d8b1bd2 100644
--- a/src/roleSetup.ts
+++ b/src/roleSetup.ts
@@ -8,7 +8,6 @@ import {XpFatal} from "./functions/xplogs";
* @property {string[] | string} roles - The role(s) to add
*/
export interface RoleSetupObject {
- guild?: string;
level: number;
roles: string[] | string;
}
@@ -23,7 +22,7 @@ export class roleSetup {
* @async
* @param {string} guildId - The guild ID
* @param {RoleSetupObject} options - Level/role options
- * @link `Documentation:` https://simplyxp.js.org/docs/roleSetup/add
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/classes/roleSetup#roleSetupadd
* @returns {Promise} - True if successful
* @throws {XpFatal} If an invalid type is provided or value is not provided.
*/
@@ -54,7 +53,7 @@ export class roleSetup {
* @async
* @param {string} guildId - The guild ID
* @param {number} levelNumber - The level number
- * @link `Documentation:` https://simplyxp.js.org/docs/roleSetup/find
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/classes/roleSetup#roleSetupfind
* @returns {Promise} - The level role object
* @throws {XpFatal} If an invalid type is provided or value is not provided.
*/
@@ -75,7 +74,7 @@ export class roleSetup {
* @async
* @param {string} guildId - The guild ID
* @param {number} levelNumber - The level number
- * @link `Documentation:` https://simplyxp.js.org/docs/roleSetup/remove
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/classes/roleSetup#roleSetupremove
* @returns {Promise} - True if successful
* @throws {XpFatal} If an invalid type is provided or value is not provided.
*/
diff --git a/src/set.ts b/src/set.ts
index 7e5cef0..4d9c30d 100644
--- a/src/set.ts
+++ b/src/set.ts
@@ -10,7 +10,7 @@ import {xp} from "../xp";
* @param {string} guildId
* @param {number} level
* @param {string} username - Username to use if auto_create is enabled
- * @link `Documentation:` https://simplyxp.js.org/docs/setlevel
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/functions/setlevel
* @returns {Promise} - Object of user data on success
* @throws {XpFatal} - If parameters are not provided correctly
*/
@@ -59,7 +59,7 @@ interface XPResult extends UserResult {
* @param {string} guildId
* @param {number} xpData
* @param {string} username - Username to use if auto_create is enabled
- * @link `Documentation:` https://simplyxp.js.org/docs/setxp
+ * @link `Documentation:` https://simplyxp.js.org/docs/next/functions/setxp
* @returns {Promise} - Object of user data on success
* @throws {XpFatal} - If parameters are not provided correctly
*/
diff --git a/tsconfig.json b/tsconfig.json
index e7601d4..1648ea7 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -29,7 +29,7 @@
"lib"
],
"include": [
- "./src/fonts/BalooBhaijaan-Regular.otf",
+ "./src/fonts/BalooBhaijaan-Regular.woff2",
"./src/**/*",
"xp.ts"
],
diff --git a/xp.ts b/xp.ts
index 4d75e3e..107b74e 100644
--- a/xp.ts
+++ b/xp.ts
@@ -22,6 +22,8 @@ export {db} from "./src/functions/database";
export {charts} from "./src/charts";
+export {compareCard, leaderboardCard, rankCard} from "./src/cards";
+
export {connect} from "./src/connect";
export {convertFrom, updateOptions} from "./src/functions/utilities";
@@ -34,8 +36,6 @@ export {leaderboard} from "./src/leaderboard";
export {migrate} from "./src/migrate";
-export {rankCard, leaderboardCard} from "./src/cards";
-
export {reset} from "./src/reset";
export {roleSetup} from "./src/roleSetup";