forked from PluralFlux/PluralFlux
feat: Add unit tests (#21)
Tests are mostly complete (though some are failing -- to be fixed in the async/await refactor. Also refactored, fixed bugs found while testing, and added ability to type `pf;m` instead of `pf;member`. --------- Co-authored-by: Aster Fialla <asterfialla@gmail.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
**/.dockerignore
|
**/.dockerignore
|
||||||
**/.env
|
.env.jest
|
||||||
**/.git
|
**/.git
|
||||||
**/.gitignore
|
**/.gitignore
|
||||||
**/.project
|
**/.project
|
||||||
|
|||||||
2
.env.jest
Normal file
2
.env.jest
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
FLUXER_BOT_TOKEN=jest-fluxer-bot-token
|
||||||
|
POSTGRES_PASSWORD=jest-postgres-password
|
||||||
@@ -36,7 +36,7 @@ All commands are prefixed by `pf;`. Currently only a few are implemented.
|
|||||||
1. Pass in a direct remote image URL, for example: `pf;member jane propic <https://cdn.pixabay.com/photo/2020/05/02/02/54/animal-5119676_1280.jpg>`. You can upload images on sites like <https://imgbb.com/>.
|
1. Pass in a direct remote image URL, for example: `pf;member jane propic <https://cdn.pixabay.com/photo/2020/05/02/02/54/animal-5119676_1280.jpg>`. You can upload images on sites like <https://imgbb.com/>.
|
||||||
2. Upload an attachment directly.
|
2. Upload an attachment directly.
|
||||||
**NOTE:** Fluxer does not save your attachments forever, so option #1 is recommended.
|
**NOTE:** Fluxer does not save your attachments forever, so option #1 is recommended.
|
||||||
- `proxy` Updates the proxy tag for a specific member based on their name. The proxy must be formatted with the tags surrounding the word 'text', for example: `pf;member jane proxy Jane:text` or `pf;member amal proxy [text]` This is so the bot can detect what the proxy tags are. **Only one proxy can be set per member currently.**
|
- `proxy` Updates the proxy tag for a specific member based on their name. The proxy must be formatted with the tags surrounding the word 'text', for example: `pf;member jane proxy Jane:text` or `pf;member amal proxy A{text}` This is so the bot can detect what the proxy tags are. **Only one proxy can be set per member currently.**
|
||||||
|
|
||||||
## Upcoming
|
## Upcoming
|
||||||
Check for, and add, feature requests in the [Issues tracker](https://github.com/pieartsy/PluralFlux/issues).
|
Check for, and add, feature requests in the [Issues tracker](https://github.com/pieartsy/PluralFlux/issues).
|
||||||
|
|||||||
302
package-lock.json
generated
302
package-lock.json
generated
@@ -8,9 +8,8 @@
|
|||||||
"name": "pluralflux",
|
"name": "pluralflux",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluxerjs/core": "^1.1.5",
|
"@fluxerjs/core": "^1.2.2",
|
||||||
"dotenv": "^17.3.1",
|
"dotenv": "^17.3.1",
|
||||||
"node-fetch": "^3.3.2",
|
|
||||||
"pg": "^8.18.0",
|
"pg": "^8.18.0",
|
||||||
"pg-hstore": "^2.3.4",
|
"pg-hstore": "^2.3.4",
|
||||||
"pm2": "^6.0.14",
|
"pm2": "^6.0.14",
|
||||||
@@ -21,8 +20,11 @@
|
|||||||
"@babel/core": "^7.29.0",
|
"@babel/core": "^7.29.0",
|
||||||
"@babel/plugin-transform-modules-commonjs": "^7.28.6",
|
"@babel/plugin-transform-modules-commonjs": "^7.28.6",
|
||||||
"@babel/preset-env": "^7.29.0",
|
"@babel/preset-env": "^7.29.0",
|
||||||
|
"@fetch-mock/jest": "^0.2.20",
|
||||||
"babel-jest": "^30.2.0",
|
"babel-jest": "^30.2.0",
|
||||||
"jest": "^30.2.0"
|
"fetch-mock": "^12.6.0",
|
||||||
|
"jest": "^30.2.0",
|
||||||
|
"jest-fetch-mock": "^3.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
@@ -1854,97 +1856,90 @@
|
|||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fluxerjs/builders": {
|
"node_modules/@fetch-mock/jest": {
|
||||||
"version": "1.1.5",
|
"version": "0.2.20",
|
||||||
"resolved": "https://registry.npmjs.org/@fluxerjs/builders/-/builders-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@fetch-mock/jest/-/jest-0.2.20.tgz",
|
||||||
"integrity": "sha512-E7O+uf/DwgrCNkbxgo9/0FzhmP3a2ZH6SBJX9R+yUSsa07u5CdelrIatlp27UyoeBgO5HhXGMdncxa1geyf1tg==",
|
"integrity": "sha512-DGX2bhBInodaWPMV3+UZ530aVM3wDj16sAPjFzkrwb0JwNWIQK07CNbYprQ3Tmd2ixDJeaNx2E0aNb+hRb8FFA==",
|
||||||
"license": "Apache-2.0",
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluxerjs/types": "1.1.5",
|
"fetch-mock": "^12.6.0"
|
||||||
"@fluxerjs/util": "1.1.5"
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.11.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@jest/globals": "*",
|
||||||
|
"jest": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fluxerjs/builders/node_modules/@fluxerjs/types": {
|
"node_modules/@fluxerjs/builders": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@fluxerjs/types/-/types-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@fluxerjs/builders/-/builders-1.2.2.tgz",
|
||||||
"integrity": "sha512-YSRt3E6eHDJLrMK+9eNC3ZRkIZjRzWmXM2Ro+6CSnYuF5c8PSuLUmTM6HGKBA3z8qCSv4whB8ewwr9/x9WxGhA==",
|
"integrity": "sha512-647yuM44Fs+Cf/MKo01XRWoVb7RoFdSOBHebFiTLLCTO/tqz+dgZ4wgVsDNu9O/BB/oZ2tjAp9jDTxvXMXkUbA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@fluxerjs/types": "1.2.2",
|
||||||
|
"@fluxerjs/util": "1.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fluxerjs/collection": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fluxerjs/collection/-/collection-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-ryytiFh38gELqOLHBtL6Tz4f7/7DIu9L2CMTOqNYoer7E1ef8ukU7YiyANASWHgTf34hcGMC0tdi0dcFFFKkKQ==",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/@fluxerjs/core": {
|
"node_modules/@fluxerjs/core": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@fluxerjs/core/-/core-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@fluxerjs/core/-/core-1.2.2.tgz",
|
||||||
"integrity": "sha512-fklZePpz5SUZ0XQ9xU4CtKjtxUSnBrwRINKOXrEK6F8GWZgYotAKxc3Qkd07HRLP37EvOsuEl03GBnD+mq+LAQ==",
|
"integrity": "sha512-P+c1jveYqwWGT6vafw+U1WmDPJ0Brp+aNa52Qp2vSsEQ3FLNUg+/25PhyrcPP0x2Mf7zoa1Wv9zGLlkHlKb9Ag==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluxerjs/builders": "1.1.5",
|
"@fluxerjs/builders": "1.2.2",
|
||||||
"@fluxerjs/collection": "1.1.5",
|
"@fluxerjs/collection": "1.2.2",
|
||||||
"@fluxerjs/rest": "1.1.5",
|
"@fluxerjs/rest": "1.2.2",
|
||||||
"@fluxerjs/types": "1.1.5",
|
"@fluxerjs/types": "1.2.2",
|
||||||
"@fluxerjs/util": "1.1.5",
|
"@fluxerjs/util": "1.2.2",
|
||||||
"@fluxerjs/ws": "1.1.5"
|
"@fluxerjs/ws": "1.2.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fluxerjs/core/node_modules/@fluxerjs/collection": {
|
|
||||||
"version": "1.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@fluxerjs/collection/-/collection-1.1.5.tgz",
|
|
||||||
"integrity": "sha512-ms72WuFzLtnl8wYM5kAJh4vQovE8K8m0yyqjKxPFHUaTyXIJXAKPR+HmUOvWGj/vYKOdy7Pnz/+QuVho4ayCHQ==",
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/@fluxerjs/core/node_modules/@fluxerjs/types": {
|
|
||||||
"version": "1.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@fluxerjs/types/-/types-1.1.5.tgz",
|
|
||||||
"integrity": "sha512-YSRt3E6eHDJLrMK+9eNC3ZRkIZjRzWmXM2Ro+6CSnYuF5c8PSuLUmTM6HGKBA3z8qCSv4whB8ewwr9/x9WxGhA==",
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/@fluxerjs/rest": {
|
"node_modules/@fluxerjs/rest": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@fluxerjs/rest/-/rest-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@fluxerjs/rest/-/rest-1.2.2.tgz",
|
||||||
"integrity": "sha512-LwksYkjSEEYYb8B/L7xPbrHjFrX8naivOY8aQ/ZjkZrO5pdmGYM4QPAcF8sgTZxZJaQdDHoxXr5ivDr459pApg==",
|
"integrity": "sha512-Y96A2wQJ8akiAPpiyQm2WWBfNvrQBGIxIuwFCTuXkWg/g+0D6hYrVB7VHxQlnBeDUlj4T+fSJiqSoAYQ3pwrCQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluxerjs/types": "1.1.5"
|
"@fluxerjs/types": "1.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fluxerjs/rest/node_modules/@fluxerjs/types": {
|
"node_modules/@fluxerjs/types": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@fluxerjs/types/-/types-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@fluxerjs/types/-/types-1.2.2.tgz",
|
||||||
"integrity": "sha512-YSRt3E6eHDJLrMK+9eNC3ZRkIZjRzWmXM2Ro+6CSnYuF5c8PSuLUmTM6HGKBA3z8qCSv4whB8ewwr9/x9WxGhA==",
|
"integrity": "sha512-z3AcTxVF2iY/D0XR8xGIcx+c6LY6eNLWR0uO46xNGmEqpm5guE3joDz/EN8DfAZXuap/ludgqX6EA8dLADIeMg==",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/@fluxerjs/util": {
|
"node_modules/@fluxerjs/util": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@fluxerjs/util/-/util-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@fluxerjs/util/-/util-1.2.2.tgz",
|
||||||
"integrity": "sha512-W3XfAXZ3wHQ7++MxK7sL/s8vfT2x9GFAu6e7rSkDOnAvLx4Uq5E7yXEuLRnvYEOejNd9xP960Ut/RXX6Zlvl9g==",
|
"integrity": "sha512-H0c6rKufJJQsz7cloZSK1EqqbRCaxEIF2LdJ4cIRiGPczQTleD2xAIOQ2NvBZxnFvwx+OeetZs79b42bsjcjHQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluxerjs/types": "1.1.5"
|
"@fluxerjs/types": "1.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fluxerjs/util/node_modules/@fluxerjs/types": {
|
|
||||||
"version": "1.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@fluxerjs/types/-/types-1.1.5.tgz",
|
|
||||||
"integrity": "sha512-YSRt3E6eHDJLrMK+9eNC3ZRkIZjRzWmXM2Ro+6CSnYuF5c8PSuLUmTM6HGKBA3z8qCSv4whB8ewwr9/x9WxGhA==",
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/@fluxerjs/ws": {
|
"node_modules/@fluxerjs/ws": {
|
||||||
"version": "1.1.5",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@fluxerjs/ws/-/ws-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@fluxerjs/ws/-/ws-1.2.2.tgz",
|
||||||
"integrity": "sha512-jzMugI6N/ZopI3MPwgaDQ09qUe4fm0L2LR5YAxekuVMImkx0eKSDpTtl/rEGXvLQjvDbMDrzvNJ17ku8we/Udg==",
|
"integrity": "sha512-twg1wYRYo4DlEHsVHSuUhSAKGK2qTmHM2PV3LNSdE0HdGQIB1EFviSuo0rWDpCb1sVTwIzBVMtthrlxyPfVtKA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluxerjs/types": "1.1.5",
|
"@fluxerjs/types": "1.2.2",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fluxerjs/ws/node_modules/@fluxerjs/types": {
|
|
||||||
"version": "1.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@fluxerjs/types/-/types-1.1.5.tgz",
|
|
||||||
"integrity": "sha512-YSRt3E6eHDJLrMK+9eNC3ZRkIZjRzWmXM2Ro+6CSnYuF5c8PSuLUmTM6HGKBA3z8qCSv4whB8ewwr9/x9WxGhA==",
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/@isaacs/cliui": {
|
"node_modules/@isaacs/cliui": {
|
||||||
"version": "8.0.2",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
@@ -2143,6 +2138,7 @@
|
|||||||
"integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==",
|
"integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/environment": "30.2.0",
|
"@jest/environment": "30.2.0",
|
||||||
"@jest/expect": "30.2.0",
|
"@jest/expect": "30.2.0",
|
||||||
@@ -2813,6 +2809,13 @@
|
|||||||
"@types/ms": "*"
|
"@types/ms": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/glob-to-regexp": {
|
||||||
|
"version": "0.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/glob-to-regexp/-/glob-to-regexp-0.4.4.tgz",
|
||||||
|
"integrity": "sha512-nDKoaKJYbnn1MZxUY0cA1bPmmgZbg0cTq7Rh13d0KWYNOiKbqoR+2d89SnRPszGh7ROzSwZ/GOjZ4jPbmmZ6Eg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/istanbul-lib-coverage": {
|
"node_modules/@types/istanbul-lib-coverage": {
|
||||||
"version": "2.0.6",
|
"version": "2.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
|
||||||
@@ -3855,6 +3858,16 @@
|
|||||||
"integrity": "sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ==",
|
"integrity": "sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/cross-fetch": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "^2.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
@@ -3947,6 +3960,16 @@
|
|||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dequal": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/detect-newline": {
|
"node_modules/detect-newline": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
|
||||||
@@ -4206,27 +4229,20 @@
|
|||||||
"integrity": "sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw==",
|
"integrity": "sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fetch-blob": {
|
"node_modules/fetch-mock": {
|
||||||
"version": "3.2.0",
|
"version": "12.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-12.6.0.tgz",
|
||||||
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
"integrity": "sha512-oAy0OqAvjAvduqCeWveBix7LLuDbARPqZZ8ERYtBcCURA3gy7EALA3XWq0tCNxsSg+RmmJqyaeeZlOCV9abv6w==",
|
||||||
"funding": [
|
"dev": true,
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/jimmywarting"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "paypal",
|
|
||||||
"url": "https://paypal.me/jimmywarting"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-domexception": "^1.0.0",
|
"@types/glob-to-regexp": "^0.4.4",
|
||||||
"web-streams-polyfill": "^3.0.3"
|
"dequal": "^2.0.3",
|
||||||
|
"glob-to-regexp": "^0.4.1",
|
||||||
|
"regexparam": "^3.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.20 || >= 14.13"
|
"node": ">=18.11.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
@@ -4292,18 +4308,6 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/formdata-polyfill": {
|
|
||||||
"version": "4.0.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
|
||||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"fetch-blob": "^3.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12.20.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
@@ -4437,6 +4441,13 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/glob-to-regexp": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
"node_modules/graceful-fs": {
|
"node_modules/graceful-fs": {
|
||||||
"version": "4.2.11",
|
"version": "4.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
@@ -4781,6 +4792,7 @@
|
|||||||
"integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==",
|
"integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/core": "30.2.0",
|
"@jest/core": "30.2.0",
|
||||||
"@jest/types": "30.2.0",
|
"@jest/types": "30.2.0",
|
||||||
@@ -5012,6 +5024,17 @@
|
|||||||
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
|
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jest-fetch-mock": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cross-fetch": "^3.0.4",
|
||||||
|
"promise-polyfill": "^8.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jest-haste-map": {
|
"node_modules/jest-haste-map": {
|
||||||
"version": "30.2.0",
|
"version": "30.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz",
|
||||||
@@ -5699,51 +5722,25 @@
|
|||||||
"node": ">= 0.4.0"
|
"node": ">= 0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-domexception": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
|
||||||
"deprecated": "Use your platform's native DOMException instead",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/jimmywarting"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://paypal.me/jimmywarting"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "3.3.2",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"data-uri-to-buffer": "^4.0.0",
|
"whatwg-url": "^5.0.0"
|
||||||
"fetch-blob": "^3.1.4",
|
|
||||||
"formdata-polyfill": "^4.0.10"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
"node": "4.x || >=6.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"peerDependencies": {
|
||||||
"type": "opencollective",
|
"encoding": "^0.1.0"
|
||||||
"url": "https://opencollective.com/node-fetch"
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"node_modules/node-fetch/node_modules/data-uri-to-buffer": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
|
||||||
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 12"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-int64": {
|
"node_modules/node-int64": {
|
||||||
@@ -6393,6 +6390,13 @@
|
|||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/promise-polyfill": {
|
||||||
|
"version": "8.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz",
|
||||||
|
"integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/promptly": {
|
"node_modules/promptly": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/promptly/-/promptly-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/promptly/-/promptly-2.2.0.tgz",
|
||||||
@@ -6504,6 +6508,16 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/regexparam": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/regexparam/-/regexparam-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/regexpu-core": {
|
"node_modules/regexpu-core": {
|
||||||
"version": "6.4.0",
|
"version": "6.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz",
|
||||||
@@ -7233,6 +7247,13 @@
|
|||||||
"integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==",
|
"integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
@@ -7470,13 +7491,22 @@
|
|||||||
"makeerror": "1.0.12"
|
"makeerror": "1.0.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/web-streams-polyfill": {
|
"node_modules/webidl-conversions": {
|
||||||
"version": "3.3.3",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"dependencies": {
|
||||||
"node": ">= 8"
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluxerjs/core": "^1.1.5",
|
"@fluxerjs/core": "^1.2.2",
|
||||||
"dotenv": "^17.3.1",
|
"dotenv": "^17.3.1",
|
||||||
"pg": "^8.18.0",
|
"pg": "^8.18.0",
|
||||||
"pg-hstore": "^2.3.4",
|
"pg-hstore": "^2.3.4",
|
||||||
@@ -21,8 +21,11 @@
|
|||||||
"@babel/core": "^7.29.0",
|
"@babel/core": "^7.29.0",
|
||||||
"@babel/plugin-transform-modules-commonjs": "^7.28.6",
|
"@babel/plugin-transform-modules-commonjs": "^7.28.6",
|
||||||
"@babel/preset-env": "^7.29.0",
|
"@babel/preset-env": "^7.29.0",
|
||||||
|
"@fetch-mock/jest": "^0.2.20",
|
||||||
"babel-jest": "^30.2.0",
|
"babel-jest": "^30.2.0",
|
||||||
"jest": "^30.2.0"
|
"fetch-mock": "^12.6.0",
|
||||||
|
"jest": "^30.2.0",
|
||||||
|
"jest-fetch-mock": "^3.0.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest"
|
"test": "jest"
|
||||||
|
|||||||
57
src/bot.js
57
src/bot.js
@@ -1,46 +1,63 @@
|
|||||||
import { Client, Events } from '@fluxerjs/core';
|
import { Client, Events, Message } from '@fluxerjs/core';
|
||||||
import { messageHelper } from "./helpers/messageHelper.js";
|
import { messageHelper } from "./helpers/messageHelper.js";
|
||||||
import {enums} from "./enums.js";
|
import {enums} from "./enums.js";
|
||||||
import {commands} from "./commands.js";
|
import {commands} from "./commands.js";
|
||||||
import {webhookHelper} from "./helpers/webhookHelper.js";
|
import {webhookHelper} from "./helpers/webhookHelper.js";
|
||||||
import * as env from 'dotenv';
|
import env from 'dotenv';
|
||||||
|
import {utils} from "./helpers/utils.js";
|
||||||
|
|
||||||
env.config();
|
env.config({path: './.env'});
|
||||||
|
|
||||||
const token = process.env.FLUXER_BOT_TOKEN;
|
export const token = process.env.FLUXER_BOT_TOKEN;
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
console.error("Missing FLUXER_BOT_TOKEN environment variable.");
|
console.error("Missing FLUXER_BOT_TOKEN environment variable.");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new Client({ intents: 0 });
|
export const client = new Client({ intents: 0 });
|
||||||
|
|
||||||
client.on(Events.MessageCreate, async (message) => {
|
client.on(Events.MessageCreate, async (message) => {
|
||||||
try {
|
await handleMessageCreate(message);
|
||||||
// Ignore bots and messages without content
|
});
|
||||||
if (message.author.bot || !message.content) return;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls functions based off the contents of a message object.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @param {Message} message - The message object
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
export const handleMessageCreate = async function(message) {
|
||||||
|
try {
|
||||||
// Parse command and arguments
|
// Parse command and arguments
|
||||||
const content = message.content.trim();
|
const content = message.content.trim();
|
||||||
|
// Ignore bots and messages without content
|
||||||
|
if (message.author.bot || content.length === 0) return;
|
||||||
|
|
||||||
// If message doesn't start with the bot prefix, it could still be a message with a proxy tag. If it's not, return.
|
// If message doesn't start with the bot prefix, it could still be a message with a proxy tag. If it's not, return.
|
||||||
if (!content.startsWith(messageHelper.prefix)) {
|
if (!content.startsWith(messageHelper.prefix)) {
|
||||||
await webhookHelper.sendMessageAsMember(client, message, content).catch((e) => {
|
await webhookHelper.sendMessageAsMember(client, message).catch((e) => {
|
||||||
throw e
|
throw e
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const commandName = content.slice(messageHelper.prefix.length).split(" ")[0];
|
const commandName = content.slice(messageHelper.prefix.length).split(" ")[0];
|
||||||
|
|
||||||
// If there's no command name (ie just the prefix)
|
// If there's no command name (ie just the prefix)
|
||||||
if (!commandName) return await message.reply(enums.help.SHORT_DESC_PLURALFLUX);
|
if (!commandName) return await message.reply(enums.help.SHORT_DESC_PLURALFLUX);
|
||||||
|
|
||||||
const args = messageHelper.parseCommandArgs(content, commandName);
|
const args = messageHelper.parseCommandArgs(content, commandName);
|
||||||
|
|
||||||
const command = commands.get(commandName);
|
let command = commands.commandsMap.get(commandName)
|
||||||
|
if (!command) {
|
||||||
|
const commandFromAlias = commands.aliasesMap.get(commandName);
|
||||||
|
command = commandFromAlias ? commands.commandsMap.get(commandFromAlias.command) : null;
|
||||||
|
}
|
||||||
|
|
||||||
if (command) {
|
if (command) {
|
||||||
await command.execute(message, client, args).catch(e => {
|
await command.execute(message, args).catch(e => {
|
||||||
throw e
|
throw e
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -52,7 +69,7 @@ client.on(Events.MessageCreate, async (message) => {
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
// return await message.reply(error.message);
|
// return await message.reply(error.message);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
client.on(Events.Ready, () => {
|
client.on(Events.Ready, () => {
|
||||||
console.log(`Logged in as ${client.user?.username}`);
|
console.log(`Logged in as ${client.user?.username}`);
|
||||||
@@ -61,27 +78,23 @@ client.on(Events.Ready, () => {
|
|||||||
let guildCount = 0;
|
let guildCount = 0;
|
||||||
client.on(Events.GuildCreate, () => {
|
client.on(Events.GuildCreate, () => {
|
||||||
guildCount++;
|
guildCount++;
|
||||||
callback();
|
debouncePrintGuilds();
|
||||||
});
|
});
|
||||||
|
|
||||||
function printGuilds() {
|
function printGuilds() {
|
||||||
console.log(`Serving ${client.guilds.size} guild(s)`);
|
console.log(`Serving ${client.guilds.size} guild(s)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const callback = Debounce(printGuilds, 2000);
|
const debouncePrintGuilds = utils.debounce(printGuilds, 2000);
|
||||||
|
export const debounceLogin = utils.debounce(client.login, 60000);
|
||||||
function Debounce(func, delay) {
|
|
||||||
let timeout = null;
|
|
||||||
return function (...args) {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
timeout = setTimeout(() => func(...args), delay);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
(async () => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
await client.login(token);
|
await client.login(token);
|
||||||
// await db.check_connection();
|
// await db.check_connection();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Login failed:', err);
|
console.error('Login failed:', err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
@@ -4,33 +4,53 @@ import {memberHelper} from "./helpers/memberHelper.js";
|
|||||||
import {EmbedBuilder} from "@fluxerjs/core";
|
import {EmbedBuilder} from "@fluxerjs/core";
|
||||||
import {importHelper} from "./helpers/importHelper.js";
|
import {importHelper} from "./helpers/importHelper.js";
|
||||||
|
|
||||||
const cmds = new Map();
|
const cmds = {
|
||||||
|
commandsMap: new Map(),
|
||||||
|
aliasesMap: new Map()
|
||||||
|
};
|
||||||
|
|
||||||
cmds.set('member', {
|
cmds.aliasesMap.set('m', {command: 'member'})
|
||||||
|
|
||||||
|
cmds.commandsMap.set('member', {
|
||||||
description: enums.help.SHORT_DESC_MEMBER,
|
description: enums.help.SHORT_DESC_MEMBER,
|
||||||
async execute(message, client, args) {
|
async execute(message, args) {
|
||||||
const authorFull = `${message.author.username}#${message.author.discriminator}`
|
await cmds.memberCommand(message, args)
|
||||||
const attachmentUrl = message.attachments.size > 0 ? message.attachments.first().url : null;
|
|
||||||
const attachmentExpires = message.attachments.size > 0 ? message.attachments.first().expires_at : null;
|
|
||||||
const reply = await memberHelper.parseMemberCommand(message.author.id, authorFull, args, attachmentUrl, attachmentExpires).catch(async (e) =>{await message.reply(e.message);});
|
|
||||||
if (typeof reply === 'string') {
|
|
||||||
return await message.reply(reply);
|
|
||||||
}
|
|
||||||
else if (reply instanceof EmbedBuilder) {
|
|
||||||
await message.reply({embeds: [reply.toJSON()]})
|
|
||||||
}
|
|
||||||
else if (typeof reply === 'object') {
|
|
||||||
const errorsText = reply.errors.length > 0 ? reply.errors.join('\n- ') : null;
|
|
||||||
return await message.reply({content: `${reply.success} ${errorsText ? "\nThese errors occurred:\n" + errorsText : ""}`, embeds: [reply.embed.toJSON()]})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
cmds.set('help', {
|
/**
|
||||||
|
* Calls the member-related functions.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @param {Message} message - The message object
|
||||||
|
* @param {string[]} args - The parsed arguments
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
cmds.memberCommand = async function(message, args) {
|
||||||
|
const authorFull = `${message.author.username}#${message.author.discriminator}`
|
||||||
|
const attachmentUrl = message.attachments.size > 0 ? message.attachments.first().url : null;
|
||||||
|
const attachmentExpires = message.attachments.size > 0 ? message.attachments.first().expires_at : null;
|
||||||
|
|
||||||
|
const reply = await memberHelper.parseMemberCommand(message.author.id, authorFull, args, attachmentUrl, attachmentExpires).catch(async (e) =>{console.error(e); await message.reply(e.message);});
|
||||||
|
|
||||||
|
if (typeof reply === 'string') {
|
||||||
|
await message.reply(reply);
|
||||||
|
}
|
||||||
|
else if (reply instanceof EmbedBuilder) {
|
||||||
|
await message.reply({embeds: [reply]})
|
||||||
|
}
|
||||||
|
else if (typeof reply === 'object') {
|
||||||
|
const errorsText = reply.errors.length > 0 ? reply.errors.join('\n- ') : null;
|
||||||
|
return await message.reply({content: `${reply.success} ${errorsText ? `\n\n${enums.err.ERRORS_OCCURRED}\n` + errorsText : ""}`, embeds: [reply.embed]})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmds.commandsMap.set('help', {
|
||||||
description: enums.help.SHORT_DESC_HELP,
|
description: enums.help.SHORT_DESC_HELP,
|
||||||
async execute(message) {
|
async execute(message) {
|
||||||
const fields = [...cmds.entries()].map(([name, cmd]) => ({
|
const fields = [...cmds.commandsMap.entries()].map(([name, cmd]) => ({
|
||||||
name: `${messageHelper.prefix}${name}`,
|
name: `${messageHelper.prefix}${name}`,
|
||||||
value: cmd.description,
|
value: cmd.description,
|
||||||
inline: true,
|
inline: true,
|
||||||
@@ -43,13 +63,26 @@ cmds.set('help', {
|
|||||||
.setFooter({ text: `Prefix: ${messageHelper.prefix}` })
|
.setFooter({ text: `Prefix: ${messageHelper.prefix}` })
|
||||||
.setTimestamp();
|
.setTimestamp();
|
||||||
|
|
||||||
await message.reply({ embeds: [embed.toJSON()] });
|
await message.reply({ embeds: [embed] });
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
cmds.set('import', {
|
cmds.commandsMap.set('import', {
|
||||||
description: enums.help.SHORT_DESC_IMPORT,
|
description: enums.help.SHORT_DESC_IMPORT,
|
||||||
async execute(message, client, args) {
|
async execute(message, args) {
|
||||||
|
await cmds.importCommand(message, args);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the import-related functions.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @param {Message} message - The message object
|
||||||
|
* @param {string[]} args - The parsed arguments
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
cmds.importCommand = async function(message, args) {
|
||||||
const attachmentUrl = message.attachments.size > 0 ? message.attachments.first().url : null;
|
const attachmentUrl = message.attachments.size > 0 ? message.attachments.first().url : null;
|
||||||
if ((message.content.includes('--help') || (args[0] === '' && args.length === 1)) && !attachmentUrl ) {
|
if ((message.content.includes('--help') || (args[0] === '' && args.length === 1)) && !attachmentUrl ) {
|
||||||
return await message.reply(enums.help.IMPORT);
|
return await message.reply(enums.help.IMPORT);
|
||||||
@@ -59,11 +92,11 @@ cmds.set('import', {
|
|||||||
}).catch(async (error) => {
|
}).catch(async (error) => {
|
||||||
if (error instanceof AggregateError) {
|
if (error instanceof AggregateError) {
|
||||||
// errors.message can be a list of successfully added members, or say that none were successful.
|
// errors.message can be a list of successfully added members, or say that none were successful.
|
||||||
let errorsText = `${error.message}.\nThese errors occurred:\n${error.errors.join('\n')}`;
|
let errorsText = `${error.message}.\n\n${enums.err.ERRORS_OCCURRED}\n${error.errors.join('\n')}`;
|
||||||
|
|
||||||
await message.reply(errorsText).catch(async () => {
|
await message.reply(errorsText).catch(async () => {
|
||||||
const returnedBuffer = messageHelper.returnBufferFromText(errorsText);
|
const returnedBuffer = messageHelper.returnBufferFromText(errorsText);
|
||||||
await message.reply({content: returnedBuffer.text, files: [{ name: 'text.pdf', data: returnedBuffer.file }]
|
await message.reply({content: returnedBuffer.text, files: [{ name: 'text.txt', data: returnedBuffer.file }]
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -73,6 +106,5 @@ cmds.set('import', {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
export const commands = cmds;
|
export const commands = cmds;
|
||||||
@@ -7,11 +7,12 @@ helperEnums.err = {
|
|||||||
ADD_ERROR: "Error adding member.",
|
ADD_ERROR: "Error adding member.",
|
||||||
MEMBER_EXISTS: "A member with that name already exists. Please pick a unique name.",
|
MEMBER_EXISTS: "A member with that name already exists. Please pick a unique name.",
|
||||||
USER_NO_MEMBERS: "You have no members created.",
|
USER_NO_MEMBERS: "You have no members created.",
|
||||||
|
NAME_REQUIRED: "You must set a unique name for the member for them to save.",
|
||||||
DISPLAY_NAME_TOO_LONG: "The maximum length of a display name is 32 characters.",
|
DISPLAY_NAME_TOO_LONG: "The maximum length of a display name is 32 characters.",
|
||||||
PROXY_EXISTS: "A duplicate proxy already exists for one of your members. Please pick a new one, or change the old one first.",
|
PROXY_EXISTS: "A duplicate proxy already exists for one of your members. Please pick a new one, or change the old one first.",
|
||||||
NO_SUCH_COMMAND: "No such command exists.",
|
NO_SUCH_COMMAND: "No such command exists.",
|
||||||
PROPIC_FAILS_REQUIREMENTS: "Profile picture must be in JPG, PNG, or WEBP format and less than 10MB.",
|
PROPIC_FAILS_REQUIREMENTS: "Profile picture must be in JPG, PNG, or WEBP format and less than 10MB.",
|
||||||
PROPIC_CANNOT_LOAD: "Profile picture could not be loaded from URL.",
|
PROPIC_CANNOT_LOAD: "Profile picture could not be loaded. Are you sure this is a valid URL? (Try visiting the link to make sure!)",
|
||||||
NO_WEBHOOKS_ALLOWED: "Channel does not support webhooks.",
|
NO_WEBHOOKS_ALLOWED: "Channel does not support webhooks.",
|
||||||
NOT_IN_SERVER: "You can only proxy in a server.",
|
NOT_IN_SERVER: "You can only proxy in a server.",
|
||||||
NO_MESSAGE_SENT_WITH_PROXY: 'Proxied message has no content.',
|
NO_MESSAGE_SENT_WITH_PROXY: 'Proxied message has no content.',
|
||||||
@@ -19,7 +20,7 @@ helperEnums.err = {
|
|||||||
NO_PROXY_WRAPPER: "You need at least one proxy tag surrounding 'text', either before or after.\nCorrect usage examples: `pf;member jane proxy J:text`, `pf;member jane [text]`",
|
NO_PROXY_WRAPPER: "You need at least one proxy tag surrounding 'text', either before or after.\nCorrect usage examples: `pf;member jane proxy J:text`, `pf;member jane [text]`",
|
||||||
NOT_JSON_FILE: "Please attach a valid JSON file.",
|
NOT_JSON_FILE: "Please attach a valid JSON file.",
|
||||||
NO_MEMBERS_IMPORTED: 'No members were imported.',
|
NO_MEMBERS_IMPORTED: 'No members were imported.',
|
||||||
IMPORT_ERROR: "Please see attached file for logs on the member import process.",
|
ERRORS_OCCURRED: "These errors occurred:",
|
||||||
COMMAND_NOT_RECOGNIZED: "Command not recognized. Try typing `pf;help` for command list.",
|
COMMAND_NOT_RECOGNIZED: "Command not recognized. Try typing `pf;help` for command list.",
|
||||||
SET_TO_NULL: "It has been set to null instead."
|
SET_TO_NULL: "It has been set to null instead."
|
||||||
}
|
}
|
||||||
@@ -36,9 +37,9 @@ helperEnums.help = {
|
|||||||
LIST: "Lists members in the system. **Currently only lists the first 25.**",
|
LIST: "Lists members in the system. **Currently only lists the first 25.**",
|
||||||
NAME: "Updates the name for a specific member based on their current name, for ex: `pf;member john name jane`. The member name should ideally be short so you can write other commands with it easily.",
|
NAME: "Updates the name for a specific member based on their current name, for ex: `pf;member john name jane`. The member name should ideally be short so you can write other commands with it easily.",
|
||||||
DISPLAY_NAME: "Updates the display name for a specific member based on their name, for example: `pf;member jane displayname \"Jane Doe | ze/hir\"`.This can be up to 32 characters long. If it has spaces, __put it in quotes__.",
|
DISPLAY_NAME: "Updates the display name for a specific member based on their name, for example: `pf;member jane displayname \"Jane Doe | ze/hir\"`.This can be up to 32 characters long. If it has spaces, __put it in quotes__.",
|
||||||
PROXY: "Updates the proxy tag for a specific member based on their name. The proxy must be formatted with the tags surrounding the word 'text', for example: `pf;member jane proxy Jane:text` or `pf;member amal proxy [text]` This is so the bot can detect what the proxy tags are. **Only one proxy can be set per member currently.**",
|
PROXY: "Updates the proxy tag for a specific member based on their name. The proxy must be formatted with the tags surrounding the word 'text', for example: `pf;member jane proxy Jane:text` or `pf;member amal proxy A{text}` This is so the bot can detect what the proxy tags are. **Only one proxy can be set per member currently.**",
|
||||||
PROPIC: "Updates the profile picture for the member. Must be in JPG, PNG, or WEBP format and less than 10MB. The two options are:\n1. Pass in a direct remote image URL, for example: `pf;member jane propic https://cdn.pixabay.com/photo/2020/05/02/02/54/animal-5119676_1280.jpg`. You can upload images on sites like https://imgbb.com/.\n2. Upload an attachment directly.\n\n**NOTE:** Fluxer does not save your attachments forever, so option #1 is recommended.",
|
PROPIC: "Updates the profile picture for the member. Must be in JPG, PNG, or WEBP format and less than 10MB. The two options are:\n1. Pass in a direct remote image URL, for example: `pf;member jane propic https://cdn.pixabay.com/photo/2020/05/02/02/54/animal-5119676_1280.jpg`. You can upload images on sites like https://imgbb.com/.\n2. Upload an attachment directly.\n\n**NOTE:** Fluxer does not save your attachments forever, so option #1 is recommended.",
|
||||||
IMPORT: "Imports from PluralKit using the JSON file provided by their export command. Importing from other proxy bots is TBD. `pf;import` and attach your JSON file to the message. This will only save the fields that are present in the bot currently, not anything else like birthdays or system handles (yet?). **Only one proxy can be set per member currently.**"
|
IMPORT: "Imports from PluralKit using the JSON file provided by their export command. Importing from other proxy bots is TBD. `pf;import` and attach your JSON file to the message. This will only save the fields that are present in the bot currently, not anything else like birthdays or system handles (yet?). **Only one proxy can be set per member currently.**\n\n**PRO TIP**: For privacy reasons, try DMing the bot with this command and your JSON file--it should still work the same."
|
||||||
}
|
}
|
||||||
|
|
||||||
helperEnums.misc = {
|
helperEnums.misc = {
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ const ih = {};
|
|||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @param {string} authorId - The author of the message
|
* @param {string} authorId - The author of the message
|
||||||
* @param {string} attachmentUrl - The attached JSON url.
|
* @param {string | null} [attachmentUrl] - The attached JSON url.
|
||||||
* @returns {string} A successful addition of all members.
|
* @returns {string} A successful addition of all members.
|
||||||
* @throws {Error} When the member exists, or creating a member doesn't work.
|
* @throws {Error} When the member exists, or creating a member doesn't work.
|
||||||
*/
|
*/
|
||||||
ih.pluralKitImport = async function (authorId, attachmentUrl) {
|
ih.pluralKitImport = async function (authorId, attachmentUrl= null) {
|
||||||
if (!attachmentUrl) {
|
if (!attachmentUrl) {
|
||||||
throw new Error(enums.err.NOT_JSON_FILE);
|
throw new Error(enums.err.NOT_JSON_FILE);
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ ih.pluralKitImport = async function (authorId, attachmentUrl) {
|
|||||||
errors.push(e.message);
|
errors.push(e.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const aggregatedText = addedMembers.length > 0 ? `Successfully added members: ${addedMembers.join(', ')}` : enums.err.NO_MEMBERS_IMPORTED;
|
const aggregatedText = addedMembers.length > 0 ? `Successfully added members: ${addedMembers.join(', ')}` : `${enums.err.NO_MEMBERS_IMPORTED}`;
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
throw new AggregateError(errors, aggregatedText);
|
throw new AggregateError(errors, aggregatedText);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,151 @@
|
|||||||
import {database} from '../database.js';
|
import {database} from '../database.js';
|
||||||
import {enums} from "../enums.js";
|
import {enums} from "../enums.js";
|
||||||
import {EmptyResultError, Op} from "sequelize";
|
import {Op} from "sequelize";
|
||||||
import {EmbedBuilder} from "@fluxerjs/core";
|
import {EmbedBuilder} from "@fluxerjs/core";
|
||||||
|
import {utils} from "./utils.js";
|
||||||
|
|
||||||
const mh = {};
|
const mh = {};
|
||||||
|
|
||||||
// Has an empty "command" to parse the help message properly
|
const commandList = ['new', 'remove', 'name', 'list', 'displayname', 'proxy', 'propic'];
|
||||||
const commandList = ['--help', 'new', 'remove', 'name', 'list', 'displayName', 'proxy', 'propic', ''];
|
const newAndRemoveCommands = ['new', 'remove'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses through the subcommands that come after "pf;member" and calls functions accordingly.
|
* Parses through the subcommands that come after "pf;member" to identify member name, command, and associated values.
|
||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @param {string} authorId - The id of the message author
|
* @param {string} authorId - The id of the message author
|
||||||
* @param {string} authorFull - The username and discriminator of the message author
|
* @param {string} authorFull - The username and discriminator of the message author
|
||||||
* @param {string[]} args - The message arguments
|
* @param {string[]} args - The message arguments
|
||||||
* @param {string | null} attachmentUrl - The message attachment url.
|
* @param {string | null} [attachmentUrl] - The attachment URL, if any
|
||||||
* @param {string | null} attachmentExpiration - The message attachment expiration (if uploaded via Fluxer)
|
* @param {string | null} [attachmentExpiration] - The attachment expiry date, if any
|
||||||
* @returns {Promise<string>} A success message.
|
* @returns {Promise<string>} A success message.
|
||||||
* @returns {Promise <EmbedBuilder>} A list of 25 members as an embed.
|
* @returns {Promise <EmbedBuilder>} A list of 25 members as an embed.
|
||||||
* @returns {Promise<{EmbedBuilder, [], string}>} A member info embed + info/errors.
|
* @returns {Promise <EmbedBuilder>} A list of member commands and descriptions.
|
||||||
|
* @returns {Promise<{EmbedBuilder, string[], string}>} A member info embed + info/errors.
|
||||||
* @throws {Error}
|
* @throws {Error}
|
||||||
*/
|
*/
|
||||||
mh.parseMemberCommand = async function (authorId, authorFull, args, attachmentUrl = null, attachmentExpiration = null) {
|
mh.parseMemberCommand = async function (authorId, authorFull, args, attachmentUrl = null, attachmentExpiration = null) {
|
||||||
let member;
|
let memberName, command, isHelp = false;
|
||||||
// checks whether command is in list, otherwise assumes it's a name
|
// checks whether command is in list, otherwise assumes it's a name
|
||||||
if (!commandList.includes(args[0]) && !args[1]) {
|
|
||||||
member = await mh.getMemberInfo(authorId, args[0]);
|
// ex: pf;member remove, pf;member remove --help
|
||||||
}
|
// ex: pf;member, pf;member --help
|
||||||
switch (args[0]) {
|
if (args.length === 0 || args[0] === '--help' || args[0] === '') {
|
||||||
case '--help':
|
|
||||||
case '':
|
|
||||||
return mh.getMemberCommandInfo();
|
return mh.getMemberCommandInfo();
|
||||||
|
}
|
||||||
|
// ex: pf;member remove somePerson
|
||||||
|
if (commandList.includes(args[0])) {
|
||||||
|
command = args[0];
|
||||||
|
if (args[1]) {
|
||||||
|
memberName = args[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ex: pf;member somePerson propic
|
||||||
|
else if (args[1] && commandList.includes(args[1])) {
|
||||||
|
command = args[1];
|
||||||
|
memberName = args[0];
|
||||||
|
}
|
||||||
|
// ex: pf;member somePerson
|
||||||
|
else if (!commandList.includes(args[0]) && !args[1]) {
|
||||||
|
memberName = args[0];
|
||||||
|
}
|
||||||
|
if (args[1] === "--help" || command && (memberName === "--help" || !memberName && command !== 'list')) {
|
||||||
|
isHelp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await mh.memberArgumentHandler(authorId, authorFull, isHelp, command, memberName, args, attachmentUrl, attachmentExpiration)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses through the command, argument, and values and calls appropriate functions based on their presence or absence.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @param {string} authorId - The id of the message author
|
||||||
|
* @param {string} authorFull - The username and discriminator of the message author
|
||||||
|
* @param {boolean} isHelp - Whether this is a help command or not
|
||||||
|
* @param {string | null} [command] - The command name
|
||||||
|
* @param {string | null} [memberName] - The member name
|
||||||
|
* @param {string[]} [args] - The message arguments
|
||||||
|
* @param {string | null} [attachmentUrl] - The attachment URL, if any
|
||||||
|
* @param {string | null} [attachmentExpiration] - The attachment expiry date, if any
|
||||||
|
* @returns {Promise<string>} A success message.
|
||||||
|
* @returns {Promise <EmbedBuilder>} A list of 25 members as an embed.
|
||||||
|
* @returns {Promise <EmbedBuilder>} A list of member commands and descriptions.
|
||||||
|
* @returns {Promise<{EmbedBuilder, [string], string}>} A member info embed + info/errors.
|
||||||
|
* @throws {Error}
|
||||||
|
*/
|
||||||
|
mh.memberArgumentHandler = async function(authorId, authorFull, isHelp, command = null, memberName = null, args = [], attachmentUrl = null, attachmentExpiration = null) {
|
||||||
|
if (!command && !memberName && !isHelp) {
|
||||||
|
throw new Error(enums.err.COMMAND_NOT_RECOGNIZED);
|
||||||
|
}
|
||||||
|
else if (isHelp) {
|
||||||
|
return mh.sendHelpEnum(command);
|
||||||
|
}
|
||||||
|
else if (command === "list") {
|
||||||
|
return await mh.getAllMembersInfo(authorId, authorFull);
|
||||||
|
}
|
||||||
|
else if (!memberName && !isHelp) {
|
||||||
|
throw new Error(enums.err.NO_MEMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove memberName and command from values to reduce confusion
|
||||||
|
const values = args.slice(2);
|
||||||
|
|
||||||
|
// ex: pf;member blah blah
|
||||||
|
if (command && memberName && (values.length > 0 || newAndRemoveCommands.includes(command) || attachmentUrl)) {
|
||||||
|
return await mh.memberCommandHandler(authorId, command, memberName, values, attachmentUrl, attachmentExpiration).catch((e) => {throw e});
|
||||||
|
}
|
||||||
|
else if (memberName && values.length === 0) {
|
||||||
|
return await mh.sendCurrentValue(authorId, memberName, command).catch((e) => {throw e});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the current value of a field based on the command.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @param {string} authorId - The id of the message author
|
||||||
|
* @param {string} memberName - The name of the member
|
||||||
|
* @param {string | null} [command] - The command being called to query a value.
|
||||||
|
* @returns {Promise<string>} A success message.
|
||||||
|
* @returns {Promise <EmbedBuilder>} A list of 25 members as an embed.
|
||||||
|
* @returns {Promise <EmbedBuilder>} A list of member commands and descriptions.
|
||||||
|
* @returns {Promise<{EmbedBuilder, string[], string}>} A member info embed + info/errors.
|
||||||
|
*/
|
||||||
|
mh.sendCurrentValue = async function(authorId, memberName, command= null) {
|
||||||
|
const member = await mh.getMemberByName(authorId, memberName).then((m) => {
|
||||||
|
if (!m) throw new Error(enums.err.NO_MEMBER);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!command) {
|
||||||
|
return mh.getMemberInfo(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case 'name':
|
||||||
|
return `The name of ${member.name} is \"${member.name}\" but you probably already knew that!`;
|
||||||
|
case 'displayname':
|
||||||
|
return member.displayname ? `The display name for ${member.name} is \"${member.displayname}\".` : `Display name ${enums.err.NO_VALUE}`;
|
||||||
|
case 'proxy':
|
||||||
|
return member.proxy ? `The proxy for ${member.name} is \"${member.proxy}\".` : `Proxy ${enums.err.NO_VALUE}`;
|
||||||
|
case 'propic':
|
||||||
|
return member.propic ? `The profile picture for ${member.name} is \"${member.propic}\".` : `Propic ${enums.err.NO_VALUE}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the help text associated with a command.
|
||||||
|
*
|
||||||
|
* @param {string} command - The command being called.
|
||||||
|
* @returns {string} - The help text associated with a command.
|
||||||
|
*/
|
||||||
|
mh.sendHelpEnum = function(command) {
|
||||||
|
switch (command) {
|
||||||
case 'new':
|
case 'new':
|
||||||
return await mh.addNewMember(authorId, args, attachmentUrl).catch((e) => {
|
return enums.help.NEW;
|
||||||
throw e
|
|
||||||
});
|
|
||||||
case 'remove':
|
case 'remove':
|
||||||
return await mh.removeMember(authorId, args).catch((e) => {
|
return enums.help.REMOVE;
|
||||||
throw e
|
|
||||||
});
|
|
||||||
case 'name':
|
case 'name':
|
||||||
return enums.help.NAME;
|
return enums.help.NAME;
|
||||||
case 'displayname':
|
case 'displayname':
|
||||||
@@ -49,35 +155,40 @@ mh.parseMemberCommand = async function (authorId, authorFull, args, attachmentUr
|
|||||||
case 'propic':
|
case 'propic':
|
||||||
return enums.help.PROPIC;
|
return enums.help.PROPIC;
|
||||||
case 'list':
|
case 'list':
|
||||||
if (args[1] && args[1] === "--help") {
|
|
||||||
return enums.help.LIST;
|
return enums.help.LIST;
|
||||||
}
|
}
|
||||||
return await mh.getAllMembersInfo(authorId, authorFull).catch((e) => {
|
|
||||||
throw e
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
switch (args[1]) {
|
|
||||||
|
/**
|
||||||
|
* Handles the commands that need to call other update/edit commands.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @param {string} authorId - The id of the message author
|
||||||
|
* @param {string} memberName - The name of the member
|
||||||
|
* @param {string} command - The command being called.
|
||||||
|
* @param {string[]} values - The values to be passed in. Only includes the values after member name and command name.
|
||||||
|
* @param {string | null} attachmentUrl - The attachment URL, if any
|
||||||
|
* @param {string | null} attachmentExpiration - The attachment expiry date, if any
|
||||||
|
* @returns {Promise<string>} A success message.
|
||||||
|
* @returns {Promise <EmbedBuilder>} A list of 25 members as an embed.
|
||||||
|
* @returns {Promise <EmbedBuilder>} A list of member commands and descriptions.
|
||||||
|
* @returns {Promise<{EmbedBuilder, [string], string}>} A member info embed + info/errors.
|
||||||
|
* @throws {Error}
|
||||||
|
*/
|
||||||
|
mh.memberCommandHandler = async function(authorId, command, memberName, values, attachmentUrl = null, attachmentExpiration = null) {
|
||||||
|
switch (command) {
|
||||||
|
case 'new':
|
||||||
|
return await mh.addNewMember(authorId, memberName, values, attachmentUrl, attachmentExpiration).catch((e) => {throw e});
|
||||||
|
case 'remove':
|
||||||
|
return await mh.removeMember(authorId, memberName).catch((e) => {throw e});
|
||||||
case 'name':
|
case 'name':
|
||||||
return await mh.updateName(authorId, args).catch((e) => {
|
return await mh.updateName(authorId, memberName, values[0]).catch((e) => {throw e});
|
||||||
throw e
|
|
||||||
});
|
|
||||||
case 'displayname':
|
case 'displayname':
|
||||||
return await mh.updateDisplayName(authorId, args).catch((e) => {
|
return await mh.updateDisplayName(authorId, memberName, values[0]).catch((e) => {throw e});
|
||||||
throw e
|
|
||||||
});
|
|
||||||
case 'proxy':
|
case 'proxy':
|
||||||
if (!args[2]) return await mh.getProxyByMember(authorId, args[0]).catch((e) => {
|
return await mh.updateProxy(authorId, memberName, values[0]).catch((e) => {throw e});
|
||||||
throw e
|
|
||||||
});
|
|
||||||
return await mh.updateProxy(authorId, args).catch((e) => {
|
|
||||||
throw e
|
|
||||||
});
|
|
||||||
case 'propic':
|
case 'propic':
|
||||||
return await mh.updatePropic(authorId, args, attachmentUrl, attachmentExpiration).catch((e) => {
|
return await mh.updatePropic(authorId, memberName, values[0], attachmentUrl, attachmentExpiration).catch((e) => {throw e});
|
||||||
throw e
|
|
||||||
});
|
|
||||||
default:
|
|
||||||
return member;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,24 +197,23 @@ mh.parseMemberCommand = async function (authorId, authorFull, args, attachmentUr
|
|||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @param {string} authorId - The author of the message
|
* @param {string} authorId - The author of the message
|
||||||
* @param {string[]} args - The message arguments
|
* @param {string} memberName - The member name
|
||||||
* @param {string | null} attachmentURL - The attachment URL, if any exists
|
* @param {string[]} values - The arguments following the member name and command
|
||||||
* @returns {Promise<string>} A successful addition.
|
* @param {string | null} [attachmentUrl] - The attachment URL, if any
|
||||||
* @throws {Error} When the member exists, or creating a member doesn't work.
|
* @param {string | null} [attachmentExpiration] - The attachment expiry date, if any
|
||||||
|
* @returns {Promise<{EmbedBuilder, string[], string}>} A successful addition.
|
||||||
|
* @throws {Error} When creating a member doesn't work.
|
||||||
*/
|
*/
|
||||||
mh.addNewMember = async function (authorId, args, attachmentURL = null) {
|
mh.addNewMember = async function (authorId, memberName, values, attachmentUrl = null, attachmentExpiration = null) {
|
||||||
if (args[1] && args[1] === "--help" || !args[1]) {
|
const displayName = values[0];
|
||||||
return enums.help.NEW;
|
const proxy = values[1];
|
||||||
}
|
const propic = values[2] ?? attachmentUrl;
|
||||||
const memberName = args[1];
|
|
||||||
const displayName = args[2];
|
|
||||||
const proxy = args[3];
|
|
||||||
const propic = args[4] ?? attachmentURL;
|
|
||||||
|
|
||||||
return await mh.addFullMember(authorId, memberName, displayName, proxy, propic).then(async(response) => {
|
return await mh.addFullMember(authorId, memberName, displayName, proxy, propic, attachmentExpiration).then((response) => {
|
||||||
const memberInfoEmbed = await mh.getMemberInfo(authorId, memberName).catch((e) => {throw e})
|
const memberInfoEmbed = mh.getMemberInfo(response.member);
|
||||||
return {embed: memberInfoEmbed, errors: response.errors, success: `${memberName} has been added successfully.`};
|
return {embed: memberInfoEmbed, errors: response.errors, success: `${memberName} has been added successfully.`};
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
|
console.error(e);
|
||||||
throw e;
|
throw e;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -113,24 +223,17 @@ mh.addNewMember = async function (authorId, args, attachmentURL = null) {
|
|||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @param {string} authorId - The author of the message
|
* @param {string} authorId - The author of the message
|
||||||
* @param {string[]} args - The message arguments
|
* @param {string} memberName - The member to update
|
||||||
|
* @param {string} name - The message arguments
|
||||||
* @returns {Promise<string>} A successful update.
|
* @returns {Promise<string>} A successful update.
|
||||||
* @throws {RangeError} When the name doesn't exist.
|
* @throws {RangeError} When the name doesn't exist.
|
||||||
*/
|
*/
|
||||||
mh.updateName = async function (authorId, args) {
|
mh.updateName = async function (authorId, memberName, name) {
|
||||||
if (args[2] && args[2] === "--help") {
|
|
||||||
return enums.help.NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = args[2];
|
|
||||||
if (!name) {
|
|
||||||
return `The name for ${args[0]} is ${args[0]}, but you probably knew that!`;
|
|
||||||
}
|
|
||||||
const trimmedName = name.trim();
|
const trimmedName = name.trim();
|
||||||
if (trimmedName === '') {
|
if (trimmedName === '') {
|
||||||
throw new RangeError(`Name ${enums.err.NO_VALUE}`);
|
throw new RangeError(`Name ${enums.err.NO_VALUE}`);
|
||||||
}
|
}
|
||||||
return await mh.updateMemberField(authorId, args).catch((e) => {
|
return await mh.updateMemberField(authorId, memberName, "name", trimmedName).catch((e) => {
|
||||||
throw e
|
throw e
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -140,34 +243,21 @@ mh.updateName = async function (authorId, args) {
|
|||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @param {string} authorId - The author of the message
|
* @param {string} authorId - The author of the message
|
||||||
* @param {string[]} args - The message arguments
|
* @param {string} membername - The member to update
|
||||||
|
* @param {string} displayname - The display name to set
|
||||||
* @returns {Promise<string>} A successful update.
|
* @returns {Promise<string>} A successful update.
|
||||||
* @throws {RangeError} When the display name is too long or doesn't exist.
|
* @throws {RangeError} When the display name is too long or doesn't exist.
|
||||||
*/
|
*/
|
||||||
mh.updateDisplayName = async function (authorId, args) {
|
mh.updateDisplayName = async function (authorId, membername, displayname) {
|
||||||
if (args[2] && args[2] === "--help") {
|
const trimmedName = displayname.trim();
|
||||||
return enums.help.DISPLAY_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
const memberName = args[0];
|
if (trimmedName.length > 32) {
|
||||||
const displayName = args[2];
|
|
||||||
const trimmedName = displayName ? displayName.trim() : null;
|
|
||||||
|
|
||||||
if (!displayName) {
|
|
||||||
return await mh.getMemberByName(authorId, memberName).then((member) => {
|
|
||||||
if (member && member.displayname) {
|
|
||||||
return `Display name for ${memberName} is: \"${member.displayname}\".`;
|
|
||||||
} else if (member) {
|
|
||||||
throw new Error(`Display name ${enums.err.NO_VALUE}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (displayName.length > 32) {
|
|
||||||
throw new RangeError(enums.err.DISPLAY_NAME_TOO_LONG);
|
throw new RangeError(enums.err.DISPLAY_NAME_TOO_LONG);
|
||||||
}
|
}
|
||||||
else if (trimmedName === '') {
|
else if (trimmedName === '') {
|
||||||
throw new RangeError(`Display name ${enums.err.NO_VALUE}`);
|
throw new RangeError(`Display name ${enums.err.NO_VALUE}`);
|
||||||
}
|
}
|
||||||
return await mh.updateMemberField(authorId, args).catch((e) => {
|
return await mh.updateMemberField(authorId, membername, "displayname", trimmedName).catch((e) => {
|
||||||
throw e
|
throw e
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -177,24 +267,16 @@ mh.updateDisplayName = async function (authorId, args) {
|
|||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @param {string} authorId - The author of the message
|
* @param {string} authorId - The author of the message
|
||||||
* @param {string[]} args - The message arguments
|
* @param {string} memberName - The member to update
|
||||||
|
* @param {string} proxy - The proxy to set
|
||||||
* @returns {Promise<string> } A successful update.
|
* @returns {Promise<string> } A successful update.
|
||||||
* @throws {RangeError | Error} When an empty proxy was provided, or no proxy exists.
|
* @throws {Error} When an empty proxy was provided, or a proxy exists.
|
||||||
*/
|
*/
|
||||||
mh.updateProxy = async function (authorId, args) {
|
mh.updateProxy = async function (authorId, memberName, proxy) {
|
||||||
if (args[2] && args[2] === "--help") {
|
// Throws error if exists
|
||||||
return enums.help.PROXY;
|
await mh.checkIfProxyExists(authorId, proxy).catch((e) => { throw e; });
|
||||||
}
|
|
||||||
const proxyExists = await mh.checkIfProxyExists(authorId, args[2]).then((proxyExists) => {
|
return await mh.updateMemberField(authorId, memberName, "proxy", proxy).catch((e) => { throw e;});
|
||||||
return proxyExists;
|
|
||||||
}).catch((e) => {
|
|
||||||
throw e
|
|
||||||
});
|
|
||||||
if (!proxyExists) {
|
|
||||||
return await mh.updateMemberField(authorId, args).catch((e) => {
|
|
||||||
throw e
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -202,53 +284,19 @@ mh.updateProxy = async function (authorId, args) {
|
|||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @param {string} authorId - The author of the message
|
* @param {string} authorId - The author of the message
|
||||||
* @param {string[]} args - The message arguments
|
* @param {string} memberName - The member to update
|
||||||
* @param {string} attachmentUrl - The url of the first attachment in the message
|
* @param {string} values - The message arguments
|
||||||
* @param {string | null} attachmentExpiry - The expiration date of the first attachment in the message (if uploaded to Fluxer)
|
* @param {string | null} attachmentUrl - The attachment URL, if any
|
||||||
|
* @param {string | null} attachmentExpiration - The attachment expiry date, if any
|
||||||
* @returns {Promise<string>} A successful update.
|
* @returns {Promise<string>} A successful update.
|
||||||
* @throws {Error} When loading the profile picture from a URL doesn't work.
|
* @throws {Error} When loading the profile picture from a URL doesn't work.
|
||||||
*/
|
*/
|
||||||
mh.updatePropic = async function (authorId, args, attachmentUrl, attachmentExpiry = null) {
|
mh.updatePropic = async function (authorId, memberName, values, attachmentUrl = null, attachmentExpiration = null) {
|
||||||
if (args[2] && args[2] === "--help") {
|
const imgUrl = values ?? attachmentUrl;
|
||||||
return enums.help.PROPIC;
|
// Throws error if invalid
|
||||||
}
|
await utils.checkImageFormatValidity(imgUrl).catch((e) => { throw e });
|
||||||
let img;
|
|
||||||
const updatedArgs = args;
|
|
||||||
if (!updatedArgs[1] && !attachmentUrl) {
|
|
||||||
return enums.help.PROPIC;
|
|
||||||
} else if (attachmentUrl) {
|
|
||||||
updatedArgs[2] = attachmentUrl;
|
|
||||||
updatedArgs[3] = attachmentExpiry;
|
|
||||||
}
|
|
||||||
if (updatedArgs[2]) {
|
|
||||||
img = updatedArgs[2];
|
|
||||||
}
|
|
||||||
const isValidImage = await mh.checkImageFormatValidity(img).catch((e) => {
|
|
||||||
throw e
|
|
||||||
});
|
|
||||||
if (isValidImage) {
|
|
||||||
return await mh.updateMemberField(authorId, updatedArgs).catch((e) => {
|
|
||||||
throw e
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return await mh.updateMemberField(authorId, memberName, "propic", imgUrl, attachmentExpiration).catch((e) => { throw e });
|
||||||
* Checks if an uploaded picture is in the right format.
|
|
||||||
*
|
|
||||||
* @async
|
|
||||||
* @param {string} imageUrl - The url of the image
|
|
||||||
* @returns {Promise<boolean>} - If the image is a valid format.
|
|
||||||
* @throws {Error} When loading the profile picture from a URL doesn't work, or it fails requirements.
|
|
||||||
*/
|
|
||||||
mh.checkImageFormatValidity = async function (imageUrl) {
|
|
||||||
const acceptableImages = ['image/png', 'image/jpg', 'image/jpeg', 'image/webp'];
|
|
||||||
return await fetch(imageUrl).then(r => r.blob()).then(blobFile => {
|
|
||||||
if (blobFile.size > 1000000 || !acceptableImages.includes(blobFile.type)) throw new Error(enums.err.PROPIC_FAILS_REQUIREMENTS);
|
|
||||||
return true;
|
|
||||||
}).catch((error) => {
|
|
||||||
throw new Error(`${enums.err.PROPIC_CANNOT_LOAD}: ${error.message}`);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -256,16 +304,11 @@ mh.checkImageFormatValidity = async function (imageUrl) {
|
|||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @param {string} authorId - The author of the message
|
* @param {string} authorId - The author of the message
|
||||||
* @param {string[]} args - The message arguments
|
* @param {string} memberName - The name of the member to remove
|
||||||
* @returns {Promise<string>} A successful removal.
|
* @returns {Promise<string>} A successful removal.
|
||||||
* @throws {EmptyResultError} When there is no member to remove.
|
* @throws {Error} When there is no member to remove.
|
||||||
*/
|
*/
|
||||||
mh.removeMember = async function (authorId, args) {
|
mh.removeMember = async function (authorId, memberName) {
|
||||||
if (args[1] && args[1] === "--help" || !args[1]) {
|
|
||||||
return enums.help.REMOVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
const memberName = args[1];
|
|
||||||
return await database.members.destroy({
|
return await database.members.destroy({
|
||||||
where: {
|
where: {
|
||||||
name: {[Op.iLike]: memberName},
|
name: {[Op.iLike]: memberName},
|
||||||
@@ -275,7 +318,7 @@ mh.removeMember = async function (authorId, args) {
|
|||||||
if (result) {
|
if (result) {
|
||||||
return `Member "${memberName}" has been deleted.`;
|
return `Member "${memberName}" has been deleted.`;
|
||||||
}
|
}
|
||||||
throw new EmptyResultError(`${enums.err.NO_MEMBER}`);
|
throw new Error(`${enums.err.NO_MEMBER}`);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,13 +330,14 @@ mh.removeMember = async function (authorId, args) {
|
|||||||
* @async
|
* @async
|
||||||
* @param {string} authorId - The author of the message
|
* @param {string} authorId - The author of the message
|
||||||
* @param {string} memberName - The name of the member.
|
* @param {string} memberName - The name of the member.
|
||||||
* @param {string | null} displayName - The display name of the member.
|
* @param {string | null} [displayName] - The display name of the member.
|
||||||
* @param {string | null} proxy - The proxy tag of the member.
|
* @param {string | null} [proxy] - The proxy tag of the member.
|
||||||
* @param {string | null} propic - The profile picture URL of the member.
|
* @param {string | null} [propic] - The profile picture URL of the member.
|
||||||
* @returns {Promise<{model, []}>} A successful addition object, including errors if there are any.
|
* @param {string | null} [attachmentExpiration] - The expiration date of an uploaded profile picture.
|
||||||
|
* @returns {Promise<{model, string[]}>} A successful addition object, including errors if there are any.
|
||||||
* @throws {Error} When the member already exists, there are validation errors, or adding a member doesn't work.
|
* @throws {Error} When the member already exists, there are validation errors, or adding a member doesn't work.
|
||||||
*/
|
*/
|
||||||
mh.addFullMember = async function (authorId, memberName, displayName = null, proxy = null, propic = null) {
|
mh.addFullMember = async function (authorId, memberName, displayName = null, proxy = null, propic = null, attachmentExpiration = null) {
|
||||||
await mh.getMemberByName(authorId, memberName).then((member) => {
|
await mh.getMemberByName(authorId, memberName).then((member) => {
|
||||||
if (member) {
|
if (member) {
|
||||||
throw new Error(`Can't add ${memberName}. ${enums.err.MEMBER_EXISTS}`);
|
throw new Error(`Can't add ${memberName}. ${enums.err.MEMBER_EXISTS}`);
|
||||||
@@ -301,10 +345,19 @@ mh.addFullMember = async function (authorId, memberName, displayName = null, pro
|
|||||||
});
|
});
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|
||||||
|
const trimmedName = memberName.trim();
|
||||||
|
if (trimmedName.length === 0) {
|
||||||
|
throw new Error(`Name ${enums.err.NO_VALUE}. ${enums.err.NAME_REQUIRED}`);
|
||||||
|
}
|
||||||
|
|
||||||
let isValidDisplayName;
|
let isValidDisplayName;
|
||||||
if (displayName && displayName.length > 0) {
|
if (displayName) {
|
||||||
const trimmedName = displayName ? displayName.trim() : null;
|
const trimmedDisplayName= displayName ? displayName.trim() : null;
|
||||||
if (trimmedName && trimmedName.length > 32) {
|
if (!trimmedDisplayName || trimmedDisplayName.length === 0) {
|
||||||
|
errors.push(`Display name ${enums.err.NO_VALUE}. ${enums.err.SET_TO_NULL}`);
|
||||||
|
isValidDisplayName = false;
|
||||||
|
}
|
||||||
|
else if (trimmedDisplayName.length > 32) {
|
||||||
errors.push(`Tried to set displayname to \"${displayName}\". ${enums.err.DISPLAY_NAME_TOO_LONG}. ${enums.err.SET_TO_NULL}`);
|
errors.push(`Tried to set displayname to \"${displayName}\". ${enums.err.DISPLAY_NAME_TOO_LONG}. ${enums.err.SET_TO_NULL}`);
|
||||||
isValidDisplayName = false;
|
isValidDisplayName = false;
|
||||||
}
|
}
|
||||||
@@ -313,6 +366,7 @@ mh.addFullMember = async function (authorId, memberName, displayName = null, pro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let isValidProxy;
|
let isValidProxy;
|
||||||
if (proxy && proxy.length > 0) {
|
if (proxy && proxy.length > 0) {
|
||||||
await mh.checkIfProxyExists(authorId, proxy).then(() => {
|
await mh.checkIfProxyExists(authorId, proxy).then(() => {
|
||||||
@@ -325,13 +379,16 @@ mh.addFullMember = async function (authorId, memberName, displayName = null, pro
|
|||||||
|
|
||||||
let isValidPropic;
|
let isValidPropic;
|
||||||
if (propic && propic.length > 0) {
|
if (propic && propic.length > 0) {
|
||||||
await mh.checkImageFormatValidity(propic).then(() => {
|
await utils.checkImageFormatValidity(propic).then(() => {
|
||||||
isValidPropic = true;
|
isValidPropic = true;
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
errors.push(`Tried to set profile picture to \"${propic}\". ${e.message}. ${enums.err.SET_TO_NULL}`);
|
errors.push(`Tried to set profile picture to \"${propic}\". ${e.message}. ${enums.err.SET_TO_NULL}`);
|
||||||
isValidPropic = false;
|
isValidPropic = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (isValidPropic && attachmentExpiration) {
|
||||||
|
errors.push(mh.setExpirationWarning(attachmentExpiration));
|
||||||
|
}
|
||||||
const member = await database.members.create({
|
const member = await database.members.create({
|
||||||
name: memberName, userid: authorId, displayname: isValidDisplayName ? displayName : null, proxy: isValidProxy ? proxy : null, propic: isValidPropic ? propic : null
|
name: memberName, userid: authorId, displayname: isValidDisplayName ? displayName : null, proxy: isValidProxy ? proxy : null, propic: isValidPropic ? propic : null
|
||||||
});
|
});
|
||||||
@@ -339,126 +396,24 @@ mh.addFullMember = async function (authorId, memberName, displayName = null, pro
|
|||||||
return {member: member, errors: errors};
|
return {member: member, errors: errors};
|
||||||
}
|
}
|
||||||
|
|
||||||
// mh.mergeFullMember = async function (authorId, memberName, displayName = null, proxy = null, propic = null) {
|
|
||||||
// await mh.getMemberByName(authorId, memberName).then((member) => {
|
|
||||||
// if (member) {
|
|
||||||
// throw new Error(`Can't add ${memberName}. ${enums.err.MEMBER_EXISTS}`);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// let isValidDisplayName;
|
|
||||||
// if (displayName) {
|
|
||||||
// const trimmedName = displayName ? displayName.trim() : null;
|
|
||||||
// if (trimmedName && trimmedName.length > 32) {
|
|
||||||
// if (!isImport) {
|
|
||||||
// throw new RangeError(`Can't add ${memberName}. ${enums.err.DISPLAY_NAME_TOO_LONG}`);
|
|
||||||
// }
|
|
||||||
// isValidDisplayName = false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let isValidProxy;
|
|
||||||
// if (proxy) {
|
|
||||||
// isValidProxy = await mh.checkIfProxyExists(authorId, proxy).then((res) => {
|
|
||||||
// return res;
|
|
||||||
// }).catch((e) => {
|
|
||||||
// if (!isImport) {
|
|
||||||
// throw e
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let isValidPropic;
|
|
||||||
// if (propic) {
|
|
||||||
// isValidPropic = await mh.checkImageFormatValidity(propic).then((valid) => {
|
|
||||||
// return valid;
|
|
||||||
// }).catch((e) => {
|
|
||||||
// if (!isImport) {
|
|
||||||
// throw (e);
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// const member = await database.members.create({
|
|
||||||
// name: memberName, userid: authorId, displayname: isValidDisplayName ? displayName: null, proxy: isValidProxy ? proxy : null, propic: isValidPropic ? propic : null,
|
|
||||||
// });
|
|
||||||
// if (!member) {
|
|
||||||
// new Error(`${enums.err.ADD_ERROR}`);
|
|
||||||
// }
|
|
||||||
// return member;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// mh.overwriteFullMemberFromImport = async function (authorId, memberName, displayName = null, proxy = null, propic = null) {
|
|
||||||
// await mh.getMemberByName(authorId, memberName).then((member) => {
|
|
||||||
// if (member) {
|
|
||||||
// throw new Error(`Can't add ${memberName}. ${enums.err.MEMBER_EXISTS}`);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// let isValidDisplayName;
|
|
||||||
// if (displayName) {
|
|
||||||
// const trimmedName = displayName ? displayName.trim() : null;
|
|
||||||
// if (trimmedName && trimmedName.length > 32) {
|
|
||||||
// if (!isImport) {
|
|
||||||
// throw new RangeError(`Can't add ${memberName}. ${enums.err.DISPLAY_NAME_TOO_LONG}`);
|
|
||||||
// }
|
|
||||||
// isValidDisplayName = false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let isValidProxy;
|
|
||||||
// if (proxy) {
|
|
||||||
// isValidProxy = await mh.checkIfProxyExists(authorId, proxy).then((res) => {
|
|
||||||
// return res;
|
|
||||||
// }).catch((e) => {
|
|
||||||
// if (!isImport) {
|
|
||||||
// throw e
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let isValidPropic;
|
|
||||||
// if (propic) {
|
|
||||||
// isValidPropic = await mh.checkImageFormatValidity(propic).then((valid) => {
|
|
||||||
// return valid;
|
|
||||||
// }).catch((e) => {
|
|
||||||
// if (!isImport) {
|
|
||||||
// throw (e);
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// const member = await database.members.create({
|
|
||||||
// name: memberName, userid: authorId, displayname: isValidDisplayName ? displayName: null, proxy: isValidProxy ? proxy : null, propic: isValidPropic ? propic : null,
|
|
||||||
// });
|
|
||||||
// if (!member) {
|
|
||||||
// new Error(`${enums.err.ADD_ERROR}`);
|
|
||||||
// }
|
|
||||||
// return member;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates one fields for a member in the database.
|
* Updates one fields for a member in the database.
|
||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @param {string} authorId - The author of the message
|
* @param {string} authorId - The author of the message
|
||||||
* @param {string[]} args - The message arguments
|
* @param {string} memberName - The member to update
|
||||||
|
* @param {string} columnName - The column name to update.
|
||||||
|
* @param {string} value - The value to update to.
|
||||||
|
* @param {string | null} [attachmentExpiration] - The attachment expiration date (if any)
|
||||||
* @returns {Promise<string>} A successful update.
|
* @returns {Promise<string>} A successful update.
|
||||||
* @throws {EmptyResultError | Error} When the member is not found, or catchall error.
|
* @throws {Error} When no member row was updated.
|
||||||
*/
|
*/
|
||||||
mh.updateMemberField = async function (authorId, args) {
|
mh.updateMemberField = async function (authorId, memberName, columnName, value, attachmentExpiration = null) {
|
||||||
const memberName = args[0];
|
|
||||||
const columnName = args[1];
|
|
||||||
const value = args[2];
|
|
||||||
let fluxerPropicWarning;
|
let fluxerPropicWarning;
|
||||||
|
|
||||||
// indicates that an attachment was uploaded on Fluxer directly
|
// indicates that an attachment was uploaded on Fluxer directly
|
||||||
if (columnName === "propic" && args[3]) {
|
if (columnName === "propic" && attachmentExpiration) {
|
||||||
fluxerPropicWarning = mh.setExpirationWarning(args[3]);
|
fluxerPropicWarning = mh.setExpirationWarning(value);
|
||||||
}
|
}
|
||||||
return await database.members.update({[columnName]: value}, {
|
return await database.members.update({[columnName]: value}, {
|
||||||
where: {
|
where: {
|
||||||
@@ -467,7 +422,7 @@ mh.updateMemberField = async function (authorId, args) {
|
|||||||
}
|
}
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (res[0] === 0) {
|
if (res[0] === 0) {
|
||||||
throw new EmptyResultError(`Can't update ${memberName}. ${enums.err.NO_MEMBER}.`);
|
throw new Error(`Can't update ${memberName}. ${enums.err.NO_MEMBER}.`);
|
||||||
} else {
|
} else {
|
||||||
return `Updated ${columnName} for ${memberName} to ${value}${fluxerPropicWarning ?? ''}.`;
|
return `Updated ${columnName} for ${memberName} to ${value}${fluxerPropicWarning ?? ''}.`;
|
||||||
}
|
}
|
||||||
@@ -491,14 +446,10 @@ mh.setExpirationWarning = function (expirationString) {
|
|||||||
/**
|
/**
|
||||||
* Gets the details for a member.
|
* Gets the details for a member.
|
||||||
*
|
*
|
||||||
* @async
|
* @param {model} member - The member object
|
||||||
* @param {string} authorId - The author of the message
|
* @returns {EmbedBuilder} The member's info.
|
||||||
* @param {string} memberName - The message arguments
|
|
||||||
* @returns {Promise<EmbedBuilder>} The member's info.
|
|
||||||
*/
|
*/
|
||||||
mh.getMemberInfo = async function (authorId, memberName) {
|
mh.getMemberInfo = function (member) {
|
||||||
return await mh.getMemberByName(authorId, memberName).then((member) => {
|
|
||||||
if (member) {
|
|
||||||
return new EmbedBuilder()
|
return new EmbedBuilder()
|
||||||
.setTitle(member.name)
|
.setTitle(member.name)
|
||||||
.setDescription(`Details for ${member.name}`)
|
.setDescription(`Details for ${member.name}`)
|
||||||
@@ -507,9 +458,7 @@ mh.getMemberInfo = async function (authorId, memberName) {
|
|||||||
value: member.displayname ?? 'unset',
|
value: member.displayname ?? 'unset',
|
||||||
inline: true
|
inline: true
|
||||||
}, {name: 'Proxy tag: ', value: member.proxy ?? 'unset', inline: true},)
|
}, {name: 'Proxy tag: ', value: member.proxy ?? 'unset', inline: true},)
|
||||||
.setImage(member.propic);
|
.setImage(member.propic ?? null);
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -539,42 +488,11 @@ mh.getAllMembersInfo = async function (authorId, authorName) {
|
|||||||
* @param {string} authorId - The author of the message.
|
* @param {string} authorId - The author of the message.
|
||||||
* @param {string} memberName - The member's name.
|
* @param {string} memberName - The member's name.
|
||||||
* @returns {Promise<model>} The member object.
|
* @returns {Promise<model>} The member object.
|
||||||
* @throws { EmptyResultError } When the member is not found.
|
|
||||||
*/
|
*/
|
||||||
mh.getMemberByName = async function (authorId, memberName) {
|
mh.getMemberByName = async function (authorId, memberName) {
|
||||||
return await database.members.findOne({where: {userid: authorId, name: {[Op.iLike]: memberName}}});
|
return await database.members.findOne({where: {userid: authorId, name: {[Op.iLike]: memberName}}});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a member based on the author and proxy tag.
|
|
||||||
*
|
|
||||||
* @async
|
|
||||||
* @param {string} authorId - The author of the message.
|
|
||||||
* @param {string} memberName - The member's name.
|
|
||||||
* @returns {Promise<string>} The member object.
|
|
||||||
* @throws { EmptyResultError } When the member is not found.
|
|
||||||
*/
|
|
||||||
mh.getProxyByMember = async function (authorId, memberName) {
|
|
||||||
return await mh.getMemberByName(authorId, memberName).then((member) => {
|
|
||||||
if (member) {
|
|
||||||
return member.proxy;
|
|
||||||
}
|
|
||||||
throw new EmptyResultError(enums.err.NO_MEMBER);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a member based on the author and proxy tag.
|
|
||||||
*
|
|
||||||
* @async
|
|
||||||
* @param {string} authorId - The author of the message
|
|
||||||
* @param {string} proxy - The proxy tag
|
|
||||||
* @returns {Promise<model>} The member object.
|
|
||||||
*/
|
|
||||||
mh.getMemberByProxy = async function (authorId, proxy) {
|
|
||||||
return await db.members.findOne({where: {userid: authorId, proxy: proxy}});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all members belonging to the author.
|
* Gets all members belonging to the author.
|
||||||
*
|
*
|
||||||
@@ -586,7 +504,6 @@ mh.getMembersByAuthor = async function (authorId) {
|
|||||||
return await database.members.findAll({where: {userid: authorId}});
|
return await database.members.findAll({where: {userid: authorId}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if proxy exists for a member.
|
* Checks if proxy exists for a member.
|
||||||
*
|
*
|
||||||
@@ -596,7 +513,6 @@ mh.getMembersByAuthor = async function (authorId) {
|
|||||||
* @throws {Error} When an empty proxy was provided, or no proxy exists.
|
* @throws {Error} When an empty proxy was provided, or no proxy exists.
|
||||||
*/
|
*/
|
||||||
mh.checkIfProxyExists = async function (authorId, proxy) {
|
mh.checkIfProxyExists = async function (authorId, proxy) {
|
||||||
if (proxy) {
|
|
||||||
const splitProxy = proxy.trim().split("text");
|
const splitProxy = proxy.trim().split("text");
|
||||||
if (splitProxy.length < 2) throw new Error(enums.err.NO_TEXT_FOR_PROXY);
|
if (splitProxy.length < 2) throw new Error(enums.err.NO_TEXT_FOR_PROXY);
|
||||||
if (!splitProxy[0] && !splitProxy[1]) throw new Error(enums.err.NO_PROXY_WRAPPER);
|
if (!splitProxy[0] && !splitProxy[1]) throw new Error(enums.err.NO_PROXY_WRAPPER);
|
||||||
@@ -609,8 +525,7 @@ mh.checkIfProxyExists = async function (authorId, proxy) {
|
|||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
throw e
|
throw e
|
||||||
});
|
});
|
||||||
}
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
import {memberHelper} from "./memberHelper.js";
|
import {memberHelper} from "./memberHelper.js";
|
||||||
import {enums} from "../enums.js";
|
|
||||||
import tmp, {setGracefulCleanup} from "tmp";
|
|
||||||
import fs from 'fs';
|
|
||||||
import {Message} from "@fluxerjs/core";
|
|
||||||
|
|
||||||
const msgh = {};
|
const msgh = {};
|
||||||
|
|
||||||
msgh.prefix = "pf;"
|
msgh.prefix = "pf;"
|
||||||
|
|
||||||
setGracefulCleanup();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses and slices up message arguments, retaining quoted strings.
|
* Parses and slices up message arguments, retaining quoted strings.
|
||||||
*
|
*
|
||||||
@@ -38,11 +32,12 @@ msgh.parseCommandArgs = function(content, commandName) {
|
|||||||
/**
|
/**
|
||||||
* Parses messages to see if any part of the text matches the tags of any member belonging to an author.
|
* Parses messages to see if any part of the text matches the tags of any member belonging to an author.
|
||||||
*
|
*
|
||||||
|
* @async
|
||||||
* @param {string} authorId - The author of the message.
|
* @param {string} authorId - The author of the message.
|
||||||
* @param {string} content - The full message content
|
* @param {string} content - The full message content
|
||||||
* @param {string | null} attachmentUrl - The url for an attachment to the message, if any exists.
|
* @param {string | null} [attachmentUrl] - The url for an attachment to the message, if any exists.
|
||||||
* @returns {{model, string, bool}} The proxy message object.
|
* @returns {{model, string, bool}} The proxy message object.
|
||||||
* @throws {Error} If a proxy message is sent with no message within it.
|
* @throws {Error} If a proxy message is sent with no message or attachment within it.
|
||||||
*/
|
*/
|
||||||
msgh.parseProxyTags = async function (authorId, content, attachmentUrl = null){
|
msgh.parseProxyTags = async function (authorId, content, attachmentUrl = null){
|
||||||
const members = await memberHelper.getMembersByAuthor(authorId);
|
const members = await memberHelper.getMembersByAuthor(authorId);
|
||||||
|
|||||||
29
src/helpers/utils.js
Normal file
29
src/helpers/utils.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import {enums} from '../enums.js'
|
||||||
|
|
||||||
|
const u = {};
|
||||||
|
|
||||||
|
u.debounce = function(func, delay) {
|
||||||
|
let timeout = null;
|
||||||
|
return function (...args) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(() => func(...args), delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if an uploaded picture is in the right format.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @param {string} imageUrl - The url of the image
|
||||||
|
* @throws {Error} When loading the profile picture from a URL doesn't work, or it fails requirements.
|
||||||
|
*/
|
||||||
|
u.checkImageFormatValidity = async function (imageUrl) {
|
||||||
|
const acceptableImages = ['image/png', 'image/jpg', 'image/jpeg', 'image/webp'];
|
||||||
|
await fetch(imageUrl).then(r => r.blob()).then(blobFile => {
|
||||||
|
if (blobFile.size > 1000000 || !acceptableImages.includes(blobFile.type)) throw new Error(enums.err.PROPIC_FAILS_REQUIREMENTS);
|
||||||
|
}).catch((error) => {
|
||||||
|
throw new Error(`${enums.err.PROPIC_CANNOT_LOAD}: ${error.message}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const utils = u;
|
||||||
322
tests/bot.test.js
Normal file
322
tests/bot.test.js
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
const env = require('dotenv').config({path: './.env.jest'})
|
||||||
|
const {enums} = require("../src/enums.js");
|
||||||
|
|
||||||
|
jest.mock('@fluxerjs/core', () => {
|
||||||
|
return {
|
||||||
|
Events: {
|
||||||
|
MessageCreate: jest.fn(),
|
||||||
|
Ready: jest.fn(),
|
||||||
|
GuildCreate: jest.fn(),
|
||||||
|
},
|
||||||
|
Client: jest.fn().mockImplementation(() => {
|
||||||
|
return {
|
||||||
|
on: jest.fn(),
|
||||||
|
intents: 0,
|
||||||
|
login: jest.fn()
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Message: jest.fn()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock("../src/helpers/messageHelper.js", () => {
|
||||||
|
return {
|
||||||
|
messageHelper: {
|
||||||
|
parseCommandArgs: jest.fn(),
|
||||||
|
prefix: "pf;"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock("../src/helpers/webhookHelper.js", () => {
|
||||||
|
return {
|
||||||
|
webhookHelper: {
|
||||||
|
sendMessageAsMember: jest.fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
jest.mock("../src/helpers/utils.js", () => {
|
||||||
|
return {
|
||||||
|
utils: {
|
||||||
|
debounce: jest.fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
jest.mock("../src/commands.js", () => {
|
||||||
|
return {
|
||||||
|
commands: {
|
||||||
|
commandsMap: {
|
||||||
|
get: jest.fn(),
|
||||||
|
},
|
||||||
|
aliasesMap: {
|
||||||
|
get: jest.fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const {Client, Events} = require('@fluxerjs/core');
|
||||||
|
const {messageHelper} = require("../src/helpers/messageHelper.js");
|
||||||
|
|
||||||
|
const {commands} = require("../src/commands.js");
|
||||||
|
const {webhookHelper} = require("../src/helpers/webhookHelper.js");
|
||||||
|
|
||||||
|
const {utils} = require("../src/helpers/utils.js");
|
||||||
|
let {handleMessageCreate, client} = require("../src/bot.js");
|
||||||
|
|
||||||
|
describe('bot', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetModules();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('handleMessageCreate', () => {
|
||||||
|
|
||||||
|
test('on message creation, if message is from bot, return', () => {
|
||||||
|
// Arrange
|
||||||
|
const message = {
|
||||||
|
author: {
|
||||||
|
bot: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Act
|
||||||
|
return handleMessageCreate(message).then((res) => {
|
||||||
|
expect(res).toBe(undefined);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
test('on message creation, if message is empty, return', () => {
|
||||||
|
// Arrange
|
||||||
|
const message = {
|
||||||
|
content: " ",
|
||||||
|
author: {
|
||||||
|
bot: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Act
|
||||||
|
return handleMessageCreate(message).then((res) => {
|
||||||
|
// Assert
|
||||||
|
expect(res).toBe(undefined);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
test("if message doesn't start with bot prefix, call sendMessageAsMember", () => {
|
||||||
|
// Arrange
|
||||||
|
webhookHelper.sendMessageAsMember.mockResolvedValue();
|
||||||
|
const message = {
|
||||||
|
content: "hello",
|
||||||
|
author: {
|
||||||
|
bot: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Act
|
||||||
|
return handleMessageCreate(message).then(() => {
|
||||||
|
// Assert
|
||||||
|
expect(webhookHelper.sendMessageAsMember).toHaveBeenCalledTimes(1);
|
||||||
|
expect(webhookHelper.sendMessageAsMember).toHaveBeenCalledWith(client, message)
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
test("if sendMessageAsMember returns error, log error", () => {
|
||||||
|
// Arrange
|
||||||
|
webhookHelper.sendMessageAsMember.mockImplementation(() => {
|
||||||
|
throw Error("error")
|
||||||
|
});
|
||||||
|
const message = {
|
||||||
|
content: "hello",
|
||||||
|
author: {
|
||||||
|
bot: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jest.mock('console', () => {
|
||||||
|
return {error: jest.fn()}
|
||||||
|
})
|
||||||
|
// Act
|
||||||
|
return handleMessageCreate(message).catch(() => {
|
||||||
|
// Assert
|
||||||
|
expect(webhookHelper.sendMessageAsMember).toHaveBeenCalledTimes(1);
|
||||||
|
expect(webhookHelper.sendMessageAsMember).toHaveBeenCalledWith(client, message)
|
||||||
|
expect(console.error).toHaveBeenCalledTimes(1);
|
||||||
|
expect(console.error).toHaveBeenCalledWith(new Error('error'))
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
test("if no command after prefix, return correct enum", () => {
|
||||||
|
// Arrange
|
||||||
|
const message = {
|
||||||
|
content: "pf;",
|
||||||
|
author: {
|
||||||
|
bot: false
|
||||||
|
},
|
||||||
|
reply: jest.fn()
|
||||||
|
}
|
||||||
|
// Act
|
||||||
|
return handleMessageCreate(message).then(() => {
|
||||||
|
// Assert
|
||||||
|
expect(message.reply).toHaveBeenCalledTimes(1);
|
||||||
|
expect(message.reply).toHaveBeenCalledWith(enums.help.SHORT_DESC_PLURALFLUX);
|
||||||
|
expect(webhookHelper.sendMessageAsMember).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
test("if command after prefix, call parseCommandArgs and commandsMap.get", () => {
|
||||||
|
// Arrange
|
||||||
|
const message = {
|
||||||
|
content: "pf;help",
|
||||||
|
author: {
|
||||||
|
bot: false
|
||||||
|
},
|
||||||
|
reply: jest.fn()
|
||||||
|
}
|
||||||
|
const command = {
|
||||||
|
execute: jest.fn().mockResolvedValue(),
|
||||||
|
}
|
||||||
|
commands.commandsMap.get = jest.fn().mockReturnValue(command);
|
||||||
|
// Act
|
||||||
|
return handleMessageCreate(message).then(() => {
|
||||||
|
// Assert
|
||||||
|
expect(messageHelper.parseCommandArgs).toHaveBeenCalledTimes(1);
|
||||||
|
expect(messageHelper.parseCommandArgs).toHaveBeenCalledWith('pf;help', 'help');
|
||||||
|
expect(commands.commandsMap.get).toHaveBeenCalledTimes(1);
|
||||||
|
expect(commands.commandsMap.get).toHaveBeenCalledWith('help');
|
||||||
|
expect(webhookHelper.sendMessageAsMember).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if commands.commandsMap.get returns undefined, call aliasesMap.get and commandsMap.get again with that value', () => {
|
||||||
|
// Arrange
|
||||||
|
const message = {
|
||||||
|
content: "pf;m",
|
||||||
|
author: {
|
||||||
|
bot: false
|
||||||
|
},
|
||||||
|
reply: jest.fn()
|
||||||
|
}
|
||||||
|
const mockAlias = {
|
||||||
|
command: 'member'
|
||||||
|
}
|
||||||
|
commands.commandsMap.get = jest.fn().mockReturnValueOnce();
|
||||||
|
commands.aliasesMap.get = jest.fn().mockReturnValueOnce(mockAlias);
|
||||||
|
// Act
|
||||||
|
return handleMessageCreate(message).then(() => {
|
||||||
|
// Assert
|
||||||
|
expect(commands.commandsMap.get).toHaveBeenCalledTimes(2);
|
||||||
|
expect(commands.commandsMap.get).toHaveBeenNthCalledWith(1, 'm');
|
||||||
|
expect(commands.commandsMap.get).toHaveBeenNthCalledWith(2, 'member');
|
||||||
|
expect(commands.aliasesMap.get).toHaveBeenCalledTimes(1);
|
||||||
|
expect(commands.aliasesMap.get).toHaveBeenCalledWith('m');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
test('if aliasesMap.get returns undefined, do not call commandsMap again', () => {
|
||||||
|
// Arrange
|
||||||
|
const message = {
|
||||||
|
content: "pf;m",
|
||||||
|
author: {
|
||||||
|
bot: false
|
||||||
|
},
|
||||||
|
reply: jest.fn()
|
||||||
|
}
|
||||||
|
const mockAlias = {
|
||||||
|
command: 'member'
|
||||||
|
}
|
||||||
|
commands.commandsMap.get = jest.fn().mockReturnValueOnce();
|
||||||
|
commands.aliasesMap.get = jest.fn().mockReturnValueOnce();
|
||||||
|
// Act
|
||||||
|
return handleMessageCreate(message).then(() => {
|
||||||
|
// Assert
|
||||||
|
expect(commands.commandsMap.get).toHaveBeenCalledTimes(1);
|
||||||
|
expect(commands.commandsMap.get).toHaveBeenNthCalledWith(1, 'm');
|
||||||
|
expect(commands.aliasesMap.get).toHaveBeenCalledTimes(1);
|
||||||
|
expect(commands.aliasesMap.get).toHaveBeenCalledWith('m');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
test("if command exists, call command.execute", () => {
|
||||||
|
// Arrange
|
||||||
|
const message = {
|
||||||
|
content: "pf;member test",
|
||||||
|
author: {
|
||||||
|
bot: false
|
||||||
|
},
|
||||||
|
reply: jest.fn()
|
||||||
|
}
|
||||||
|
const command = {
|
||||||
|
execute: jest.fn()
|
||||||
|
}
|
||||||
|
messageHelper.parseCommandArgs = jest.fn().mockReturnValue(['test']);
|
||||||
|
commands.commandsMap.get = jest.fn().mockReturnValue(command);
|
||||||
|
command.execute = jest.fn().mockResolvedValue();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
return handleMessageCreate(message).then(() => {
|
||||||
|
// Assert
|
||||||
|
expect(command.execute).toHaveBeenCalledTimes(1);
|
||||||
|
expect(command.execute).toHaveBeenCalledWith(message, ['test']);
|
||||||
|
expect(webhookHelper.sendMessageAsMember).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
test("if command.execute returns error, log error", () => {
|
||||||
|
// Arrange
|
||||||
|
const command = {
|
||||||
|
execute: jest.fn()
|
||||||
|
}
|
||||||
|
commands.get = jest.fn().mockReturnValue(command);
|
||||||
|
command.execute.mockImplementation(() => {
|
||||||
|
throw Error("error")
|
||||||
|
});
|
||||||
|
const message = {
|
||||||
|
content: "pf;member test",
|
||||||
|
author: {
|
||||||
|
bot: false
|
||||||
|
},
|
||||||
|
reply: jest.fn()
|
||||||
|
}
|
||||||
|
jest.mock('console', () => {
|
||||||
|
return {error: jest.fn()}
|
||||||
|
})
|
||||||
|
// Act
|
||||||
|
return handleMessageCreate(message).catch(() => {
|
||||||
|
// Assert
|
||||||
|
expect(console.error).toHaveBeenCalledTimes(1);
|
||||||
|
expect(console.error).toHaveBeenCalledWith(new Error('error'))
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
test("if command does not exist, return correct enum", () => {
|
||||||
|
// Arrange
|
||||||
|
commands.commandsMap.get = jest.fn().mockReturnValue();
|
||||||
|
commands.aliasesMap.get = jest.fn().mockReturnValue();
|
||||||
|
const message = {
|
||||||
|
content: "pf;asdfjlas",
|
||||||
|
author: {
|
||||||
|
bot: false
|
||||||
|
},
|
||||||
|
reply: jest.fn()
|
||||||
|
}
|
||||||
|
// Act
|
||||||
|
return handleMessageCreate(message).then(() => {
|
||||||
|
// Assert
|
||||||
|
expect(message.reply).toHaveBeenCalledWith(enums.err.COMMAND_NOT_RECOGNIZED);
|
||||||
|
expect(message.reply).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('calls client.login with correct argument', () => {
|
||||||
|
// Act
|
||||||
|
client.login = jest.fn().mockResolvedValue();
|
||||||
|
// Assert
|
||||||
|
expect(client.login).toHaveBeenCalledTimes(1);
|
||||||
|
expect(client.login).toHaveBeenCalledWith(process.env.FLUXER_BOT_TOKEN)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// restore the spy created with spyOn
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
})
|
||||||
183
tests/commands.test.js
Normal file
183
tests/commands.test.js
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
import {enums} from "../src/enums.js";
|
||||||
|
|
||||||
|
jest.mock("../src/helpers/messageHelper.js", () => {
|
||||||
|
return {
|
||||||
|
messageHelper: {
|
||||||
|
returnBufferFromText: jest.fn(),
|
||||||
|
prefix: 'pf;'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
jest.mock('../src/helpers/memberHelper.js', () => {
|
||||||
|
return {
|
||||||
|
memberHelper: {
|
||||||
|
parseMemberCommand: jest.fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
jest.mock('../src/helpers/importHelper.js', () => {
|
||||||
|
return {
|
||||||
|
importHelper: {
|
||||||
|
pluralKitImport: jest.fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
jest.mock('console', () => {
|
||||||
|
return {error: jest.fn()}
|
||||||
|
})
|
||||||
|
|
||||||
|
import {messageHelper, prefix} from "../src/helpers/messageHelper.js";
|
||||||
|
|
||||||
|
import {memberHelper} from "../src/helpers/memberHelper.js";
|
||||||
|
import {EmbedBuilder} from "@fluxerjs/core";
|
||||||
|
import {importHelper} from "../src/helpers/importHelper.js";
|
||||||
|
import {commands} from "../src/commands.js";
|
||||||
|
|
||||||
|
|
||||||
|
describe('commands', () => {
|
||||||
|
const authorId = '123';
|
||||||
|
const discriminator = '123';
|
||||||
|
const username = 'somePerson'
|
||||||
|
const attachmentUrl = 'oya.png';
|
||||||
|
const attachmentExpiration = new Date('2026-01-01').toDateString();
|
||||||
|
const message = {
|
||||||
|
author: {
|
||||||
|
username: username,
|
||||||
|
id: authorId,
|
||||||
|
discriminator: discriminator,
|
||||||
|
},
|
||||||
|
attachments: {
|
||||||
|
size: 1,
|
||||||
|
first: jest.fn().mockImplementation(() => ({
|
||||||
|
expires_at: attachmentExpiration,
|
||||||
|
url: attachmentUrl
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
reply: jest.fn().mockResolvedValue(),
|
||||||
|
}
|
||||||
|
const args = ['new']
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
|
||||||
|
jest.resetModules();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('memberCommand', () => {
|
||||||
|
|
||||||
|
|
||||||
|
test('calls parseMemberCommand with the correct arguments', () => {
|
||||||
|
// Arrange
|
||||||
|
memberHelper.parseMemberCommand = jest.fn().mockResolvedValue("parsed command");
|
||||||
|
// Act
|
||||||
|
return commands.memberCommand(message, args).then(() => {
|
||||||
|
expect(memberHelper.parseMemberCommand).toHaveBeenCalledTimes(1);
|
||||||
|
expect(memberHelper.parseMemberCommand).toHaveBeenCalledWith(authorId, `${username}#${discriminator}`, args, attachmentUrl, attachmentExpiration);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if parseMemberCommand returns error, log error and reply with error', () => {
|
||||||
|
// Arrange
|
||||||
|
memberHelper.parseMemberCommand = jest.fn().mockImplementation(() => {throw new Error('error')});
|
||||||
|
// Act
|
||||||
|
return commands.memberCommand(message, args).catch(() => {
|
||||||
|
expect(message.reply).toHaveBeenCalledTimes(1);
|
||||||
|
expect(message.reply).toHaveBeenCalledWith('error');
|
||||||
|
expect(console.error).toHaveBeenCalledWith(new Error('error'));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if parseMemberCommand returns embed, reply with embed', () => {
|
||||||
|
// Arrange
|
||||||
|
const embed = new EmbedBuilder();
|
||||||
|
memberHelper.parseMemberCommand = jest.fn().mockResolvedValue();
|
||||||
|
// Act
|
||||||
|
return commands.memberCommand(message, args).catch(() => {
|
||||||
|
// Assert
|
||||||
|
expect(message.reply).toHaveBeenCalledTimes(1);
|
||||||
|
expect(message.reply).toHaveBeenCalledWith({embeds: [embed]})
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if parseMemberCommand returns object, reply with embed and content', () => {
|
||||||
|
// Arrange
|
||||||
|
const reply = {
|
||||||
|
errors: ['error', 'error2'],
|
||||||
|
success: 'success',
|
||||||
|
embed: {}
|
||||||
|
}
|
||||||
|
memberHelper.parseMemberCommand = jest.fn().mockResolvedValue(reply);
|
||||||
|
// Act
|
||||||
|
return commands.memberCommand(message, args).catch(() => {
|
||||||
|
// Assert
|
||||||
|
expect(message.reply).toHaveBeenCalledTimes(1);
|
||||||
|
expect(message.reply).toHaveBeenCalledWith({content: `success\n\n${enums.err.ERRORS_OCCURRED}\n\nerror\nerror2}`, embeds: [reply.embed]})
|
||||||
|
});
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('importCommand', () => {
|
||||||
|
test('if message includes --help and no attachmentURL, return help message', () => {
|
||||||
|
const args = ["--help"];
|
||||||
|
message.content = "pf;import --help";
|
||||||
|
message.attachments.size = 0;
|
||||||
|
return commands.importCommand(message, args).then(() => {
|
||||||
|
expect(message.reply).toHaveBeenCalledTimes(1);
|
||||||
|
expect(message.reply).toHaveBeenCalledWith(enums.help.IMPORT);
|
||||||
|
expect(importHelper.pluralKitImport).not.toHaveBeenCalled();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if no args and no attachmentURL, return help message', () => {
|
||||||
|
const args = [""];
|
||||||
|
message.content = 'pf;import'
|
||||||
|
message.attachments.size = 0;
|
||||||
|
return commands.importCommand(message, args).then(() => {
|
||||||
|
expect(message.reply).toHaveBeenCalledTimes(1);
|
||||||
|
expect(message.reply).toHaveBeenCalledWith(enums.help.IMPORT);
|
||||||
|
expect(importHelper.pluralKitImport).not.toHaveBeenCalled();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if attachment URL, call pluralKitImport with correct arguments', () => {
|
||||||
|
const args = [""];
|
||||||
|
message.content = 'pf;import'
|
||||||
|
importHelper.pluralKitImport = jest.fn().mockResolvedValue('success');
|
||||||
|
return commands.importCommand(message, args).then(() => {
|
||||||
|
expect(message.reply).toHaveBeenCalledTimes(1);
|
||||||
|
expect(message.reply).toHaveBeenCalledWith('success');
|
||||||
|
expect(importHelper.pluralKitImport).toHaveBeenCalledTimes(1);
|
||||||
|
expect(importHelper.pluralKitImport).toHaveBeenCalledWith(authorId, attachmentUrl);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if pluralKitImport returns aggregate errors, send errors.', () => {
|
||||||
|
const args = [""];
|
||||||
|
message.content = 'pf;import'
|
||||||
|
importHelper.pluralKitImport = jest.fn().mockImplementation(() => {throw new AggregateError(['error1', 'error2'], 'errors')});
|
||||||
|
return commands.importCommand(message, args).catch(() => {
|
||||||
|
expect(message.reply).toHaveBeenCalledTimes(1);
|
||||||
|
expect(message.reply).toHaveBeenCalledWith(`errors. \n\n${enums.err.ERRORS_OCCURRED}\n\nerror1\nerror2`);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if message.reply throws error, call returnBufferFromText and message.reply again.', () => {
|
||||||
|
// Arrange
|
||||||
|
const args = [""];
|
||||||
|
message.content = 'pf;import'
|
||||||
|
message.reply = jest.fn().mockImplementationOnce(() => {throw e})
|
||||||
|
messageHelper.returnBufferFromText = jest.fn().mockResolvedValue({file: 'test.txt', text: 'normal content'});
|
||||||
|
return commands.importCommand(message, args).catch(() => {
|
||||||
|
expect(message.reply).toHaveBeenCalledTimes(2);
|
||||||
|
expect(message.reply).toHaveBeenNthCalledWith(1, {content: 'normal content', files: [{name: 'test.txt', data: 'test.txt' }],});
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// restore the spy created with spyOn
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
})
|
||||||
100
tests/helpers/importHelper.test.js
Normal file
100
tests/helpers/importHelper.test.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
const {enums} = require('../../src/enums.js');
|
||||||
|
const fetchMock = require('jest-fetch-mock');
|
||||||
|
|
||||||
|
jest.mock('../../src/helpers/memberHelper.js', () => {
|
||||||
|
return {
|
||||||
|
memberHelper: {
|
||||||
|
addFullMember: jest.fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
fetchMock.enableMocks();
|
||||||
|
const {memberHelper} = require("../../src/helpers/memberHelper.js");
|
||||||
|
const {importHelper} = require('../../src/helpers/importHelper.js');
|
||||||
|
|
||||||
|
describe('importHelper', () => {
|
||||||
|
const authorId = '123';
|
||||||
|
const attachmentUrl = 'system.json';
|
||||||
|
const mockImportedMember = {
|
||||||
|
proxy_tags: [{
|
||||||
|
prefix: "SP{",
|
||||||
|
suffix: "}"
|
||||||
|
}],
|
||||||
|
display_name: "SomePerson",
|
||||||
|
avatar_url: 'oya.png',
|
||||||
|
name: 'somePerson'
|
||||||
|
}
|
||||||
|
const mockData = {
|
||||||
|
members: [mockImportedMember]
|
||||||
|
};
|
||||||
|
const mockAddReturnMember = {
|
||||||
|
proxy: "SP{text}",
|
||||||
|
displayname: "SomePerson",
|
||||||
|
propic: 'oya.png',
|
||||||
|
name: 'somePerson'
|
||||||
|
}
|
||||||
|
const mockAddReturn = {
|
||||||
|
member: mockAddReturnMember,
|
||||||
|
errors: []
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
global.fetch = jest.fn();
|
||||||
|
global.fetch = jest.fn().mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
json: () => Promise.resolve(mockData)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('pluralKitImport', () => {
|
||||||
|
|
||||||
|
test('if no attachment URL, throws error', () => {
|
||||||
|
return importHelper.pluralKitImport(authorId).catch((e) => {
|
||||||
|
expect(e).toEqual(new Error(enums.err.NOT_JSON_FILE));
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if attachment URL, calls fetch and addFullMember and returns value', () => {
|
||||||
|
memberHelper.addFullMember.mockResolvedValue(mockAddReturn);
|
||||||
|
return importHelper.pluralKitImport(authorId, attachmentUrl).then((res) => {
|
||||||
|
expect(fetch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(fetch).toHaveBeenCalledWith(attachmentUrl);
|
||||||
|
expect(memberHelper.addFullMember).toHaveBeenCalledWith(authorId, mockImportedMember.name, mockImportedMember.display_name, 'SP{text}', mockImportedMember.avatar_url);
|
||||||
|
expect(res).toEqual(`Successfully added members: ${mockAddReturnMember.name}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if addFullMember returns nothing, return correct enum', () => {
|
||||||
|
memberHelper.addFullMember.mockResolvedValue();
|
||||||
|
return importHelper.pluralKitImport(authorId, attachmentUrl).catch((res) => {
|
||||||
|
expect(res).toEqual(new AggregateError([], enums.err.NO_MEMBERS_IMPORTED));
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if addFullMember returns nothing and throws error, catch and return error', () => {
|
||||||
|
memberHelper.addFullMember.mockResolvedValue(new Error('error'));
|
||||||
|
return importHelper.pluralKitImport(authorId, attachmentUrl).catch((res) => {
|
||||||
|
expect(res).toEqual(new AggregateError([new Error('error')], enums.err.NO_MEMBERS_IMPORTED))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if addFullMember returns member but also contains error, return member and error', () => {
|
||||||
|
// Arrange
|
||||||
|
const memberObj = {errors: ['error'], member: mockAddReturnMember};
|
||||||
|
memberHelper.addFullMember.mockResolvedValue(memberObj);
|
||||||
|
// Act
|
||||||
|
return importHelper.pluralKitImport(authorId, attachmentUrl).catch((res) => {
|
||||||
|
// Assert
|
||||||
|
expect(res).toEqual(new AggregateError(['error'], `Successfully added members: ${mockAddReturnMember.name}`))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// restore the spy created with spyOn
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
})
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,6 @@
|
|||||||
const env = require('dotenv');
|
const env = require('dotenv');
|
||||||
env.config();
|
env.config();
|
||||||
|
|
||||||
const {memberHelper} = require("../../src/helpers/memberHelper.js");
|
|
||||||
const {Message} = require("@fluxerjs/core");
|
|
||||||
const {fs} = require('fs');
|
|
||||||
const {enums} = require('../../src/enums');
|
|
||||||
const {tmp, setGracefulCleanup} = require('tmp');
|
|
||||||
|
|
||||||
jest.mock('../../src/helpers/memberHelper.js', () => {
|
jest.mock('../../src/helpers/memberHelper.js', () => {
|
||||||
return {memberHelper: {
|
return {memberHelper: {
|
||||||
@@ -13,10 +8,7 @@ jest.mock('../../src/helpers/memberHelper.js', () => {
|
|||||||
}}
|
}}
|
||||||
})
|
})
|
||||||
|
|
||||||
jest.mock('tmp');
|
const {memberHelper} = require("../../src/helpers/memberHelper.js");
|
||||||
jest.mock('fs');
|
|
||||||
jest.mock('@fluxerjs/core');
|
|
||||||
|
|
||||||
const {messageHelper} = require("../../src/helpers/messageHelper.js");
|
const {messageHelper} = require("../../src/helpers/messageHelper.js");
|
||||||
|
|
||||||
describe('messageHelper', () => {
|
describe('messageHelper', () => {
|
||||||
|
|||||||
19
tests/helpers/utils.test.js
Normal file
19
tests/helpers/utils.test.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
const {enums} = require("../../src/enums");
|
||||||
|
|
||||||
|
const fetchMock = require('jest-fetch-mock');
|
||||||
|
fetchMock.enableMocks();
|
||||||
|
|
||||||
|
const {utils} = require("../../src/helpers/utils.js");
|
||||||
|
|
||||||
|
describe('utils', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetModules();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// restore the spy created with spyOn
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user