diff --git a/.env.template b/.dev.env similarity index 60% rename from .env.template rename to .dev.env index a247929..bb123f5 100644 --- a/.env.template +++ b/.dev.env @@ -7,7 +7,19 @@ # any secret values. BOT_TOKEN= -BOT_VER=3.0.7 +BOT_VER=3.1 BOT_AUTHOR=Vylpes BOT_DATE=08 May 2023 BOT_OWNERID=147392775707426816 +BOT_CLIENTID=682942374040961060 + +ABOUT_FUNDING=https://ko-fi.com/vylpes +ABOUT_REPO=https://gitea.vylpes.xyz/RabbitLabs/vylbot-app + +DB_HOST=127.0.0.1 +DB_PORT=3101 +DB_NAME=vylbot +DB_AUTH_USER=dev +DB_AUTH_PASS=dev +DB_SYNC=true +DB_LOGGING=true \ No newline at end of file diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..ae31a87 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,72 @@ +--- + +kind: pipeline +name: deployment + +steps: +- name: deploy + image: appleboy/drone-ssh + settings: + host: 192.168.68.121 + username: vylpes + password: + from_secret: ssh_password + port: 22 + script: + - sh /home/vylpes/scripts/vylbot/deploy_prod.sh + +trigger: + event: + - tag + +--- + +kind: pipeline +name: staging + +steps: +- name: stage + image: appleboy/drone-ssh + settings: + host: 192.168.68.121 + username: vylpes + password: + from_secret: ssh_password + port: 22 + script: + - sh /home/vylpes/scripts/vylbot/deploy_stage.sh + +trigger: + branch: + - develop + event: + - push + +--- + +kind: pipeline +name: integration + +steps: +- name: build + image: node + commands: + - yarn install --frozen-lockfile + - yarn build + +- name: test + image: node + commands: + - yarn install --frozen-lockfile + - yarn test + +trigger: + branch: + - main + - develop + - hotfix/* + - feature/* + - renovate/* + event: + - push + - pull_request diff --git a/.gitea/ISSUE_TEMPLATE.md b/.gitea/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..090d6b3 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE.md @@ -0,0 +1,18 @@ +Epic: \ +Story Points: + +--- + +*No description* + +## Acceptance Criteria + +*No acceptance criteria* + +## Subtasks + +*No subtasks* + +## Notes + +*No notes* \ No newline at end of file diff --git a/.gitea/PULL_REQUEST_TEMPLATE.md b/.gitea/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..698d07e --- /dev/null +++ b/.gitea/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,29 @@ +# Description + +Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. + +Fixes # (issue) + +## Type of change + +Please delete options that are not relevant. + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +# How Has This Been Tested? + +Please describe the tests that you ran to verify the changes. Provide instructions so we can reproduce. Please also list any relevant details to your test configuration. + +# Checklist + +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that provde my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] Any dependent changes have been merged and published in downstream modules \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..f3d5c41 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/epic.md b/.github/ISSUE_TEMPLATE/epic.md new file mode 100644 index 0000000..8dfd3c5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/epic.md @@ -0,0 +1,16 @@ +--- +name: Epic +about: Agile Epic +title: '' +labels: epic +assignees: '' + +--- + +*Description here* + +## Stories +*Stories linked to this epic* + +## Bugs +*Bugs linked to this epic* diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..11fc491 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/user-story.md b/.github/ISSUE_TEMPLATE/user-story.md new file mode 100644 index 0000000..644fad5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/user-story.md @@ -0,0 +1,24 @@ +--- +name: User Story +about: Agile User Story +title: '' +labels: needs criteria, story +assignees: '' + +--- + +Epic Link: N/A +Story Points: N/A + +--- + +*Description here* + +## Acceptance Criteria +*Add your ACs here* + +## Notes +*Any extra information* + +## Subtasks +*Add technical subtasks here* diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..a773253 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,30 @@ +# Description + +Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. + +Fixes # (issue) + +## Type of change + +Please delete options that are not relevant. + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +# How Has This Been Tested? + +Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration + + +# Checklist: + +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] Any dependent changes have been merged and published in downstream modules \ No newline at end of file diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml deleted file mode 100644 index 1f76252..0000000 --- a/.github/workflows/testing.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Testing - -on: - pull_request: - branches: - - main - - develop - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [16.x] - - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - run: yarn install - - run: yarn build - - run: yarn test - diff --git a/.prod.env b/.prod.env new file mode 100644 index 0000000..25cac3d --- /dev/null +++ b/.prod.env @@ -0,0 +1,24 @@ +# Security Warning! Do not commit this file to any VCS! +# This is a local file to speed up development process, +# so you don't have to change your environment variables. +# +# This is not applied to `.env.template`! +# Template files must be committed to the VCS, but must not contain +# any secret values. + +BOT_TOKEN= +BOT_VER=3.1 +BOT_AUTHOR=Vylpes +BOT_OWNERID=147392775707426816 +BOT_CLIENTID=680083120896081954 + +ABOUT_FUNDING=https://ko-fi.com/vylpes +ABOUT_REPO=https://gitea.vylpes.xyz/RabbitLabs/vylbot-app + +DB_HOST=127.0.0.1 +DB_PORT=3121 +DB_NAME=vylbot +DB_AUTH_USER=prod +DB_AUTH_PASS=prod +DB_SYNC=false +DB_LOGGING=false \ No newline at end of file diff --git a/.stage.env b/.stage.env new file mode 100644 index 0000000..383569a --- /dev/null +++ b/.stage.env @@ -0,0 +1,24 @@ +# Security Warning! Do not commit this file to any VCS! +# This is a local file to speed up development process, +# so you don't have to change your environment variables. +# +# This is not applied to `.env.template`! +# Template files must be committed to the VCS, but must not contain +# any secret values. + +BOT_TOKEN= +BOT_VER=3.1 +BOT_AUTHOR=Vylpes +BOT_OWNERID=147392775707426816 +BOT_CLIENTID=1016767908740857949 + +ABOUT_FUNDING=https://ko-fi.com/vylpes +ABOUT_REPO=https://gitea.vylpes.xyz/RabbitLabs/vylbot-app + +DB_HOST=127.0.0.1 +DB_PORT=3111 +DB_NAME=vylbot +DB_AUTH_USER=stage +DB_AUTH_PASS=stage +DB_SYNC=false +DB_LOGGING=false \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5b5a4b9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Vylpes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/data/rules/290149509969739776.json b/data/rules/290149509969739776.json index 9fa7a1e..a01ef0c 100644 --- a/data/rules/290149509969739776.json +++ b/data/rules/290149509969739776.json @@ -6,7 +6,7 @@ "title": "Vylpes' Den", "description": [ "Welcome to Vylpes' Den! Make sure to say hi!", - "Invite link: https://discord.gg/UyAhAVp" + "Invite link: https://go.vylpes.xyz/A6HcA" ] }, { @@ -70,19 +70,17 @@ "description": [ "This server uses a bot made by me, VylBot, to help moderate the server.", "For more information on it, see the GitHub repositories:", - "https://github.com/Vylpes/vylbot-core", - "https://github.com/Vylpes/vylbot-app" + "https://gitea.vylpes.xyz/rabbitlabs/vylbot-app" ] }, { "title": "Links", "description": [ - "YouTube: https://www.youtube.com/channel/UCwPlzKwCmP5Q9bCX3fHk2BA", + "YouTube: https://www.youtube.com/@vylpes", "Patreon: https://www.patreon.com/vylpes", "Twitch: https://www.twitch.tv/vylpes_", - "Twitter: https://twitter.com/vylpes", - "Blog: https://vylpes.xyz" + "Twitter: https://twitter.com/vylpes" ], - "footer": "Last updated 01/02/2022" + "footer": "Last updated 20/06/2023" } ] \ No newline at end of file diff --git a/data/rules/442730357897429002.json b/data/rules/442730357897429002.json index 6fef8cd..bf195b1 100644 --- a/data/rules/442730357897429002.json +++ b/data/rules/442730357897429002.json @@ -1,88 +1,12 @@ [ { - "image": "https://i.imgur.com/bjH1gza.png" - }, - { - "title": "Bot Testing Ground", + "title": "Welcome to Mankalor's Discord Server!", "description": [ - "Welcome to Vylpes' Den! Make sure to say hi!", - "Invite link: https://discord.gg/UyAhAVp" - ] - }, - { - "title": "Discord TOS", - "description": [ - "All servers are required to follow the Discord Terms of Service. This includes minimum age requirements (13+). If the moderation team discover a breach of TOS we are required by discord to ban. Make sure you know them!", - "https://discord.com/terms" - ] - }, - { - "title": "Rules", - "description": [ - "**English Only**", - "In order for everyone to understand each other we would like to ask everyone to speak in English only.", + "*You must follow Discord's TOS, including the rule where", "you must be 13 years or older.", + "If moderators know you're under 13, we will have to ban you!*", "", - "**No NSFW or Obscene Content**", - "This includes text, images, or links featuring nudity, sex, hard violence, or other graphically disturbing content.", - "", - "**Treat Everyone with Respect**", - "Absolutely no harassment, witch hunting, sexism, racism, or hate speech will be tolerated.", - "", - "**No spam or self promotion**", - "Outside of #self-promo. This includes DMing fellow members.", - "", - "**Keep Politics to #general**", - "And make sure it doesn't become too heated. Debate don't argue.", - "", - "**Drama From Other Servers**", - "Please don't bring up drama from other servers, keep that to DMs", - "", - "**Bot Abuse**", - "Don't abuse the bots or you will be blocked from using them", - "", - "**Event Spoilers**", - "Contents of events and keynotes, such as the Nintendo Direct, must be spoken about in events, this rule applies for up to 24 hours after the event ends. Even though we will only enforce talking there for a set time, please be considerate of those who haven't watched the event yet." + "You need to input a code in *#entry* which is somewhere in this message before you can start chatting, so read the server rules and info below.", + "If you still don't see the other channels after writing this code, message a moderator. For any issues with this bot, message <@147392775707426816>." ] - }, - { - "title": "Moderators Discretion", - "description": [ - "Don't argue with a mod's decision. A moderator's choice is final. If you have an issue with a member of the mod team DM me (Vylpes#0001)." - ] - }, - { - "title": "Supporters", - "description": [ - "If you are a Twitch Subscriber or a Patreon Member and have linked your profiles to your discord account you will get exclusive access to the Vylpes Plus channels, including early access to videos!" - ] - }, - { - "title": "Self-Assignable Roles", - "description": [ - "If you want to assign yourself roles, go to #bot-stuff and type v!role . The current roles you can get are:", - "Notify: Get pinged when a new stream or video releases.", - "VotePings: Get pinged when I start a new poll", - "ProjectUpdates: Get pinged when I update my projects as well as new for them" - ] - }, - { - "title": "VylBot", - "description": [ - "This server uses a bot made by me, VylBot, to help moderate the server.", - "For more information on it, see the GitHub repositories:", - "https://github.com/Vylpes/vylbot-core", - "https://github.com/Vylpes/vylbot-app" - ] - }, - { - "title": "Links", - "description": [ - "YouTube: https://www.youtube.com/channel/UCwPlzKwCmP5Q9bCX3fHk2BA", - "Patreon: https://www.patreon.com/vylpes", - "Twitch: https://www.twitch.tv/vylpes_", - "Twitter: https://twitter.com/vylpes", - "Blog: https://vylpes.xyz" - ], - "footer": "Last updated 01/02/2022" } ] \ No newline at end of file diff --git a/data/rules/501231711271780357.json b/data/rules/501231711271780357.json index 324c40e..5b1ef37 100644 --- a/data/rules/501231711271780357.json +++ b/data/rules/501231711271780357.json @@ -6,7 +6,7 @@ "If moderators know you're under 13, we will have to ban you!*", "", "You need to input a code in *#entry* which is somewhere in this message before you can start chatting, so read the server rules and info below.", - "If you still don't see the other channels after writing this code, message a moderator. For any issues with this bot, message Vylpes#5725." + "If you still don't see the other channels after writing this code, message a moderator. For any issues with this bot, message <@147392775707426816>." ] }, { diff --git a/database/3.1/1662399171315-CreateBase/Up/01-table/Audit.sql b/database/3.1/1662399171315-CreateBase/Up/01-table/Audit.sql new file mode 100644 index 0000000..fa4db1b --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/01-table/Audit.sql @@ -0,0 +1,11 @@ +CREATE TABLE `audit` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL, + `AuditId` varchar(255) NOT NULL, + `UserId` varchar(255) NOT NULL, + `AuditType` int NOT NULL, + `Reason` varchar(255) NOT NULL, + `ModeratorId` varchar(255) NOT NULL, + `ServerId` varchar(255) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/01-table/IgnoredChannel.sql b/database/3.1/1662399171315-CreateBase/Up/01-table/IgnoredChannel.sql new file mode 100644 index 0000000..93d919b --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/01-table/IgnoredChannel.sql @@ -0,0 +1,5 @@ +CREATE TABLE `ignored_channel` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/01-table/Lobby.sql b/database/3.1/1662399171315-CreateBase/Up/01-table/Lobby.sql new file mode 100644 index 0000000..e8ec996 --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/01-table/Lobby.sql @@ -0,0 +1,10 @@ +CREATE TABLE `lobby` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL, + `ChannelId` varchar(255) NOT NULL, + `RoleId` varchar(255) NOT NULL, + `Cooldown` int NOT NULL, + `LastUsed` datetime NOT NULL, + `Name` varchar(255) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/01-table/Role.sql b/database/3.1/1662399171315-CreateBase/Up/01-table/Role.sql new file mode 100644 index 0000000..7c781db --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/01-table/Role.sql @@ -0,0 +1,7 @@ +CREATE TABLE `role` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL, + `RoleId` varchar(255) NOT NULL, + `serverId` varchar(255) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/01-table/Server.sql b/database/3.1/1662399171315-CreateBase/Up/01-table/Server.sql new file mode 100644 index 0000000..c6c721f --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/01-table/Server.sql @@ -0,0 +1,5 @@ +CREATE TABLE `server` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/01-table/Setting.sql b/database/3.1/1662399171315-CreateBase/Up/01-table/Setting.sql new file mode 100644 index 0000000..dac609a --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/01-table/Setting.sql @@ -0,0 +1,8 @@ +CREATE TABLE `setting` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL, + `Key` varchar(255) NOT NULL, + `Value` varchar(255) NOT NULL, + `serverId` varchar(255) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/02-key/Audit.sql b/database/3.1/1662399171315-CreateBase/Up/02-key/Audit.sql new file mode 100644 index 0000000..b8411a1 --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/02-key/Audit.sql @@ -0,0 +1,2 @@ +ALTER TABLE `audit` + ADD PRIMARY KEY (`Id`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/02-key/IgnoredChannel.sql b/database/3.1/1662399171315-CreateBase/Up/02-key/IgnoredChannel.sql new file mode 100644 index 0000000..1288be1 --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/02-key/IgnoredChannel.sql @@ -0,0 +1,2 @@ +ALTER TABLE `ignored_channel` + ADD PRIMARY KEY (`Id`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/02-key/Lobby.sql b/database/3.1/1662399171315-CreateBase/Up/02-key/Lobby.sql new file mode 100644 index 0000000..e8e34ef --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/02-key/Lobby.sql @@ -0,0 +1,2 @@ +ALTER TABLE `lobby` + ADD PRIMARY KEY (`Id`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/02-key/Role.sql b/database/3.1/1662399171315-CreateBase/Up/02-key/Role.sql new file mode 100644 index 0000000..5d0a591 --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/02-key/Role.sql @@ -0,0 +1,3 @@ +ALTER TABLE `role` + ADD PRIMARY KEY (`Id`), + ADD KEY `FK_d9e438d88cfb64f7f8e1ae593c3` (`serverId`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/02-key/Server.sql b/database/3.1/1662399171315-CreateBase/Up/02-key/Server.sql new file mode 100644 index 0000000..951587f --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/02-key/Server.sql @@ -0,0 +1,2 @@ +ALTER TABLE `server` + ADD PRIMARY KEY (`Id`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/02-key/Setting.sql b/database/3.1/1662399171315-CreateBase/Up/02-key/Setting.sql new file mode 100644 index 0000000..8d03bec --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/02-key/Setting.sql @@ -0,0 +1,3 @@ +ALTER TABLE `setting` + ADD PRIMARY KEY (`Id`), + ADD KEY `FK_a3623ec541bdb12fa0f58bdfde7` (`serverId`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/03-constraint/Role.sql b/database/3.1/1662399171315-CreateBase/Up/03-constraint/Role.sql new file mode 100644 index 0000000..22ffbc0 --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/03-constraint/Role.sql @@ -0,0 +1,2 @@ +ALTER TABLE `role` + ADD CONSTRAINT `FK_d9e438d88cfb64f7f8e1ae593c3` FOREIGN KEY (`serverId`) REFERENCES `server` (`Id`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/03-constraint/Setting.sql b/database/3.1/1662399171315-CreateBase/Up/03-constraint/Setting.sql new file mode 100644 index 0000000..d5172ac --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/03-constraint/Setting.sql @@ -0,0 +1,2 @@ +ALTER TABLE `setting` + ADD CONSTRAINT `FK_a3623ec541bdb12fa0f58bdfde7` FOREIGN KEY (`serverId`) REFERENCES `server` (`Id`); \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..ae37fc5 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,31 @@ +version: "3.9" + +volumes: + prod_database_data: + +services: + # discord: + # build: . + + database: + image: mysql/mysql-server + command: --default-authentication-plugin=mysql_native_password + restart: always + environment: + - MYSQL_DATABASE=vylbot + - MYSQL_USER=prod + - MYSQL_PASSWORD=prod + - MYSQL_ROOT_PASSWORD=root + - MYSQL_ROOT_HOST=0.0.0.0 + ports: + - "3121:3306" + volumes: + - prod_database_data:/var/lib/mysql + + phpmyadmin: + image: phpmyadmin + restart: always + ports: + - "3122:80" + environment: + - PMA_ARBITRARY=1 \ No newline at end of file diff --git a/docker-compose.stage.yml b/docker-compose.stage.yml new file mode 100644 index 0000000..e25a6f1 --- /dev/null +++ b/docker-compose.stage.yml @@ -0,0 +1,31 @@ +version: "3.9" + +volumes: + stage_database_data: + +services: + # discord: + # build: . + + database: + image: mysql/mysql-server + command: --default-authentication-plugin=mysql_native_password + restart: always + environment: + - MYSQL_DATABASE=vylbot + - MYSQL_USER=stage + - MYSQL_PASSWORD=stage + - MYSQL_ROOT_PASSWORD=root + - MYSQL_ROOT_HOST=0.0.0.0 + ports: + - "3111:3306" + volumes: + - stage_database_data:/var/lib/mysql + + phpmyadmin: + image: phpmyadmin + restart: always + ports: + - "3112:80" + environment: + - PMA_ARBITRARY=1 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index a10aebd..92ce70a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,8 @@ version: "3.9" + +volumes: + dev_database_data: + services: # discord: # build: . @@ -12,13 +16,16 @@ services: - MYSQL_USER=dev - MYSQL_PASSWORD=dev - MYSQL_ROOT_PASSWORD=root + - MYSQL_ROOT_HOST=0.0.0.0 ports: - - 3306:3306 + - "3101:3306" + volumes: + - dev_database_data:/var/lib/mysql phpmyadmin: image: phpmyadmin restart: always ports: - - 8080:80 + - "3102:80" environment: - PMA_ARBITRARY=1 \ No newline at end of file diff --git a/package.json b/package.json index 03cf155..4aa91b7 100644 --- a/package.json +++ b/package.json @@ -1,41 +1,46 @@ { "name": "vylbot-app", - "version": "3.0.7", + "version": "3.1.0", "description": "A discord bot made for Vylpes' Den", "main": "./dist/vylbot", "typings": "./dist", "scripts": { + "clean": "rm -rf node_modules/ dist/", "build": "tsc", "start": "node ./dist/vylbot", "test": "jest", - "db:up": "typeorm migration:run", - "db:down": "typeorm migration:revert" + "db:up": "typeorm migration:run -d dist/database/dataSources/appDataSource.js", + "db:down": "typeorm migration:revert -d dist/database/dataSources/appDataSource.js" }, "repository": { "type": "git", - "url": "git+https://github.com/Vylpes/vylbot-app.git" + "url": "https://github.com/Vylpes/vylbot-app" }, "author": "Vylpes", "license": "MIT", - "bugs": "https://github.com/Vylpes/vylbot-app/issues", + "bugs": { + "url": "https://github.com/Vylpes/vylbot-app/issues", + "email": "helpdesk@vylpes.com" + }, "homepage": "https://github.com/Vylpes/vylbot-app", + "funding": "https://ko-fi.com/vylpes", "dependencies": { - "@types/jest": "^27.0.3", - "@types/uuid": "^8.3.4", - "discord.js": "^13.6.0", - "dotenv": "^10.0.0", - "emoji-regex": "^9.2.0", - "jest": "^27.4.5", - "jest-mock-extended": "^2.0.4", - "minimatch": "3.1.2", + "@discordjs/rest": "^1.1.0", + "@types/jest": "^29.0.0", + "@types/uuid": "^9.0.0", + "discord.js": "^14.3.0", + "dotenv": "^16.0.0", + "emoji-regex": "^10.0.0", + "jest": "^29.0.0", + "jest-mock-extended": "^3.0.0", + "minimatch": "9.0.2", "mysql": "^2.18.1", - "random-bunny": "^2.0.0", - "ts-jest": "^27.1.2", - "typeorm": "0.3.14", - "uuid": "^8.3.2" + "random-bunny": "^2.0.5", + "ts-jest": "^29.0.0", + "typeorm": "0.3.17" }, "devDependencies": { - "@types/node": "^16.11.10", - "typescript": "^4.5.2" + "@types/node": "^20.0.0", + "typescript": "^5.0.0" } } diff --git a/scripts/deploy_prod.sh b/scripts/deploy_prod.sh new file mode 100644 index 0000000..7adef0a --- /dev/null +++ b/scripts/deploy_prod.sh @@ -0,0 +1,23 @@ +#! /bin/bash + +export PATH="$HOME/.yarn/bin:$PATH" +export PATH="$HOME/.nodeuse/bin:$PATH" + +export BOT_TOKEN=$(cat $HOME/scripts/vylbot/prod_key.txt) + +cd ~/apps/vylbot/vylbot_prod \ +&& git checkout main \ +&& git fetch \ +&& git pull \ +&& docker compose --file docker-compose.prod.yml down \ +&& (pm2 stop vylbot_prod || true) \ +&& (pm2 delete vylbot_prod || true) \ +&& cp .prod.env .env \ +&& yarn clean \ +&& yarn install --frozen-lockfile \ +&& yarn build \ +&& docker compose --file docker-compose.prod.yml up -d \ +&& echo "Sleeping for 10 seconds to let database load..." \ +&& sleep 10 \ +&& yarn run db:up \ +&& NODE_ENV=production pm2 start --name vylbot_prod dist/vylbot.js \ No newline at end of file diff --git a/scripts/deploy_stage.sh b/scripts/deploy_stage.sh new file mode 100644 index 0000000..d12a421 --- /dev/null +++ b/scripts/deploy_stage.sh @@ -0,0 +1,23 @@ +#! /bin/bash + +export PATH="$HOME/.yarn/bin:$PATH" +export PATH="$HOME/.nodeuse/bin:$PATH" + +export BOT_TOKEN=$(cat $HOME/scripts/vylbot/stage_key.txt) + +cd ~/apps/vylbot/vylbot_stage \ +&& git checkout develop \ +&& git fetch \ +&& git pull \ +&& docker compose --file docker-compose.stage.yml down \ +&& (pm2 stop vylbot_stage || true) \ +&& (pm2 delete vylbot_stage || true) \ +&& cp .stage.env .env \ +&& yarn clean \ +&& yarn install --frozen-lockfile \ +&& yarn build \ +&& docker compose --file docker-compose.stage.yml up -d \ +&& echo "Sleeping for 10 seconds to let database load..." \ +&& sleep 10 \ +&& yarn run db:up \ +&& NODE_ENV=production pm2 start --name vylbot_stage dist/vylbot.js \ No newline at end of file diff --git a/src/client/client.ts b/src/client/client.ts index 36f738f..d544ec7 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -1,19 +1,19 @@ import { Client } from "discord.js"; import * as dotenv from "dotenv"; import { createConnection } from "typeorm"; -import DefaultValues from "../constants/DefaultValues"; +import { EventType } from "../constants/EventType"; import ICommandItem from "../contracts/ICommandItem"; import IEventItem from "../contracts/IEventItem"; import { Command } from "../type/command"; -import { Event } from "../type/event"; import { Events } from "./events"; import { Util } from "./util"; +import AppDataSource from "../database/dataSources/appDataSource"; export class CoreClient extends Client { private static _commandItems: ICommandItem[]; private static _eventItems: IEventItem[]; - + private _events: Events; private _util: Util; @@ -25,12 +25,10 @@ export class CoreClient extends Client { return this._eventItems; } - constructor(intents: number[], devmode: boolean = false) { + constructor(intents: number[]) { super({ intents: intents }); dotenv.config(); - DefaultValues.useDevPrefix = devmode; - CoreClient._commandItems = []; CoreClient._eventItems = []; @@ -44,19 +42,17 @@ export class CoreClient extends Client { return; } - await createConnection().catch(e => { - console.error(e); - return; - }); + await AppDataSource.initialize() + .then(() => console.log("Data Source Initialized")) + .catch((err) => console.error("Error Initialising Data Source", err)); - super.on("messageCreate", (message) => { - this._events.onMessageCreate(message, CoreClient._commandItems) - }); + super.on("interactionCreate", this._events.onInteractionCreate); super.on("ready", this._events.onReady); - super.login(process.env.BOT_TOKEN); + await super.login(process.env.BOT_TOKEN); this._util.loadEvents(this, CoreClient._eventItems); + this._util.loadSlashCommands(this); } public static RegisterCommand(name: string, command: Command, serverId?: string) { @@ -69,9 +65,10 @@ export class CoreClient extends Client { CoreClient._commandItems.push(item); } - public static RegisterEvent(event: Event) { + public static RegisterEvent(eventType: EventType, func: Function) { const item: IEventItem = { - Event: event, + EventType: eventType, + ExecutionFunction: func, }; CoreClient._eventItems.push(item); diff --git a/src/client/events.ts b/src/client/events.ts index 059df52..2d36001 100644 --- a/src/client/events.ts +++ b/src/client/events.ts @@ -1,33 +1,40 @@ -import { Message } from "discord.js"; +import { Interaction } from "discord.js"; import ICommandItem from "../contracts/ICommandItem"; import SettingsHelper from "../helpers/SettingsHelper"; -import { Util } from "./util"; +import { CoreClient } from "./client"; export class Events { - private _util: Util; + public async onInteractionCreate(interaction: Interaction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.guildId) return; - constructor() { - this._util = new Util(); - } + const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId); + const disabledCommands = disabledCommandsString?.split(","); - // Emit when a message is sent - // Used to check for commands - public async onMessageCreate(message: Message, commands: ICommandItem[]) { - if (!message.guild) return; - if (message.author.bot) return; + const disabledCommandsMessage = await SettingsHelper.GetSetting("commands.disabled.message", interaction.guildId); - const prefix = await SettingsHelper.GetSetting("bot.prefix", message.guild.id); - - if (!prefix) return; - - if (message.content.substring(0, prefix.length).toLowerCase() == prefix.toLowerCase()) { - const args = message.content.substring(prefix.length).split(" "); - const name = args.shift(); - - if (!name) return; - - await this._util.loadCommand(name, args, message, commands); + if (disabledCommands?.find(x => x == interaction.commandName)) { + await interaction.reply(disabledCommandsMessage || "This command is disabled."); + return; } + + const item = CoreClient.commandItems.find(x => x.Name == interaction.commandName && !x.ServerId); + const itemForServer = CoreClient.commandItems.find(x => x.Name == interaction.commandName && x.ServerId == interaction.guildId); + + let itemToUse: ICommandItem; + + if (!itemForServer) { + if (!item) { + await interaction.reply('Command not found'); + return; + } + + itemToUse = item; + } else { + itemToUse = itemForServer; + } + + itemToUse.Command.execute(interaction); } // Emit when bot is logged in and ready to use diff --git a/src/client/util.ts b/src/client/util.ts index f136e26..a9c2bad 100644 --- a/src/client/util.ts +++ b/src/client/util.ts @@ -1,101 +1,95 @@ -// Required Components -import { Client, Message } from "discord.js"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandItem from "../contracts/ICommandItem"; +import { Client, REST, Routes, SlashCommandBuilder } from "discord.js"; +import { EventType } from "../constants/EventType"; import IEventItem from "../contracts/IEventItem"; -import SettingsHelper from "../helpers/SettingsHelper"; -import StringTools from "../helpers/StringTools"; -import { CommandResponse } from "../constants/CommandResponse"; -import ErrorMessages from "../constants/ErrorMessages"; +import { CoreClient } from "./client"; -// Util Class export class Util { - public async loadCommand(name: string, args: string[], message: Message, commands: ICommandItem[]) { - if (!message.member) return; - if (!message.guild) return; + public loadSlashCommands(client: Client) { + const registeredCommands = CoreClient.commandItems; - const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", message.guild?.id); - const disabledCommands = disabledCommandsString?.split(","); + const globalCommands = registeredCommands.filter(x => !x.ServerId); + const guildCommands = registeredCommands.filter(x => x.ServerId); - if (disabledCommands?.find(x => x == name)) { - message.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled."); - return; - } + const globalCommandData: SlashCommandBuilder[] = globalCommands + .filter(x => x.Command.CommandBuilder) + .flatMap(x => x.Command.CommandBuilder); - const item = commands.find(x => x.Name == name && !x.ServerId); - const itemForServer = commands.find(x => x.Name == name && x.ServerId == message.guild?.id); + const guildIds: string[] = []; - let itemToUse: ICommandItem; - - if (!itemForServer) { - if (!item) { - message.reply('Command not found'); - return; + for (let command of guildCommands) { + if (!guildIds.find(x => x == command.ServerId)) { + guildIds.push(command.ServerId!); } - - itemToUse = item; - } else { - itemToUse = itemForServer; } - const requiredRoles = itemToUse.Command.Roles; + const rest = new REST({ version: '10' }).setToken(process.env.BOT_TOKEN!); - if (message.author.id != process.env.BOT_OWNERID && message.author.id != message.guild.ownerId) { - for (const i in requiredRoles) { - if (message.guild) { - const setting = await SettingsHelper.GetSetting(`role.${requiredRoles[i]}`, message.guild?.id); - - if (!setting) { - message.reply("Unable to verify if you have this role, please contact your bot administrator"); - return; - } - - if (!message.member.roles.cache.find(role => role.name == setting)) { - message.reply(`You require the \`${StringTools.Capitalise(setting)}\` role to run this command`); - return; - } + rest.put( + Routes.applicationCommands(process.env.BOT_CLIENTID!), + { + body: globalCommandData + } + ); + + for (let guild of guildIds) { + const guildCommandData = guildCommands.filter(x => x.ServerId == guild) + .filter(x => x.Command.CommandBuilder) + .flatMap(x => x.Command.CommandBuilder); + + if (!client.guilds.cache.has(guild)) continue; + + rest.put( + Routes.applicationGuildCommands(process.env.BOT_CLIENTID!, guild), + { + body: guildCommandData } - } + ) } - - const context: ICommandContext = { - name: name, - args: args, - message: message - }; - - const precheckResponse = itemToUse.Command.precheck(context); - const precheckAsyncResponse = await itemToUse.Command.precheckAsync(context); - - if (precheckResponse != CommandResponse.Ok) { - message.reply(ErrorMessages.GetErrorMessage(precheckResponse)); - return; - } - - if (precheckAsyncResponse != CommandResponse.Ok) { - message.reply(ErrorMessages.GetErrorMessage(precheckAsyncResponse)); - return; - } - - itemToUse.Command.execute(context); } // Load the events loadEvents(client: Client, events: IEventItem[]) { events.forEach((e) => { - client.on('channelCreate', e.Event.channelCreate); - client.on('channelDelete', e.Event.channelDelete); - client.on('channelUpdate', e.Event.channelUpdate); - client.on('guildBanAdd', e.Event.guildBanAdd); - client.on('guildBanRemove', e.Event.guildBanRemove); - client.on('guildCreate', e.Event.guildCreate); - client.on('guildMemberAdd', e.Event.guildMemberAdd); - client.on('guildMemberRemove', e.Event.guildMemberRemove); - client.on('guildMemberUpdate', e.Event.guildMemberUpdate); - client.on('messageCreate', e.Event.messageCreate); - client.on('messageDelete', e.Event.messageDelete); - client.on('messageUpdate', e.Event.messageUpdate); - client.on('ready', e.Event.ready); + switch(e.EventType) { + case EventType.ChannelCreate: + client.on('channelCreate', (channel) => e.ExecutionFunction(channel)); + break; + case EventType.ChannelDelete: + client.on('channelDelete', (channel) => e.ExecutionFunction(channel)); + break; + case EventType.ChannelUpdate: + client.on('channelUpdate', (channel) => e.ExecutionFunction(channel)); + break; + case EventType.GuildBanAdd: + client.on('guildBanAdd', (ban) => e.ExecutionFunction(ban)); + break; + case EventType.GuildBanRemove: + client.on('guildBanRemove', (ban) => e.ExecutionFunction(ban)); + break; + case EventType.GuildCreate: + client.on('guildCreate', (guild) => e.ExecutionFunction(guild)); + break; + case EventType.GuildMemberAdd: + client.on('guildMemberAdd', (member) => e.ExecutionFunction(member)); + break; + case EventType.GuildMemberRemove: + client.on('guildMemberRemove', (member) => e.ExecutionFunction(member)); + break; + case EventType.GuildMemberUpdate: + client.on('guildMemberUpdate', (oldMember, newMember) => e.ExecutionFunction(oldMember, newMember)); + break; + case EventType.MessageCreate: + client.on('messageCreate', (message) => e.ExecutionFunction(message)); + break; + case EventType.MessageDelete: + client.on('messageDelete', (message) => e.ExecutionFunction(message)); + break; + case EventType.MessageUpdate: + client.on('messageUpdate', (oldMessage, newMessage) => e.ExecutionFunction(oldMessage, newMessage)); + break; + default: + console.error('Event not implemented.'); + } }); } } diff --git a/src/commands/501231711271780357/Lobby/add.ts b/src/commands/501231711271780357/Lobby/add.ts new file mode 100644 index 0000000..0bc3ca0 --- /dev/null +++ b/src/commands/501231711271780357/Lobby/add.ts @@ -0,0 +1,58 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../database/entities/501231711271780357/Lobby"; + +export default class AddRole extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('addlobby') + .setDescription('Add lobby channel') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addChannelOption(option => + option + .setName('channel') + .setDescription('The channel') + .setRequired(true)) + .addRoleOption(option => + option + .setName('role') + .setDescription('The role to ping on request') + .setRequired(true)) + .addNumberOption(option => + option + .setName('cooldown') + .setDescription('The cooldown in minutes') + .setRequired(true)) + .addStringOption(option => + option + .setName('name') + .setDescription('The game name') + .setRequired(true)); + } + + public override async execute(interaction: CommandInteraction) { + const channel = interaction.options.get('channel'); + const role = interaction.options.get('role'); + const cooldown = interaction.options.get('cooldown'); + const gameName = interaction.options.get('name'); + + if (!channel || !channel.channel || !role || !role.role || !cooldown || !cooldown.value || !gameName || !gameName.value) { + await interaction.reply('Fields are required.'); + return; + } + + const lobby = await eLobby.FetchOneByChannelId(channel.channel.id); + + if (lobby) { + await interaction.reply('This channel has already been setup.'); + return; + } + + const entity = new eLobby(channel.channel.id, role.role.id, cooldown.value as number, gameName.value as string); + await entity.Save(eLobby, entity); + + await interaction.reply(`Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown.value} minutes \` and will ping \`${role.name}\` on use`); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/Lobby/list.ts b/src/commands/501231711271780357/Lobby/list.ts new file mode 100644 index 0000000..41bc4a3 --- /dev/null +++ b/src/commands/501231711271780357/Lobby/list.ts @@ -0,0 +1,48 @@ +import { CacheType, CommandInteraction, EmbedBuilder, GuildBasedChannel, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../database/entities/501231711271780357/Lobby"; +import EmbedColours from "../../../constants/EmbedColours"; + +export default class ListLobby extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('listlobby') + .setDescription('Lists all channels set up as lobbies') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.guild) { + await interaction.reply('Guild not found.'); + return; + } + + const channels: eLobby[] = []; + + for (let channel of interaction.guild.channels.cache.map(x => x)) { + const lobby = await eLobby.FetchOneByChannelId(channel.id); + + if (lobby) { + channels.push(lobby); + } + } + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Lobbies") + .setDescription(`Channels: ${channels.length}`); + + for (let lobby of channels) { + embed.addFields([ + { + name: `# ${lobby.Name}`, + value: `Last Used: ${lobby.LastUsed}` + } + ]); + } + + await interaction.reply({ embeds: [ embed ]}); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/Lobby/lobby.ts b/src/commands/501231711271780357/Lobby/lobby.ts new file mode 100644 index 0000000..83849a6 --- /dev/null +++ b/src/commands/501231711271780357/Lobby/lobby.ts @@ -0,0 +1,41 @@ +import { CommandInteraction, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../database/entities/501231711271780357/Lobby"; + +export default class Lobby extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('lobby') + .setDescription('Attempt to organise a lobby'); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.channelId) return; + + const lobby = await eLobby.FetchOneByChannelId(interaction.channelId); + + if (!lobby) { + await interaction.reply('This channel is disabled from using the lobby command.'); + return; + } + + const timeNow = Date.now(); + const timeLength = lobby.Cooldown * 60 * 1000; // x minutes in ms + const timeAgo = timeNow - timeLength; + + // If it was less than x minutes ago + if (lobby.LastUsed.getTime() > timeAgo) { + const timeLeft = Math.ceil((timeLength - (timeNow - lobby.LastUsed.getTime())) / 1000 / 60); + + await interaction.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`); + return; + } + + lobby.MarkAsUsed(); + await lobby.Save(eLobby, lobby); + + await interaction.reply(`${interaction.user} would like to organise a lobby of **${lobby.Name}**! <@&${lobby.RoleId}>`); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/Lobby/remove.ts b/src/commands/501231711271780357/Lobby/remove.ts new file mode 100644 index 0000000..2f241d2 --- /dev/null +++ b/src/commands/501231711271780357/Lobby/remove.ts @@ -0,0 +1,40 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../database/entities/501231711271780357/Lobby"; +import BaseEntity from "../../../contracts/BaseEntity"; + +export default class RemoveLobby extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('removelobby') + .setDescription('Remove a lobby channel') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addChannelOption(option => + option + .setName('channel') + .setDescription('The channel') + .setRequired(true)); + } + + public override async execute(interaction: CommandInteraction) { + const channel = interaction.options.get('channel'); + + if (!channel || !channel.channel) { + await interaction.reply('Channel is required.'); + return; + } + + const entity = await eLobby.FetchOneByChannelId(channel.channel.id); + + if (!entity) { + await interaction.reply('Channel not found.'); + return; + } + + await BaseEntity.Remove(eLobby, entity); + + await interaction.reply(`Removed <#${channel.channel.id}> from the list of lobby channels`); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/entry.ts b/src/commands/501231711271780357/entry.ts index 2c0f4ae..fd246ca 100644 --- a/src/commands/501231711271780357/entry.ts +++ b/src/commands/501231711271780357/entry.ts @@ -1,5 +1,5 @@ -import { ICommandContext } from "../../contracts/ICommandContext"; -import PublicEmbed from "../../helpers/embeds/PublicEmbed"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import EmbedColours from "../../constants/EmbedColours"; import SettingsHelper from "../../helpers/SettingsHelper"; import { Command } from "../../type/command"; @@ -7,19 +7,23 @@ export default class Entry extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + super.CommandBuilder = new SlashCommandBuilder() + .setName('entry') + .setDescription('Sends the entry embed') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers); } - public override async execute(context: ICommandContext) { - if (!context.message.guild) return; + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId) return; + if (!interaction.channel) return; - const rulesChannelId = await SettingsHelper.GetSetting("channels.rules", context.message.guild.id) || "rules"; + const rulesChannelId = await SettingsHelper.GetSetting("channels.rules", interaction.guildId) || "rules"; - const embedInfo = new PublicEmbed(context, "", `Welcome to the server! Please make sure to read the rules in the <#${rulesChannelId}> channel and type the code found there in here to proceed to the main part of the server.`); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Welcome") + .setDescription(`Welcome to the server! Please make sure to read the rules in the <#${rulesChannelId}> channel and type the code found there in here to proceed to the main part of the server.`); - await embedInfo.SendToCurrentChannel(); + await interaction.channel.send({ embeds: [ embed ]}); } } \ No newline at end of file diff --git a/src/commands/501231711271780357/lobby.ts b/src/commands/501231711271780357/lobby.ts deleted file mode 100644 index bb6f2fa..0000000 --- a/src/commands/501231711271780357/lobby.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { TextChannel } from "discord.js"; -import { ICommandContext } from "../../contracts/ICommandContext"; -import { Command } from "../../type/command"; -import { default as eLobby } from "../../entity/501231711271780357/Lobby"; -import SettingsHelper from "../../helpers/SettingsHelper"; -import PublicEmbed from "../../helpers/embeds/PublicEmbed"; -import { readFileSync } from "fs"; -import ErrorEmbed from "../../helpers/embeds/ErrorEmbed"; -import BaseEntity from "../../contracts/BaseEntity"; - -export default class Lobby extends Command { - constructor() { - super(); - - super.Category = "General"; - } - - public override async execute(context: ICommandContext) { - if (!context.message.guild) return; - - switch (context.args[0]) { - case "config": - await this.UseConfig(context); - break; - default: - await this.UseDefault(context); - } - } - - // ======= - // Default - // ======= - - private async UseDefault(context: ICommandContext) { - const channel = context.message.channel as TextChannel; - const channelId = channel.id; - - const lobby = await eLobby.FetchOneByChannelId(channelId); - - if (!lobby) { - this.SendDisabled(context); - return; - } - - const timeNow = Date.now(); - const timeLength = lobby.Cooldown * 60 * 1000; // x minutes in ms - const timeAgo = timeNow - timeLength; - - // If it was less than x minutes ago - if (lobby.LastUsed.getTime() > timeAgo) { - this.SendOnCooldown(context, timeLength, new Date(timeNow), lobby.LastUsed); - return; - } - - await this.RequestLobby(context, lobby); - } - - private async RequestLobby(context: ICommandContext, lobby: eLobby) { - lobby.MarkAsUsed(); - await lobby.Save(eLobby, lobby); - - context.message.channel.send(`${context.message.author} would like to organise a lobby of **${lobby.Name}**! <@&${lobby.RoleId}>`); - } - - private SendOnCooldown(context: ICommandContext, timeLength: number, timeNow: Date, timeUsed: Date) { - const timeLeft = Math.ceil((timeLength - (timeNow.getTime() - timeUsed.getTime())) / 1000 / 60); - - context.message.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`); - } - - private SendDisabled(context: ICommandContext) { - context.message.reply("This channel hasn't been setup for lobbies."); - } - - // ====== - // Config - // ====== - private async UseConfig(context: ICommandContext) { - const moderatorRole = await SettingsHelper.GetSetting("role.moderator", context.message.guild!.id); - - if (!context.message.member?.roles.cache.find(x => x.name == moderatorRole)) { - const errorEmbed = new ErrorEmbed(context, "Sorry, you must be a moderator to be able to configure this command"); - await errorEmbed.SendToCurrentChannel(); - - return; - } - - switch (context.args[1]) { - case "add": - await this.AddLobbyConfig(context); - break; - case "remove": - await this.RemoveLobbyConfig(context); - break; - case "help": - default: - await this.SendConfigHelp(context); - } - } - - private async SendConfigHelp(context: ICommandContext) { - const helpText = readFileSync(`${process.cwd()}/data/usage/lobby.txt`).toString(); - - const embed = new PublicEmbed(context, "Configure Lobby Command", helpText); - await embed.SendToCurrentChannel(); - } - - private async AddLobbyConfig(context: ICommandContext) { - const channel = context.message.guild!.channels.cache.find(x => x.id == context.args[2]); - const role = context.message.guild!.roles.cache.find(x => x.id == context.args[3]); - const cooldown = Number(context.args[4]) || 30; - const gameName = context.args.splice(5).join(" "); - - if (!channel) { - const errorEmbed = new ErrorEmbed(context, "The channel id you provided is invalid or channel does not exist."); - errorEmbed.SendToCurrentChannel(); - - return; - } - - if (!role) { - const errorEmbed = new ErrorEmbed(context, "The role id you provided is invalid or role does not exist."); - errorEmbed.SendToCurrentChannel(); - - return; - } - - const lobby = await eLobby.FetchOneByChannelId(channel.id); - - if (lobby) { - const errorEmbed = new ErrorEmbed(context, "This channel has already been setup."); - errorEmbed.SendToCurrentChannel(); - - return; - } - - const entity = new eLobby(channel.id, role.id, cooldown, gameName); - await entity.Save(eLobby, entity); - - const embed = new PublicEmbed(context, "", `Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown} minutes\` and will ping \`${role.name}\` on use`); - await embed.SendToCurrentChannel(); - } - - private async RemoveLobbyConfig(context: ICommandContext) { - const entity = await eLobby.FetchOneByChannelId(context.args[2]); - - if (!entity) { - const errorEmbed = new ErrorEmbed(context, "The channel id you provided has not been setup as a lobby, unable to remove."); - await errorEmbed.SendToCurrentChannel(); - - return; - } - - await BaseEntity.Remove(eLobby, entity); - - const embed = new PublicEmbed(context, "", `Removed <#${context.args[2]}> from the list of lobby channels`); - await embed.SendToCurrentChannel(); - } -} \ No newline at end of file diff --git a/src/commands/Role/config.ts b/src/commands/Role/config.ts new file mode 100644 index 0000000..aeb5c67 --- /dev/null +++ b/src/commands/Role/config.ts @@ -0,0 +1,54 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../type/command"; +import { default as eRole } from "../../database/entities/Role"; +import Server from "../../database/entities/Server"; + +export default class ConfigRole extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('configrole') + .setDescription('Toggle your roles') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageRoles) + .addRoleOption(option => + option + .setName('role') + .setDescription('The role name') + .setRequired(true)); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId || !interaction.guild) return; + if (!interaction.member) return; + + const role = interaction.options.get('role'); + + if (!role || !role.role) { + await interaction.reply('Fields are required.'); + return; + } + + const existingRole = await eRole.FetchOneByRoleId(role.role.id); + + if (existingRole) { + await eRole.Remove(eRole, existingRole); + + await interaction.reply('Removed role from configuration.'); + } else { + const server = await Server.FetchOneById(Server, interaction.guildId); + + if (!server) { + await interaction.reply('This server has not been setup.'); + return; + } + + const newRole = new eRole(role.role.id); + newRole.SetServer(server); + + await newRole.Save(eRole, newRole); + + await interaction.reply('Added role to configuration.'); + } + } +} diff --git a/src/commands/Role/role.ts b/src/commands/Role/role.ts new file mode 100644 index 0000000..296b98f --- /dev/null +++ b/src/commands/Role/role.ts @@ -0,0 +1,109 @@ +import { CommandInteraction, EmbedBuilder, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../type/command"; +import { default as eRole } from "../../database/entities/Role"; +import EmbedColours from "../../constants/EmbedColours"; + +export default class Role extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('role') + .setDescription('Toggle your roles') + .addSubcommand(subcommand => + subcommand + .setName('toggle') + .setDescription('Toggle your role') + .addRoleOption(option => + option + .setName('role') + .setDescription('The role name') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('list') + .setDescription('List togglable roles')); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + + switch (interaction.options.getSubcommand()) { + case 'toggle': + await this.ToggleRole(interaction); + break; + case 'list': + await this.SendRolesList(interaction); + break; + default: + await interaction.reply('Subcommand not found.'); + } + } + + private async SendRolesList(interaction: CommandInteraction) { + const roles = await this.GetRolesList(interaction); + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Roles") + .setDescription(`Roles: ${roles.length}\n\n${roles.join("\n")}`); + + await interaction.reply({ embeds: [ embed ]}); + } + + private async ToggleRole(interaction: CommandInteraction) { + if (!interaction.guild) return; + if (!interaction.member) return; + + const roles = await this.GetRolesList(interaction); + const requestedRole = interaction.options.get('role'); + + if (!requestedRole || !requestedRole.role) { + await interaction.reply('Fields are required.'); + return; + } + + if (!roles.includes(requestedRole.role.name)) { + await interaction.reply('This role isn\'t marked as assignable.'); + return; + } + + const roleManager = interaction.member.roles as GuildMemberRoleManager; + + const userRole = roleManager.cache.find(x => x.name == requestedRole.role!.name); + const assignRole = interaction.guild.roles.cache.find(x => x.id == requestedRole.role!.id); + + if (!assignRole) return; + + if (!assignRole.editable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } + + if (!userRole) { + await roleManager.add(assignRole); + await interaction.reply(`Gave role: \`${assignRole.name}\``); + } else { + await roleManager.remove(assignRole); + await interaction.reply(`Removed role: \`${assignRole.name}\``); + } + } + + private async GetRolesList(interaction: CommandInteraction): Promise { + if (!interaction.guildId || !interaction.guild) return []; + + const rolesArray = await eRole.FetchAllByServerId(interaction.guildId); + + const roles: string[] = []; + + for (let i = 0; i < rolesArray.length; i++) { + const serverRole = interaction.guild.roles.cache.find(x => x.id == rolesArray[i].RoleId); + + if (serverRole) { + roles.push(serverRole.name); + } + } + + return roles; + } +} diff --git a/src/commands/about.ts b/src/commands/about.ts index 4246113..5da00b8 100644 --- a/src/commands/about.ts +++ b/src/commands/about.ts @@ -1,19 +1,56 @@ -import { ICommandContext } from "../contracts/ICommandContext"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; import { Command } from "../type/command"; export default class About extends Command { constructor() { super(); - super.Category = "General"; + + super.CommandBuilder = new SlashCommandBuilder() + .setName('about') + .setDescription('About VylBot'); } - public override async execute(context: ICommandContext) { - const embed = new PublicEmbed(context, "About", "") - .addField("Version", process.env.BOT_VER!) - .addField("Author", process.env.BOT_AUTHOR!) - .addField("Date", process.env.BOT_DATE!); - - await embed.SendToCurrentChannel(); + public override async execute(interaction: CommandInteraction) { + const fundingLink = process.env.ABOUT_FUNDING; + const repoLink = process.env.ABOUT_REPO; + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("About") + .setDescription("Discord Bot made by Vylpes"); + + embed.addFields([ + { + name: "Version", + value: process.env.BOT_VER!, + inline: true, + }, + { + name: "Author", + value: process.env.BOT_AUTHOR!, + inline: true, + }, + ]); + + const row = new ActionRowBuilder(); + + if (repoLink) { + row.addComponents( + new ButtonBuilder() + .setURL(repoLink) + .setLabel("Repo") + .setStyle(ButtonStyle.Link)); + } + + if (fundingLink) { + row.addComponents( + new ButtonBuilder() + .setURL(fundingLink) + .setLabel("Funding") + .setStyle(ButtonStyle.Link)); + } + + await interaction.reply({ embeds: [ embed ], components: row.components.length > 0 ? [ row ] : [] }); } } \ No newline at end of file diff --git a/src/commands/audits.ts b/src/commands/audits.ts new file mode 100644 index 0000000..c6c2366 --- /dev/null +++ b/src/commands/audits.ts @@ -0,0 +1,212 @@ +import Audit from "../database/entities/Audit"; +import AuditTools from "../helpers/AuditTools"; +import { Command } from "../type/command"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { AuditType } from "../constants/AuditType"; +import EmbedColours from "../constants/EmbedColours"; + +export default class Audits extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName("audits") + .setDescription("View audits of a particular user in the server") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addSubcommand(subcommand => + subcommand + .setName('user') + .setDescription('View all audits done against a user') + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('view') + .setDescription('View a particular audit') + .addStringOption(option => + option + .setName('auditid') + .setDescription('The audit id in caps') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('clear') + .setDescription('Clears an audit from a user') + .addStringOption(option => + option + .setName('auditid') + .setDescription('The audit id in caps') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('add') + .setDescription('Manually add an audit') + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('type') + .setDescription('The type of audit') + .setRequired(true) + .addChoices( + { name: 'General', value: AuditType.General.toString() }, + { name: 'Warn', value: AuditType.Warn.toString() }, + { name: 'Mute', value: AuditType.Mute.toString() }, + { name: 'Kick', value: AuditType.Kick.toString() }, + { name: 'Ban', value: AuditType.Ban.toString() }, + ) + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason'))); + + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + + switch (interaction.options.getSubcommand()) { + case "user": + await this.SendAuditForUser(interaction); + break; + case "view": + await this.SendAudit(interaction); + break; + case "clear": + await this.ClearAudit(interaction); + break; + case "add": + await this.AddAudit(interaction); + break; + default: + await interaction.reply("Subcommand doesn't exist."); + } + } + + private async SendAuditForUser(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const user = interaction.options.getUser('target'); + + if (!user) { + await interaction.reply("User not found."); + return; + } + + const audits = await Audit.FetchAuditsByUserId(user.id, interaction.guildId); + + if (!audits || audits.length == 0) { + await interaction.reply("There are no audits for this user."); + return; + } + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Audits") + .setDescription(`Audits: ${audits.length}`); + + for (let audit of audits) { + embed.addFields([ + { + name: `${audit.AuditId} // ${AuditTools.TypeToFriendlyText(audit.AuditType)}`, + value: audit.WhenCreated.toString(), + } + ]); + } + + await interaction.reply({ embeds: [ embed ]}); + } + + private async SendAudit(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const auditId = interaction.options.get('auditid'); + + if (!auditId || !auditId.value) { + await interaction.reply("AuditId not found."); + return; + } + + const audit = await Audit.FetchAuditByAuditId(auditId.value.toString().toUpperCase(), interaction.guildId); + + if (!audit) { + await interaction.reply("Audit not found."); + return; + } + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Audit") + .setDescription(audit.AuditId.toUpperCase()) + .addFields([ + { + name: "Reason", + value: audit.Reason || "*none*", + inline: true, + }, + { + name: "Type", + value: AuditTools.TypeToFriendlyText(audit.AuditType), + inline: true, + }, + { + name: "Moderator", + value: `<@${audit.ModeratorId}>`, + inline: true, + }, + ]); + + await interaction.reply({ embeds: [ embed ]}); + } + + private async ClearAudit(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const auditId = interaction.options.get('auditid'); + + if (!auditId || !auditId.value) { + await interaction.reply("AuditId not found."); + return; + } + + const audit = await Audit.FetchAuditByAuditId(auditId.value.toString().toUpperCase(), interaction.guildId); + + if (!audit) { + await interaction.reply("Audit not found."); + return; + } + + await Audit.Remove(Audit, audit); + + await interaction.reply("Audit cleared."); + } + + private async AddAudit(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const user = interaction.options.getUser('target'); + const auditType = interaction.options.get('type'); + const reasonInput = interaction.options.get('reason'); + + if (!user || !auditType || !auditType.value) { + await interaction.reply("Invalid input."); + return; + } + + const type = auditType.value as AuditType; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : ""; + + const audit = new Audit(user.id, type, reason, interaction.user.id, interaction.guildId); + + await audit.Save(Audit, audit); + + await interaction.reply(`Created new audit with ID \`${audit.AuditId}\``); + } +} \ No newline at end of file diff --git a/src/commands/ban.ts b/src/commands/ban.ts index ad39f03..219a078 100644 --- a/src/commands/ban.ts +++ b/src/commands/ban.ts @@ -1,83 +1,79 @@ -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import ErrorMessages from "../constants/ErrorMessages"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; +import Audit from "../database/entities/Audit"; +import { AuditType } from "../constants/AuditType"; +import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; +import SettingsHelper from "../helpers/SettingsHelper"; export default class Ban extends Command { constructor() { super(); - - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + + super.CommandBuilder = new SlashCommandBuilder() + .setName("ban") + .setDescription("Ban a member from the server with an optional reason") + .setDefaultMemberPermissions(PermissionsBitField.Flags.BanMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); } - public override async execute(context: ICommandContext): Promise { - const targetUser = context.message.mentions.users.first(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.guildId) return; + if (!interaction.guild) return; - if (!targetUser) { - const embed = new ErrorEmbed(context, "User does not exist"); - await embed.SendToCurrentChannel(); + const targetUser = interaction.options.get('target'); + const reasonInput = interaction.options.get('reason'); - return { - commandContext: context, - embeds: [embed], - }; + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply("User not found."); + return; } - const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); + const member = targetUser.member as GuildMember; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; - if (!targetMember) { - const embed = new ErrorEmbed(context, "User is not in this server"); - await embed.SendToCurrentChannel(); + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Banned") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason, + }, + ]); - return { - commandContext: context, - embeds: [embed], - }; + if (!member.bannable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; } - const reasonArgs = context.args; - reasonArgs.splice(0, 1) - - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [], - }; + await member.ban(); + await interaction.reply(`\`${targetUser.user.tag}\` has been banned.`); + + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); + + if (!channelName) return; + + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); } - if (!targetMember.bannable) { - const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed], - }; - } - - const logEmbed = new LogEmbed(context, "Member Banned"); - logEmbed.AddUser("User", targetUser, true); - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); - - const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been banned`); - - await targetMember.ban({ reason: `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}` }); - - await logEmbed.SendToModLogsChannel(); - await publicEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed], - }; + const audit = new Audit(targetUser.user.id, AuditType.Ban, reason, interaction.user.id, interaction.guildId); + await audit.Save(Audit, audit); } } \ No newline at end of file diff --git a/src/commands/bunny.ts b/src/commands/bunny.ts index 47bafa6..b735f61 100644 --- a/src/commands/bunny.ts +++ b/src/commands/bunny.ts @@ -1,29 +1,45 @@ -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; -import { ICommandContext } from "../contracts/ICommandContext"; import randomBunny from "random-bunny"; +import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; export default class Bunny extends Command { constructor() { super(); - super.Category = "Fun"; + super.CommandBuilder = new SlashCommandBuilder() + .setName("bunny") + .setDescription("Get a random picture of a rabbit."); } - public override async execute(context: ICommandContext) { - const result = await randomBunny('rabbits', 'hot'); + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + + const subreddits = [ + 'rabbits', + 'bunnieswithhats', + 'buncomfortable', + 'bunnytongues', + 'dutchbunnymafia', + ]; + + const random = Math.floor(Math.random() * subreddits.length); + const selectedSubreddit = subreddits[random]; + + const result = await randomBunny(selectedSubreddit, 'hot'); if (result.IsSuccess) { - const embed = new PublicEmbed(context, result.Result!.Title, "") + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle(result.Result!.Title) + .setDescription(result.Result!.Permalink) .setImage(result.Result!.Url) .setURL(`https://reddit.com${result.Result!.Permalink}`) - .setFooter({ text: `r/Rabbits · ${result.Result!.Ups} upvotes` }); - - await embed.SendToCurrentChannel(); + .setFooter({ text: `r/${selectedSubreddit} · ${result.Result!.Ups} upvotes`}); + + await interaction.reply({ embeds: [ embed ]}); } else { - const errorEmbed = new ErrorEmbed(context, "There was an error using this command."); - await errorEmbed.SendToCurrentChannel(); + await interaction.reply("There was an error running this command."); } } } \ No newline at end of file diff --git a/src/commands/clear.ts b/src/commands/clear.ts index 1bd3e55..a448a7b 100644 --- a/src/commands/clear.ts +++ b/src/commands/clear.ts @@ -1,50 +1,43 @@ -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import { TextChannel } from "discord.js"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; import { Command } from "../type/command"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; export default class Clear extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + super.CommandBuilder = new SlashCommandBuilder() + .setName("clear") + .setDescription("Clears the channel of messages") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageMessages) + .addNumberOption(option => + option + .setName('count') + .setDescription('The amount to delete') + .setRequired(true) + .setMinValue(1) + .setMaxValue(100)); } - public override async execute(context: ICommandContext): Promise { - if (context.args.length == 0) { - const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100"); - await errorEmbed.SendToCurrentChannel(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.channel) return; - return { - commandContext: context, - embeds: [errorEmbed] - }; - } - - const totalToClear = Number.parseInt(context.args[0]); + const totalToClear = interaction.options.getNumber('count'); if (!totalToClear || totalToClear <= 0 || totalToClear > 100) { - const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100"); - await errorEmbed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [errorEmbed] - }; + await interaction.reply('Please specify an amount between 1 and 100.'); + return; } - await (context.message.channel as TextChannel).bulkDelete(totalToClear); + const channel = interaction.channel as TextChannel; - const embed = new PublicEmbed(context, "", `${totalToClear} message(s) were removed`); - await embed.SendToCurrentChannel(); + if (!channel.manageable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } - return { - commandContext: context, - embeds: [embed] - }; + await channel.bulkDelete(totalToClear); + + await interaction.reply(`${totalToClear} message(s) were removed.`); } } \ No newline at end of file diff --git a/src/commands/code.ts b/src/commands/code.ts index cdfac96..2f0f7e2 100644 --- a/src/commands/code.ts +++ b/src/commands/code.ts @@ -1,7 +1,4 @@ -import { CommandResponse } from "../constants/CommandResponse"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import SettingsHelper from "../helpers/SettingsHelper"; import StringTools from "../helpers/StringTools"; import { Command } from "../type/command"; @@ -10,85 +7,58 @@ export default class Code extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + super.CommandBuilder = new SlashCommandBuilder() + .setName('code') + .setDescription('Manage the verification code of the server') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addSubcommand(subcommand => + subcommand + .setName('randomise') + .setDescription('Regenerates the verification code for this server')) + .addSubcommand(subcommand => + subcommand + .setName('embed') + .setDescription('Sends the embed with the current code to the current channel')); } - public override async precheckAsync(context: ICommandContext): Promise { - if (!context.message.guild){ - return CommandResponse.NotInServer; - } + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; - const isEnabled = await SettingsHelper.GetSetting("verification.enabled", context.message.guild?.id); - - if (!isEnabled) { - return CommandResponse.FeatureDisabled; - } - - if (isEnabled.toLocaleLowerCase() != 'true') { - return CommandResponse.FeatureDisabled; - } - - return CommandResponse.Ok; - } - - public override async execute(context: ICommandContext) { - const action = context.args[0]; - - switch (action) { + switch (interaction.options.getSubcommand()) { case "randomise": - await this.Randomise(context); + await this.Randomise(interaction); break; case "embed": - await this.SendEmbed(context); + await this.SendEmbed(interaction); break; - default: - await this.SendUsage(context); } } - private async SendUsage(context: ICommandContext) { - const description = [ - "USAGE: ", - "", - "randomise: Sets the server's entry code to a random code", - "embed: Sends an embed with the server's entry code" - ].join("\n"); - - const embed = new PublicEmbed(context, "", description); - await embed.SendToCurrentChannel(); - } - - private async Randomise(context: ICommandContext) { - if (!context.message.guild) { - return; - } + private async Randomise(interaction: CommandInteraction) { + if (!interaction.guildId) return; const randomCode = StringTools.RandomString(5); - await SettingsHelper.SetSetting("verification.code", context.message.guild.id, randomCode); + await SettingsHelper.SetSetting("verification.code", interaction.guildId, randomCode); - const embed = new PublicEmbed(context, "Code", `Entry code has been set to \`${randomCode}\``); - await embed.SendToCurrentChannel(); + await interaction.reply(`Entry code has been set to \`${randomCode}\``); } - private async SendEmbed(context: ICommandContext) { - if (!context.message.guild) { - return; - } + private async SendEmbed(interaction: CommandInteraction) { + if (!interaction.guildId) return; + if (!interaction.channel) return; - const code = await SettingsHelper.GetSetting("verification.code", context.message.guild.id); + const code = await SettingsHelper.GetSetting("verification.code", interaction.guildId); if (!code || code == "") { - const errorEmbed = new ErrorEmbed(context, "There is no code for this server setup."); - errorEmbed.SendToCurrentChannel(); - + await interaction.reply("There is no code for this server setup."); return; } - const embed = new PublicEmbed(context, "Entry Code", code!); - await embed.SendToCurrentChannel(); + const embed = new EmbedBuilder() + .setTitle("Entry Code") + .setDescription(code); + + await interaction.channel.send({ embeds: [ embed ]}); } } \ No newline at end of file diff --git a/src/commands/config.ts b/src/commands/config.ts index f3710e3..d86c7e3 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -1,126 +1,186 @@ +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import { readFileSync } from "fs"; -import { CommandResponse } from "../constants/CommandResponse"; import DefaultValues from "../constants/DefaultValues"; -import { ICommandContext } from "../contracts/ICommandContext"; -import Server from "../entity/Server"; -import Setting from "../entity/Setting"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import EmbedColours from "../constants/EmbedColours"; +import Server from "../database/entities/Server"; +import Setting from "../database/entities/Setting"; import { Command } from "../type/command"; export default class Config extends Command { constructor() { super(); - super.Category = "Administration"; - super.Roles = [ - "administrator" - ] + + super.CommandBuilder = new SlashCommandBuilder() + .setName('config') + .setDescription('Configure the current server') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) + .addSubcommand(subcommand => + subcommand + .setName('reset') + .setDescription('Reset a setting to the default') + .addStringOption(option => + option + .setName('key') + .setDescription('The key') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('get') + .setDescription('Gets a setting for the server') + .addStringOption(option => + option + .setName('key') + .setDescription('The key') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('set') + .setDescription('Sets a setting to a specified value') + .addStringOption(option => + option + .setName('key') + .setDescription('The key') + .setRequired(true)) + .addStringOption(option => + option + .setName('value') + .setDescription('The value') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('list') + .setDescription('Lists all settings')) } - public override async precheckAsync(context: ICommandContext): Promise { - if (!context.message.guild) { - return CommandResponse.ServerNotSetup; - } + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.guildId) return; - const server = await Server.FetchOneById(Server, context.message.guild?.id, [ + const server = await Server.FetchOneById(Server, interaction.guildId, [ "Settings", ]); if (!server) { - return CommandResponse.ServerNotSetup; - } - - return CommandResponse.Ok; - } - - public override async execute(context: ICommandContext) { - if (!context.message.guild) { + await interaction.reply('Server not setup. Please use the setup command,'); return; } - const server = await Server.FetchOneById(Server, context.message.guild?.id, [ - "Settings", - ]); - - if (!server) { - return; - } - - const key = context.args[0]; - const action = context.args[1]; - const value = context.args.splice(2).join(" "); - - if (!key) { - this.SendHelpText(context); - } else if (!action) { - this.GetValue(context, server, key); - } else { - switch(action) { - case 'reset': - this.ResetValue(context, server, key); - break; - case 'set': - if (!value) { - const errorEmbed = new ErrorEmbed(context, "Value is required when setting"); - errorEmbed.SendToCurrentChannel(); - return; - } - - this.SetValue(context, server, key, value); - break; - default: - const errorEmbed = new ErrorEmbed(context, "Action must be either set or reset"); - errorEmbed.SendToCurrentChannel(); - return; - } + switch (interaction.options.getSubcommand()) { + case 'list': + await this.SendHelpText(interaction); + break; + case 'reset': + await this.ResetValue(interaction); + break; + case 'get': + await this.GetValue(interaction); + break; + case 'set': + await this.SetValue(interaction); + break; + default: + await interaction.reply('Subcommand not found.'); } } - private async SendHelpText(context: ICommandContext) { + private async SendHelpText(interaction: CommandInteraction) { const description = readFileSync(`${process.cwd()}/data/usage/config.txt`).toString(); - const embed = new PublicEmbed(context, "Config", description); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Config") + .setDescription(description); - await embed.SendToCurrentChannel(); + await interaction.reply({ embeds: [ embed ]}); } - private async GetValue(context: ICommandContext, server: Server, key: string) { - const setting = server.Settings.filter(x => x.Key == key)[0]; + private async GetValue(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const key = interaction.options.get('key'); + + if (!key || !key.value) { + await interaction.reply('Fields are required.'); + return; + } + + const server = await Server.FetchOneById(Server, interaction.guildId, [ + "Settings", + ]); + + if (!server) { + await interaction.reply('Server not found.'); + return; + } + + const setting = server.Settings.filter(x => x.Key == key.value)[0]; if (setting) { - const embed = new PublicEmbed(context, "", `${key}: ${setting.Value}`); - await embed.SendToCurrentChannel(); + await interaction.reply(`\`${key}\`: \`${setting.Value}\``); } else { - const embed = new PublicEmbed(context, "", `${key}: ${DefaultValues.GetValue(key)} `); - await embed.SendToCurrentChannel(); + await interaction.reply(`\`${key}\`: \`${DefaultValues.GetValue(key.value.toString())}\` `); } } - private async ResetValue(context: ICommandContext, server: Server, key: string) { - const setting = server.Settings.filter(x => x.Key == key)[0]; + private async ResetValue(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const key = interaction.options.get('key'); + + if (!key || !key.value) { + await interaction.reply('Fields are required.'); + return; + } + + const server = await Server.FetchOneById(Server, interaction.guildId, [ + "Settings", + ]); + + if (!server) { + await interaction.reply('Server not found.'); + return; + } + + const setting = server.Settings.filter(x => x.Key == key.value)[0]; if (!setting) { - const embed = new PublicEmbed(context, "", "Setting has been reset"); - await embed.SendToCurrentChannel(); - + await interaction.reply('Setting not found.'); return; } await Setting.Remove(Setting, setting); - const embed = new PublicEmbed(context, "", "Setting has been reset"); - await embed.SendToCurrentChannel(); + await interaction.reply('The setting has been reset to the default.'); } - private async SetValue(context: ICommandContext, server: Server, key: string, value: string) { - const setting = server.Settings.filter(x => x.Key == key)[0]; + private async SetValue(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const key = interaction.options.get('key'); + const value = interaction.options.get('value'); + + if (!key || !key.value || !value || !value.value) { + await interaction.reply('Fields are required.'); + return; + } + + const server = await Server.FetchOneById(Server, interaction.guildId, [ + "Settings", + ]); + + if (!server) { + await interaction.reply('Server not found.'); + return; + } + + const setting = server.Settings.filter(x => x.Key == key.value)[0]; if (setting) { - setting.UpdateBasicDetails(key, value); + setting.UpdateBasicDetails(key.value.toString(), value.value.toString()); await setting.Save(Setting, setting); } else { - const newSetting = new Setting(key, value); + const newSetting = new Setting(key.value.toString(), value.value.toString()); await newSetting.Save(Setting, newSetting); @@ -129,7 +189,6 @@ export default class Config extends Command { await server.Save(Server, server); } - const embed = new PublicEmbed(context, "", "Setting has been set"); - await embed.SendToCurrentChannel(); + await interaction.reply('Setting has been set.'); } } \ No newline at end of file diff --git a/src/commands/disable.ts b/src/commands/disable.ts index 390da51..4cca622 100644 --- a/src/commands/disable.ts +++ b/src/commands/disable.ts @@ -1,5 +1,4 @@ -import { ICommandContext } from "../contracts/ICommandContext"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; @@ -7,87 +6,86 @@ export default class Disable extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + super.CommandBuilder = new SlashCommandBuilder() + .setName('disable') + .setDescription('Disables a command') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) + .addSubcommand(subcommand => + subcommand + .setName('add') + .setDescription('Disables a command for the server') + .addStringOption(option => + option + .setName('name') + .setDescription('The name of the command') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('remove') + .setDescription('Enables a command for the server') + .addStringOption(option => + option + .setName('name') + .setDescription('The name of the command') + .setRequired(true))); } - public override async execute(context: ICommandContext) { - const action = context.args[0]; + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; - switch (action) { + switch (interaction.options.getSubcommand()) { case "add": - await this.Add(context); + await this.Add(interaction); break; case "remove": - await this.Remove(context); + await this.Remove(interaction); break; default: - await this.SendUsage(context); + await interaction.reply('Subcommand not found.'); } } - private async SendUsage(context: ICommandContext) { - const description = [ - "USAGE: ", - "", - "add: Adds the command name to the server's disabled command string", - "remove: Removes the command name from the server's disabled command string", - "name: The name of the command to enable/disable" - ].join("\n"); - - const embed = new PublicEmbed(context, "", description); - await embed.SendToCurrentChannel(); - } + private async Add(interaction: CommandInteraction) { + if (!interaction.guildId) return; - private async Add(context: ICommandContext) { - if (!context.message.guild) { + const commandName = interaction.options.get('name'); + + if (!commandName || !commandName.value) { + await interaction.reply('Fields are required.'); return; } - const commandName = context.args[1]; - - if (!commandName) { - this.SendUsage(context); - return; - } - - const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id); + const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId); const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : []; - disabledCommands?.push(commandName); + disabledCommands?.push(commandName.value.toString()); - await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(",")); + await SettingsHelper.SetSetting("commands.disabled", interaction.guildId, disabledCommands!.join(",")); - const embed = new PublicEmbed(context, "", `Disabled command: ${commandName}`); - await embed.SendToCurrentChannel(); + await interaction.reply(`Disabled command ${commandName.value}`); } - private async Remove(context: ICommandContext) { - if (!context.message.guild) { + private async Remove(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const commandName = interaction.options.get('name'); + + if (!commandName || !commandName.value) { + await interaction.reply('Fields are required.'); return; } - const commandName = context.args[1]; - - if (!commandName) { - this.SendUsage(context); - return; - } - - const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id); + const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId); const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : []; - const disabledCommandsInstance = disabledCommands?.findIndex(x => x == commandName); + const disabledCommandsInstance = disabledCommands?.findIndex(x => x == commandName.value!.toString()); if (disabledCommandsInstance! > -1) { disabledCommands?.splice(disabledCommandsInstance!, 1); } - await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(",")); + await SettingsHelper.SetSetting("commands.disabled", interaction.guildId, disabledCommands!.join(",")); - const embed = new PublicEmbed(context, "", `Enabled command: ${commandName}`); - await embed.SendToCurrentChannel(); + await interaction.reply(`Enabled command ${commandName.value}`); } } \ No newline at end of file diff --git a/src/commands/help.ts b/src/commands/help.ts deleted file mode 100644 index 5366acf..0000000 --- a/src/commands/help.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { existsSync, readdirSync } from "fs"; -import { CoreClient } from "../client/client"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; -import StringTools from "../helpers/StringTools"; -import { Command } from "../type/command"; - -export interface ICommandData { - Exists: boolean; - Name?: string; - Category?: string; - Roles?: string[]; -} - -export default class Help extends Command { - constructor() { - super(); - - super.Category = "General"; - } - - public override async execute(context: ICommandContext) { - if (context.args.length == 0) { - await this.SendAll(context); - } else { - await this.SendSingle(context); - } - } - - public async SendAll(context: ICommandContext) { - const allCommands = CoreClient.commandItems - .filter(x => !x.ServerId || x.ServerId == context.message.guild?.id); - const cateogries = [...new Set(allCommands.map(x => x.Command.Category))]; - - const embed = new PublicEmbed(context, "Commands", ""); - - cateogries.forEach(category => { - let filtered = allCommands.filter(x => x.Command.Category == category); - - embed.addField(StringTools.Capitalise(category || "Uncategorised"), StringTools.CapitaliseArray(filtered.flatMap(x => x.Name)).join(", ")); - }); - - await embed.SendToCurrentChannel(); - } - - public async SendSingle(context: ICommandContext) { - const command = CoreClient.commandItems.find(x => x.Name == context.args[0] && !x.ServerId); - const exclusiveCommand = CoreClient.commandItems.find(x => x.Name == context.args[0] && x.ServerId == context.message.guild?.id); - - if (exclusiveCommand) { - const embed = new PublicEmbed(context, StringTools.Capitalise(exclusiveCommand.Name), ""); - embed.addField("Category", StringTools.Capitalise(exclusiveCommand.Command.Category || "Uncategorised")); - embed.addField("Required Roles", StringTools.Capitalise(exclusiveCommand.Command.Roles.join(", ")) || "Everyone"); - - await embed.SendToCurrentChannel(); - } else if (command) { - const embed = new PublicEmbed(context, StringTools.Capitalise(command.Name), ""); - embed.addField("Category", StringTools.Capitalise(command.Command.Category || "Uncategorised")); - embed.addField("Required Roles", StringTools.Capitalise(command.Command.Roles.join(", ")) || "Everyone"); - - await embed.SendToCurrentChannel(); - } else { - const errorEmbed = new ErrorEmbed(context, "Command does not exist"); - await errorEmbed.SendToCurrentChannel(); - } - } -} diff --git a/src/commands/ignore.ts b/src/commands/ignore.ts new file mode 100644 index 0000000..a93e801 --- /dev/null +++ b/src/commands/ignore.ts @@ -0,0 +1,39 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import IgnoredChannel from "../database/entities/IgnoredChannel"; +import { Command } from "../type/command"; + +export default class Ignore extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('ignore') + .setDescription('Ignore events in this channel') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const isChannelIgnored = await IgnoredChannel.IsChannelIgnored(interaction.guildId); + + if (isChannelIgnored) { + const entity = await IgnoredChannel.FetchOneById(IgnoredChannel, interaction.guildId); + + if (!entity) { + await interaction.reply('Unable to find channel.'); + return; + } + + await IgnoredChannel.Remove(IgnoredChannel, entity); + + await interaction.reply('This channel will start being logged again.'); + } else { + const entity = new IgnoredChannel(interaction.guildId); + + await entity.Save(IgnoredChannel, entity); + + await interaction.reply('This channel will now be ignored from logging.'); + } + } +} \ No newline at end of file diff --git a/src/commands/kick.ts b/src/commands/kick.ts index 81b1af9..0008589 100644 --- a/src/commands/kick.ts +++ b/src/commands/kick.ts @@ -1,83 +1,79 @@ -import ErrorMessages from "../constants/ErrorMessages"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; +import Audit from "../database/entities/Audit"; +import { AuditType } from "../constants/AuditType"; +import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; +import SettingsHelper from "../helpers/SettingsHelper"; export default class Kick extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + super.CommandBuilder = new SlashCommandBuilder() + .setName("kick") + .setDescription("Kick a member from the server with an optional reason") + .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); } - public override async execute(context: ICommandContext): Promise { - const targetUser = context.message.mentions.users.first(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.guildId) return; + if (!interaction.guild) return; - if (!targetUser) { - const embed = new ErrorEmbed(context, "User does not exist"); - await embed.SendToCurrentChannel(); + const targetUser = interaction.options.get('target'); + const reasonInput = interaction.options.get('reason'); - return { - commandContext: context, - embeds: [embed] - }; + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply("User not found."); + return; } - const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); + const member = targetUser.member as GuildMember; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; - if (!targetMember) { - const embed = new ErrorEmbed(context, "User is not in this server"); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Kicked") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason, + }, + ]); + + if (!member.kickable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; } - const reasonArgs = context.args; - reasonArgs.splice(0, 1) - - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [] - }; + await member.kick(); + await interaction.reply(`\`${targetUser.user.tag}\` has been kicked.`); + + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); + + if (!channelName) return; + + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); } - if (!targetMember.kickable) { - const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - const logEmbed = new LogEmbed(context, "Member Kicked"); - logEmbed.AddUser("User", targetUser, true); - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); - - const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been kicked`); - - await targetMember.kick(`Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); - - await logEmbed.SendToModLogsChannel(); - await publicEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed] - }; + const audit = new Audit(targetUser.user.id, AuditType.Kick, reason, interaction.user.id, interaction.guildId); + await audit.Save(Audit, audit); } } \ No newline at end of file diff --git a/src/commands/mute.ts b/src/commands/mute.ts index e5d43df..3e92231 100644 --- a/src/commands/mute.ts +++ b/src/commands/mute.ts @@ -1,96 +1,85 @@ -import ErrorMessages from "../constants/ErrorMessages"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import { AuditType } from "../constants/AuditType"; +import EmbedColours from "../constants/EmbedColours"; +import Audit from "../database/entities/Audit"; +import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; export default class Mute extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + super.CommandBuilder = new SlashCommandBuilder() + .setName("mute") + .setDescription("(DEPRECATED) Mute a member in the server with an optional reason") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); } - public override async execute(context: ICommandContext): Promise { - const targetUser = context.message.mentions.users.first(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.guild || !interaction.guildId) return; - if (!targetUser) { - const embed = new ErrorEmbed(context, "User does not exist"); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; + const targetUser = interaction.options.get('target'); + const reasonInput = interaction.options.get('reason'); + + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply('Fields are required.'); + return; } - const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); + const targetMember = targetUser.member as GuildMember; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; - if (!targetMember) { - const embed = new ErrorEmbed(context, "User is not in this server"); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Muted") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason, + }, + ]); - const reasonArgs = context.args; - reasonArgs.splice(0, 1); + const mutedRole = interaction.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [] - }; + if (!mutedRole) { + await interaction.reply('Muted role not found.'); + return; } if (!targetMember.manageable) { - const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; } - const logEmbed = new LogEmbed(context, "Member Muted"); - logEmbed.AddUser("User", targetUser, true) - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); + await targetMember.roles.add(mutedRole); - const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been muted`); - publicEmbed.AddReason(reason); + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); - const mutedRole = context.message.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); + if (!channelName) return; - if (!mutedRole) { - const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); } - await targetMember.roles.add(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); + const audit = new Audit(targetUser.user.id, AuditType.Mute, reason, interaction.user.id, interaction.guildId); + await audit.Save(Audit, audit); - await logEmbed.SendToModLogsChannel(); - await publicEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed] - }; + await interaction.reply("Please note the mute and unmute commands have been deprecated and will be removed in a future update. Please use timeout instead"); } } \ No newline at end of file diff --git a/src/commands/poll.ts b/src/commands/poll.ts deleted file mode 100644 index 3e84220..0000000 --- a/src/commands/poll.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; -import { Command } from "../type/command"; - -export default class Poll extends Command { - constructor() { - super(); - - super.Category = "General"; - } - - public override async execute(context: ICommandContext): Promise { - const argsJoined = context.args.join(" "); - const argsSplit = argsJoined.split(";"); - - if (argsSplit.length < 3 || argsSplit.length > 10) { - const errorEmbed = new ErrorEmbed(context, "Usage: ;<option 1>;<option 2>... (separate options with semicolons), maximum of 9 options"); - await errorEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [errorEmbed] - }; - } - - const title = argsSplit[0]; - - const arrayOfNumbers = [ - ':one:', - ':two:', - ':three:', - ':four:', - ':five:', - ':six:', - ':seven:', - ':eight:', - ':nine:' - ]; - - const reactionEmojis = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣"]; - - const description = arrayOfNumbers.splice(0, argsSplit.length - 1); - - description.forEach((value, index) => { - description[index] = `${value} ${argsSplit[index + 1]}`; - }); - - const embed = new PublicEmbed(context, title, description.join("\n")); - - const message = await context.message.channel.send({ embeds: [ embed ]}); - - description.forEach(async (value, index) => { - await message.react(reactionEmojis[index]); - }); - - if (context.message.deletable) { - await context.message.delete(); - } - - return { - commandContext: context, - embeds: [embed] - }; - } -} \ No newline at end of file diff --git a/src/commands/role.ts b/src/commands/role.ts deleted file mode 100644 index 952b590..0000000 --- a/src/commands/role.ts +++ /dev/null @@ -1,232 +0,0 @@ -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; -import { Role as DiscordRole } from "discord.js"; -import { Command } from "../type/command"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import SettingsHelper from "../helpers/SettingsHelper"; -import { readFileSync } from "fs"; -import { default as eRole } from "../entity/Role"; -import Server from "../entity/Server"; - -export default class Role extends Command { - constructor() { - super(); - - super.Category = "General"; - } - - public override async execute(context: ICommandContext) { - if (!context.message.guild) return; - - switch (context.args[0]) { - case "config": - await this.UseConfig(context); - break; - default: - await this.UseDefault(context); - } - } - - // ======= - // Default - // ======= - - private async UseDefault(context: ICommandContext) { - if (context.args.length == 0) { - await this.SendRolesList(context, context.message.guild!.id); - } else { - await this.ToggleRole(context); - } - } - - public async GetRolesList(context: ICommandContext): Promise<string[]> { - const rolesArray = await eRole.FetchAllByServerId(context.message.guild!.id); - - const stringArray: string[] = []; - - for (let i = 0; i < rolesArray.length; i++) { - const serverRole = context.message.guild!.roles.cache.find(x => x.id == rolesArray[i].RoleId); - - if (serverRole) { - stringArray.push(serverRole.name); - } - } - - return stringArray; - } - - public async SendRolesList(context: ICommandContext, serverId: string): Promise<ICommandReturnContext> { - const roles = await this.GetRolesList(context); - - const botPrefix = await SettingsHelper.GetServerPrefix(serverId); - const description = roles.length == 0 ? "*no roles*" : `Do ${botPrefix}role <role> to get the role!\n\n${roles.join('\n')}`; - - const embed = new PublicEmbed(context, "Roles", description); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - public async ToggleRole(context: ICommandContext): Promise<ICommandReturnContext> { - const roles = await this.GetRolesList(context); - const requestedRole = context.args.join(" "); - - if (!roles.includes(requestedRole)) { - const errorEmbed = new ErrorEmbed(context, "This role isn't marked as assignable, to see a list of assignable roles, run this command without any parameters"); - await errorEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [errorEmbed] - }; - } - - const assignRole = context.message.guild?.roles.cache.find(x => x.name == requestedRole); - - if (!assignRole) { - const errorEmbed = new ErrorEmbed(context, "The current server doesn't have this role. Please contact the server's moderators"); - await errorEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [errorEmbed] - }; - } - - const role = context.message.member?.roles.cache.find(x => x.name == requestedRole) - - if (!role) { - await this.AddRole(context, assignRole); - } else { - await this.RemoveRole(context, assignRole); - } - - return { - commandContext: context, - embeds: [] - }; - } - - public async AddRole(context: ICommandContext, role: DiscordRole): Promise<ICommandReturnContext> { - await context.message.member?.roles.add(role, "Toggled with role command"); - - const embed = new PublicEmbed(context, "", `Gave role: \`${role.name}\``); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - public async RemoveRole(context: ICommandContext, role: DiscordRole): Promise<ICommandReturnContext> { - await context.message.member?.roles.remove(role, "Toggled with role command"); - - const embed = new PublicEmbed(context, "", `Removed role: \`${role.name}\``); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - // ====== - // Config - // ====== - - private async UseConfig(context: ICommandContext) { - const moderatorRole = await SettingsHelper.GetSetting("role.moderator", context.message.guild!.id); - - if (!context.message.member?.roles.cache.find(x => x.name == moderatorRole)) { - const errorEmbed = new ErrorEmbed(context, "Sorry, you must be a moderator to be able to configure this command"); - await errorEmbed.SendToCurrentChannel(); - - return; - } - - switch (context.args[1]) { - case "add": - await this.AddRoleConfig(context); - break; - case "remove": - await this.RemoveRoleConfig(context); - break; - default: - await this.SendConfigHelp(context); - } - } - - private async SendConfigHelp(context: ICommandContext) { - const helpText = readFileSync(`${process.cwd()}/data/usage/role.txt`).toString(); - - const embed = new PublicEmbed(context, "Configure Role Command", helpText); - await embed.SendToCurrentChannel(); - } - - private async AddRoleConfig(context: ICommandContext) { - const role = context.message.guild!.roles.cache.find(x => x.id == context.args[2]); - - if (!role) { - this.SendConfigHelp(context); - return; - } - - const existingRole = await eRole.FetchOneByRoleId(role.id); - - if (existingRole) { - const errorEmbed = new ErrorEmbed(context, "This role has already been setup"); - await errorEmbed.SendToCurrentChannel(); - - return; - } - - const server = await Server.FetchOneById(Server, context.message.guild!.id, [ - "Roles", - ]); - - if (!server) { - const errorEmbed = new ErrorEmbed(context, "Server not setup, please request the server owner runs the setup command."); - await errorEmbed.SendToCurrentChannel(); - - return; - } - - const roleSetting = new eRole(role.id); - - await roleSetting.Save(eRole, roleSetting); - - server.AddRoleToServer(roleSetting); - await server.Save(Server, server); - - const embed = new PublicEmbed(context, "", `Added \`${role.name}\` as a new assignable role`); - await embed.SendToCurrentChannel(); - } - - private async RemoveRoleConfig(context: ICommandContext) { - const role = context.message.guild!.roles.cache.find(x => x.id == context.args[2]); - - if (!role) { - this.SendConfigHelp(context); - return; - } - - const existingRole = await eRole.FetchOneByRoleId(role.id); - - if (!existingRole) { - const errorEmbed = new ErrorEmbed(context, "Unable to find this role"); - errorEmbed.SendToCurrentChannel(); - - return; - } - - await eRole.Remove(eRole, existingRole); - - const embed = new PublicEmbed(context, "", `Removed \`${role.name}\` from the list of assignable roles`); - await embed.SendToCurrentChannel(); - } -} diff --git a/src/commands/rules.ts b/src/commands/rules.ts index 8730057..930138c 100644 --- a/src/commands/rules.ts +++ b/src/commands/rules.ts @@ -1,7 +1,6 @@ +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import { existsSync, readFileSync } from "fs"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import EmbedColours from "../constants/EmbedColours"; import { Command } from "../type/command"; interface IRules { @@ -15,38 +14,61 @@ export default class Rules extends Command { constructor() { super(); - super.Category = "Admin"; - super.Roles = [ - "administrator" - ]; + super.CommandBuilder = new SlashCommandBuilder() + .setName("rules") + .setDescription("Send the rules embeds for this server") + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); } - public override async execute(context: ICommandContext) { - if (!existsSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`)) { - const errorEmbed = new ErrorEmbed(context, "Rules file doesn't exist"); - await errorEmbed.SendToCurrentChannel(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId) return; + if (!existsSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`)) { + await interaction.reply('Rules file doesn\'t exist.'); return; } - const rulesFile = readFileSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`).toString(); + const rulesFile = readFileSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`).toString(); const rules = JSON.parse(rulesFile) as IRules[]; - const embeds: PublicEmbed[] = []; - - rules.forEach(rule => { - const embed = new PublicEmbed(context, rule.title || "", rule.description?.join("\n") || ""); + const embeds: EmbedBuilder[] = []; - embed.setImage(rule.image || ""); - embed.setFooter({ text: rule.footer || "" }); + if (rules.length == 0) { + await interaction.reply({ content: "No rules have been supplied within code base for this server.", ephemeral: true }); + return; + } + + rules.forEach(rule => { + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle(rule.title || "Rules") + .setDescription(rule.description ? rule.description.join("\n") : "*none*"); + + if (rule.image) { + embed.setImage(rule.image); + } + + if (rule.footer) { + embed.setFooter({ text: rule.footer }); + } embeds.push(embed); }); - for (let i = 0; i < embeds.length; i++) { - const embed = embeds[i]; + const channel = interaction.channel; - await embed.SendToCurrentChannel(); + if (!channel) { + await interaction.reply({ content: "Channel not found.", ephemeral: true }); + return; } + + await channel.send({ embeds: embeds }); + + const successEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Success") + .setDescription("The rules have sent to this channel successfully"); + + await interaction.reply({ embeds: [ successEmbed ], ephemeral: true }); } } \ No newline at end of file diff --git a/src/commands/setup.ts b/src/commands/setup.ts index 8267ff1..5b1e3fc 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -1,37 +1,31 @@ -import { ICommandContext } from "../contracts/ICommandContext"; -import Server from "../entity/Server"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import Server from "../database/entities/Server"; import { Command } from "../type/command"; export default class Setup extends Command { constructor() { super(); - super.Category = "Administration"; - super.Roles = [ - "moderator" - ] + + super.CommandBuilder = new SlashCommandBuilder() + .setName('setup') + .setDescription('Makes the server ready to be configured') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); } - public override async execute(context: ICommandContext) { - if (!context.message.guild) { - return; - } + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId) return; - const server = await Server.FetchOneById(Server, context.message.guild?.id); + const server = await Server.FetchOneById(Server, interaction.guildId); if (server) { - const embed = new ErrorEmbed(context, "This server has already been setup, please configure using the config command"); - await embed.SendToCurrentChannel(); - + await interaction.reply('This server has already been setup, please configure using the config command.'); return; } - const newServer = new Server(context.message.guild?.id); + const newServer = new Server(interaction.guildId); await newServer.Save(Server, newServer); - const embed = new PublicEmbed(context, "Success", "Please configure using the config command"); - await embed.SendToCurrentChannel(); + await interaction.reply('Success, please configure using the configure command.'); } } \ No newline at end of file diff --git a/src/commands/timeout.ts b/src/commands/timeout.ts new file mode 100644 index 0000000..52fe1e7 --- /dev/null +++ b/src/commands/timeout.ts @@ -0,0 +1,156 @@ +import { CacheType, CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import { AuditType } from "../constants/AuditType"; +import EmbedColours from "../constants/EmbedColours"; +import Audit from "../database/entities/Audit"; +import SettingsHelper from "../helpers/SettingsHelper"; +import TimeLengthInput from "../helpers/TimeLengthInput"; +import { Command } from "../type/command"; + +export default class Timeout extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName("timeout") + .setDescription("Timeouts a user out, sending them a DM with the reason if possible") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName("length") + .setDescription("How long to timeout for? (Example: 24h, 60m)") + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); + } + + public override async execute(interaction: CommandInteraction<CacheType>) { + if (!interaction.guild || !interaction.guildId) return; + + // Interaction Inputs + const targetUser = interaction.options.get('target'); + const lengthInput = interaction.options.get('length'); + const reasonInput = interaction.options.get('reason'); + + // Validation + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply('Fields are required.'); + return; + } + + if (!lengthInput || !lengthInput.value) { + await interaction.reply('Fields are required.'); + return; + } + + if (targetUser.user.bot) { + await interaction.reply('Cannot timeout bots.'); + return; + } + + // General Variables + const targetMember = targetUser.member as GuildMember; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : null; + + const timeLength = new TimeLengthInput(lengthInput.value.toString()); + + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Timed Out") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason || "*none*", + }, + { + name: "Length", + value: timeLength.GetLengthShort(), + }, + { + name: "Until", + value: timeLength.GetDateFromNow().toString(), + }, + ]); + + // Bot Permissions Check + if (!targetMember.manageable) { + await interaction.reply('Insufficient bot permissions. Please contact a moderator.'); + return; + } + + // Execute Timeout + await targetMember.timeout(timeLength.GetMilliseconds(), reason || ""); + + // Log Embed To Channel + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); + + if (!channelName) return; + + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); + } + + // Create Audit + const audit = new Audit(targetUser.user.id, AuditType.Timeout, reason || "*none*", interaction.user.id, interaction.guildId); + await audit.Save(Audit, audit); + + // DM User, if possible + const resultEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setDescription(`<@${targetUser.user.id}> has been timed out`); + + const dmEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setDescription(`You have been timed out in ${interaction.guild.name}`) + .addFields([ + { + name: "Reason", + value: reason || "*none*" + }, + { + name: "Length", + value: timeLength.GetLengthShort(), + }, + { + name: "Until", + value: timeLength.GetDateFromNow().toString(), + }, + ]); + + try { + const dmChannel = await targetUser.user.createDM(); + + await dmChannel.send({ embeds: [ dmEmbed ]}); + + resultEmbed.addFields([ + { + name: "DM Sent", + value: "true", + }, + ]); + } catch { + resultEmbed.addFields([ + { + name: "DM Sent", + value: "false", + }, + ]); + } + + // Success Reply + await interaction.reply({ embeds: [ resultEmbed ]}); + } +} \ No newline at end of file diff --git a/src/commands/unmute.ts b/src/commands/unmute.ts index b006dab..1a3bf95 100644 --- a/src/commands/unmute.ts +++ b/src/commands/unmute.ts @@ -1,96 +1,80 @@ -import ErrorMessages from "../constants/ErrorMessages"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; +import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; export default class Unmute extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + super.CommandBuilder = new SlashCommandBuilder() + .setName("unmute") + .setDescription("(DEPRECATED) Unmute a member in the server with an optional reason") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); } - public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { - const targetUser = context.message.mentions.users.first(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.guild || !interaction.guildId) return; - if (!targetUser) { - const embed = new ErrorEmbed(context, "User does not exist"); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; + const targetUser = interaction.options.get('target'); + const reasonInput = interaction.options.get('reason'); + + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply('Fields are required.'); + return; } - const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); + const targetMember = targetUser.member as GuildMember; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; - if (!targetMember) { - const embed = new ErrorEmbed(context, "User is not in this server"); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Unmuted") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason, + }, + ]); - const reasonArgs = context.args; - reasonArgs.splice(0, 1); + const mutedRole = interaction.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [] - }; + if (!mutedRole) { + await interaction.reply('Muted role not found.'); + return; } if (!targetMember.manageable) { - const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; } - const logEmbed = new LogEmbed(context, "Member Unmuted"); - logEmbed.AddUser("User", targetUser, true) - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); + await targetMember.roles.remove(mutedRole); - const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been unmuted`); - publicEmbed.AddReason(reason); + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); - const mutedRole = context.message.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); + if (!channelName) return; - if (!mutedRole) { - const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound); - await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); } - await targetMember.roles.remove(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); - - await logEmbed.SendToModLogsChannel(); - await publicEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed] - }; + await interaction.reply("Please note the mute and unmute commands have been deprecated and will be removed in a future update. Please use timeout instead"); } } \ No newline at end of file diff --git a/src/commands/warn.ts b/src/commands/warn.ts index a36e28e..618118b 100644 --- a/src/commands/warn.ts +++ b/src/commands/warn.ts @@ -1,71 +1,70 @@ -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import { AuditType } from "../constants/AuditType"; +import EmbedColours from "../constants/EmbedColours"; +import Audit from "../database/entities/Audit"; +import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; export default class Warn extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + super.CommandBuilder = new SlashCommandBuilder() + .setName("warn") + .setDescription("Warns a member in the server with an optional reason") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); } - public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { - const user = context.message.mentions.users.first(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.guild || !interaction.guildId) return; - if (!user) { - const errorEmbed = new ErrorEmbed(context, "User does not exist"); - await errorEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [errorEmbed] - }; + const targetUser = interaction.options.get('target'); + const reasonInput = interaction.options.get('reason'); + + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply('Fields are required.'); + return; } - const member = context.message.guild?.members.cache.find(x => x.user.id == user.id); + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; - if (!member) { - const errorEmbed = new ErrorEmbed(context, "User is not in this server"); - await errorEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [errorEmbed] - }; + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Warned") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason, + }, + ]); + + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); + + if (!channelName) return; + + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); } - const reasonArgs = context.args; - reasonArgs.splice(0, 1); + const audit = new Audit(targetUser.user.id, AuditType.Warn, reason, interaction.user.id, interaction.guildId); + await audit.Save(Audit, audit); - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [] - }; - } - - const logEmbed = new LogEmbed(context, "Member Warned"); - logEmbed.AddUser("User", user, true); - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); - - const publicEmbed = new PublicEmbed(context, "", `${user} has been warned`); - publicEmbed.AddReason(reason); - - await logEmbed.SendToModLogsChannel(); - await publicEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed] - }; + await interaction.reply('Successfully warned user.'); } } \ No newline at end of file diff --git a/src/constants/AuditType.ts b/src/constants/AuditType.ts new file mode 100644 index 0000000..0fd7325 --- /dev/null +++ b/src/constants/AuditType.ts @@ -0,0 +1,8 @@ +export enum AuditType { + General, + Warn, + Mute, + Kick, + Ban, + Timeout, +} \ No newline at end of file diff --git a/src/constants/CommandResponse.ts b/src/constants/CommandResponse.ts deleted file mode 100644 index b876ce8..0000000 --- a/src/constants/CommandResponse.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum CommandResponse { - Ok, - Unauthorised, - ServerNotSetup, - NotInServer, - FeatureDisabled, -} \ No newline at end of file diff --git a/src/constants/DefaultValues.ts b/src/constants/DefaultValues.ts index ec2a4b3..3f5ba09 100644 --- a/src/constants/DefaultValues.ts +++ b/src/constants/DefaultValues.ts @@ -17,11 +17,7 @@ export default class DefaultValues { private static SetValues() { if (this.values.length == 0) { // Bot - if (this.useDevPrefix) { - this.values.push({ Key: "bot.prefix", Value: "d!" }); - } else { - this.values.push({ Key: "bot.prefix", Value: "v!" }); - } + this.values.push({ Key: "bot.prefix", Value: process.env.BOT_PREFIX || "v!" }) // Commands this.values.push({ Key: "commands.disabled", Value: "" }); diff --git a/src/constants/EmbedColours.ts b/src/constants/EmbedColours.ts new file mode 100644 index 0000000..023c77a --- /dev/null +++ b/src/constants/EmbedColours.ts @@ -0,0 +1,3 @@ +export default class EmbedColours { + public static readonly Ok = 0x3050ba; +} \ No newline at end of file diff --git a/src/constants/ErrorMessages.ts b/src/constants/ErrorMessages.ts deleted file mode 100644 index 588a143..0000000 --- a/src/constants/ErrorMessages.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { CommandResponse } from "./CommandResponse"; - -export default class ErrorMessages { - public static readonly InsufficientBotPermissions = "Unable to do this action, am I missing permissions?"; - public static readonly ChannelNotFound = "Unable to find channel"; - public static readonly RoleNotFound = "Unable to find role"; - - public static readonly UserUnauthorised = "You are not authorised to use this command"; - public static readonly ServerNotSetup = "This server hasn't been setup yet, please run the setup command"; - public static readonly NotInServer = "This command requires to be ran inside of a server"; - public static readonly FeatureDisabled = "This feature is currently disabled by a server moderator"; - - public static GetErrorMessage(response: CommandResponse): string { - switch (response) { - case CommandResponse.Unauthorised: - return this.UserUnauthorised; - case CommandResponse.ServerNotSetup: - return this.ServerNotSetup; - case CommandResponse.NotInServer: - return this.NotInServer; - case CommandResponse.FeatureDisabled: - return this.FeatureDisabled; - default: - return ""; - } - } -} \ No newline at end of file diff --git a/src/constants/EventType.ts b/src/constants/EventType.ts new file mode 100644 index 0000000..c27c6ed --- /dev/null +++ b/src/constants/EventType.ts @@ -0,0 +1,15 @@ +export enum EventType { + ChannelCreate, + ChannelDelete, + ChannelUpdate, + GuildBanAdd, + GuildBanRemove, + GuildCreate, + GuildMemberAdd, + GuildMemberRemove, + GuildMemberUpdate, + MessageCreate, + MessageDelete, + MessageUpdate, + Ready, +} \ No newline at end of file diff --git a/src/contracts/BaseEntity.ts b/src/contracts/BaseEntity.ts index bb4d550..941ba6f 100644 --- a/src/contracts/BaseEntity.ts +++ b/src/contracts/BaseEntity.ts @@ -1,5 +1,6 @@ -import { Column, DeepPartial, EntityTarget, getConnection, PrimaryColumn, ObjectLiteral, FindOptionsWhere } from "typeorm"; +import { Column, DeepPartial, EntityTarget, PrimaryColumn, ObjectLiteral, FindOptionsWhere } from "typeorm"; import { v4 } from "uuid"; +import AppDataSource from "../database/dataSources/appDataSource"; export default class BaseEntity { constructor() { @@ -21,25 +22,19 @@ export default class BaseEntity { public async Save<T extends BaseEntity>(target: EntityTarget<T>, entity: DeepPartial<T>): Promise<void> { this.WhenUpdated = new Date(); - const connection = getConnection(); - - const repository = connection.getRepository<T>(target); + const repository = AppDataSource.getRepository<T>(target); await repository.save(entity); } public static async Remove<T extends BaseEntity>(target: EntityTarget<T>, entity: T): Promise<void> { - const connection = getConnection(); - - const repository = connection.getRepository<T>(target); + const repository = AppDataSource.getRepository<T>(target); await repository.remove(entity); } public static async FetchAll<T extends BaseEntity>(target: EntityTarget<T>, relations?: string[]): Promise<T[]> { - const connection = getConnection(); - - const repository = connection.getRepository<T>(target); + const repository = AppDataSource.getRepository<T>(target); const all = await repository.find({ relations: relations || [] }); @@ -47,9 +42,7 @@ export default class BaseEntity { } public static async FetchOneById<T extends BaseEntity>(target: EntityTarget<T>, id: string, relations?: string[]): Promise<T | null> { - const connection = getConnection(); - - const repository = connection.getRepository<T>(target); + const repository = AppDataSource.getRepository<T>(target); const single = await repository.findOne({ where: ({ Id: id } as FindOptionsWhere<T>), relations: relations || {} }); @@ -57,9 +50,7 @@ export default class BaseEntity { } public static async Any<T extends ObjectLiteral>(target: EntityTarget<T>): Promise<boolean> { - const connection = getConnection(); - - const repository = connection.getRepository<T>(target); + const repository = AppDataSource.getRepository<T>(target); const any = await repository.find(); diff --git a/src/contracts/ICommandContext.ts b/src/contracts/ICommandContext.ts deleted file mode 100644 index 78f0d17..0000000 --- a/src/contracts/ICommandContext.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Message } from "discord.js"; - -export interface ICommandContext { - name: string; - args: string[]; - message: Message; -} \ No newline at end of file diff --git a/src/contracts/ICommandReturnContext.ts b/src/contracts/ICommandReturnContext.ts deleted file mode 100644 index 144e981..0000000 --- a/src/contracts/ICommandReturnContext.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { MessageEmbed } from "discord.js"; -import { ICommandContext } from "./ICommandContext"; - -export default interface ICommandReturnContext { - commandContext: ICommandContext, - embeds: MessageEmbed[] -} \ No newline at end of file diff --git a/src/contracts/IEventItem.ts b/src/contracts/IEventItem.ts index 51bb881..ea32a3e 100644 --- a/src/contracts/IEventItem.ts +++ b/src/contracts/IEventItem.ts @@ -1,6 +1,7 @@ -import { Event } from "../type/event"; +import { EventType } from "../constants/EventType"; export default interface IEventItem { - Event: Event, + EventType: EventType, + ExecutionFunction: Function, } \ No newline at end of file diff --git a/src/contracts/IEventReturnContext.ts b/src/contracts/IEventReturnContext.ts deleted file mode 100644 index ccbe56d..0000000 --- a/src/contracts/IEventReturnContext.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { MessageEmbed } from "discord.js"; -import { ICommandContext } from "./ICommandContext"; - -export default interface ICommandReturnContext { - embeds: MessageEmbed[] -} \ No newline at end of file diff --git a/src/database/dataSources/appDataSource.ts b/src/database/dataSources/appDataSource.ts new file mode 100644 index 0000000..89cd27f --- /dev/null +++ b/src/database/dataSources/appDataSource.ts @@ -0,0 +1,26 @@ +import { DataSource } from "typeorm"; +import * as dotenv from "dotenv"; + +dotenv.config(); + +const AppDataSource = new DataSource({ + type: "mysql", + host: process.env.DB_HOST, + port: Number(process.env.DB_PORT), + username: process.env.DB_AUTH_USER, + password: process.env.DB_AUTH_PASS, + database: process.env.DB_NAME, + synchronize: process.env.DB_SYNC == "true", + logging: process.env.DB_LOGGING == "true", + entities: [ + "dist/database/entities/**/*.js", + ], + migrations: [ + "dist/database/migrations/**/*.js", + ], + subscribers: [ + "dist/database/subscribers/**/*.js", + ], +}); + +export default AppDataSource; \ No newline at end of file diff --git a/src/entity/501231711271780357/Lobby.ts b/src/database/entities/501231711271780357/Lobby.ts similarity index 79% rename from src/entity/501231711271780357/Lobby.ts rename to src/database/entities/501231711271780357/Lobby.ts index b9f588b..95eafc8 100644 --- a/src/entity/501231711271780357/Lobby.ts +++ b/src/database/entities/501231711271780357/Lobby.ts @@ -1,5 +1,6 @@ -import { Column, Entity, getConnection } from "typeorm"; -import BaseEntity from "../../contracts/BaseEntity"; +import { Column, Entity } from "typeorm"; +import BaseEntity from "../../../contracts/BaseEntity"; +import AppDataSource from "../../dataSources/appDataSource"; @Entity() export default class Lobby extends BaseEntity { @@ -34,9 +35,7 @@ export default class Lobby extends BaseEntity { } public static async FetchOneByChannelId(channelId: string, relations?: string[]): Promise<Lobby | null> { - const connection = getConnection(); - - const repository = connection.getRepository(Lobby); + const repository = AppDataSource.getRepository(Lobby); const single = await repository.findOne({ where: { ChannelId: channelId }, relations: relations || [] }); diff --git a/src/database/entities/Audit.ts b/src/database/entities/Audit.ts new file mode 100644 index 0000000..af4b1a5 --- /dev/null +++ b/src/database/entities/Audit.ts @@ -0,0 +1,53 @@ +import { Column, Entity } from "typeorm"; +import { AuditType } from "../../constants/AuditType"; +import BaseEntity from "../../contracts/BaseEntity"; +import StringTools from "../../helpers/StringTools"; +import AppDataSource from "../dataSources/appDataSource"; + +@Entity() +export default class Audit extends BaseEntity { + constructor(userId: string, auditType: AuditType, reason: string, moderatorId: string, serverId: string) { + super(); + + this.AuditId = StringTools.RandomString(5).toUpperCase(); + this.UserId = userId; + this.AuditType = auditType; + this.Reason = reason; + this.ModeratorId = moderatorId; + this.ServerId = serverId; + } + + @Column() + AuditId: string; + + @Column() + UserId: string; + + @Column() + AuditType: AuditType; + + @Column() + Reason: string; + + @Column() + ModeratorId: string; + + @Column() + ServerId: string; + + public static async FetchAuditsByUserId(userId: string, serverId: string): Promise<Audit[] | null> { + const repository = AppDataSource.getRepository(Audit); + + const all = await repository.find({ where: { UserId: userId, ServerId: serverId } }); + + return all; + } + + public static async FetchAuditByAuditId(auditId: string, serverId: string): Promise<Audit | null> { + const repository = AppDataSource.getRepository(Audit); + + const single = await repository.findOne({ where: { AuditId: auditId, ServerId: serverId } }); + + return single; + } +} \ No newline at end of file diff --git a/src/database/entities/IgnoredChannel.ts b/src/database/entities/IgnoredChannel.ts new file mode 100644 index 0000000..7b40be7 --- /dev/null +++ b/src/database/entities/IgnoredChannel.ts @@ -0,0 +1,20 @@ +import { Entity } from "typeorm"; +import BaseEntity from "../../contracts/BaseEntity"; +import AppDataSource from "../dataSources/appDataSource"; + +@Entity() +export default class IgnoredChannel extends BaseEntity { + constructor(channelId: string) { + super(); + + this.Id = channelId; + } + + public static async IsChannelIgnored(channelId: string): Promise<boolean> { + const repository = AppDataSource.getRepository(IgnoredChannel); + + const single = await repository.findOne({ where: { Id: channelId } }); + + return single != undefined; + } +} \ No newline at end of file diff --git a/src/entity/Role.ts b/src/database/entities/Role.ts similarity index 68% rename from src/entity/Role.ts rename to src/database/entities/Role.ts index 5745d7d..6f83bf5 100644 --- a/src/entity/Role.ts +++ b/src/database/entities/Role.ts @@ -1,6 +1,7 @@ -import { Column, Entity, EntityTarget, getConnection, ManyToOne } from "typeorm"; -import BaseEntity from "../contracts/BaseEntity" +import { Column, Entity, ManyToOne } from "typeorm"; +import BaseEntity from "../../contracts/BaseEntity" import Server from "./Server"; +import AppDataSource from "../dataSources/appDataSource"; @Entity() export default class Role extends BaseEntity { @@ -16,10 +17,12 @@ export default class Role extends BaseEntity { @ManyToOne(() => Server, x => x.Roles) Server: Server; - public static async FetchOneByRoleId(roleId: string, relations?: string[]): Promise<Role | null> { - const connection = getConnection(); + public SetServer(server: Server) { + this.Server = server; + } - const repository = connection.getRepository(Role); + public static async FetchOneByRoleId(roleId: string, relations?: string[]): Promise<Role | null> { + const repository = AppDataSource.getRepository(Role); const single = await repository.findOne({ where: { RoleId: roleId }, relations: relations || []}); @@ -27,9 +30,7 @@ export default class Role extends BaseEntity { } public static async FetchAllByServerId(serverId: string): Promise<Role[]> { - const connection = getConnection(); - - const repository = connection.getRepository(Server); + const repository = AppDataSource.getRepository(Server); const all = await repository.findOne({ where: { Id: serverId }, relations: [ "Roles", diff --git a/src/entity/Server.ts b/src/database/entities/Server.ts similarity index 91% rename from src/entity/Server.ts rename to src/database/entities/Server.ts index f669e58..211ea9c 100644 --- a/src/entity/Server.ts +++ b/src/database/entities/Server.ts @@ -1,5 +1,5 @@ import { Entity, OneToMany } from "typeorm"; -import BaseEntity from "../contracts/BaseEntity"; +import BaseEntity from "../../contracts/BaseEntity"; import Role from "./Role"; import Setting from "./Setting"; diff --git a/src/entity/Setting.ts b/src/database/entities/Setting.ts similarity index 75% rename from src/entity/Setting.ts rename to src/database/entities/Setting.ts index 813accf..e7b27d1 100644 --- a/src/entity/Setting.ts +++ b/src/database/entities/Setting.ts @@ -1,6 +1,7 @@ -import { Column, Entity, getConnection, ManyToOne } from "typeorm"; -import BaseEntity from "../contracts/BaseEntity"; +import { Column, Entity, ManyToOne } from "typeorm"; +import BaseEntity from "../../contracts/BaseEntity"; import Server from "./Server"; +import AppDataSource from "../dataSources/appDataSource"; @Entity() export default class Setting extends BaseEntity { @@ -26,9 +27,7 @@ export default class Setting extends BaseEntity { } public static async FetchOneByKey(key: string, relations?: string[]): Promise<Setting | null> { - const connection = getConnection(); - - const repository = connection.getRepository(Setting); + const repository = AppDataSource.getRepository(Setting); const single = await repository.findOne({ where: { Key: key }, relations: relations || {} }); diff --git a/src/database/migrations/3.1/1662399171315-CreateBase.ts b/src/database/migrations/3.1/1662399171315-CreateBase.ts new file mode 100644 index 0000000..b05aee7 --- /dev/null +++ b/src/database/migrations/3.1/1662399171315-CreateBase.ts @@ -0,0 +1,30 @@ +import { MigrationInterface, QueryRunner } from "typeorm" +import MigrationHelper from "../../../helpers/MigrationHelper" + +export class vylbot1662399171315 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise<void> { + MigrationHelper.Up('1662399171315-CreateBase', '3.1', [ + "01-table/Audit", + "01-table/IgnoredChannel", + "01-table/Lobby", + "01-table/Role", + "01-table/Server", + "01-table/Setting", + + "02-key/Audit", + "02-key/IgnoredChannel", + "02-key/Lobby", + "02-key/Role", + "02-key/Server", + "02-key/Setting", + + "03-constraint/Role", + "03-constraint/Setting", + ], queryRunner); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + } + +} diff --git a/src/events/MemberEvents.ts b/src/events/MemberEvents.ts deleted file mode 100644 index 43d07e8..0000000 --- a/src/events/MemberEvents.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Event } from "../type/event"; -import { GuildMember } from "discord.js"; -import EventEmbed from "../helpers/embeds/EventEmbed"; -import GuildMemberUpdate from "./MemberEvents/GuildMemberUpdate"; -import SettingsHelper from "../helpers/SettingsHelper"; - -export default class MemberEvents extends Event { - constructor() { - super(); - } - - public override async guildMemberAdd(member: GuildMember) { - if (!member.guild) return; - - const enabled = await SettingsHelper.GetSetting("event.member.add.enabled", member.guild.id); - if (!enabled || enabled.toLowerCase() != "true") return; - - const embed = new EventEmbed(member.client, member.guild, "Member Joined"); - embed.AddUser("User", member.user, true); - embed.addField("Created", member.user.createdAt.toISOString()); - embed.setFooter({ text: `Id: ${member.user.id}` }); - - const channel = await SettingsHelper.GetSetting("event.member.add.channel", member.guild.id); - if (!channel || !member.guild.channels.cache.find(x => x.name == channel)) return; - - await embed.SendToChannel(channel); - } - - public override async guildMemberRemove(member: GuildMember) { - if (!member.guild) return; - - const enabled = await SettingsHelper.GetSetting("event.member.remove.enabled", member.guild.id); - if (!enabled || enabled.toLowerCase() != "true") return; - - const embed = new EventEmbed(member.client, member.guild, "Member Left"); - embed.AddUser("User", member.user, true); - embed.addField("Joined", member.joinedAt?.toISOString() || "n/a"); - embed.setFooter({ text: `Id: ${member.user.id}` }); - - const channel = await SettingsHelper.GetSetting("event.member.remove.channel", member.guild.id); - if (!channel || !member.guild.channels.cache.find(x => x.name == channel)) return; - - await embed.SendToChannel(channel); - } - - public override async guildMemberUpdate(oldMember: GuildMember, newMember: GuildMember) { - const handler = new GuildMemberUpdate(oldMember, newMember); - - if (oldMember.nickname != newMember.nickname) { // Nickname change - await handler.NicknameChanged(); - } - } -} \ No newline at end of file diff --git a/src/events/MemberEvents/GuildMemberAdd.ts b/src/events/MemberEvents/GuildMemberAdd.ts new file mode 100644 index 0000000..025be0b --- /dev/null +++ b/src/events/MemberEvents/GuildMemberAdd.ts @@ -0,0 +1,35 @@ +import { EmbedBuilder, GuildMember, TextChannel } from "discord.js"; +import EmbedColours from "../../constants/EmbedColours"; +import SettingsHelper from "../../helpers/SettingsHelper"; + +export default async function GuildMemberAdd(member: GuildMember) { + if (!member.guild) return; + + const enabled = await SettingsHelper.GetSetting("event.member.add.enabled", member.guild.id); + if (!enabled || enabled.toLowerCase() != "true") return; + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle('Member Joined') + .setDescription(`${member.user} \`${member.user.tag}\``) + .setFooter({ text: `Id: ${member.user.id}` }) + .setThumbnail(member.avatarURL()) + .addFields([ + { + name: 'Created', + value: member.user.createdAt.toISOString(), + } + ]); + + const channelSetting = await SettingsHelper.GetSetting("event.member.add.channel", member.guild.id); + + if (!channelSetting) return; + + const channel = member.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [embed ]}); +} \ No newline at end of file diff --git a/src/events/MemberEvents/GuildMemberRemove.ts b/src/events/MemberEvents/GuildMemberRemove.ts new file mode 100644 index 0000000..bf6ae96 --- /dev/null +++ b/src/events/MemberEvents/GuildMemberRemove.ts @@ -0,0 +1,35 @@ +import { EmbedBuilder, GuildMember, TextChannel } from "discord.js"; +import EmbedColours from "../../constants/EmbedColours"; +import SettingsHelper from "../../helpers/SettingsHelper"; + +export default async function GuildMemberRemove(member: GuildMember) { + if (!member.guild) return; + + const enabled = await SettingsHelper.GetSetting("event.member.remove.enabled", member.guild.id); + if (!enabled || enabled.toLowerCase() != "true") return; + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle('Member Left') + .setDescription(`${member.user} \`${member.user.tag}\``) + .setFooter({ text: `Id: ${member.user.id}` }) + .setThumbnail(member.avatarURL()) + .addFields([ + { + name: 'Joined', + value: member.joinedAt ? member.joinedAt.toISOString() : "*none*", + } + ]); + + const channelSetting = await SettingsHelper.GetSetting("event.member.remove.channel", member.guild.id); + + if (!channelSetting) return; + + const channel = member.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [embed ]}); +} \ No newline at end of file diff --git a/src/events/MemberEvents/GuildMemberUpdate.ts b/src/events/MemberEvents/GuildMemberUpdate.ts index 0abd37d..0a7bbb1 100644 --- a/src/events/MemberEvents/GuildMemberUpdate.ts +++ b/src/events/MemberEvents/GuildMemberUpdate.ts @@ -1,32 +1,8 @@ import { GuildMember } from "discord.js"; -import EventEmbed from "../../helpers/embeds/EventEmbed"; -import SettingsHelper from "../../helpers/SettingsHelper"; +import NicknameChanged from "./GuildMemberUpdate/NicknameChanged"; -export default class GuildMemberUpdate { - public oldMember: GuildMember; - public newMember: GuildMember; - - constructor(oldMember: GuildMember, newMember: GuildMember) { - this.oldMember = oldMember; - this.newMember = newMember; - } - - public async NicknameChanged() { - const enabled = await SettingsHelper.GetSetting("event.member.update.enabled", this.newMember.guild.id); - if (!enabled || enabled.toLowerCase() != "true") return; - - const oldNickname = this.oldMember.nickname || "*none*"; - const newNickname = this.newMember.nickname || "*none*"; - - const embed = new EventEmbed(this.oldMember.client, this.newMember.guild, "Nickname Changed"); - embed.AddUser("User", this.newMember.user, true); - embed.addField("Before", oldNickname, true); - embed.addField("After", newNickname, true); - embed.setFooter({ text: `Id: ${this.newMember.user.id}` }); - - const channel = await SettingsHelper.GetSetting("event.member.update.channel", this.newMember.guild.id); - if (!channel || channel.toLowerCase() != "true") return; - - await embed.SendToChannel(channel); +export default async function GuildMemberUpdate(oldMember: GuildMember, newMember: GuildMember) { + if (oldMember.nickname != newMember.nickname) { // Nickname change + await NicknameChanged(oldMember, newMember); } } \ No newline at end of file diff --git a/src/events/MemberEvents/GuildMemberUpdate/NicknameChanged.ts b/src/events/MemberEvents/GuildMemberUpdate/NicknameChanged.ts new file mode 100644 index 0000000..671d755 --- /dev/null +++ b/src/events/MemberEvents/GuildMemberUpdate/NicknameChanged.ts @@ -0,0 +1,40 @@ +import { EmbedBuilder, GuildMember, TextChannel } from "discord.js"; +import EmbedColours from "../../../constants/EmbedColours"; +import SettingsHelper from "../../../helpers/SettingsHelper"; + +export default async function NicknameChanged(oldMember: GuildMember, newMember: GuildMember) { + const enabled = await SettingsHelper.GetSetting("event.member.update.enabled", newMember.guild.id); + if (!enabled || enabled.toLowerCase() != "true") return; + + const oldNickname = oldMember.nickname || "*none*"; + const newNickname = newMember.nickname || "*none*"; + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle('Nickname Changed') + .setDescription(`${newMember.user} \`${newMember.user.tag}\``) + .setFooter({ text: `Id: ${newMember.user.id}` }) + .setThumbnail(newMember.avatarURL()) + .addFields([ + { + name: 'Before', + value: oldNickname, + }, + { + name: 'After', + value: newNickname, + }, + ]); + + const channelSetting = await SettingsHelper.GetSetting("event.member.update.channel", newMember.guild.id); + + if (!channelSetting) return; + + const channel = newMember.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [embed ]}); +} \ No newline at end of file diff --git a/src/events/MessageEvents.ts b/src/events/MessageEvents.ts deleted file mode 100644 index cfeb13e..0000000 --- a/src/events/MessageEvents.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Event } from "../type/event"; -import { Message } from "discord.js"; -import EventEmbed from "../helpers/embeds/EventEmbed"; -import SettingsHelper from "../helpers/SettingsHelper"; -import OnMessage from "./MessageEvents/OnMessage"; - -export default class MessageEvents extends Event { - constructor() { - super(); - } - - public override async messageDelete(message: Message) { - if (!message.guild) return; - if (message.author.bot) return; - - const enabled = await SettingsHelper.GetSetting("event.message.delete.enabled", message.guild.id); - if (!enabled || enabled.toLowerCase() != "true") return; - - const embed = new EventEmbed(message.client, message.guild, "Message Deleted"); - embed.AddUser("User", message.author, true); - embed.addField("Channel", message.channel.toString(), true); - embed.addField("Content", `\`\`\`${message.content || "*none*"}\`\`\``); - - if (message.attachments.size > 0) { - embed.addField("Attachments", `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\``); - } - - const channel = await SettingsHelper.GetSetting("event.message.delete.channel", message.guild.id); - if (!channel || !message.guild.channels.cache.find(x => x.name == channel)) return; - - await embed.SendToChannel(channel); - } - - public override async messageUpdate(oldMessage: Message, newMessage: Message) { - if (!newMessage.guild) return; - if (newMessage.author.bot) return; - if (oldMessage.content == newMessage.content) return; - - const enabled = await SettingsHelper.GetSetting("event.message.update.enabled", newMessage.guild.id); - if (!enabled || enabled.toLowerCase() != "true") return; - - const embed = new EventEmbed(newMessage.client, newMessage.guild, "Message Edited"); - embed.AddUser("User", newMessage.author, true); - embed.addField("Channel", newMessage.channel.toString(), true); - embed.addField("Before", `\`\`\`${oldMessage.content || "*none*"}\`\`\``); - embed.addField("After", `\`\`\`${newMessage.content || "*none*"}\`\`\``); - - const channel = await SettingsHelper.GetSetting("event.message.update.channel", newMessage.guild.id); - if (!channel || !newMessage.guild.channels.cache.find(x => x.name == channel)) return; - - await embed.SendToChannel(channel); - } - - public override async messageCreate(message: Message) { - if (!message.guild) return; - if (message.author.bot) return; - - const isVerificationEnabled = await SettingsHelper.GetSetting("verification.enabled", message.guild.id); - - if (isVerificationEnabled && isVerificationEnabled.toLocaleLowerCase() == "true") { - await OnMessage.VerificationCheck(message); - } - } -} \ No newline at end of file diff --git a/src/events/MessageEvents/MessageCreate.ts b/src/events/MessageEvents/MessageCreate.ts new file mode 100644 index 0000000..a91250d --- /dev/null +++ b/src/events/MessageEvents/MessageCreate.ts @@ -0,0 +1,14 @@ +import { Message } from "discord.js"; +import SettingsHelper from "../../helpers/SettingsHelper"; +import VerificationCheck from "./MessageCreate/VerificationCheck"; + +export default async function MessageCreate(message: Message) { + if (!message.guild) return; + if (message.author.bot) return; + + const isVerificationEnabled = await SettingsHelper.GetSetting("verification.enabled", message.guild.id); + + if (isVerificationEnabled && isVerificationEnabled.toLocaleLowerCase() == "true") { + await VerificationCheck(message); + } +} \ No newline at end of file diff --git a/src/events/MessageEvents/MessageCreate/VerificationCheck.ts b/src/events/MessageEvents/MessageCreate/VerificationCheck.ts new file mode 100644 index 0000000..1dba4e3 --- /dev/null +++ b/src/events/MessageEvents/MessageCreate/VerificationCheck.ts @@ -0,0 +1,57 @@ +import { Message } from "discord.js"; +import SettingsHelper from "../../../helpers/SettingsHelper"; + +export default async function VerificationCheck(message: Message) { + if (!message.guild) return; + + const verificationChannel = await SettingsHelper.GetSetting("verification.channel", message.guild.id); + + if (!verificationChannel) { + return; + } + + const channel = message.guild.channels.cache.find(x => x.name == verificationChannel); + + if (!channel) { + return; + } + + const currentChannel = message.guild.channels.cache.find(x => x == message.channel); + + if (!currentChannel || currentChannel.name != verificationChannel) { + return; + } + + const verificationCode = await SettingsHelper.GetSetting("verification.code", message.guild.id); + + if (!verificationCode || verificationCode == "") { + await message.reply("`verification.code` is not set inside of the server's config. Please contact the server's mod team."); + await message.delete(); + + return; + } + + const verificationRoleName = await SettingsHelper.GetSetting("verification.role", message.guild.id); + + if (!verificationRoleName) { + await message.reply("`verification.role` is not set inside of the server's config. Please contact the server's mod team."); + await message.delete(); + return; + } + + const role = message.guild.roles.cache.find(x => x.name == verificationRoleName); + + if (!role) { + await message.reply("The entry role configured for this server does not exist. Please contact the server's mod team."); + await message.delete(); + return; + } + + if (message.content.toLocaleLowerCase() != verificationCode.toLocaleLowerCase()) { + await message.delete(); + return; + } + + await message.member?.roles.add(role); + await message.delete(); +} \ No newline at end of file diff --git a/src/events/MessageEvents/MessageDelete.ts b/src/events/MessageEvents/MessageDelete.ts new file mode 100644 index 0000000..4bb9199 --- /dev/null +++ b/src/events/MessageEvents/MessageDelete.ts @@ -0,0 +1,53 @@ +import { EmbedBuilder, Message, TextChannel } from "discord.js"; +import EmbedColours from "../../constants/EmbedColours"; +import IgnoredChannel from "../../database/entities/IgnoredChannel"; +import SettingsHelper from "../../helpers/SettingsHelper"; + +export default async function MessageDelete(message: Message) { + if (!message.guild) return; + if (message.author.bot) return; + + const enabled = await SettingsHelper.GetSetting("event.message.delete.enabled", message.guild.id); + if (!enabled || enabled.toLowerCase() != "true") return; + + const ignored = await IgnoredChannel.IsChannelIgnored(message.channel.id); + if (ignored) return; + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Message Deleted") + .setDescription(`${message.author} \`${message.author.tag}\``) + .setThumbnail(message.author.avatarURL()) + .addFields([ + { + name: "Channel", + value: message.channel.toString(), + inline: true, + }, + { + name: "Content", + value: `\`\`\`${message.content || "*none*"}\`\`\``, + } + ]); + + if (message.attachments.size > 0) { + embed.addFields([ + { + name: "Attachments", + value: `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\`` + } + ]); + } + + const channelSetting = await SettingsHelper.GetSetting("event.message.delete.channel", message.guild.id); + + if (!channelSetting) return; + + const channel = message.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [ embed ]}); +} \ No newline at end of file diff --git a/src/events/MessageEvents/MessageUpdate.ts b/src/events/MessageEvents/MessageUpdate.ts new file mode 100644 index 0000000..7564a2f --- /dev/null +++ b/src/events/MessageEvents/MessageUpdate.ts @@ -0,0 +1,49 @@ +import { EmbedBuilder, Message, TextChannel } from "discord.js"; +import EmbedColours from "../../constants/EmbedColours"; +import IgnoredChannel from "../../database/entities/IgnoredChannel"; +import SettingsHelper from "../../helpers/SettingsHelper"; + +export default async function MessageUpdate(oldMessage: Message, newMessage: Message) { + if (!newMessage.guild) return; + if (newMessage.author.bot) return; + if (oldMessage.content == newMessage.content) return; + + const enabled = await SettingsHelper.GetSetting("event.message.update.enabled", newMessage.guild.id); + if (!enabled || enabled.toLowerCase() != "true") return; + + const ignored = await IgnoredChannel.IsChannelIgnored(newMessage.channel.id); + if (ignored) return; + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Message Edited") + .setDescription(`${newMessage.author} \`${newMessage.author.tag}\``) + .setThumbnail(newMessage.author.avatarURL()) + .addFields([ + { + name: "Channel", + value: newMessage.channel.toString(), + inline: true, + }, + { + name: "Before", + value: `\`\`\`${oldMessage.content || "*none*"}\`\`\``, + }, + { + name: "After", + value: `\`\`\`${newMessage.content || "*none*"}\`\`\``, + } + ]); + + const channelSetting = await SettingsHelper.GetSetting("event.message.delete.channel", newMessage.guild.id); + + if (!channelSetting) return; + + const channel = newMessage.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [ embed ]}); +} \ No newline at end of file diff --git a/src/events/MessageEvents/OnMessage.ts b/src/events/MessageEvents/OnMessage.ts deleted file mode 100644 index 18c2a57..0000000 --- a/src/events/MessageEvents/OnMessage.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Message as Message } from "discord.js"; -import SettingsHelper from "../../helpers/SettingsHelper"; - -export default class OnMessage { - public static async VerificationCheck(message: Message) { - if (!message.guild) return; - - const verificationChannel = await SettingsHelper.GetSetting("verification.channel", message.guild.id); - - if (!verificationChannel) { - return; - } - - const channel = message.guild.channels.cache.find(x => x.name == verificationChannel); - - if (!channel) { - return; - } - - const currentChannel = message.guild.channels.cache.find(x => x == message.channel); - - if (!currentChannel || currentChannel.name != verificationChannel) { - return; - } - - const verificationCode = await SettingsHelper.GetSetting("verification.code", message.guild.id); - - if (!verificationCode || verificationCode == "") { - await message.reply("`verification.code` is not set inside of the server's config. Please contact the server's mod team."); - await message.delete(); - - return; - } - - const verificationRoleName = await SettingsHelper.GetSetting("verification.role", message.guild.id); - - if (!verificationRoleName) { - await message.reply("`verification.role` is not set inside of the server's config. Please contact the server's mod team."); - await message.delete(); - return; - } - - const role = message.guild.roles.cache.find(x => x.name == verificationRoleName); - - if (!role) { - await message.reply("The entry role configured for this server does not exist. Please contact the server's mod team."); - await message.delete(); - return; - } - - if (message.content.toLocaleLowerCase() != verificationCode.toLocaleLowerCase()) { - await message.delete(); - return; - } - - await message.member?.roles.add(role); - await message.delete(); - } -} \ No newline at end of file diff --git a/src/helpers/AuditTools.ts b/src/helpers/AuditTools.ts new file mode 100644 index 0000000..9833a3c --- /dev/null +++ b/src/helpers/AuditTools.ts @@ -0,0 +1,41 @@ +import { AuditType } from "../constants/AuditType"; + +export default class AuditTools { + public static TypeToFriendlyText(auditType: AuditType): string { + switch (auditType) { + case AuditType.General: + return "General"; + case AuditType.Warn: + return "Warn"; + case AuditType.Mute: + return "Mute"; + case AuditType.Kick: + return "Kick"; + case AuditType.Ban: + return "Ban"; + case AuditType.Timeout: + return "Timeout"; + default: + return "Other"; + } + } + + public static StringToType(str: string): AuditType { + switch (str.toLowerCase()) { + case "general": + return AuditType.General; + case "warn": + return AuditType.Warn; + case "mute": + return AuditType.Mute; + case "kick": + return AuditType.Kick; + case "ban": + return AuditType.Ban; + case "timeout": + return AuditType.Timeout; + default: + return AuditType.General; + } + } +} \ No newline at end of file diff --git a/src/helpers/MigrationHelper.ts b/src/helpers/MigrationHelper.ts new file mode 100644 index 0000000..69fee4a --- /dev/null +++ b/src/helpers/MigrationHelper.ts @@ -0,0 +1,20 @@ +import { readFileSync } from "fs"; +import { QueryRunner } from "typeorm"; + +export default class MigrationHelper { + public static Up(migrationName: string, version: string, queryFiles: string[], queryRunner: QueryRunner) { + for (let path of queryFiles) { + const query = readFileSync(`${process.cwd()}/database/${version}/${migrationName}/Up/${path}.sql`).toString(); + + queryRunner.query(query); + } + } + + public static Down(migrationName: string, version: string, queryFiles: string[], queryRunner: QueryRunner) { + for (let path of queryFiles) { + const query = readFileSync(`${process.cwd()}/database/${version}/${migrationName}/Down/${path}.sql`).toString(); + + queryRunner.query(query); + } + } +} \ No newline at end of file diff --git a/src/helpers/SettingsHelper.ts b/src/helpers/SettingsHelper.ts index 1c6fd6a..66c92a8 100644 --- a/src/helpers/SettingsHelper.ts +++ b/src/helpers/SettingsHelper.ts @@ -1,6 +1,6 @@ import DefaultValues from "../constants/DefaultValues"; -import Server from "../entity/Server"; -import Setting from "../entity/Setting"; +import Server from "../database/entities/Server"; +import Setting from "../database/entities/Setting"; export default class SettingsHelper { public static async GetSetting(key: string, serverId: string): Promise<string | undefined> { diff --git a/src/helpers/StringTools.ts b/src/helpers/StringTools.ts index 5119f94..0254dc5 100644 --- a/src/helpers/StringTools.ts +++ b/src/helpers/StringTools.ts @@ -35,4 +35,8 @@ export default class StringTools { return result; } + + public static ReplaceAll(str: string, find: string, replace: string) { + return str.replace(new RegExp(find, 'g'), replace); + } } \ No newline at end of file diff --git a/src/helpers/TimeLengthInput.ts b/src/helpers/TimeLengthInput.ts new file mode 100644 index 0000000..95befa6 --- /dev/null +++ b/src/helpers/TimeLengthInput.ts @@ -0,0 +1,121 @@ +import StringTools from "./StringTools"; + +export default class TimeLengthInput { + public readonly value: string; + + constructor(input: string) { + this.value = StringTools.ReplaceAll(input, ',', ''); + } + + public GetDays(): number { + return this.GetValue('d'); + } + + public GetHours(): number { + return this.GetValue('h'); + } + + public GetMinutes(): number { + return this.GetValue('m'); + } + + public GetSeconds(): number { + return this.GetValue('s'); + } + + public GetMilliseconds(): number { + const days = this.GetDays(); + const hours = this.GetHours(); + const minutes = this.GetMinutes(); + const seconds = this.GetSeconds(); + + let milliseconds = 0; + + milliseconds += seconds * 1000; + milliseconds += minutes * 60 * 1000; + milliseconds += hours * 60 * 60 * 1000; + milliseconds += days * 24 * 60 * 60 * 1000; + + return milliseconds; + } + + public GetDateFromNow(): Date { + const now = Date.now(); + + const dateFromNow = now + + (1000 * this.GetSeconds()) + + (1000 * 60 * this.GetMinutes()) + + (1000 * 60 * 60 * this.GetHours()) + + (1000 * 60 * 60 * 24 * this.GetDays()); + + return new Date(dateFromNow); + } + + public GetLength(): string { + const days = this.GetDays(); + const hours = this.GetHours(); + const minutes = this.GetMinutes(); + const seconds = this.GetSeconds(); + + const value = []; + + if (days) { + value.push(`${days} days`); + } + + if (hours) { + value.push(`${hours} hours`); + } + + if (minutes) { + value.push(`${minutes} minutes`); + } + + if (seconds) { + value.push(`${seconds} seconds`); + } + + return value.join(", "); + } + + public GetLengthShort(): string { + const days = this.GetDays(); + const hours = this.GetHours(); + const minutes = this.GetMinutes(); + const seconds = this.GetSeconds(); + + const value = []; + + if (days) { + value.push(`${days}d`); + } + + if (hours) { + value.push(`${hours}h`); + } + + if (minutes) { + value.push(`${minutes}m`); + } + + if (seconds) { + value.push(`${seconds}s`); + } + + return value.join(" "); + } + + private GetValue(designation: string): number { + const valueSplit = this.value.split(' '); + + const desString = valueSplit.find(x => x.charAt(x.length - 1) == designation); + + if (!desString) return 0; + + const desNumber = Number(desString.substring(0, desString.length - 1)); + + if (!desNumber) return 0; + + return desNumber; + } +} \ No newline at end of file diff --git a/src/helpers/embeds/ErrorEmbed.ts b/src/helpers/embeds/ErrorEmbed.ts deleted file mode 100644 index 958b8fc..0000000 --- a/src/helpers/embeds/ErrorEmbed.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { MessageEmbed, Permissions, TextChannel } from "discord.js"; -import { ICommandContext } from "../../contracts/ICommandContext"; - -export default class ErrorEmbed extends MessageEmbed { - public context: ICommandContext; - - constructor(context: ICommandContext, message: string) { - super(); - - super.setColor(0xd52803); - super.setDescription(message); - - this.context = context; - } - - public async SendToCurrentChannel() { - const channel = this.context.message.channel as TextChannel; - const botMember = await this.context.message.guild?.members.fetch({ user: this.context.message.client.user! }); - - if (!botMember) return; - - const permissions = channel.permissionsFor(botMember); - - if (!permissions.has(Permissions.FLAGS.SEND_MESSAGES)) return; - - this.context.message.channel.send({ embeds: [ this ]}); - } -} \ No newline at end of file diff --git a/src/helpers/embeds/EventEmbed.ts b/src/helpers/embeds/EventEmbed.ts deleted file mode 100644 index 782f98a..0000000 --- a/src/helpers/embeds/EventEmbed.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { MessageEmbed, TextChannel, User, Guild, Client, Permissions } from "discord.js"; -import { ICommandContext } from "../../contracts/ICommandContext"; -import SettingsHelper from "../SettingsHelper"; - -export default class EventEmbed extends MessageEmbed { - public guild: Guild; - - private client: Client; - - constructor(client: Client, guild: Guild, title: string) { - super(); - - super.setColor(0x3050ba); - super.setTitle(title); - - this.guild = guild; - this.client = client; - } - - // Detail methods - public AddUser(title: string, user: User, setThumbnail: boolean = false) { - this.addField(title, `${user} \`${user.tag}\``, true); - - if (setThumbnail) { - this.setThumbnail(user.displayAvatarURL()); - } - } - - public AddReason(message: string) { - this.addField("Reason", message || "*none*"); - } - - // Send methods - public async SendToChannel(name: string) { - const channel = this.guild.channels.cache - .find(channel => channel.name == name) as TextChannel; - - if (!channel) { - console.error(`Unable to find channel ${name}`); - return; - } - - const botMember = await this.guild.members.fetch({ user: this.client.user! }); - - if (!botMember) return; - - const permissions = channel.permissionsFor(botMember); - - if (!permissions.has(Permissions.FLAGS.SEND_MESSAGES)) return; - - channel.send({embeds: [ this ]}); - } - - public async SendToMessageLogsChannel() { - const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.guild.id); - - if (!channelName) { - return; - } - - this.SendToChannel(channelName); - } - - public async SendToMemberLogsChannel() { - const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.guild.id); - - if (!channelName) { - return; - } - - this.SendToChannel(channelName); - } - - public async SendToModLogsChannel() { - const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.guild.id); - - if (!channelName) { - return; - } - - this.SendToChannel(channelName); - } -} \ No newline at end of file diff --git a/src/helpers/embeds/LogEmbed.ts b/src/helpers/embeds/LogEmbed.ts deleted file mode 100644 index 105c6a9..0000000 --- a/src/helpers/embeds/LogEmbed.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { MessageEmbed, Permissions, TextChannel, User } from "discord.js"; -import ErrorMessages from "../../constants/ErrorMessages"; -import { ICommandContext } from "../../contracts/ICommandContext"; -import SettingsHelper from "../SettingsHelper"; -import ErrorEmbed from "./ErrorEmbed"; - -export default class LogEmbed extends MessageEmbed { - public context: ICommandContext; - - constructor(context: ICommandContext, title: string) { - super(); - - super.setColor(0x3050ba); - super.setTitle(title); - - this.context = context; - } - - // Detail methods - public AddUser(title: string, user: User, setThumbnail: boolean = false) { - this.addField(title, `${user} \`${user.tag}\``, true); - - if (setThumbnail) { - this.setThumbnail(user.displayAvatarURL()); - } - } - - public AddReason(message: string) { - this.addField("Reason", message || "*none*"); - } - - // Send methods - public async SendToCurrentChannel() { - const channel = this.context.message.channel as TextChannel; - const botMember = await this.context.message.guild?.members.fetch({ user: this.context.message.client.user! }); - - if (!botMember) return; - - const permissions = channel.permissionsFor(botMember); - - if (!permissions.has(Permissions.FLAGS.SEND_MESSAGES)) return; - - this.context.message.channel.send({ embeds: [ this ]}); - } - - public SendToChannel(name: string) { - const channel = this.context.message.guild?.channels.cache - .find(channel => channel.name == name) as TextChannel; - - if (!channel) { - const errorEmbed = new ErrorEmbed(this.context, ErrorMessages.ChannelNotFound); - errorEmbed.SendToCurrentChannel(); - return; - } - - channel.send({ embeds: [ this ]}); - } - - public async SendToMessageLogsChannel() { - const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.context.message.guild?.id!); - - if (!channelName) { - return; - } - - this.SendToChannel(channelName); - } - - public async SendToMemberLogsChannel() { - const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.context.message.guild?.id!); - - if (!channelName) { - return; - } - - this.SendToChannel(channelName); - } - - public async SendToModLogsChannel() { - const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.context.message.guild?.id!); - - if (!channelName) { - return; - } - - this.SendToChannel(channelName); - } -} \ No newline at end of file diff --git a/src/helpers/embeds/PublicEmbed.ts b/src/helpers/embeds/PublicEmbed.ts deleted file mode 100644 index 84be5c9..0000000 --- a/src/helpers/embeds/PublicEmbed.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { MessageEmbed, Permissions, TextChannel } from "discord.js"; -import { ICommandContext } from "../../contracts/ICommandContext"; - -export default class PublicEmbed extends MessageEmbed { - public context: ICommandContext; - - constructor(context: ICommandContext, title: string, description: string) { - super(); - - super.setColor(0x3050ba); - super.setTitle(title); - super.setDescription(description); - - this.context = context; - } - - // Detail methods - public AddReason(message: string) { - this.addField("Reason", message || "*none*"); - } - - // Send methods - public async SendToCurrentChannel() { - const channel = this.context.message.channel as TextChannel; - const botMember = await this.context.message.guild?.members.fetch({ user: this.context.message.client.user! }); - - if (!botMember) return; - - const permissions = channel.permissionsFor(botMember); - - if (!permissions.has(Permissions.FLAGS.SEND_MESSAGES)) return; - - this.context.message.channel.send({ embeds: [ this ]}); - } -} \ No newline at end of file diff --git a/src/migration/1652375907691-CreateRole.ts b/src/migration/1652375907691-CreateRole.ts deleted file mode 100644 index 255ed0f..0000000 --- a/src/migration/1652375907691-CreateRole.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class migration1652375907691 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise<void> { - queryRunner.query(`CREATE TABLE role (Id varchar(255), WhenCreated datetime, WhenUpdated datetime, RoleId varchar(255), serverId varchar(255), PRIMARY KEY (Id), FOREIGN KEY (serverId) REFERENCES server(Id))`); - } - - public async down(queryRunner: QueryRunner): Promise<void> { - queryRunner.query(`DROP TABLE role`); - } - -} diff --git a/src/registry.ts b/src/registry.ts index 9d073b1..50beeab 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -1,61 +1,85 @@ import { CoreClient } from "./client/client"; +import { EventType } from "./constants/EventType"; // Command Imports import About from "./commands/about"; +import Audits from "./commands/audits"; import Ban from "./commands/ban"; +import Bunny from "./commands/bunny"; import Clear from "./commands/clear"; import Code from "./commands/code"; import Config from "./commands/config"; import Disable from "./commands/disable"; -import Help from "./commands/help"; +import Ignore from "./commands/ignore"; import Kick from "./commands/kick"; import Mute from "./commands/mute"; -import Poll from "./commands/poll"; -import Role from "./commands/role"; +import Role from "./commands/Role/role"; +import ConfigRole from "./commands/Role/config"; import Rules from "./commands/rules"; import Setup from "./commands/setup"; +import Timeout from "./commands/timeout"; import Unmute from "./commands/unmute"; import Warn from "./commands/warn"; // Command Imports: MankBot import Entry from "./commands/501231711271780357/entry"; -import Lobby from "./commands/501231711271780357/lobby"; +import Lobby from "./commands/501231711271780357/Lobby/lobby"; +import AddLobby from "./commands/501231711271780357/Lobby/add"; +import RemoveLobby from "./commands/501231711271780357/Lobby/remove"; +import ListLobby from "./commands/501231711271780357/Lobby/list"; // Event Imports -import MemberEvents from "./events/MemberEvents"; -import MessageEvents from "./events/MessageEvents"; -import Bunny from "./commands/bunny"; +import GuildMemberAdd from "./events/MemberEvents/GuildMemberAdd"; +import GuildMemberRemove from "./events/MemberEvents/GuildMemberRemove"; +import GuildMemberUpdate from "./events/MemberEvents/GuildMemberUpdate"; +import MessageDelete from "./events/MessageEvents/MessageDelete"; +import MessageUpdate from "./events/MessageEvents/MessageUpdate"; +import MessageCreate from "./events/MessageEvents/MessageCreate"; export default class Registry { public static RegisterCommands() { CoreClient.RegisterCommand("about", new About()); + CoreClient.RegisterCommand("audits", new Audits()); CoreClient.RegisterCommand("ban", new Ban()); CoreClient.RegisterCommand("bunny", new Bunny()); CoreClient.RegisterCommand("clear", new Clear()); - CoreClient.RegisterCommand("help", new Help()); + CoreClient.RegisterCommand("code", new Code()); + CoreClient.RegisterCommand("config", new Config()); + CoreClient.RegisterCommand("disable", new Disable()); + CoreClient.RegisterCommand("ignore", new Ignore()); CoreClient.RegisterCommand("kick", new Kick()); CoreClient.RegisterCommand("mute", new Mute()); - CoreClient.RegisterCommand("poll", new Poll()); - CoreClient.RegisterCommand("role", new Role()); CoreClient.RegisterCommand("rules", new Rules()); + CoreClient.RegisterCommand("setup", new Setup()); + CoreClient.RegisterCommand("timeout", new Timeout()); CoreClient.RegisterCommand("unmute", new Unmute()); CoreClient.RegisterCommand("warn", new Warn()); - CoreClient.RegisterCommand("setup", new Setup()); - CoreClient.RegisterCommand("config", new Config()); - CoreClient.RegisterCommand("code", new Code()); - CoreClient.RegisterCommand("disable", new Disable()); + + CoreClient.RegisterCommand("role", new Role()); + CoreClient.RegisterCommand("configrole", new ConfigRole()); // Exclusive Commands: MankBot CoreClient.RegisterCommand("lobby", new Lobby(), "501231711271780357"); + CoreClient.RegisterCommand("lobbyAdd", new AddLobby(), "501231711271780357"); + CoreClient.RegisterCommand("lobbyRemove", new RemoveLobby(), "501231711271780357"); + CoreClient.RegisterCommand("listlobby", new ListLobby(), "501231711271780357"); CoreClient.RegisterCommand("entry", new Entry(), "501231711271780357"); // Add Exclusive Commands to Test Server CoreClient.RegisterCommand("lobby", new Lobby(), "442730357897429002"); + CoreClient.RegisterCommand("addlobby", new AddLobby(), "442730357897429002"); + CoreClient.RegisterCommand("removelobby", new RemoveLobby(), "442730357897429002"); + CoreClient.RegisterCommand("listlobby", new ListLobby(), "442730357897429002"); CoreClient.RegisterCommand("entry", new Entry(), "442730357897429002"); } public static RegisterEvents() { - CoreClient.RegisterEvent(new MemberEvents()); - CoreClient.RegisterEvent(new MessageEvents()); + CoreClient.RegisterEvent(EventType.GuildMemberAdd, GuildMemberAdd); + CoreClient.RegisterEvent(EventType.GuildMemberRemove, GuildMemberRemove); + CoreClient.RegisterEvent(EventType.GuildMemberUpdate, GuildMemberUpdate); + + CoreClient.RegisterEvent(EventType.MessageDelete, MessageDelete); + CoreClient.RegisterEvent(EventType.MessageUpdate, MessageUpdate); + CoreClient.RegisterEvent(EventType.MessageCreate, MessageCreate); } } \ No newline at end of file diff --git a/src/type/command.ts b/src/type/command.ts index 1d04686..10d091d 100644 --- a/src/type/command.ts +++ b/src/type/command.ts @@ -1,23 +1,9 @@ -import { CommandResponse } from "../constants/CommandResponse"; -import { ICommandContext } from "../contracts/ICommandContext"; +import { CommandInteraction } from "discord.js"; export class Command { - public Roles: string[]; - public Category?: string; - - constructor() { - this.Roles = []; - } - - public precheck(context: ICommandContext): CommandResponse { - return CommandResponse.Ok; - } - - public async precheckAsync(context: ICommandContext): Promise<CommandResponse> { - return CommandResponse.Ok; - } - - public execute(context: ICommandContext) { + public CommandBuilder: any; + + public execute(interaction: CommandInteraction) { } } diff --git a/src/type/event.ts b/src/type/event.ts deleted file mode 100644 index 77771de..0000000 --- a/src/type/event.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Channel, Guild, GuildMember, Message, PartialDMChannel, PartialGuildMember, PartialMessage, GuildBan } from "discord.js"; - -export class Event { - public channelCreate(channel: Channel) { - - } - - public channelDelete(channel: Channel | PartialDMChannel) { - - } - - public channelUpdate(oldChannel: Channel, newChannel: Channel) { - - } - - public guildBanAdd(ban: GuildBan) { - - } - - public guildBanRemove(ban: GuildBan) { - - } - - public guildCreate(guild: Guild) { - - } - - public guildMemberAdd(member: GuildMember) { - - } - - public guildMemberRemove(member: GuildMember | PartialGuildMember) { - - } - - public guildMemberUpdate(oldMember: GuildMember | PartialGuildMember, newMember: GuildMember) { - - } - - public messageCreate(message: Message) { - - } - - public messageDelete(message: Message | PartialMessage) { - - } - - public messageUpdate(oldMessage: Message | PartialMessage, newMessage: Message | PartialMessage) { - - } - - public ready() { - - } -} diff --git a/src/vylbot.ts b/src/vylbot.ts index 50f1d87..668c8e5 100644 --- a/src/vylbot.ts +++ b/src/vylbot.ts @@ -1,7 +1,7 @@ import { CoreClient } from "./client/client"; import * as dotenv from "dotenv"; import registry from "./registry"; -import { Intents } from "discord.js"; +import { IntentsBitField } from "discord.js"; dotenv.config(); @@ -9,8 +9,14 @@ const requiredConfigs: string[] = [ "BOT_TOKEN", "BOT_VER", "BOT_AUTHOR", - "BOT_DATE", "BOT_OWNERID", + "BOT_CLIENTID", + "DB_HOST", + "DB_PORT", + "DB_AUTH_USER", + "DB_AUTH_PASS", + "DB_SYNC", + "DB_LOGGING", ]; requiredConfigs.forEach(config => { @@ -19,15 +25,14 @@ requiredConfigs.forEach(config => { } }); -const devmode = process.argv.find(x => x.toLowerCase() == "--dev") != null; - const client = new CoreClient([ - Intents.FLAGS.GUILDS, - Intents.FLAGS.GUILD_MESSAGES, - Intents.FLAGS.GUILD_MEMBERS, -], devmode); + IntentsBitField.Flags.Guilds, + IntentsBitField.Flags.GuildMessages, + IntentsBitField.Flags.GuildMembers, + IntentsBitField.Flags.MessageContent, +]); registry.RegisterCommands(); registry.RegisterEvents(); -client.start(); \ No newline at end of file +client.start(); diff --git a/tests/commands/timeout.test.ts b/tests/commands/timeout.test.ts new file mode 100644 index 0000000..1646734 --- /dev/null +++ b/tests/commands/timeout.test.ts @@ -0,0 +1,766 @@ +import { APIEmbed, CacheType, CommandInteraction, CommandInteractionOption, DMChannel, Embed, EmbedBuilder, EmbedField, Guild, GuildChannel, GuildMember, InteractionReplyOptions, JSONEncodable, Message, MessageCreateOptions, MessagePayload, SlashCommandBuilder, TextChannel, User } from "discord.js"; +import { mock } from "jest-mock-extended"; +import Timeout from "../../src/commands/timeout"; +import SettingsHelper from "../../src/helpers/SettingsHelper"; +import Audit from "../../src/database/entities/Audit"; +import EmbedColours from "../../src/constants/EmbedColours"; +import { DeepPartial, EntityTarget } from "typeorm"; +import BaseEntity from "../../src/contracts/BaseEntity"; +import { AuditType } from "../../src/constants/AuditType"; + +describe('Constructor', () => { + test('EXPECT CommandBuilder to be configured', () => { + const command = new Timeout(); + + expect(command.CommandBuilder).toBeDefined(); + + const commandBuilder = command.CommandBuilder as SlashCommandBuilder; + + expect(commandBuilder.name).toBe("timeout"); + expect(commandBuilder.description).toBe("Timeouts a user out, sending them a DM with the reason if possible"); + expect(commandBuilder.options.length).toBe(3); + }); +}); + +describe('execute', () => { + // Happy flow + test('GIVEN all checks have passed, EXPECT user to be timed out', async () => { + let embeds: APIEmbed[] | undefined; + + const command = new Timeout(); + + const interactionReply = jest.fn((options: InteractionReplyOptions) => { + embeds = options.embeds as APIEmbed[]; + }); + + let savedAudit: DeepPartial<Audit> | undefined; + + const getSetting = jest.spyOn(SettingsHelper, 'GetSetting').mockResolvedValue('mod-logs'); + const auditSave = jest.spyOn(Audit.prototype, 'Save').mockImplementation((target: EntityTarget<BaseEntity>, entity: DeepPartial<BaseEntity>): Promise<void> => { + savedAudit = entity; + + return Promise.resolve(); + }); + + const timeoutFunc = jest.fn(); + + let dmChannelSentEmbeds: (APIEmbed | JSONEncodable<APIEmbed>)[] | undefined; + let logsChannelSentEmbeds: (APIEmbed | JSONEncodable<APIEmbed>)[] | undefined; + + const dmChannel = { + send: jest.fn().mockImplementation((options: MessageCreateOptions) => { + dmChannelSentEmbeds = options.embeds; + }), + } as unknown as DMChannel; + + const userInput = { + user: { + id: 'userId', + tag: 'userTag', + createDM: jest.fn().mockResolvedValue(dmChannel), + } as unknown as User, + member: { + manageable: true, + timeout: timeoutFunc, + } as unknown as GuildMember, + } as CommandInteractionOption; + + const lengthInput = { + value: '1s', + } as CommandInteractionOption; + + const reasonInput = { + value: 'Test reason', + } as CommandInteractionOption; + + const logsChannel = { + name: 'mod-logs', + send: jest.fn().mockImplementation((options: MessageCreateOptions) => { + logsChannelSentEmbeds = options.embeds; + }), + } as unknown as TextChannel; + + const interaction = { + guild: { + channels: { + cache: { + find: jest.fn() + .mockReturnValue(logsChannel), + } + }, + name: "Test Guild", + } as unknown as Guild, + guildId: 'guildId', + reply: interactionReply, + options: { + get: jest.fn() + .mockReturnValueOnce(userInput) + .mockReturnValueOnce(lengthInput) + .mockReturnValue(reasonInput), + }, + user: { + id: 'moderatorId' + } + } as unknown as CommandInteraction; + + await command.execute(interaction); + + // EXPECT user to be timed out + expect(timeoutFunc).toBeCalledWith(1000, 'Test reason'); + + // EXPECT embeds to be sent + expect(embeds).toBeDefined(); + expect(embeds!.length).toBe(1); + + // EXPECT resultEmbed to be correctly configured + const resultEmbed = embeds![0] as EmbedBuilder; + + expect(resultEmbed.data.description).toBe('<@userId> has been timed out'); + expect(resultEmbed.data.fields).toBeDefined(); + expect(resultEmbed.data.fields!.length).toBe(1); + + // EXPECT DM field to be configured + const resultEmbedDMField = resultEmbed.data.fields![0]; + + expect(resultEmbedDMField.name).toBe("DM Sent"); + expect(resultEmbedDMField.value).toBe("true"); + + // EXPECT user to be DM's with embed + expect(dmChannel.send).toBeCalled(); + expect(dmChannelSentEmbeds).toBeDefined(); + expect(dmChannelSentEmbeds?.length).toBe(1); + + const dmChannelSentEmbed = (dmChannelSentEmbeds![0] as any).data; + + expect(dmChannelSentEmbed.color).toBe(EmbedColours.Ok); + expect(dmChannelSentEmbed.description).toBe("You have been timed out in Test Guild"); + expect(dmChannelSentEmbed.fields?.length).toBe(3); + + expect(dmChannelSentEmbed.fields![0].name).toBe("Reason"); + expect(dmChannelSentEmbed.fields![0].value).toBe("Test reason"); + + expect(dmChannelSentEmbed.fields![1].name).toBe("Length"); + expect(dmChannelSentEmbed.fields![1].value).toBe("1s"); + + expect(dmChannelSentEmbed.fields![2].name).toBe("Until"); + expect(dmChannelSentEmbed.fields![2].value).toBeDefined(); + + // EXPECT log embed to be sent + expect(logsChannel.send).toBeCalled(); + expect(logsChannelSentEmbeds).toBeDefined(); + expect(logsChannelSentEmbeds?.length).toBe(1); + + const logsChannelSentEmbed = (logsChannelSentEmbeds![0] as any).data; + + expect(logsChannelSentEmbed.color).toBe(EmbedColours.Ok); + expect(logsChannelSentEmbed.title).toBe("Member Timed Out"); + expect(logsChannelSentEmbed.description).toBe("<@userId> `userTag`"); + expect(logsChannelSentEmbed.fields?.length).toBe(4); + + expect(logsChannelSentEmbed.fields![0].name).toBe("Moderator"); + expect(logsChannelSentEmbed.fields![0].value).toBe("<@moderatorId>"); + + expect(logsChannelSentEmbed.fields![1].name).toBe("Reason"); + expect(logsChannelSentEmbed.fields![1].value).toBe("Test reason"); + + expect(logsChannelSentEmbed.fields![2].name).toBe("Length"); + expect(logsChannelSentEmbed.fields![2].value).toBe("1s"); + + expect(logsChannelSentEmbed.fields![3].name).toBe("Until"); + expect(logsChannelSentEmbed.fields![3].value).toBeDefined(); + + // EXPECT Audit to be saved + expect(auditSave).toBeCalled(); + + expect(savedAudit).toBeDefined(); + expect(savedAudit?.UserId).toBe('userId'); + expect(savedAudit?.AuditType).toBe(AuditType.Timeout); + expect(savedAudit?.Reason).toBe("Test reason"); + expect(savedAudit?.ModeratorId).toBe('moderatorId'); + expect(savedAudit?.ServerId).toBe('guildId'); + }); + + // Null checks + test('GIVEN interaction.guild IS NULL, EXPECT nothing to happen', async () => { + const command = new Timeout(); + + const interaction = { + guild: null, + reply: jest.fn(), + } as unknown as CommandInteraction; + + await command.execute(interaction); + + expect(interaction.reply).not.toBeCalled(); + }); + + test('GIVEN interaction.guildId IS NULL, EXPECT nothing to happen', async () => { + const command = new Timeout(); + + const interaction = { + guild: mock<Guild>(), + guildId: null, + reply: jest.fn(), + } as unknown as CommandInteraction; + + await command.execute(interaction); + + expect(interaction.reply).not.toBeCalled(); + }); + + // Validation + test('GIVEN targetUser IS NULL, EXPECT validation error', async () => { + const command = new Timeout(); + + const interaction = { + reply: jest.fn(), + guild: mock<Guild>(), + guildId: 'guildId', + options: { + get: jest.fn().mockReturnValue(undefined), + } + } as unknown as CommandInteraction; + + await command.execute(interaction); + + expect(interaction.reply).toBeCalledWith('Fields are required.'); + }); + + test('GIVEN targetUser.user IS NULL, EXPECT validation error', async () => { + const command = new Timeout(); + + const interaction = { + reply: jest.fn(), + guild: mock<Guild>(), + guildId: 'guildId', + options: { + get: jest.fn((value: string): CommandInteractionOption<CacheType> | null => { + switch (value) { + case 'target': + return {} as CommandInteractionOption; + case 'length': + return { + value: '1m', + } as CommandInteractionOption; + case 'reason': + return { + value: 'Test reason', + } as CommandInteractionOption; + default: + return null; + } + }), + } + } as unknown as CommandInteraction; + + await command.execute(interaction); + + expect(interaction.reply).toBeCalledWith('Fields are required.'); + }); + + test('GIVEN targetUser.member IS NULL, EXPECT validation error', async () => { + const command = new Timeout(); + + const interaction = { + reply: jest.fn(), + guild: mock<Guild>(), + guildId: 'guildId', + options: { + get: jest.fn((value: string): CommandInteractionOption<CacheType> | null => { + switch (value) { + case 'target': + return { + user: {} as User, + } as CommandInteractionOption; + case 'length': + return { + value: '1m', + } as CommandInteractionOption; + case 'reason': + return { + value: 'Test reason', + } as CommandInteractionOption; + default: + return null; + } + }), + } + } as unknown as CommandInteraction; + + await command.execute(interaction); + + expect(interaction.reply).toBeCalledWith('Fields are required.'); + }); + + test('GIVEN lengthInput IS NULL, EXPECT validation error', async () => { + const command = new Timeout(); + + const interaction = { + reply: jest.fn(), + guild: mock<Guild>(), + guildId: 'guildId', + options: { + get: jest.fn((value: string): CommandInteractionOption<CacheType> | null => { + switch (value) { + case 'target': + return { + user: {} as User, + member: {} as GuildMember + } as CommandInteractionOption; + case 'length': + return null; + case 'reason': + return { + value: 'Test reason', + } as CommandInteractionOption; + default: + return null; + } + }), + } + } as unknown as CommandInteraction; + + await command.execute(interaction); + + expect(interaction.reply).toBeCalledWith('Fields are required.'); + }); + + test('GIVEN lengthInput.value IS NULL, EXPECT validation error', async () => { + const command = new Timeout(); + + const interaction = { + reply: jest.fn(), + guild: mock<Guild>(), + guildId: 'guildId', + options: { + get: jest.fn((value: string): CommandInteractionOption<CacheType> | null => { + switch (value) { + case 'target': + return { + user: {} as User, + member: {} as GuildMember + } as CommandInteractionOption; + case 'length': + return { + value: undefined, + } as CommandInteractionOption; + case 'reason': + return { + value: 'Test reason', + } as CommandInteractionOption; + default: + return null; + } + }), + } + } as unknown as CommandInteraction; + + await command.execute(interaction); + + expect(interaction.reply).toBeCalledWith('Fields are required.'); + }); + + test('GIVEN targetUser is a bot, EXPECT error', async () => { + const interaction = { + reply: jest.fn(), + guild: mock<Guild>(), + guildId: 'guildId', + options: { + get: jest.fn((value: string): CommandInteractionOption<CacheType> | null => { + switch (value) { + case 'target': + return { + user: { + bot: true, + } as User, + member: {} as GuildMember + } as CommandInteractionOption; + case 'length': + return { + value: '1m', + } as CommandInteractionOption; + case 'reason': + return { + value: 'Test reason', + } as CommandInteractionOption; + default: + return null; + } + }), + } + } as unknown as CommandInteraction; + + const command = new Timeout(); + + await command.execute(interaction); + + expect(interaction.reply).toBeCalledWith('Cannot timeout bots.'); + }); + + test('GIVEN targetMember IS NOT manageable by the bot, EXPECT insufficient permissions error', async () => { + const command = new Timeout(); + + const interaction = { + reply: jest.fn(), + guild: mock<Guild>(), + guildId: 'guildId', + user: { + id: 'moderatorId', + }, + options: { + get: jest.fn((value: string): CommandInteractionOption<CacheType> | null => { + switch (value) { + case 'target': + return { + user: { + id: 'userId', + tag: 'userTag', + } as User, + member: { + manageable: false, + } as GuildMember + } as CommandInteractionOption; + case 'length': + return { + value: '1m', + } as CommandInteractionOption; + case 'reason': + return { + value: 'Test reason', + } as CommandInteractionOption; + default: + return null; + } + }), + } + } as unknown as CommandInteraction; + + await command.execute(interaction); + + expect(interaction.reply).toBeCalledWith('Insufficient bot permissions. Please contact a moderator.'); + }); + + // Reason variable + test('GIVEN reason IS NULL, EXPECT to be ran with empty string', async () => { + const command = new Timeout(); + + let savedAudit: DeepPartial<Audit> | undefined; + + const auditSave = jest.spyOn(Audit.prototype, 'Save').mockImplementation((target: EntityTarget<BaseEntity>, entity: DeepPartial<BaseEntity>): Promise<void> => { + savedAudit = entity; + + return Promise.resolve(); + }); + + const timeoutFunc = jest.fn(); + + const sentEmbeds: EmbedBuilder[] = []; + + const interaction = { + reply: jest.fn(), + guild: { + channels: { + cache: { + find: jest.fn().mockReturnValue(mock<TextChannel>()), + } + } + }, + guildId: 'guildId', + user: { + id: 'moderatorId', + }, + options: { + get: jest.fn((value: string): CommandInteractionOption<CacheType> | null => { + switch (value) { + case 'target': + return { + user: { + id: 'userId', + tag: 'userTag', + createDM: jest.fn().mockReturnValue({ + send: jest.fn(async (options: MessageCreateOptions): Promise<Message<false>> => { + sentEmbeds.push(options.embeds![0] as EmbedBuilder); + + return mock<Message<false>>(); + }) + }) as unknown as DMChannel, + } as unknown as User, + member: { + manageable: true, + timeout: timeoutFunc, + } as unknown as GuildMember + } as CommandInteractionOption; + case 'length': + return { + value: '1m' + } as CommandInteractionOption; + case 'reason': + return { + value: undefined, + } as CommandInteractionOption; + default: + return null; + } + }), + } + } as unknown as CommandInteraction; + + + await command.execute(interaction); + + expect(timeoutFunc).toBeCalledWith(1000 * 60 * 1, ""); + expect(savedAudit?.Reason).toBe("*none*"); + + const dmEmbed = (sentEmbeds[0] as any).data; + const dmEmbedReasonField = dmEmbed.fields![0] as EmbedField; + + expect(dmEmbedReasonField.value).toBe("*none*"); + }); + + // Log embed + test('GIVEN channelName IS NULL, EXPECT execution to return', async () => { + const command = new Timeout(); + + let savedAudit: DeepPartial<Audit> | undefined; + + const auditSave = jest.spyOn(Audit.prototype, 'Save').mockImplementation((target: EntityTarget<BaseEntity>, entity: DeepPartial<BaseEntity>): Promise<void> => { + savedAudit = entity; + + return Promise.resolve(); + }); + + const settingsGet = jest.spyOn(SettingsHelper, 'GetSetting').mockResolvedValue(undefined); + + const timeoutFunc = jest.fn(); + + const sentEmbeds: EmbedBuilder[] = []; + + const logChannelSendFunc = jest.fn(); + + const interaction = { + reply: jest.fn(), + guild: { + channels: { + cache: { + find: jest.fn().mockReturnValue({ + send: logChannelSendFunc, + } as unknown as TextChannel), + } + } + }, + guildId: 'guildId', + user: { + id: 'moderatorId', + }, + options: { + get: jest.fn((value: string): CommandInteractionOption<CacheType> | null => { + switch (value) { + case 'target': + return { + user: { + id: 'userId', + tag: 'userTag', + createDM: jest.fn().mockReturnValue({ + send: jest.fn(async (options: MessageCreateOptions): Promise<Message<false>> => { + sentEmbeds.push(options.embeds![0] as EmbedBuilder); + + return mock<Message<false>>(); + }) + }) as unknown as DMChannel, + } as unknown as User, + member: { + manageable: true, + timeout: timeoutFunc, + } as unknown as GuildMember + } as CommandInteractionOption; + case 'length': + return { + value: '1m' + } as CommandInteractionOption; + case 'reason': + return { + value: 'Test reason', + } as CommandInteractionOption; + default: + return null; + } + }), + } + } as unknown as CommandInteraction; + + + await command.execute(interaction); + + expect(timeoutFunc).toBeCalled(); + expect(sentEmbeds.length).toBe(0); + expect(logChannelSendFunc).not.toBeCalled(); + }); + + test('GIVEN channel IS NULL, EXPECT embed to not be sent', async () => { + const command = new Timeout(); + + let savedAudit: DeepPartial<Audit> | undefined; + + const auditSave = jest.spyOn(Audit.prototype, 'Save').mockImplementation((target: EntityTarget<BaseEntity>, entity: DeepPartial<BaseEntity>): Promise<void> => { + savedAudit = entity; + + return Promise.resolve(); + }); + + const settingsGet = jest.spyOn(SettingsHelper, 'GetSetting').mockResolvedValue('mod-logs'); + + const timeoutFunc = jest.fn(); + + const sentEmbeds: EmbedBuilder[] = []; + + const interaction = { + reply: jest.fn(), + guild: { + channels: { + cache: { + find: jest.fn().mockReturnValue(undefined), + } + } + }, + guildId: 'guildId', + user: { + id: 'moderatorId', + }, + options: { + get: jest.fn((value: string): CommandInteractionOption<CacheType> | null => { + switch (value) { + case 'target': + return { + user: { + id: 'userId', + tag: 'userTag', + createDM: jest.fn().mockReturnValue({ + send: jest.fn(async (options: MessageCreateOptions): Promise<Message<false>> => { + sentEmbeds.push(options.embeds![0] as EmbedBuilder); + + return mock<Message<false>>(); + }) + }) as unknown as DMChannel, + } as unknown as User, + member: { + manageable: true, + timeout: timeoutFunc, + } as unknown as GuildMember + } as CommandInteractionOption; + case 'length': + return { + value: '1m' + } as CommandInteractionOption; + case 'reason': + return { + value: 'Test reason', + } as CommandInteractionOption; + default: + return null; + } + }), + } + } as unknown as CommandInteraction; + + + await command.execute(interaction); + + expect(timeoutFunc).toBeCalled(); + expect(sentEmbeds.length).toBeGreaterThan(0); + }); + + // DM user + test('GIVEN user can NOT be messaged, EXPECT resultEmbed to contain "DM Sent = false"', async () => { + let embeds: APIEmbed[] | undefined; + + const command = new Timeout(); + + const interactionReply = jest.fn((options: InteractionReplyOptions) => { + embeds = options.embeds as APIEmbed[]; + }); + + let savedAudit: DeepPartial<Audit> | undefined; + + const getSetting = jest.spyOn(SettingsHelper, 'GetSetting').mockResolvedValue('mod-logs'); + const auditSave = jest.spyOn(Audit.prototype, 'Save').mockImplementation((target: EntityTarget<BaseEntity>, entity: DeepPartial<BaseEntity>): Promise<void> => { + savedAudit = entity; + + return Promise.resolve(); + }); + + const timeoutFunc = jest.fn(); + + let dmChannelSentEmbeds: (APIEmbed | JSONEncodable<APIEmbed>)[] | undefined; + let logsChannelSentEmbeds: (APIEmbed | JSONEncodable<APIEmbed>)[] | undefined; + + const dmChannel = { + send: jest.fn().mockImplementation((options: MessageCreateOptions) => { + dmChannelSentEmbeds = options.embeds; + }), + } as unknown as DMChannel; + + const userInput = { + user: { + id: 'userId', + tag: 'userTag', + createDM: jest.fn().mockRejectedValue(undefined), + } as unknown as User, + member: { + manageable: true, + timeout: timeoutFunc, + } as unknown as GuildMember, + } as CommandInteractionOption; + + const lengthInput = { + value: '1s', + } as CommandInteractionOption; + + const reasonInput = { + value: 'Test reason', + } as CommandInteractionOption; + + const logsChannel = { + name: 'mod-logs', + send: jest.fn().mockImplementation((options: MessageCreateOptions) => { + logsChannelSentEmbeds = options.embeds; + }), + } as unknown as TextChannel; + + const interaction = { + guild: { + channels: { + cache: { + find: jest.fn() + .mockReturnValue(logsChannel), + } + }, + name: "Test Guild", + } as unknown as Guild, + guildId: 'guildId', + reply: interactionReply, + options: { + get: jest.fn() + .mockReturnValueOnce(userInput) + .mockReturnValueOnce(lengthInput) + .mockReturnValue(reasonInput), + }, + user: { + id: 'moderatorId' + } + } as unknown as CommandInteraction; + + await command.execute(interaction); + + // EXPECT embeds to be sent + expect(embeds).toBeDefined(); + expect(embeds!.length).toBe(1); + + const resultEmbed = embeds![0] as EmbedBuilder; + + // EXPECT DM field to be configured + const resultEmbedDMField = resultEmbed.data.fields![0]; + + expect(resultEmbedDMField.name).toBe("DM Sent"); + expect(resultEmbedDMField.value).toBe("false"); + }); +}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 418b5b2..fe4eccd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,7 +22,7 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.7.tgz#61caffb60776e49a57ba61a88f02bedd8714f6bc" integrity sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA== -"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": +"@babel/core@^7.11.6", "@babel/core@^7.12.3": version "7.21.8" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.8.tgz#2a8c7f0f53d60100ba4c32470ba0281c92aa9aa4" integrity sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ== @@ -124,7 +124,7 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-string-parser@^7.21.5": +"@babel/helper-string-parser@^7.19.4", "@babel/helper-string-parser@^7.21.5": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== @@ -157,11 +157,16 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.5", "@babel/parser@^7.21.8": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.21.5", "@babel/parser@^7.21.8": version "7.21.8" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8" integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA== +"@babel/parser@^7.20.7": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3" + integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ== + "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -197,6 +202,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" + integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -253,6 +265,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/runtime@^7.21.0": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200" + integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q== + dependencies: + regenerator-runtime "^0.13.11" + "@babel/template@^7.20.7", "@babel/template@^7.3.3": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" @@ -278,7 +297,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3": +"@babel/types@^7.0.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6" integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q== @@ -287,26 +306,78 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.21.0": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05" + integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@discordjs/builders@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-0.11.0.tgz#4102abe3e0cd093501f3f71931df43eb92f5b0cc" - integrity sha512-ZTB8yJdJKrKlq44dpWkNUrAtEJEq0gqpb7ASdv4vmq6/mZal5kOv312hQ56I/vxwMre+VIkoHquNUAfnTbiYtg== +"@discordjs/builders@^1.6.3": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.6.3.tgz#994b4fe57e77b47096f74bb5a1f664870a930a43" + integrity sha512-CTCh8NqED3iecTNuiz49mwSsrc2iQb4d0MjMdmS/8pb69Y4IlzJ/DIy/p5GFlgOrFbNO2WzMHkWKQSiJ3VNXaw== dependencies: - "@sindresorhus/is" "^4.2.0" - discord-api-types "^0.26.0" - ts-mixer "^6.0.0" - tslib "^2.3.1" - zod "^3.11.6" + "@discordjs/formatters" "^0.3.1" + "@discordjs/util" "^0.3.1" + "@sapphire/shapeshift" "^3.8.2" + discord-api-types "^0.37.41" + fast-deep-equal "^3.1.3" + ts-mixer "^6.0.3" + tslib "^2.5.0" -"@discordjs/collection@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.4.0.tgz#b6488286a1cc7b41b644d7e6086f25a1c1e6f837" - integrity sha512-zmjq+l/rV35kE6zRrwe8BHqV78JvIh2ybJeZavBi5NySjWXqN3hmmAKg7kYMMXSeiWtSsMoZ/+MQi0DiQWy2lw== +"@discordjs/collection@^1.5.1": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.5.1.tgz#bc7ca557838dc29247bf19860426637f103bc383" + integrity sha512-aWEc9DCf3TMDe9iaJoOnO2+JVAjeRNuRxPZQA6GVvBf+Z3gqUuWYBy2NWh4+5CLYq5uoc3MOvUQ5H5m8CJBqOA== + +"@discordjs/formatters@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@discordjs/formatters/-/formatters-0.3.1.tgz#81393cf25e6e3223361061629752ea727475e842" + integrity sha512-M7X4IGiSeh4znwcRGcs+49B5tBkNDn4k5bmhxJDAUhRxRHTiFAOTVUNQ6yAKySu5jZTnCbSvTYHW3w0rAzV1MA== + dependencies: + discord-api-types "^0.37.41" + +"@discordjs/rest@^1.1.0", "@discordjs/rest@^1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-1.7.1.tgz#eeef0e71a37c95fa27962129729b2aa9de8e3752" + integrity sha512-Ofa9UqT0U45G/eX86cURQnX7gzOJLG2oC28VhIk/G6IliYgQF7jFByBJEykPSHE4MxPhqCleYvmsrtfKh1nYmQ== + dependencies: + "@discordjs/collection" "^1.5.1" + "@discordjs/util" "^0.3.0" + "@sapphire/async-queue" "^1.5.0" + "@sapphire/snowflake" "^3.4.2" + discord-api-types "^0.37.41" + file-type "^18.3.0" + tslib "^2.5.0" + undici "^5.22.0" + +"@discordjs/util@^0.3.0", "@discordjs/util@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-0.3.1.tgz#4e8737e1dcff7e9f5eccc3116fb44755b65b1e97" + integrity sha512-HxXKYKg7vohx2/OupUN/4Sd02Ev3PBJ5q0gtjdcvXb0ErCva8jNHWfe/v5sU3UKjIB/uxOhc+TDOnhqffj9pRA== + +"@discordjs/ws@^0.8.3": + version "0.8.3" + resolved "https://registry.yarnpkg.com/@discordjs/ws/-/ws-0.8.3.tgz#77db8d563b731a2198c1b40f63b1ef8d230504f7" + integrity sha512-hcYtppanjHecbdNyCKQNH2I4RP9UrphDgmRgLYrATEQF1oo4sYSve7ZmGsBEXSzH72MO2tBPdWSThunbxUVk0g== + dependencies: + "@discordjs/collection" "^1.5.1" + "@discordjs/rest" "^1.7.1" + "@discordjs/util" "^0.3.1" + "@sapphire/async-queue" "^1.5.0" + "@types/ws" "^8.5.4" + "@vladfrangu/async_event_emitter" "^2.2.1" + discord-api-types "^0.37.41" + tslib "^2.5.0" + ws "^8.13.0" "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -324,173 +395,196 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" - integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== +"@jest/console@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.5.0.tgz#593a6c5c0d3f75689835f1b3b4688c4f8544cb57" + integrity sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^27.5.1" - jest-util "^27.5.1" + jest-message-util "^29.5.0" + jest-util "^29.5.0" slash "^3.0.0" -"@jest/core@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" - integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== +"@jest/core@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.5.0.tgz#76674b96904484e8214614d17261cc491e5f1f03" + integrity sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ== dependencies: - "@jest/console" "^27.5.1" - "@jest/reporters" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.5.0" + "@jest/reporters" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - emittery "^0.8.1" + ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^27.5.1" - jest-config "^27.5.1" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-resolve-dependencies "^27.5.1" - jest-runner "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - jest-watcher "^27.5.1" + jest-changed-files "^29.5.0" + jest-config "^29.5.0" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-resolve-dependencies "^29.5.0" + jest-runner "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" + jest-watcher "^29.5.0" micromatch "^4.0.4" - rimraf "^3.0.0" + pretty-format "^29.5.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" - integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== +"@jest/environment@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65" + integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ== dependencies: - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" - jest-mock "^27.5.1" + jest-mock "^29.5.0" -"@jest/fake-timers@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" - integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== +"@jest/expect-utils@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" + integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== dependencies: - "@jest/types" "^27.5.1" - "@sinonjs/fake-timers" "^8.0.1" + jest-get-type "^29.4.3" + +"@jest/expect@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.5.0.tgz#80952f5316b23c483fbca4363ce822af79c38fba" + integrity sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g== + dependencies: + expect "^29.5.0" + jest-snapshot "^29.5.0" + +"@jest/fake-timers@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c" + integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg== + dependencies: + "@jest/types" "^29.5.0" + "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-util "^27.5.1" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-util "^29.5.0" -"@jest/globals@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" - integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== +"@jest/globals@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.5.0.tgz#6166c0bfc374c58268677539d0c181f9c1833298" + integrity sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ== dependencies: - "@jest/environment" "^27.5.1" - "@jest/types" "^27.5.1" - expect "^27.5.1" + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/types" "^29.5.0" + jest-mock "^29.5.0" -"@jest/reporters@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" - integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== +"@jest/reporters@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.5.0.tgz#985dfd91290cd78ddae4914ba7921bcbabe8ac9b" + integrity sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" - glob "^7.1.2" + glob "^7.1.3" graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" istanbul-lib-instrument "^5.1.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-haste-map "^27.5.1" - jest-resolve "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + jest-worker "^29.5.0" slash "^3.0.0" - source-map "^0.6.0" string-length "^4.0.1" - terminal-link "^2.0.0" - v8-to-istanbul "^8.1.0" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" -"@jest/source-map@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" - integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== dependencies: + "@sinclair/typebox" "^0.25.16" + +"@jest/source-map@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20" + integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.15" callsites "^3.0.0" graceful-fs "^4.2.9" - source-map "^0.6.0" -"@jest/test-result@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" - integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== +"@jest/test-result@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.5.0.tgz#7c856a6ca84f45cc36926a4e9c6b57f1973f1408" + integrity sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ== dependencies: - "@jest/console" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.5.0" + "@jest/types" "^29.5.0" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" - integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== +"@jest/test-sequencer@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz#34d7d82d3081abd523dbddc038a3ddcb9f6d3cc4" + integrity sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ== dependencies: - "@jest/test-result" "^27.5.1" + "@jest/test-result" "^29.5.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-runtime "^27.5.1" + jest-haste-map "^29.5.0" + slash "^3.0.0" -"@jest/transform@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" - integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== +"@jest/transform@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9" + integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw== dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^27.5.1" + "@babel/core" "^7.11.6" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-regex-util "^27.5.1" - jest-util "^27.5.1" + jest-haste-map "^29.5.0" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" + write-file-atomic "^4.0.2" -"@jest/types@^27.4.2", "@jest/types@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" - integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== dependencies: + "@jest/schemas" "^29.4.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" - "@types/yargs" "^16.0.0" + "@types/yargs" "^17.0.8" chalk "^4.0.0" "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": @@ -522,7 +616,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.18" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== @@ -530,48 +624,66 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@sapphire/async-queue@^1.1.9": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.3.1.tgz#9d861e626dbffae02d808e13f823d4510e450a78" - integrity sha512-FFTlPOWZX1kDj9xCAsRzH5xEJfawg1lNoYAA+ecOWJMHOfiZYb1uXOI3ne9U4UILSEPwfE68p3T9wUHwIQfR0g== +"@sapphire/async-queue@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.0.tgz#2f255a3f186635c4fb5a2381e375d3dfbc5312d8" + integrity sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA== -"@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.2.0": +"@sapphire/shapeshift@^3.8.2": + version "3.8.2" + resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.8.2.tgz#f9f25cba74c710b56f8790de76a9642a9635e7db" + integrity sha512-NXpnJAsxN3/h9TqQPntOeVWZrpIuucqXI3IWF6tj2fWCoRLCuVK5wx7Dtg7pRrtkYfsMUbDqgKoX26vrC5iYfA== + dependencies: + fast-deep-equal "^3.1.3" + lodash "^4.17.21" + +"@sapphire/snowflake@^3.4.2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.4.2.tgz#365af8e7b57ada924ec8e85383b921280f81d128" + integrity sha512-KJwlv5gkGjs1uFV7/xx81n3tqgBwBJvH94n1xDyH3q+JSmtsMeSleJffarEBfG2yAFeJiFA4BnGOK6FFPHc19g== + +"@sinclair/typebox@^0.25.16": + version "0.25.24" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" + integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== + +"@sindresorhus/is@4.6.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== -"@sinonjs/commons@^1.7.0": - version "1.8.6" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" - integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^8.0.1": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" - integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== +"@sinonjs/fake-timers@^10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz#d10549ed1f423d80639c528b6c7f5a1017747d0c" + integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== dependencies: - "@sinonjs/commons" "^1.7.0" + "@sinonjs/commons" "^2.0.0" "@sqltools/formatter@^1.2.5": version "1.2.5" resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.5.tgz#3abc203c79b8c3e90fd6c156a0c62d5403520e12" integrity sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw== -"@szmarczak/http-timer@^4.0.5": +"@szmarczak/http-timer@4.0.6": version "4.0.6" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== dependencies: defer-to-connect "^2.0.0" -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tokenizer/token@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" + integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": +"@types/babel__core@^7.1.14": version "7.20.0" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== @@ -597,35 +709,20 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": version "7.18.5" resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.5.tgz#c107216842905afafd3b6e774f6f935da6f5db80" integrity sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q== dependencies: "@babel/types" "^7.3.0" -"@types/cacheable-request@^6.0.1": - version "6.0.2" - resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" - integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== - dependencies: - "@types/http-cache-semantics" "*" - "@types/keyv" "*" - "@types/node" "*" - "@types/responselike" "*" - -"@types/graceful-fs@^4.1.2": +"@types/graceful-fs@^4.1.3": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== dependencies: "@types/node" "*" -"@types/http-cache-semantics@*": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" - integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== - "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" @@ -645,50 +742,30 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^27.0.3": - version "27.0.3" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.0.3.tgz#0cf9dfe9009e467f70a342f0f94ead19842a783a" - integrity sha512-cmmwv9t7gBYt7hNKH5Spu7Kuu/DotGa+Ff+JGRKZ4db5eh8PnKS4LuebJ3YLUoyOyIHraTGyULn23YtEAm0VSg== +"@types/jest@^29.0.0": + version "29.5.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.1.tgz#83c818aa9a87da27d6da85d3378e5a34d2f31a47" + integrity sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ== dependencies: - jest-diff "^27.0.0" - pretty-format "^27.0.0" - -"@types/json-buffer@~3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64" - integrity sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ== - -"@types/keyv@*": - version "3.1.4" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" - integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== - dependencies: - "@types/node" "*" - -"@types/node-fetch@^2.5.12": - version "2.6.1" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" - integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== - dependencies: - "@types/node" "*" - form-data "^3.0.0" + expect "^29.0.0" + pretty-format "^29.0.0" "@types/node@*": version "20.1.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.1.0.tgz#258805edc37c327cf706e64c6957f241ca4c4c20" integrity sha512-O+z53uwx64xY7D6roOi4+jApDGFg0qn6WHcxe5QeqjMaTezBO/mxdfFXIVAVVyNWKx84OmPB3L8kbVYOTeN34A== -"@types/node@^16.11.10": - version "16.11.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.10.tgz#2e3ad0a680d96367103d3e670d41c2fed3da61ae" - integrity sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA== +"@types/node@^20.0.0": + version "20.1.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.1.4.tgz#83f148d2d1f5fe6add4c53358ba00d97fc4cdb71" + integrity sha512-At4pvmIOki8yuwLtd7BNHl3CiWNbtclUbNtScGx4OHfBd4/oWoJC8KRCIxXwkdndzhxOsPXihrsOoydxBjlE9Q== "@types/prettier@^2.1.5": version "2.7.2" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== -"@types/responselike@*", "@types/responselike@^1.0.0": +"@types/responselike@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== @@ -700,15 +777,15 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== -"@types/uuid@^8.3.4": - version "8.3.4" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" - integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== +"@types/uuid@^9.0.0": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.1.tgz#98586dc36aee8dacc98cc396dbca8d0429647aa6" + integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== -"@types/ws@^8.2.2": - version "8.5.3" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" - integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== +"@types/ws@^8.5.4": + version "8.5.4" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" + integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== dependencies: "@types/node" "*" @@ -717,47 +794,17 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== -"@types/yargs@^16.0.0": - version "16.0.5" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.5.tgz#12cc86393985735a283e387936398c2f9e5f88e3" - integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ== +"@types/yargs@^17.0.8": + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== dependencies: "@types/yargs-parser" "*" -abab@^2.0.3, abab@^2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" - integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== - -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - -acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.2.4: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== - -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" +"@vladfrangu/async_event_emitter@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.1.tgz#5f44b3095d5edd695a9d12079f81b41826ad7831" + integrity sha512-XtUEAS0m6uVddXW+EImGunLiJZzWNWAZQBoQCUneowrYXPQ6y7c0iWEm/wVYyGpTixTIhUfLRSoYCwojL64htA== ansi-escapes@^4.2.1: version "4.3.2" @@ -815,21 +862,15 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -babel-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" - integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== +babel-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" + integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q== dependencies: - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/transform" "^29.5.0" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^27.5.1" + babel-preset-jest "^29.5.0" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -845,14 +886,14 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" - integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" - "@types/babel__core" "^7.0.0" + "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" babel-preset-current-node-syntax@^1.0.0: @@ -873,12 +914,12 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" - integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== +babel-preset-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" + integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== dependencies: - babel-plugin-jest-hoist "^27.5.1" + babel-plugin-jest-hoist "^29.5.0" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -918,11 +959,6 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - browserslist@^4.21.3: version "4.21.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" @@ -960,12 +996,19 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -cacheable-lookup@^5.0.3: - version "5.0.4" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" - integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== +busboy@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" -cacheable-request@^7.0.2: +cacheable-lookup@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz#0330a543471c61faa4e9035db583aad753b36385" + integrity sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww== + +cacheable-request@7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== @@ -994,9 +1037,9 @@ camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001449: - version "1.0.30001486" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001486.tgz#56a08885228edf62cbe1ac8980f2b5dae159997e" - integrity sha512-uv7/gXuHi10Whlj0pp5q/tsK/32J2QSqVRKQhs2j8VsDCjgyruAh/eEXHF822VqO9yT6iZKw3nRwZRSPBE9OQg== + version "1.0.30001469" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001469.tgz#3dd505430c8522fdc9f94b4a19518e330f5c945a" + integrity sha512-Rcp7221ScNqQPP3W+lVOYDyjdR6dC+neEQCttoNr5bAyz54AboB4iwpnWgyi8P4YUsPybVzT4LgWiBbI3drL4g== chalk@^2.0.0: version "2.4.2" @@ -1061,9 +1104,9 @@ cliui@^8.0.1: wrap-ansi "^7.0.0" clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q== + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== dependencies: mimic-response "^1.0.0" @@ -1101,31 +1144,21 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -compress-brotli@^1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.8.tgz#0c0a60c97a989145314ec381e84e26682e7b38db" - integrity sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ== - dependencies: - "@types/json-buffer" "~3.0.0" - json-buffer "~3.0.1" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -1140,44 +1173,20 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== +date-fns@^2.29.3: + version "2.30.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" + integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== dependencies: - cssom "~0.3.6" + "@babel/runtime" "^7.21.0" -data-urls@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== - dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.4: +debug@^4.1.0, debug@^4.1.1, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -decimal.js@^10.2.1: - version "10.4.3" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" - integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== - decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" @@ -1190,11 +1199,6 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== -deep-is@~0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - deepmerge@^4.2.2: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" @@ -1205,54 +1209,42 @@ defer-to-connect@^2.0.0: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -diff-sequences@^27.4.0, diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== -discord-api-types@^0.26.0: - version "0.26.1" - resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.26.1.tgz#726f766ddc37d60da95740991d22cb6ef2ed787b" - integrity sha512-T5PdMQ+Y1MEECYMV5wmyi9VEYPagEDEi4S0amgsszpWY0VB9JJ/hEvM6BgLhbdnKky4gfmZEXtEEtojN8ZKJQQ== +discord-api-types@^0.37.41: + version "0.37.42" + resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.42.tgz#3c196267ed31e9ea249e6880c15e2af1c6428629" + integrity sha512-1Huaj9cQ1W7/uryS8MZs/tZemnoKB94thM1cE40lep3rpU3q7WHqkdjN/veX0prTkYlPhcyLd/DeF/pBO8X8oQ== -discord.js@^13.6.0: - version "13.6.0" - resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-13.6.0.tgz#d8a8a591dbf25cbcf9c783d5ddf22c4694860475" - integrity sha512-tXNR8zgsEPxPBvGk3AQjJ9ljIIC6/LOPjzKwpwz8Y1Q2X66Vi3ZqFgRHYwnHKC0jC0F+l4LzxlhmOJsBZDNg9g== +discord.js@^14.3.0: + version "14.11.0" + resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.11.0.tgz#6529d49f30d10fc5a9ff8e6796661aa998769afe" + integrity sha512-CkueWYFQ28U38YPR8HgsBR/QT35oPpMbEsTNM30Fs8loBIhnA4s70AwQEoy6JvLcpWWJO7GY0y2BUzZmuBMepQ== dependencies: - "@discordjs/builders" "^0.11.0" - "@discordjs/collection" "^0.4.0" - "@sapphire/async-queue" "^1.1.9" - "@types/node-fetch" "^2.5.12" - "@types/ws" "^8.2.2" - discord-api-types "^0.26.0" - form-data "^4.0.0" - node-fetch "^2.6.1" - ws "^8.4.0" + "@discordjs/builders" "^1.6.3" + "@discordjs/collection" "^1.5.1" + "@discordjs/formatters" "^0.3.1" + "@discordjs/rest" "^1.7.1" + "@discordjs/util" "^0.3.1" + "@discordjs/ws" "^0.8.3" + "@sapphire/snowflake" "^3.4.2" + "@types/ws" "^8.5.4" + discord-api-types "^0.37.41" + fast-deep-equal "^3.1.3" + lodash.snakecase "^4.1.1" + tslib "^2.5.0" + undici "^5.22.0" + ws "^8.13.0" -domexception@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== - dependencies: - webidl-conversions "^5.0.0" - -dotenv@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" - integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== - -dotenv@^16.0.3: +dotenv@^16.0.0, dotenv@^16.0.3: version "16.0.3" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== @@ -1262,21 +1254,21 @@ electron-to-chromium@^1.4.284: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.385.tgz#1afd8d6280d510145148777b899ff481c65531ff" integrity sha512-L9zlje9bIw0h+CwPQumiuVlfMcV4boxRjFIWDcLfFqTZNbkwOExBzfmswytHawObQX4OUhtNv8gIiB21kOurIg== -emittery@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" - integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + +emoji-regex@^10.0.0: + version "10.2.1" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.2.1.tgz#a41c330d957191efd3d9dfe6e1e8e1e9ab048b3f" + integrity sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emoji-regex@^9.2.0: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -1306,33 +1298,11 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -1353,26 +1323,27 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" - integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== +expect@^29.0.0, expect@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" + integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== dependencies: - "@jest/types" "^27.5.1" - jest-get-type "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" + "@jest/expect-utils" "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - fb-watchman@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" @@ -1380,6 +1351,15 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +file-type@^18.3.0: + version "18.3.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-18.3.0.tgz#116d4df9120b7f17e973ad5c976b82a40407bd07" + integrity sha512-pkPZ5OGIq0TYb37b8bHDLNeQSe1H2KlaQ2ySGpJkkr2KZdaWsO4QhPzHA0mQcsUW2cSqJk+4gM/UyLz/UFbXdQ== + dependencies: + readable-web-to-node-stream "^3.0.2" + strtok3 "^7.0.0" + token-types "^5.0.1" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -1395,23 +1375,10 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" +form-data-encoder@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" + integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== fs.realpath@^1.0.0: version "1.0.0" @@ -1450,7 +1417,7 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -1462,7 +1429,7 @@ glob-parent@^6.0.0: dependencies: is-glob "^4.0.3" -glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: +glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -1490,22 +1457,23 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -got@^11.8.3: - version "11.8.5" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046" - integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ== +got-cjs@^12.5.4: + version "12.5.4" + resolved "https://registry.yarnpkg.com/got-cjs/-/got-cjs-12.5.4.tgz#b46419c0e8e5fb5503b926941807408049ae2e11" + integrity sha512-Uas6lAsP8bRCt5WXGMhjFf/qEHTrm4v4qxGR02rLG2kdG9qedctvlkdwXVcDJ7Cs84X+r4dPU7vdwGjCaspXug== dependencies: - "@sindresorhus/is" "^4.0.0" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.2" + "@sindresorhus/is" "4.6.0" + "@szmarczak/http-timer" "4.0.6" + "@types/responselike" "1.0.0" + cacheable-lookup "6.1.0" + cacheable-request "7.0.2" decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.2" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" + form-data-encoder "1.7.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "2.0.0" + p-cancelable "2.1.1" + responselike "2.0.1" graceful-fs@^4.2.9: version "4.2.11" @@ -1534,13 +1502,6 @@ highlight.js@^10.7.1: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== - dependencies: - whatwg-encoding "^1.0.5" - html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -1551,43 +1512,19 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - -http2-wrapper@^1.0.0-beta.5.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" - integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== +http2-wrapper@^2.1.10: + version "2.2.0" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.0.tgz#b80ad199d216b7d3680195077bd7b9060fa9d7f3" + integrity sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ== dependencies: quick-lru "^5.1.1" - resolve-alpn "^1.0.0" - -https-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" + resolve-alpn "^1.2.0" human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -1614,7 +1551,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1634,7 +1571,7 @@ is-core-module@^2.11.0: is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" @@ -1658,25 +1595,15 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== - is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-typedarray@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== - isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" @@ -1725,427 +1652,373 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jest-changed-files@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" - integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== +jest-changed-files@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" + integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== dependencies: - "@jest/types" "^27.5.1" execa "^5.0.0" - throat "^6.0.1" + p-limit "^3.1.0" -jest-circus@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" - integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== +jest-circus@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.5.0.tgz#b5926989449e75bff0d59944bae083c9d7fb7317" + integrity sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA== dependencies: - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" - expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" + jest-each "^29.5.0" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + p-limit "^3.1.0" + pretty-format "^29.5.0" + pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" - throat "^6.0.1" -jest-cli@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" - integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== +jest-cli@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.5.0.tgz#b34c20a6d35968f3ee47a7437ff8e53e086b4a67" + integrity sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw== dependencies: - "@jest/core" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/core" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-config "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" prompts "^2.0.1" - yargs "^16.2.0" + yargs "^17.3.1" -jest-config@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" - integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== +jest-config@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.5.0.tgz#3cc972faec8c8aaea9ae158c694541b79f3748da" + integrity sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA== dependencies: - "@babel/core" "^7.8.0" - "@jest/test-sequencer" "^27.5.1" - "@jest/types" "^27.5.1" - babel-jest "^27.5.1" + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.5.0" + "@jest/types" "^29.5.0" + babel-jest "^29.5.0" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" - glob "^7.1.1" + glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-get-type "^27.5.1" - jest-jasmine2 "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runner "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-circus "^29.5.0" + jest-environment-node "^29.5.0" + jest-get-type "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-runner "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^27.5.1" + pretty-format "^29.5.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^27.0.0: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.4.2.tgz#786b2a5211d854f848e2dcc1e324448e9481f36f" - integrity sha512-ujc9ToyUZDh9KcqvQDkk/gkbf6zSaeEg9AiBxtttXW59H/AcqEYp1ciXAtJp+jXWva5nAf/ePtSsgWwE5mqp4Q== +jest-diff@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" + integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== dependencies: chalk "^4.0.0" - diff-sequences "^27.4.0" - jest-get-type "^27.4.0" - pretty-format "^27.4.2" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" -jest-diff@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== - dependencies: - chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - -jest-docblock@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" - integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== +jest-docblock@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" + integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== dependencies: detect-newline "^3.0.0" -jest-each@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" - integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== +jest-each@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.5.0.tgz#fc6e7014f83eac68e22b7195598de8554c2e5c06" + integrity sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" chalk "^4.0.0" - jest-get-type "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" + jest-get-type "^29.4.3" + jest-util "^29.5.0" + pretty-format "^29.5.0" -jest-environment-jsdom@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" - integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== +jest-environment-node@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967" + integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw== dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" - jsdom "^16.6.0" + jest-mock "^29.5.0" + jest-util "^29.5.0" -jest-environment-node@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" - integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== + +jest-haste-map@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de" + integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA== dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" - -jest-get-type@^27.4.0, jest-get-type@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== - -jest-haste-map@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" - integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== - dependencies: - "@jest/types" "^27.5.1" - "@types/graceful-fs" "^4.1.2" + "@jest/types" "^29.5.0" + "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^27.5.1" - jest-serializer "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" + jest-worker "^29.5.0" micromatch "^4.0.4" - walker "^1.0.7" + walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" - integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== +jest-leak-detector@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c" + integrity sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow== dependencies: - "@jest/environment" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^27.5.1" - is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - throat "^6.0.1" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" -jest-leak-detector@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" - integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== - dependencies: - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - -jest-matcher-utils@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" - integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== +jest-matcher-utils@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" + integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== dependencies: chalk "^4.0.0" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" -jest-message-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" - integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== +jest-message-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" + integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^27.5.1" + pretty-format "^29.5.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock-extended@^2.0.4: - version "2.0.9" - resolved "https://registry.yarnpkg.com/jest-mock-extended/-/jest-mock-extended-2.0.9.tgz#bc0e4a269cdb6047d7cc086e1d9722f7215ca795" - integrity sha512-eRZq7/FgwHbxOMm3Lo4DpQX6S2zi4OvwMVFHEb3FgDLp0Xy3P1WARkF93xxO5uD4nAHiEPYHZ25qVU9mAVxoLQ== +jest-mock-extended@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/jest-mock-extended/-/jest-mock-extended-3.0.4.tgz#12a5f993d27aa46232012c439a9b7c54f3b0a1fd" + integrity sha512-2ynEZ7IEJNrhrgshklDMhrOdnmW4Nt+PhkyRqZxRgpwMo7JjmFWMzyp0+eSyk+H9KK1QjXI5xTZIw6x7cVDcRg== dependencies: ts-essentials "^7.0.3" -jest-mock@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" - integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== +jest-mock@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" + integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" "@types/node" "*" + jest-util "^29.5.0" jest-pnp-resolver@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== -jest-regex-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" - integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== -jest-resolve-dependencies@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" - integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== +jest-resolve-dependencies@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz#f0ea29955996f49788bf70996052aa98e7befee4" + integrity sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg== dependencies: - "@jest/types" "^27.5.1" - jest-regex-util "^27.5.1" - jest-snapshot "^27.5.1" + jest-regex-util "^29.4.3" + jest-snapshot "^29.5.0" -jest-resolve@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" - integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== +jest-resolve@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.5.0.tgz#b053cc95ad1d5f6327f0ac8aae9f98795475ecdc" + integrity sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w== dependencies: - "@jest/types" "^27.5.1" chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" + jest-haste-map "^29.5.0" jest-pnp-resolver "^1.2.2" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-util "^29.5.0" + jest-validate "^29.5.0" resolve "^1.20.0" - resolve.exports "^1.1.0" + resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" - integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== +jest-runner@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.5.0.tgz#6a57c282eb0ef749778d444c1d758c6a7693b6f8" + integrity sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ== dependencies: - "@jest/console" "^27.5.1" - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.5.0" + "@jest/environment" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" - emittery "^0.8.1" + emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-haste-map "^27.5.1" - jest-leak-detector "^27.5.1" - jest-message-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runtime "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - source-map-support "^0.5.6" - throat "^6.0.1" + jest-docblock "^29.4.3" + jest-environment-node "^29.5.0" + jest-haste-map "^29.5.0" + jest-leak-detector "^29.5.0" + jest-message-util "^29.5.0" + jest-resolve "^29.5.0" + jest-runtime "^29.5.0" + jest-util "^29.5.0" + jest-watcher "^29.5.0" + jest-worker "^29.5.0" + p-limit "^3.1.0" + source-map-support "0.5.13" -jest-runtime@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" - integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== +jest-runtime@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.5.0.tgz#c83f943ee0c1da7eb91fa181b0811ebd59b03420" + integrity sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw== dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/globals" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/globals" "^29.5.0" + "@jest/source-map" "^29.4.3" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - execa "^5.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" slash "^3.0.0" strip-bom "^4.0.0" -jest-serializer@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" - integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== +jest-snapshot@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.5.0.tgz#c9c1ce0331e5b63cd444e2f95a55a73b84b1e8ce" + integrity sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g== dependencies: - "@types/node" "*" - graceful-fs "^4.2.9" - -jest-snapshot@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" - integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== - dependencies: - "@babel/core" "^7.7.2" + "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" - "@babel/types" "^7.0.0" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/babel__traverse" "^7.0.4" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/babel__traverse" "^7.0.6" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^27.5.1" + expect "^29.5.0" graceful-fs "^4.2.9" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - jest-haste-map "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-util "^27.5.1" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" natural-compare "^1.4.0" - pretty-format "^27.5.1" - semver "^7.3.2" + pretty-format "^29.5.0" + semver "^7.3.5" -jest-util@^27.0.0, jest-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" - integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== +jest-util@^29.0.0, jest-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" - integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== +jest-validate@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.5.0.tgz#8e5a8f36178d40e47138dc00866a5f3bd9916ffc" + integrity sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^27.5.1" + jest-get-type "^29.4.3" leven "^3.1.0" - pretty-format "^27.5.1" + pretty-format "^29.5.0" -jest-watcher@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" - integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== +jest-watcher@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.5.0.tgz#cf7f0f949828ba65ddbbb45c743a382a4d911363" + integrity sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA== dependencies: - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^27.5.1" + emittery "^0.13.1" + jest-util "^29.5.0" string-length "^4.0.1" -jest-worker@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== +jest-worker@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" + integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== dependencies: "@types/node" "*" + jest-util "^29.5.0" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.4.5: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" - integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== +jest@^29.0.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" + integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== dependencies: - "@jest/core" "^27.5.1" + "@jest/core" "^29.5.0" + "@jest/types" "^29.5.0" import-local "^3.0.2" - jest-cli "^27.5.1" + jest-cli "^29.5.0" js-tokens@^4.0.0: version "4.0.0" @@ -2160,45 +2033,12 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -jsdom@^16.6.0: - version "16.7.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -json-buffer@3.0.1, json-buffer@~3.0.1: +json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== @@ -2208,17 +2048,16 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== -json5@2.x, json5@^2.2.2: +json5@^2.2.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== keyv@^4.0.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.3.2.tgz#e839df676a0c7ee594c8835e7c1c83742558e5c2" - integrity sha512-kn8WmodVBe12lmHpA6W8OY7SNh6wVR+Z+wZESF4iF5FCazaVXGWOtnbnvX0tMQ1bO+/TmOD9LziuYMvrIIs0xw== + version "4.5.2" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56" + integrity sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g== dependencies: - compress-brotli "^1.3.8" json-buffer "3.0.1" kleur@^3.0.3: @@ -2231,19 +2070,16 @@ leven@^3.1.0: resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +linqts@^1.14.4: + version "1.14.4" + resolved "https://registry.yarnpkg.com/linqts/-/linqts-1.14.4.tgz#0aa0f78fc6be073d7db874e0a0480fda5d06db7d" + integrity sha512-b5sJjG1ZQ8iLSTJV19jWgMLoQicrQVVRkkQN7B/aboU+cf30lgnhIoGM8vEjqPxZFpryDU78PFpuTnfYNIHMeg== + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -2256,12 +2092,17 @@ lodash.memoize@4.x: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== -lodash@^4.7.0: +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -lowercase-keys@^2.0.0: +lowercase-keys@2.0.0, lowercase-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== @@ -2312,18 +2153,6 @@ micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -2339,7 +2168,14 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.1.1: +minimatch@9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.2.tgz#397e387fff22f6795844d00badc903a3d5de7057" + integrity sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4, minimatch@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -2387,13 +2223,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -node-fetch@^2.6.1: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -2421,11 +2250,6 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -nwsapi@^2.2.0: - version "2.2.4" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.4.tgz#fd59d5e904e8e1f03c25a7d5a15cfa16c714a1e5" - integrity sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g== - object-assign@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -2434,7 +2258,7 @@ object-assign@^4.0.1: once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" @@ -2445,19 +2269,7 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -p-cancelable@^2.0.0: +p-cancelable@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== @@ -2469,6 +2281,13 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -2498,16 +2317,16 @@ parse5-htmlparser2-tree-adapter@^6.0.0: dependencies: parse5 "^6.0.1" -parse5@6.0.1, parse5@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== - parse5@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -2528,6 +2347,11 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +peek-readable@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.0.0.tgz#7ead2aff25dc40458c60347ea76cfdfd63efdfec" + integrity sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -2550,29 +2374,14 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== - -pretty-format@^27.0.0: - version "27.4.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.4.2.tgz#e4ce92ad66c3888423d332b40477c87d1dac1fb8" - integrity sha512-p0wNtJ9oLuvgOQDEIZ9zQjZffK7KtyR6Si0jnXULIDwrlNF8Cuir3AZP0hHv0jmKuNN/edOnbMjnzd4uTcmWiw== +pretty-format@^29.0.0, pretty-format@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" + integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== dependencies: - "@jest/types" "^27.4.2" - ansi-regex "^5.0.1" + "@jest/schemas" "^29.4.3" ansi-styles "^5.0.0" - react-is "^17.0.1" - -pretty-format@^27.4.2, pretty-format@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" - integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== - dependencies: - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^17.0.1" + react-is "^18.0.0" process-nextick-args@~2.0.0: version "2.0.1" @@ -2587,11 +2396,6 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -psl@^1.1.33: - version "1.9.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" - integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== - pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -2600,33 +2404,29 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.1.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== - -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== +pure-rand@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" + integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ== quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== -random-bunny@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/random-bunny/-/random-bunny-2.0.0.tgz#f9950f53c7f5183a6dad26386db14f702d477cf7" - integrity sha512-xIDaPghs0nslKWNfxDLAGNtsUPUlmNagUmdNAHP7U4LSrGySbgSfAzCV8eECTeuny2csmf0SL5dBxEZdVvHgOA== +random-bunny@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/random-bunny/-/random-bunny-2.1.0.tgz#ddabec169b0dc2207a67eeed454cf19f87b4de48" + integrity sha512-NtPS4Ltqh2mdVddUeDCzP+lgbx5XAxwyq04BSRuchFDRycWVO9XqO2NEEim/rYYu7iA99v7I5B3/VUZb1NiE0g== dependencies: glob-parent "^6.0.0" - got "^11.8.3" + got-cjs "^12.5.4" + linqts "^1.14.4" -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== readable-stream@2.3.7: version "2.3.7" @@ -2641,22 +2441,38 @@ readable-stream@2.3.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-web-to-node-stream@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" + integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== + dependencies: + readable-stream "^3.6.0" + reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== +regenerator-runtime@^0.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - -resolve-alpn@^1.0.0: +resolve-alpn@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== @@ -2673,10 +2489,10 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve.exports@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" - integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== resolve@^1.20.0: version "1.22.2" @@ -2687,43 +2503,24 @@ resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -responselike@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" - integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== +responselike@2.0.1, responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== dependencies: lowercase-keys "^2.0.0" -rimraf@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1: +safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -saxes@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== - dependencies: - xmlchars "^2.2.0" - -semver@7.x, semver@^7.3.2: +semver@7.x: version "7.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== @@ -2735,6 +2532,13 @@ semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.3.5: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" + sha.js@^2.4.11: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" @@ -2755,7 +2559,7 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -signal-exit@^3.0.2, signal-exit@^3.0.3: +signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -2770,24 +2574,19 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -source-map-support@^0.5.6: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3: - version "0.7.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" - integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -2796,7 +2595,7 @@ sprintf-js@~1.0.2: sqlstring@2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.1.tgz#475393ff9e91479aea62dcaf0ca3d14983a7fb40" - integrity sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A= + integrity sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ== stack-utils@^2.0.3: version "2.0.6" @@ -2805,6 +2604,11 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -2822,6 +2626,13 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -2851,6 +2662,14 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strtok3@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-7.0.0.tgz#868c428b4ade64a8fd8fee7364256001c1a4cbe5" + integrity sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ== + dependencies: + "@tokenizer/token" "^0.3.0" + peek-readable "^5.0.0" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -2858,7 +2677,7 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -2872,32 +2691,11 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" - integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== - dependencies: - has-flag "^4.0.0" - supports-color "^7.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -symbol-tree@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== - dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" - test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -2921,11 +2719,6 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" -throat@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" - integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== - tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -2943,69 +2736,43 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -tough-cookie@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" - integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== +token-types@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/token-types/-/token-types-5.0.1.tgz#aa9d9e6b23c420a675e55413b180635b86a093b4" + integrity sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg== dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.2.0" - url-parse "^1.5.3" - -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== - dependencies: - punycode "^2.1.1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + "@tokenizer/token" "^0.3.0" + ieee754 "^1.2.1" ts-essentials@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ== -ts-jest@^27.1.2: - version "27.1.5" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.5.tgz#0ddf1b163fbaae3d5b7504a1e65c914a95cff297" - integrity sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA== +ts-jest@^29.0.0: + version "29.1.0" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.0.tgz#4a9db4104a49b76d2b368ea775b6c9535c603891" + integrity sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" - jest-util "^27.0.0" - json5 "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" lodash.memoize "4.x" make-error "1.x" semver "7.x" - yargs-parser "20.x" + yargs-parser "^21.0.1" -ts-mixer@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.1.tgz#7c2627fb98047eb5f3c7f2fee39d1521d18fe87a" - integrity sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg== - -tslib@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +ts-mixer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.3.tgz#69bd50f406ff39daa369885b16c77a6194c7cae6" + integrity sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ== tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== - dependencies: - prelude-ls "~1.1.2" - type-detect@4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -3016,23 +2783,17 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - -typeorm@0.3.14: - version "0.3.14" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.14.tgz#d46bc685aa92d0caf910849f22753ef319822327" - integrity sha512-tEPEN8qmA2a2wmjkaDcWBZ6LsECHofJW2vaCQMklYs+4JRJMAJ5FfbPIWMbhJ3ANJGMtLAmU1GfC8rLFIpbWsg== +typeorm@0.3.17: + version "0.3.17" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.17.tgz#a73c121a52e4fbe419b596b244777be4e4b57949" + integrity sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig== dependencies: "@sqltools/formatter" "^1.2.5" app-root-path "^3.1.0" buffer "^6.0.3" chalk "^4.1.2" cli-highlight "^2.1.11" + date-fns "^2.29.3" debug "^4.3.4" dotenv "^16.0.3" glob "^8.1.0" @@ -3043,15 +2804,17 @@ typeorm@0.3.14: uuid "^9.0.0" yargs "^17.6.2" -typescript@^4.5.2: - version "4.5.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998" - integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw== +typescript@^5.0.0: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== -universalify@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" - integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== +undici@^5.22.0: + version "5.22.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.22.0.tgz#5e205d82a5aecc003fc4388ccd3d2c6e8674a0ad" + integrity sha512-fR9RXCc+6Dxav4P9VV/sp5w3eFiSdOjJYsbtWfd4s5L5C4ogyuVpdKIVHeW0vV1MloM65/f7W45nR9ZxwVdyiA== + dependencies: + busboy "^1.6.0" update-browserslist-db@^1.0.10: version "1.0.11" @@ -3061,103 +2824,32 @@ update-browserslist-db@^1.0.10: escalade "^3.1.1" picocolors "^1.0.0" -url-parse@^1.5.3: - version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== uuid@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== -v8-to-istanbul@^8.1.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" - integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== +v8-to-istanbul@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== dependencies: + "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" - source-map "^0.7.3" -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== - dependencies: - xml-name-validator "^3.0.0" - -walker@^1.0.7: +walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== dependencies: makeerror "1.0.12" -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== - -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" - -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -whatwg-url@^8.0.0, whatwg-url@^8.5.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== - dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -3165,11 +2857,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -3184,35 +2871,18 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" + signal-exit "^3.0.7" -ws@^7.4.6: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== - -ws@^8.4.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== - -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== - -xmlchars@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +ws@^8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== y18n@^5.0.5: version "5.0.8" @@ -3229,17 +2899,17 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yargs-parser@20.x, yargs-parser@^20.2.2: +yargs-parser@^20.2.2: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-parser@^21.1.1: +yargs-parser@^21.0.1, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs@^16.0.0, yargs@^16.2.0: +yargs@^16.0.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== @@ -3252,7 +2922,7 @@ yargs@^16.0.0, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.6.2: +yargs@^17.3.1, yargs@^17.6.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== @@ -3265,7 +2935,7 @@ yargs@^17.6.2: y18n "^5.0.5" yargs-parser "^21.1.1" -zod@^3.11.6: - version "3.14.4" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.14.4.tgz#e678fe9e5469f4663165a5c35c8f3dc66334a5d6" - integrity sha512-U9BFLb2GO34Sfo9IUYp0w3wJLlmcyGoMd75qU9yf+DrdGA4kEx6e+l9KOkAlyUO0PSQzZCa3TR4qVlcmwqSDuw== +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==