├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── LICENSE ├── README.md ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── rollup.config.mjs ├── src ├── functions │ ├── create.js │ └── load.js ├── index.js └── utils.js └── types └── index.d.ts /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: build 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [18.x, 20.x, 22.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: npm ci 30 | - run: npm run build --if-present 31 | - run: npm run lint --if-present 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Node ### 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .DS_Store 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Compiled binary addons (https://nodejs.org/api/addons.html) 21 | build/Release 22 | 23 | # Dependency directories 24 | node_modules/ 25 | 26 | # TypeScript v1 declaration files 27 | typings/ 28 | 29 | # TypeScript cache 30 | *.tsbuildinfo 31 | 32 | # Optional npm cache directory 33 | .npm 34 | 35 | # Optional eslint cache 36 | .eslintcache 37 | 38 | # Output of 'npm pack' 39 | *.tgz 40 | 41 | # dotenv environment variables file 42 | .env 43 | .env.test 44 | .env.local 45 | .env.development.local 46 | .env.test.local 47 | .env.production.local 48 | 49 | # rollup.js default build output 50 | dist/ 51 | 52 | # Temporary folders 53 | tmp/ 54 | temp/ 55 | 56 | # Certs 57 | *.crt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Outwalk Studios 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @outwalk/discord-backup 2 | 3 | ![build](https://github.com/OutwalkStudios/discord-backup/workflows/build/badge.svg) 4 | [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/OutwalkStudios/discord-backup/blob/master/LICENSE) 5 | [![twitter](https://img.shields.io/badge/follow-on%20twitter-4AA1EC.svg)](https://twitter.com/OutwalkStudios) 6 | 7 | Discord Backup is a module for backing up and restoring discord servers. 8 | 9 | This package is heavily inspired by [discord-backup](https://github.com/Androz2091/discord-backup) which has become unmaintained. This package has been updated to support discord.js v14 and aims to maintain API compatibility with discord-backup. 10 | 11 | 12 | --- 13 | 14 | ## Installation 15 | 16 | You can install @outwalk/discord-backup using npm: 17 | 18 | ``` 19 | npm install @outwalk/discord-backup 20 | ``` 21 | 22 | --- 23 | 24 | ### Create 25 | 26 | Create a backup for the specified server. **You don't need to provide `toBackup` or `doNotBackup` options**—the module will back up all data by default if these options are not specified. 27 | 28 | ```js 29 | import backup from "@outwalk/discord-backup"; 30 | 31 | const backupData = await backup.create(guild, options); 32 | ``` 33 | 34 | ### Load 35 | 36 | Allows you to load a backup on a Discord server! **You don't need to provide toLoad or doNotLoad options**—the module will load all data by default if these options are not specified. 37 | 38 | ```js 39 | import backup from "@outwalk/discord-backup"; 40 | 41 | await backup.load(backupId, guild); 42 | await backup.remove(backupId); 43 | ``` 44 | 45 | ### Fetch 46 | 47 | Fetches information from a backup. 48 | The backup info provides a `data`, `id`, and `size` property. 49 | 50 | ```js 51 | import backup from "@outwalk/discord-backup"; 52 | 53 | const backupInfo = await backup.fetch(backupId); 54 | ``` 55 | 56 | ### Remove 57 | 58 | **Warn**: once the backup is removed, it is impossible to recover it! 59 | 60 | ```js 61 | import backup from "@outwalk/discord-backup"; 62 | 63 | backup.remove(backupID); 64 | ``` 65 | 66 | ### List 67 | 68 | **Note**: `backup.list()` simply returns an array of IDs, you must fetch the ID to get complete information. 69 | 70 | ```js 71 | import backup from "@outwalk/discord-backup"; 72 | 73 | const backups = backup.list(); 74 | ``` 75 | 76 | ### SetStorageFolder 77 | 78 | Updates the storage folder to another 79 | 80 | ```js 81 | import backup from "@outwalk/discord-backup"; 82 | import path from "path"; 83 | 84 | backup.setStorageFolder(path.join(process.cwd(), "backups")); 85 | ``` 86 | --- 87 | 88 | ## Advanced usage 89 | 90 | ### Create [advanced] 91 | 92 | You can use more options for backup creation: 93 | 94 | ```js 95 | import backup from "@outwalk/discord-backup"; 96 | 97 | await backup.create(guild, { 98 | backupId: "mybackup", 99 | maxMessagesPerChannel: 10, 100 | jsonSave: false, 101 | jsonBeautify: true, 102 | doNotBackup: ["bans", "roles", "emojis", "channels"], 103 | // toBackup: ["channels"] 104 | backupMembers: false, 105 | saveImages: "base64", 106 | speed: 250, 107 | ignore2FA: false, 108 | onStatusChange: (status) => { 109 | console.log(`Saving ${status.step}... (${status.progress}) (${status.percentage})`); 110 | } 111 | }); 112 | ``` 113 | Note: If `toBackup` or `doNotBackup` are not provided, the module will back up all data by default.
114 | 115 | **backupId**: Specify an Id to be used for the backup, if not provided a random one will be generated.
116 | **maxMessagesPerChannel**: Maximum of messages to save in each channel. "0" won't save any messages.
117 | **jsonSave**: Whether to save the backup into a json file. You will have to save the backup data in your own db to load it later. 118 | **jsonBeautify**: Whether you want your json backup pretty formatted.
119 | **doNotBackup**: Items you want to exclude from the backup. Available options are `bans`, `roles`, `emojis`, and `channels`. You can specify all channels, a subset of channels, or even a category to exclude all channels under that category: 120 | - **Exclude specific channels**: 121 | ```js 122 | doNotBackup: [{ channels: ["channel_id_1", "channel_id_2"] }] 123 | ``` 124 | - **Exclude an entire category and its child channels**: 125 | ```js 126 | doNotBackup: [{ channels: ["category_id_1"] }] 127 | ``` 128 | - **Exclude all channels**: 129 | ```js 130 | doNotBackup: ["channels"] 131 | ``` 132 | - **Note**: You cannot use `doNotBackup` at the same time as `toBackup.` You must choose one or the other. 133 | 134 | **toBackup**: Items you want to include in the backup. Available options are `bans`, `roles`, `emojis`, and `channels`. You can specify all channels, a subset of channels, or even a category to include all channels under that category: 135 | - **Include specific channels**: 136 | ```js 137 | toBackup: [ 138 | { 139 | channels: ["channel_id_3", "channel_id_4"] 140 | } 141 | ] 142 | ``` 143 | - **Include an entire category and its child channels**: 144 | ```js 145 | toBackup: [{ channels: ["category_id_2"] }] 146 | ``` 147 | - **Include all channels**: 148 | ```js 149 | toBackup: ["channels"] 150 | ``` 151 | - **Note**: You cannot use `toBackup` at the same time as `doNotBackup`. You must choose one or the other. 152 | 153 | **backupMembers**: Wether or not to save information on the members of the server.
154 | **saveImages**: How to save images like guild icon and emojis. Set to "url" by default, restoration may not work if the old server is deleted. So, `url` is recommended if you want to clone a server (or if you need very light backups), and `base64` if you want to backup a server. Save images as base64 creates heavier backups.
155 | **speed**: What speed to run at, default is 250 (measured in ms)
156 | **verbose**: Derermines if the output should be verbose or not.
157 | **ignore2FA**: Disables attempting to grab items that require 2FA
158 | **onStatusChange**: A callback function to handle the status updates during the backup process.
159 | The status object contains three properties:
160 | - **step**: The current step (e.g., "Channels"). 161 | - **progress**: Progress for that step (e.g., "1/100"). 162 | - **percentage**: Percentage completion (e.g., "1%"). 163 | - **info**: A detailed description or update of what is happening in the current step (e.g., "Backed up Channel: ⚡︱weather-api (Category: ☁︱weather)"). This can provide real-time context about which item or action is being processed in the backup. 164 | 165 | You can use the onStatusChange callback to display status messages in Discord, for example: 166 | ```js 167 | onStatusChange: async (status) => { 168 | // Create an embed for the status update 169 | const statusEmbed = new EmbedBuilder() 170 | .setColor("#0099ff") 171 | .setTitle(`Backup Progress: ${status.step}`) 172 | .setDescription(`Status update for backup in progress.`) 173 | .addFields( 174 | { name: "Step", value: status.step, inline: true }, 175 | { name: "Progress", value: status.progress, inline: true }, 176 | { name: "Percentage", value: status.percentage, inline: true }, 177 | { name: "Info", value: status.info || "N/A" } 178 | ) 179 | .setTimestamp() 180 | .setFooter({ text: "Backup Status", iconURL: guild.iconURL() }); 181 | 182 | // Send the embed to the channel 183 | await interaction.channel.send({ embeds: [statusEmbed] }); 184 | console.log( 185 | `[Backing up] Step: ${status.step} | Progress: ${ 186 | status.progress 187 | } | Percentage: ${status.percentage} | Info: ${ 188 | status.info || "N/A" 189 | }` 190 | ); 191 | }, 192 | ``` 193 | This allows you to send real-time updates as the backup progresses. 194 | 195 | #### Requires 2FA 196 | - Auto Moderation Rules 197 | - Bans 198 | 199 | ### Load [advanced] 200 | 201 | As you can see, you're able to load a backup from your own data instead of from an ID: 202 | 203 | ```js 204 | import backup from "@outwalk/discord-backup"; 205 | 206 | await backup.load(backupData, guild, { 207 | clearGuildBeforeRestore: true, 208 | maxMessagesPerChannel: 10, 209 | speed: 250, 210 | doNotLoad: ["roleAssignments", "emojis"], 211 | // toLoad: ["channels"], 212 | onStatusChange: (status) => { 213 | console.log( 214 | `[Restoring] Step: ${status.step} | Progress: ${ 215 | status.progress 216 | } | Percentage: ${status.percentage} | Info: ${status.info || "N/A"}` 217 | ); 218 | }); 219 | ``` 220 | Note: If `toLoad` or `doNotLoad` are not provided, the module will load all data by default.
221 | 222 | **clearGuildBeforeRestore**: Whether to clear the guild (roles, channels, etc... will be deleted) before the backup restoration (recommended).
223 | **maxMessagesPerChannel**: Maximum of messages to restore in each channel. "0" won't restore any messages.
224 | **speed**: What speed to run at, default is 250 (measured in ms)
225 | **verbose**: Determines if the output should be verbose or not.
226 | **doNotLoad**: Items you don't want to restore. Available options are `main`, `roleAssignments`, `emojis`, `roles`, and `channels`. You can specify all channels, a subset of channels, or even a category to exclude all channels under that category: 227 | - **Exclude specific channels**: 228 | ```js 229 | doNotLoad: [{ channels: ["channel_id_1", "channel_id_2"] }] 230 | ``` 231 | - **Exclude an entire category and its child channels**: 232 | ```js 233 | doNotLoad: [{ channels: ["category_id_1"] }] 234 | ``` 235 | - **Exclude all channels**: 236 | ```js 237 | doNotLoad: ["channels"] 238 | ``` 239 | - **Note**: You cannot use `doNotLoad` at the same time as `toLoad`. You must choose one or the other. 240 | 241 | **toLoad**: Items you want to restore. Available options are `main`, `roleAssignments`, `emojis`, `roles`, and `channels`. You can specify all channels, a subset of channels, or even a category to include all channels under that category: 242 | - **Include specific channels**: 243 | ```js 244 | toLoad: [ 245 | { 246 | channels: ["channel_id_3", "channel_id_4"] 247 | } 248 | ] 249 | ``` 250 | - **Include an entire category and its child channels**: 251 | ```js 252 | toLoad: [{ channels: ["category_id_2"] }] 253 | ``` 254 | - **Include all channels**: 255 | ```js 256 | toLoad: ["channels"] 257 | ``` 258 | - **Note**: You cannot use `toLoad` at the same time as `doNotLoad`. You must choose one or the other. 259 | 260 | **onStatusChange**: A callback function to handle the status updates during the restoration process. Similar to backup, it provides the `step`, `progress`, `percentage`, and `info`.
261 | 262 | --- 263 | 264 | ## Reporting Issues 265 | 266 | If you are having trouble getting something to work or run into any problems, you can create a new [issue](https://github.com/OutwalkStudios/discord-backup/issues). 267 | 268 | --- 269 | 270 | ## License 271 | 272 | @outwalk/discord-backup is licensed under the terms of the [**MIT**](https://github.com/OutwalkStudios/discord-backup/blob/master/LICENSE) license. -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import js from "@eslint/js"; 3 | 4 | export default { 5 | files: ["src/**/*.{js,mjs,cjs}"], 6 | languageOptions: { globals: { ...globals.node } }, 7 | rules: { 8 | ...js.configs.recommended.rules, 9 | "indent": ["error", 4, { "SwitchCase": 1 }], 10 | "linebreak-style": ["error", "unix"], 11 | "quotes": ["error", "double", { "allowTemplateLiterals": true }], 12 | "semi": ["error", "always"] 13 | } 14 | }; -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@outwalk/discord-backup", 3 | "version": "0.8.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@outwalk/discord-backup", 9 | "version": "0.8.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "axios": "^1.7.7", 13 | "bottleneck": "^2.19.5" 14 | }, 15 | "devDependencies": { 16 | "@eslint/js": "^9.15.0", 17 | "@rollup/plugin-commonjs": "^28.0.1", 18 | "@rollup/plugin-json": "^6.1.0", 19 | "@rollup/plugin-node-resolve": "^15.3.0", 20 | "esbuild": "^0.24.0", 21 | "eslint": "^9.15.0", 22 | "globals": "^15.12.0", 23 | "rollup": "^4.27.3", 24 | "rollup-plugin-esbuild": "^6.1.1" 25 | }, 26 | "engines": { 27 | "node": ">=16" 28 | }, 29 | "peerDependencies": { 30 | "discord.js": "^14.x.x" 31 | } 32 | }, 33 | "node_modules/@aashutoshrathi/word-wrap": { 34 | "version": "1.2.6", 35 | "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", 36 | "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", 37 | "dev": true, 38 | "engines": { 39 | "node": ">=0.10.0" 40 | } 41 | }, 42 | "node_modules/@discordjs/builders": { 43 | "version": "1.9.0", 44 | "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.9.0.tgz", 45 | "integrity": "sha512-0zx8DePNVvQibh5ly5kCEei5wtPBIUbSoE9n+91Rlladz4tgtFbJ36PZMxxZrTEOQ7AHMZ/b0crT/0fCy6FTKg==", 46 | "license": "Apache-2.0", 47 | "peer": true, 48 | "dependencies": { 49 | "@discordjs/formatters": "^0.5.0", 50 | "@discordjs/util": "^1.1.1", 51 | "@sapphire/shapeshift": "^4.0.0", 52 | "discord-api-types": "0.37.97", 53 | "fast-deep-equal": "^3.1.3", 54 | "ts-mixer": "^6.0.4", 55 | "tslib": "^2.6.3" 56 | }, 57 | "engines": { 58 | "node": ">=18" 59 | }, 60 | "funding": { 61 | "url": "https://github.com/discordjs/discord.js?sponsor" 62 | } 63 | }, 64 | "node_modules/@discordjs/builders/node_modules/discord-api-types": { 65 | "version": "0.37.97", 66 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz", 67 | "integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==", 68 | "license": "MIT", 69 | "peer": true 70 | }, 71 | "node_modules/@discordjs/collection": { 72 | "version": "1.5.3", 73 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", 74 | "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==", 75 | "peer": true, 76 | "engines": { 77 | "node": ">=16.11.0" 78 | } 79 | }, 80 | "node_modules/@discordjs/formatters": { 81 | "version": "0.5.0", 82 | "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.5.0.tgz", 83 | "integrity": "sha512-98b3i+Y19RFq1Xke4NkVY46x8KjJQjldHUuEbCqMvp1F5Iq9HgnGpu91jOi/Ufazhty32eRsKnnzS8n4c+L93g==", 84 | "license": "Apache-2.0", 85 | "peer": true, 86 | "dependencies": { 87 | "discord-api-types": "0.37.97" 88 | }, 89 | "engines": { 90 | "node": ">=18" 91 | }, 92 | "funding": { 93 | "url": "https://github.com/discordjs/discord.js?sponsor" 94 | } 95 | }, 96 | "node_modules/@discordjs/formatters/node_modules/discord-api-types": { 97 | "version": "0.37.97", 98 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz", 99 | "integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==", 100 | "license": "MIT", 101 | "peer": true 102 | }, 103 | "node_modules/@discordjs/rest": { 104 | "version": "2.4.0", 105 | "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.4.0.tgz", 106 | "integrity": "sha512-Xb2irDqNcq+O8F0/k/NaDp7+t091p+acb51iA4bCKfIn+WFWd6HrNvcsSbMMxIR9NjcMZS6NReTKygqiQN+ntw==", 107 | "license": "Apache-2.0", 108 | "peer": true, 109 | "dependencies": { 110 | "@discordjs/collection": "^2.1.1", 111 | "@discordjs/util": "^1.1.1", 112 | "@sapphire/async-queue": "^1.5.3", 113 | "@sapphire/snowflake": "^3.5.3", 114 | "@vladfrangu/async_event_emitter": "^2.4.6", 115 | "discord-api-types": "0.37.97", 116 | "magic-bytes.js": "^1.10.0", 117 | "tslib": "^2.6.3", 118 | "undici": "6.19.8" 119 | }, 120 | "engines": { 121 | "node": ">=18" 122 | }, 123 | "funding": { 124 | "url": "https://github.com/discordjs/discord.js?sponsor" 125 | } 126 | }, 127 | "node_modules/@discordjs/rest/node_modules/@discordjs/collection": { 128 | "version": "2.1.1", 129 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", 130 | "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", 131 | "license": "Apache-2.0", 132 | "peer": true, 133 | "engines": { 134 | "node": ">=18" 135 | }, 136 | "funding": { 137 | "url": "https://github.com/discordjs/discord.js?sponsor" 138 | } 139 | }, 140 | "node_modules/@discordjs/rest/node_modules/discord-api-types": { 141 | "version": "0.37.97", 142 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz", 143 | "integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==", 144 | "license": "MIT", 145 | "peer": true 146 | }, 147 | "node_modules/@discordjs/util": { 148 | "version": "1.1.1", 149 | "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz", 150 | "integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==", 151 | "license": "Apache-2.0", 152 | "peer": true, 153 | "engines": { 154 | "node": ">=18" 155 | }, 156 | "funding": { 157 | "url": "https://github.com/discordjs/discord.js?sponsor" 158 | } 159 | }, 160 | "node_modules/@discordjs/ws": { 161 | "version": "1.1.1", 162 | "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.1.1.tgz", 163 | "integrity": "sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA==", 164 | "peer": true, 165 | "dependencies": { 166 | "@discordjs/collection": "^2.1.0", 167 | "@discordjs/rest": "^2.3.0", 168 | "@discordjs/util": "^1.1.0", 169 | "@sapphire/async-queue": "^1.5.2", 170 | "@types/ws": "^8.5.10", 171 | "@vladfrangu/async_event_emitter": "^2.2.4", 172 | "discord-api-types": "0.37.83", 173 | "tslib": "^2.6.2", 174 | "ws": "^8.16.0" 175 | }, 176 | "engines": { 177 | "node": ">=16.11.0" 178 | }, 179 | "funding": { 180 | "url": "https://github.com/discordjs/discord.js?sponsor" 181 | } 182 | }, 183 | "node_modules/@discordjs/ws/node_modules/@discordjs/collection": { 184 | "version": "2.1.0", 185 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.0.tgz", 186 | "integrity": "sha512-mLcTACtXUuVgutoznkh6hS3UFqYirDYAg5Dc1m8xn6OvPjetnUlf/xjtqnnc47OwWdaoCQnHmHh9KofhD6uRqw==", 187 | "peer": true, 188 | "engines": { 189 | "node": ">=18" 190 | }, 191 | "funding": { 192 | "url": "https://github.com/discordjs/discord.js?sponsor" 193 | } 194 | }, 195 | "node_modules/@esbuild/aix-ppc64": { 196 | "version": "0.24.0", 197 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", 198 | "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", 199 | "cpu": [ 200 | "ppc64" 201 | ], 202 | "dev": true, 203 | "license": "MIT", 204 | "optional": true, 205 | "os": [ 206 | "aix" 207 | ], 208 | "engines": { 209 | "node": ">=18" 210 | } 211 | }, 212 | "node_modules/@esbuild/android-arm": { 213 | "version": "0.24.0", 214 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", 215 | "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", 216 | "cpu": [ 217 | "arm" 218 | ], 219 | "dev": true, 220 | "license": "MIT", 221 | "optional": true, 222 | "os": [ 223 | "android" 224 | ], 225 | "engines": { 226 | "node": ">=18" 227 | } 228 | }, 229 | "node_modules/@esbuild/android-arm64": { 230 | "version": "0.24.0", 231 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", 232 | "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", 233 | "cpu": [ 234 | "arm64" 235 | ], 236 | "dev": true, 237 | "license": "MIT", 238 | "optional": true, 239 | "os": [ 240 | "android" 241 | ], 242 | "engines": { 243 | "node": ">=18" 244 | } 245 | }, 246 | "node_modules/@esbuild/android-x64": { 247 | "version": "0.24.0", 248 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", 249 | "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", 250 | "cpu": [ 251 | "x64" 252 | ], 253 | "dev": true, 254 | "license": "MIT", 255 | "optional": true, 256 | "os": [ 257 | "android" 258 | ], 259 | "engines": { 260 | "node": ">=18" 261 | } 262 | }, 263 | "node_modules/@esbuild/darwin-arm64": { 264 | "version": "0.24.0", 265 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", 266 | "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", 267 | "cpu": [ 268 | "arm64" 269 | ], 270 | "dev": true, 271 | "license": "MIT", 272 | "optional": true, 273 | "os": [ 274 | "darwin" 275 | ], 276 | "engines": { 277 | "node": ">=18" 278 | } 279 | }, 280 | "node_modules/@esbuild/darwin-x64": { 281 | "version": "0.24.0", 282 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", 283 | "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", 284 | "cpu": [ 285 | "x64" 286 | ], 287 | "dev": true, 288 | "license": "MIT", 289 | "optional": true, 290 | "os": [ 291 | "darwin" 292 | ], 293 | "engines": { 294 | "node": ">=18" 295 | } 296 | }, 297 | "node_modules/@esbuild/freebsd-arm64": { 298 | "version": "0.24.0", 299 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", 300 | "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", 301 | "cpu": [ 302 | "arm64" 303 | ], 304 | "dev": true, 305 | "license": "MIT", 306 | "optional": true, 307 | "os": [ 308 | "freebsd" 309 | ], 310 | "engines": { 311 | "node": ">=18" 312 | } 313 | }, 314 | "node_modules/@esbuild/freebsd-x64": { 315 | "version": "0.24.0", 316 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", 317 | "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", 318 | "cpu": [ 319 | "x64" 320 | ], 321 | "dev": true, 322 | "license": "MIT", 323 | "optional": true, 324 | "os": [ 325 | "freebsd" 326 | ], 327 | "engines": { 328 | "node": ">=18" 329 | } 330 | }, 331 | "node_modules/@esbuild/linux-arm": { 332 | "version": "0.24.0", 333 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", 334 | "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", 335 | "cpu": [ 336 | "arm" 337 | ], 338 | "dev": true, 339 | "license": "MIT", 340 | "optional": true, 341 | "os": [ 342 | "linux" 343 | ], 344 | "engines": { 345 | "node": ">=18" 346 | } 347 | }, 348 | "node_modules/@esbuild/linux-arm64": { 349 | "version": "0.24.0", 350 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", 351 | "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", 352 | "cpu": [ 353 | "arm64" 354 | ], 355 | "dev": true, 356 | "license": "MIT", 357 | "optional": true, 358 | "os": [ 359 | "linux" 360 | ], 361 | "engines": { 362 | "node": ">=18" 363 | } 364 | }, 365 | "node_modules/@esbuild/linux-ia32": { 366 | "version": "0.24.0", 367 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", 368 | "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", 369 | "cpu": [ 370 | "ia32" 371 | ], 372 | "dev": true, 373 | "license": "MIT", 374 | "optional": true, 375 | "os": [ 376 | "linux" 377 | ], 378 | "engines": { 379 | "node": ">=18" 380 | } 381 | }, 382 | "node_modules/@esbuild/linux-loong64": { 383 | "version": "0.24.0", 384 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", 385 | "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", 386 | "cpu": [ 387 | "loong64" 388 | ], 389 | "dev": true, 390 | "license": "MIT", 391 | "optional": true, 392 | "os": [ 393 | "linux" 394 | ], 395 | "engines": { 396 | "node": ">=18" 397 | } 398 | }, 399 | "node_modules/@esbuild/linux-mips64el": { 400 | "version": "0.24.0", 401 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", 402 | "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", 403 | "cpu": [ 404 | "mips64el" 405 | ], 406 | "dev": true, 407 | "license": "MIT", 408 | "optional": true, 409 | "os": [ 410 | "linux" 411 | ], 412 | "engines": { 413 | "node": ">=18" 414 | } 415 | }, 416 | "node_modules/@esbuild/linux-ppc64": { 417 | "version": "0.24.0", 418 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", 419 | "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", 420 | "cpu": [ 421 | "ppc64" 422 | ], 423 | "dev": true, 424 | "license": "MIT", 425 | "optional": true, 426 | "os": [ 427 | "linux" 428 | ], 429 | "engines": { 430 | "node": ">=18" 431 | } 432 | }, 433 | "node_modules/@esbuild/linux-riscv64": { 434 | "version": "0.24.0", 435 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", 436 | "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", 437 | "cpu": [ 438 | "riscv64" 439 | ], 440 | "dev": true, 441 | "license": "MIT", 442 | "optional": true, 443 | "os": [ 444 | "linux" 445 | ], 446 | "engines": { 447 | "node": ">=18" 448 | } 449 | }, 450 | "node_modules/@esbuild/linux-s390x": { 451 | "version": "0.24.0", 452 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", 453 | "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", 454 | "cpu": [ 455 | "s390x" 456 | ], 457 | "dev": true, 458 | "license": "MIT", 459 | "optional": true, 460 | "os": [ 461 | "linux" 462 | ], 463 | "engines": { 464 | "node": ">=18" 465 | } 466 | }, 467 | "node_modules/@esbuild/linux-x64": { 468 | "version": "0.24.0", 469 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", 470 | "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", 471 | "cpu": [ 472 | "x64" 473 | ], 474 | "dev": true, 475 | "license": "MIT", 476 | "optional": true, 477 | "os": [ 478 | "linux" 479 | ], 480 | "engines": { 481 | "node": ">=18" 482 | } 483 | }, 484 | "node_modules/@esbuild/netbsd-x64": { 485 | "version": "0.24.0", 486 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", 487 | "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", 488 | "cpu": [ 489 | "x64" 490 | ], 491 | "dev": true, 492 | "license": "MIT", 493 | "optional": true, 494 | "os": [ 495 | "netbsd" 496 | ], 497 | "engines": { 498 | "node": ">=18" 499 | } 500 | }, 501 | "node_modules/@esbuild/openbsd-arm64": { 502 | "version": "0.24.0", 503 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", 504 | "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", 505 | "cpu": [ 506 | "arm64" 507 | ], 508 | "dev": true, 509 | "license": "MIT", 510 | "optional": true, 511 | "os": [ 512 | "openbsd" 513 | ], 514 | "engines": { 515 | "node": ">=18" 516 | } 517 | }, 518 | "node_modules/@esbuild/openbsd-x64": { 519 | "version": "0.24.0", 520 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", 521 | "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", 522 | "cpu": [ 523 | "x64" 524 | ], 525 | "dev": true, 526 | "license": "MIT", 527 | "optional": true, 528 | "os": [ 529 | "openbsd" 530 | ], 531 | "engines": { 532 | "node": ">=18" 533 | } 534 | }, 535 | "node_modules/@esbuild/sunos-x64": { 536 | "version": "0.24.0", 537 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", 538 | "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", 539 | "cpu": [ 540 | "x64" 541 | ], 542 | "dev": true, 543 | "license": "MIT", 544 | "optional": true, 545 | "os": [ 546 | "sunos" 547 | ], 548 | "engines": { 549 | "node": ">=18" 550 | } 551 | }, 552 | "node_modules/@esbuild/win32-arm64": { 553 | "version": "0.24.0", 554 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", 555 | "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", 556 | "cpu": [ 557 | "arm64" 558 | ], 559 | "dev": true, 560 | "license": "MIT", 561 | "optional": true, 562 | "os": [ 563 | "win32" 564 | ], 565 | "engines": { 566 | "node": ">=18" 567 | } 568 | }, 569 | "node_modules/@esbuild/win32-ia32": { 570 | "version": "0.24.0", 571 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", 572 | "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", 573 | "cpu": [ 574 | "ia32" 575 | ], 576 | "dev": true, 577 | "license": "MIT", 578 | "optional": true, 579 | "os": [ 580 | "win32" 581 | ], 582 | "engines": { 583 | "node": ">=18" 584 | } 585 | }, 586 | "node_modules/@esbuild/win32-x64": { 587 | "version": "0.24.0", 588 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", 589 | "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", 590 | "cpu": [ 591 | "x64" 592 | ], 593 | "dev": true, 594 | "license": "MIT", 595 | "optional": true, 596 | "os": [ 597 | "win32" 598 | ], 599 | "engines": { 600 | "node": ">=18" 601 | } 602 | }, 603 | "node_modules/@eslint-community/eslint-utils": { 604 | "version": "4.4.0", 605 | "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", 606 | "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", 607 | "dev": true, 608 | "dependencies": { 609 | "eslint-visitor-keys": "^3.3.0" 610 | }, 611 | "engines": { 612 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 613 | }, 614 | "peerDependencies": { 615 | "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 616 | } 617 | }, 618 | "node_modules/@eslint-community/regexpp": { 619 | "version": "4.12.1", 620 | "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", 621 | "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", 622 | "dev": true, 623 | "license": "MIT", 624 | "engines": { 625 | "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 626 | } 627 | }, 628 | "node_modules/@eslint/config-array": { 629 | "version": "0.19.0", 630 | "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz", 631 | "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==", 632 | "dev": true, 633 | "license": "Apache-2.0", 634 | "dependencies": { 635 | "@eslint/object-schema": "^2.1.4", 636 | "debug": "^4.3.1", 637 | "minimatch": "^3.1.2" 638 | }, 639 | "engines": { 640 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 641 | } 642 | }, 643 | "node_modules/@eslint/core": { 644 | "version": "0.9.0", 645 | "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz", 646 | "integrity": "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==", 647 | "dev": true, 648 | "license": "Apache-2.0", 649 | "engines": { 650 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 651 | } 652 | }, 653 | "node_modules/@eslint/eslintrc": { 654 | "version": "3.2.0", 655 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", 656 | "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", 657 | "dev": true, 658 | "license": "MIT", 659 | "dependencies": { 660 | "ajv": "^6.12.4", 661 | "debug": "^4.3.2", 662 | "espree": "^10.0.1", 663 | "globals": "^14.0.0", 664 | "ignore": "^5.2.0", 665 | "import-fresh": "^3.2.1", 666 | "js-yaml": "^4.1.0", 667 | "minimatch": "^3.1.2", 668 | "strip-json-comments": "^3.1.1" 669 | }, 670 | "engines": { 671 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 672 | }, 673 | "funding": { 674 | "url": "https://opencollective.com/eslint" 675 | } 676 | }, 677 | "node_modules/@eslint/eslintrc/node_modules/globals": { 678 | "version": "14.0.0", 679 | "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", 680 | "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", 681 | "dev": true, 682 | "license": "MIT", 683 | "engines": { 684 | "node": ">=18" 685 | }, 686 | "funding": { 687 | "url": "https://github.com/sponsors/sindresorhus" 688 | } 689 | }, 690 | "node_modules/@eslint/js": { 691 | "version": "9.15.0", 692 | "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", 693 | "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", 694 | "dev": true, 695 | "license": "MIT", 696 | "engines": { 697 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 698 | } 699 | }, 700 | "node_modules/@eslint/object-schema": { 701 | "version": "2.1.4", 702 | "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", 703 | "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", 704 | "dev": true, 705 | "license": "Apache-2.0", 706 | "engines": { 707 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 708 | } 709 | }, 710 | "node_modules/@eslint/plugin-kit": { 711 | "version": "0.2.3", 712 | "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", 713 | "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", 714 | "dev": true, 715 | "license": "Apache-2.0", 716 | "dependencies": { 717 | "levn": "^0.4.1" 718 | }, 719 | "engines": { 720 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 721 | } 722 | }, 723 | "node_modules/@humanfs/core": { 724 | "version": "0.19.1", 725 | "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", 726 | "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", 727 | "dev": true, 728 | "license": "Apache-2.0", 729 | "engines": { 730 | "node": ">=18.18.0" 731 | } 732 | }, 733 | "node_modules/@humanfs/node": { 734 | "version": "0.16.6", 735 | "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", 736 | "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", 737 | "dev": true, 738 | "license": "Apache-2.0", 739 | "dependencies": { 740 | "@humanfs/core": "^0.19.1", 741 | "@humanwhocodes/retry": "^0.3.0" 742 | }, 743 | "engines": { 744 | "node": ">=18.18.0" 745 | } 746 | }, 747 | "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { 748 | "version": "0.3.1", 749 | "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", 750 | "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", 751 | "dev": true, 752 | "license": "Apache-2.0", 753 | "engines": { 754 | "node": ">=18.18" 755 | }, 756 | "funding": { 757 | "type": "github", 758 | "url": "https://github.com/sponsors/nzakas" 759 | } 760 | }, 761 | "node_modules/@humanwhocodes/module-importer": { 762 | "version": "1.0.1", 763 | "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", 764 | "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 765 | "dev": true, 766 | "engines": { 767 | "node": ">=12.22" 768 | }, 769 | "funding": { 770 | "type": "github", 771 | "url": "https://github.com/sponsors/nzakas" 772 | } 773 | }, 774 | "node_modules/@humanwhocodes/retry": { 775 | "version": "0.4.1", 776 | "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", 777 | "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", 778 | "dev": true, 779 | "license": "Apache-2.0", 780 | "engines": { 781 | "node": ">=18.18" 782 | }, 783 | "funding": { 784 | "type": "github", 785 | "url": "https://github.com/sponsors/nzakas" 786 | } 787 | }, 788 | "node_modules/@jridgewell/sourcemap-codec": { 789 | "version": "1.4.15", 790 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 791 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", 792 | "dev": true 793 | }, 794 | "node_modules/@rollup/plugin-commonjs": { 795 | "version": "28.0.1", 796 | "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.1.tgz", 797 | "integrity": "sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==", 798 | "dev": true, 799 | "license": "MIT", 800 | "dependencies": { 801 | "@rollup/pluginutils": "^5.0.1", 802 | "commondir": "^1.0.1", 803 | "estree-walker": "^2.0.2", 804 | "fdir": "^6.2.0", 805 | "is-reference": "1.2.1", 806 | "magic-string": "^0.30.3", 807 | "picomatch": "^4.0.2" 808 | }, 809 | "engines": { 810 | "node": ">=16.0.0 || 14 >= 14.17" 811 | }, 812 | "peerDependencies": { 813 | "rollup": "^2.68.0||^3.0.0||^4.0.0" 814 | }, 815 | "peerDependenciesMeta": { 816 | "rollup": { 817 | "optional": true 818 | } 819 | } 820 | }, 821 | "node_modules/@rollup/plugin-commonjs/node_modules/fdir": { 822 | "version": "6.4.2", 823 | "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", 824 | "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", 825 | "dev": true, 826 | "license": "MIT", 827 | "peerDependencies": { 828 | "picomatch": "^3 || ^4" 829 | }, 830 | "peerDependenciesMeta": { 831 | "picomatch": { 832 | "optional": true 833 | } 834 | } 835 | }, 836 | "node_modules/@rollup/plugin-commonjs/node_modules/picomatch": { 837 | "version": "4.0.2", 838 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", 839 | "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", 840 | "dev": true, 841 | "license": "MIT", 842 | "engines": { 843 | "node": ">=12" 844 | }, 845 | "funding": { 846 | "url": "https://github.com/sponsors/jonschlinkert" 847 | } 848 | }, 849 | "node_modules/@rollup/plugin-json": { 850 | "version": "6.1.0", 851 | "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", 852 | "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", 853 | "dev": true, 854 | "license": "MIT", 855 | "dependencies": { 856 | "@rollup/pluginutils": "^5.1.0" 857 | }, 858 | "engines": { 859 | "node": ">=14.0.0" 860 | }, 861 | "peerDependencies": { 862 | "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" 863 | }, 864 | "peerDependenciesMeta": { 865 | "rollup": { 866 | "optional": true 867 | } 868 | } 869 | }, 870 | "node_modules/@rollup/plugin-node-resolve": { 871 | "version": "15.3.0", 872 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz", 873 | "integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==", 874 | "dev": true, 875 | "license": "MIT", 876 | "dependencies": { 877 | "@rollup/pluginutils": "^5.0.1", 878 | "@types/resolve": "1.20.2", 879 | "deepmerge": "^4.2.2", 880 | "is-module": "^1.0.0", 881 | "resolve": "^1.22.1" 882 | }, 883 | "engines": { 884 | "node": ">=14.0.0" 885 | }, 886 | "peerDependencies": { 887 | "rollup": "^2.78.0||^3.0.0||^4.0.0" 888 | }, 889 | "peerDependenciesMeta": { 890 | "rollup": { 891 | "optional": true 892 | } 893 | } 894 | }, 895 | "node_modules/@rollup/pluginutils": { 896 | "version": "5.1.0", 897 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", 898 | "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", 899 | "dev": true, 900 | "dependencies": { 901 | "@types/estree": "^1.0.0", 902 | "estree-walker": "^2.0.2", 903 | "picomatch": "^2.3.1" 904 | }, 905 | "engines": { 906 | "node": ">=14.0.0" 907 | }, 908 | "peerDependencies": { 909 | "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" 910 | }, 911 | "peerDependenciesMeta": { 912 | "rollup": { 913 | "optional": true 914 | } 915 | } 916 | }, 917 | "node_modules/@rollup/rollup-android-arm-eabi": { 918 | "version": "4.27.3", 919 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.3.tgz", 920 | "integrity": "sha512-EzxVSkIvCFxUd4Mgm4xR9YXrcp976qVaHnqom/Tgm+vU79k4vV4eYTjmRvGfeoW8m9LVcsAy/lGjcgVegKEhLQ==", 921 | "cpu": [ 922 | "arm" 923 | ], 924 | "dev": true, 925 | "license": "MIT", 926 | "optional": true, 927 | "os": [ 928 | "android" 929 | ] 930 | }, 931 | "node_modules/@rollup/rollup-android-arm64": { 932 | "version": "4.27.3", 933 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.3.tgz", 934 | "integrity": "sha512-LJc5pDf1wjlt9o/Giaw9Ofl+k/vLUaYsE2zeQGH85giX2F+wn/Cg8b3c5CDP3qmVmeO5NzwVUzQQxwZvC2eQKw==", 935 | "cpu": [ 936 | "arm64" 937 | ], 938 | "dev": true, 939 | "license": "MIT", 940 | "optional": true, 941 | "os": [ 942 | "android" 943 | ] 944 | }, 945 | "node_modules/@rollup/rollup-darwin-arm64": { 946 | "version": "4.27.3", 947 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.3.tgz", 948 | "integrity": "sha512-OuRysZ1Mt7wpWJ+aYKblVbJWtVn3Cy52h8nLuNSzTqSesYw1EuN6wKp5NW/4eSre3mp12gqFRXOKTcN3AI3LqA==", 949 | "cpu": [ 950 | "arm64" 951 | ], 952 | "dev": true, 953 | "license": "MIT", 954 | "optional": true, 955 | "os": [ 956 | "darwin" 957 | ] 958 | }, 959 | "node_modules/@rollup/rollup-darwin-x64": { 960 | "version": "4.27.3", 961 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.3.tgz", 962 | "integrity": "sha512-xW//zjJMlJs2sOrCmXdB4d0uiilZsOdlGQIC/jjmMWT47lkLLoB1nsNhPUcnoqyi5YR6I4h+FjBpILxbEy8JRg==", 963 | "cpu": [ 964 | "x64" 965 | ], 966 | "dev": true, 967 | "license": "MIT", 968 | "optional": true, 969 | "os": [ 970 | "darwin" 971 | ] 972 | }, 973 | "node_modules/@rollup/rollup-freebsd-arm64": { 974 | "version": "4.27.3", 975 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.3.tgz", 976 | "integrity": "sha512-58E0tIcwZ+12nK1WiLzHOD8I0d0kdrY/+o7yFVPRHuVGY3twBwzwDdTIBGRxLmyjciMYl1B/U515GJy+yn46qw==", 977 | "cpu": [ 978 | "arm64" 979 | ], 980 | "dev": true, 981 | "license": "MIT", 982 | "optional": true, 983 | "os": [ 984 | "freebsd" 985 | ] 986 | }, 987 | "node_modules/@rollup/rollup-freebsd-x64": { 988 | "version": "4.27.3", 989 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.3.tgz", 990 | "integrity": "sha512-78fohrpcVwTLxg1ZzBMlwEimoAJmY6B+5TsyAZ3Vok7YabRBUvjYTsRXPTjGEvv/mfgVBepbW28OlMEz4w8wGA==", 991 | "cpu": [ 992 | "x64" 993 | ], 994 | "dev": true, 995 | "license": "MIT", 996 | "optional": true, 997 | "os": [ 998 | "freebsd" 999 | ] 1000 | }, 1001 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 1002 | "version": "4.27.3", 1003 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.3.tgz", 1004 | "integrity": "sha512-h2Ay79YFXyQi+QZKo3ISZDyKaVD7uUvukEHTOft7kh00WF9mxAaxZsNs3o/eukbeKuH35jBvQqrT61fzKfAB/Q==", 1005 | "cpu": [ 1006 | "arm" 1007 | ], 1008 | "dev": true, 1009 | "license": "MIT", 1010 | "optional": true, 1011 | "os": [ 1012 | "linux" 1013 | ] 1014 | }, 1015 | "node_modules/@rollup/rollup-linux-arm-musleabihf": { 1016 | "version": "4.27.3", 1017 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.3.tgz", 1018 | "integrity": "sha512-Sv2GWmrJfRY57urktVLQ0VKZjNZGogVtASAgosDZ1aUB+ykPxSi3X1nWORL5Jk0sTIIwQiPH7iE3BMi9zGWfkg==", 1019 | "cpu": [ 1020 | "arm" 1021 | ], 1022 | "dev": true, 1023 | "license": "MIT", 1024 | "optional": true, 1025 | "os": [ 1026 | "linux" 1027 | ] 1028 | }, 1029 | "node_modules/@rollup/rollup-linux-arm64-gnu": { 1030 | "version": "4.27.3", 1031 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.3.tgz", 1032 | "integrity": "sha512-FPoJBLsPW2bDNWjSrwNuTPUt30VnfM8GPGRoLCYKZpPx0xiIEdFip3dH6CqgoT0RnoGXptaNziM0WlKgBc+OWQ==", 1033 | "cpu": [ 1034 | "arm64" 1035 | ], 1036 | "dev": true, 1037 | "license": "MIT", 1038 | "optional": true, 1039 | "os": [ 1040 | "linux" 1041 | ] 1042 | }, 1043 | "node_modules/@rollup/rollup-linux-arm64-musl": { 1044 | "version": "4.27.3", 1045 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.3.tgz", 1046 | "integrity": "sha512-TKxiOvBorYq4sUpA0JT+Fkh+l+G9DScnG5Dqx7wiiqVMiRSkzTclP35pE6eQQYjP4Gc8yEkJGea6rz4qyWhp3g==", 1047 | "cpu": [ 1048 | "arm64" 1049 | ], 1050 | "dev": true, 1051 | "license": "MIT", 1052 | "optional": true, 1053 | "os": [ 1054 | "linux" 1055 | ] 1056 | }, 1057 | "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { 1058 | "version": "4.27.3", 1059 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.3.tgz", 1060 | "integrity": "sha512-v2M/mPvVUKVOKITa0oCFksnQQ/TqGrT+yD0184/cWHIu0LoIuYHwox0Pm3ccXEz8cEQDLk6FPKd1CCm+PlsISw==", 1061 | "cpu": [ 1062 | "ppc64" 1063 | ], 1064 | "dev": true, 1065 | "license": "MIT", 1066 | "optional": true, 1067 | "os": [ 1068 | "linux" 1069 | ] 1070 | }, 1071 | "node_modules/@rollup/rollup-linux-riscv64-gnu": { 1072 | "version": "4.27.3", 1073 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.3.tgz", 1074 | "integrity": "sha512-LdrI4Yocb1a/tFVkzmOE5WyYRgEBOyEhWYJe4gsDWDiwnjYKjNs7PS6SGlTDB7maOHF4kxevsuNBl2iOcj3b4A==", 1075 | "cpu": [ 1076 | "riscv64" 1077 | ], 1078 | "dev": true, 1079 | "license": "MIT", 1080 | "optional": true, 1081 | "os": [ 1082 | "linux" 1083 | ] 1084 | }, 1085 | "node_modules/@rollup/rollup-linux-s390x-gnu": { 1086 | "version": "4.27.3", 1087 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.3.tgz", 1088 | "integrity": "sha512-d4wVu6SXij/jyiwPvI6C4KxdGzuZOvJ6y9VfrcleHTwo68fl8vZC5ZYHsCVPUi4tndCfMlFniWgwonQ5CUpQcA==", 1089 | "cpu": [ 1090 | "s390x" 1091 | ], 1092 | "dev": true, 1093 | "license": "MIT", 1094 | "optional": true, 1095 | "os": [ 1096 | "linux" 1097 | ] 1098 | }, 1099 | "node_modules/@rollup/rollup-linux-x64-gnu": { 1100 | "version": "4.27.3", 1101 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.3.tgz", 1102 | "integrity": "sha512-/6bn6pp1fsCGEY5n3yajmzZQAh+mW4QPItbiWxs69zskBzJuheb3tNynEjL+mKOsUSFK11X4LYF2BwwXnzWleA==", 1103 | "cpu": [ 1104 | "x64" 1105 | ], 1106 | "dev": true, 1107 | "license": "MIT", 1108 | "optional": true, 1109 | "os": [ 1110 | "linux" 1111 | ] 1112 | }, 1113 | "node_modules/@rollup/rollup-linux-x64-musl": { 1114 | "version": "4.27.3", 1115 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.3.tgz", 1116 | "integrity": "sha512-nBXOfJds8OzUT1qUreT/en3eyOXd2EH5b0wr2bVB5999qHdGKkzGzIyKYaKj02lXk6wpN71ltLIaQpu58YFBoQ==", 1117 | "cpu": [ 1118 | "x64" 1119 | ], 1120 | "dev": true, 1121 | "license": "MIT", 1122 | "optional": true, 1123 | "os": [ 1124 | "linux" 1125 | ] 1126 | }, 1127 | "node_modules/@rollup/rollup-win32-arm64-msvc": { 1128 | "version": "4.27.3", 1129 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.3.tgz", 1130 | "integrity": "sha512-ogfbEVQgIZOz5WPWXF2HVb6En+kWzScuxJo/WdQTqEgeyGkaa2ui5sQav9Zkr7bnNCLK48uxmmK0TySm22eiuw==", 1131 | "cpu": [ 1132 | "arm64" 1133 | ], 1134 | "dev": true, 1135 | "license": "MIT", 1136 | "optional": true, 1137 | "os": [ 1138 | "win32" 1139 | ] 1140 | }, 1141 | "node_modules/@rollup/rollup-win32-ia32-msvc": { 1142 | "version": "4.27.3", 1143 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.3.tgz", 1144 | "integrity": "sha512-ecE36ZBMLINqiTtSNQ1vzWc5pXLQHlf/oqGp/bSbi7iedcjcNb6QbCBNG73Euyy2C+l/fn8qKWEwxr+0SSfs3w==", 1145 | "cpu": [ 1146 | "ia32" 1147 | ], 1148 | "dev": true, 1149 | "license": "MIT", 1150 | "optional": true, 1151 | "os": [ 1152 | "win32" 1153 | ] 1154 | }, 1155 | "node_modules/@rollup/rollup-win32-x64-msvc": { 1156 | "version": "4.27.3", 1157 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.3.tgz", 1158 | "integrity": "sha512-vliZLrDmYKyaUoMzEbMTg2JkerfBjn03KmAw9CykO0Zzkzoyd7o3iZNam/TpyWNjNT+Cz2iO3P9Smv2wgrR+Eg==", 1159 | "cpu": [ 1160 | "x64" 1161 | ], 1162 | "dev": true, 1163 | "license": "MIT", 1164 | "optional": true, 1165 | "os": [ 1166 | "win32" 1167 | ] 1168 | }, 1169 | "node_modules/@sapphire/async-queue": { 1170 | "version": "1.5.3", 1171 | "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.3.tgz", 1172 | "integrity": "sha512-x7zadcfJGxFka1Q3f8gCts1F0xMwCKbZweM85xECGI0hBTeIZJGGCrHgLggihBoprlQ/hBmDR5LKfIPqnmHM3w==", 1173 | "peer": true, 1174 | "engines": { 1175 | "node": ">=v14.0.0", 1176 | "npm": ">=7.0.0" 1177 | } 1178 | }, 1179 | "node_modules/@sapphire/shapeshift": { 1180 | "version": "4.0.0", 1181 | "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-4.0.0.tgz", 1182 | "integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==", 1183 | "license": "MIT", 1184 | "peer": true, 1185 | "dependencies": { 1186 | "fast-deep-equal": "^3.1.3", 1187 | "lodash": "^4.17.21" 1188 | }, 1189 | "engines": { 1190 | "node": ">=v16" 1191 | } 1192 | }, 1193 | "node_modules/@sapphire/snowflake": { 1194 | "version": "3.5.3", 1195 | "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz", 1196 | "integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==", 1197 | "peer": true, 1198 | "engines": { 1199 | "node": ">=v14.0.0", 1200 | "npm": ">=7.0.0" 1201 | } 1202 | }, 1203 | "node_modules/@types/estree": { 1204 | "version": "1.0.6", 1205 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", 1206 | "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", 1207 | "dev": true, 1208 | "license": "MIT" 1209 | }, 1210 | "node_modules/@types/json-schema": { 1211 | "version": "7.0.15", 1212 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 1213 | "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 1214 | "dev": true, 1215 | "license": "MIT" 1216 | }, 1217 | "node_modules/@types/node": { 1218 | "version": "22.2.0", 1219 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.2.0.tgz", 1220 | "integrity": "sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ==", 1221 | "peer": true, 1222 | "dependencies": { 1223 | "undici-types": "~6.13.0" 1224 | } 1225 | }, 1226 | "node_modules/@types/resolve": { 1227 | "version": "1.20.2", 1228 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", 1229 | "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", 1230 | "dev": true 1231 | }, 1232 | "node_modules/@types/ws": { 1233 | "version": "8.5.12", 1234 | "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", 1235 | "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", 1236 | "peer": true, 1237 | "dependencies": { 1238 | "@types/node": "*" 1239 | } 1240 | }, 1241 | "node_modules/@vladfrangu/async_event_emitter": { 1242 | "version": "2.4.6", 1243 | "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz", 1244 | "integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==", 1245 | "license": "MIT", 1246 | "peer": true, 1247 | "engines": { 1248 | "node": ">=v14.0.0", 1249 | "npm": ">=7.0.0" 1250 | } 1251 | }, 1252 | "node_modules/acorn": { 1253 | "version": "8.14.0", 1254 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", 1255 | "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", 1256 | "dev": true, 1257 | "license": "MIT", 1258 | "bin": { 1259 | "acorn": "bin/acorn" 1260 | }, 1261 | "engines": { 1262 | "node": ">=0.4.0" 1263 | } 1264 | }, 1265 | "node_modules/acorn-jsx": { 1266 | "version": "5.3.2", 1267 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 1268 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 1269 | "dev": true, 1270 | "license": "MIT", 1271 | "peerDependencies": { 1272 | "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 1273 | } 1274 | }, 1275 | "node_modules/ajv": { 1276 | "version": "6.12.6", 1277 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 1278 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 1279 | "dev": true, 1280 | "license": "MIT", 1281 | "dependencies": { 1282 | "fast-deep-equal": "^3.1.1", 1283 | "fast-json-stable-stringify": "^2.0.0", 1284 | "json-schema-traverse": "^0.4.1", 1285 | "uri-js": "^4.2.2" 1286 | }, 1287 | "funding": { 1288 | "type": "github", 1289 | "url": "https://github.com/sponsors/epoberezkin" 1290 | } 1291 | }, 1292 | "node_modules/ansi-styles": { 1293 | "version": "4.3.0", 1294 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1295 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1296 | "dev": true, 1297 | "dependencies": { 1298 | "color-convert": "^2.0.1" 1299 | }, 1300 | "engines": { 1301 | "node": ">=8" 1302 | }, 1303 | "funding": { 1304 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 1305 | } 1306 | }, 1307 | "node_modules/argparse": { 1308 | "version": "2.0.1", 1309 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 1310 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 1311 | "dev": true, 1312 | "license": "Python-2.0" 1313 | }, 1314 | "node_modules/asynckit": { 1315 | "version": "0.4.0", 1316 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 1317 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 1318 | }, 1319 | "node_modules/axios": { 1320 | "version": "1.7.7", 1321 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", 1322 | "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", 1323 | "license": "MIT", 1324 | "dependencies": { 1325 | "follow-redirects": "^1.15.6", 1326 | "form-data": "^4.0.0", 1327 | "proxy-from-env": "^1.1.0" 1328 | } 1329 | }, 1330 | "node_modules/balanced-match": { 1331 | "version": "1.0.2", 1332 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1333 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1334 | "dev": true, 1335 | "license": "MIT" 1336 | }, 1337 | "node_modules/bottleneck": { 1338 | "version": "2.19.5", 1339 | "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", 1340 | "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", 1341 | "license": "MIT" 1342 | }, 1343 | "node_modules/brace-expansion": { 1344 | "version": "1.1.11", 1345 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1346 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1347 | "dev": true, 1348 | "license": "MIT", 1349 | "dependencies": { 1350 | "balanced-match": "^1.0.0", 1351 | "concat-map": "0.0.1" 1352 | } 1353 | }, 1354 | "node_modules/callsites": { 1355 | "version": "3.1.0", 1356 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 1357 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 1358 | "dev": true, 1359 | "license": "MIT", 1360 | "engines": { 1361 | "node": ">=6" 1362 | } 1363 | }, 1364 | "node_modules/chalk": { 1365 | "version": "4.1.2", 1366 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 1367 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 1368 | "dev": true, 1369 | "dependencies": { 1370 | "ansi-styles": "^4.1.0", 1371 | "supports-color": "^7.1.0" 1372 | }, 1373 | "engines": { 1374 | "node": ">=10" 1375 | }, 1376 | "funding": { 1377 | "url": "https://github.com/chalk/chalk?sponsor=1" 1378 | } 1379 | }, 1380 | "node_modules/color-convert": { 1381 | "version": "2.0.1", 1382 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1383 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1384 | "dev": true, 1385 | "dependencies": { 1386 | "color-name": "~1.1.4" 1387 | }, 1388 | "engines": { 1389 | "node": ">=7.0.0" 1390 | } 1391 | }, 1392 | "node_modules/color-name": { 1393 | "version": "1.1.4", 1394 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1395 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1396 | "dev": true 1397 | }, 1398 | "node_modules/combined-stream": { 1399 | "version": "1.0.8", 1400 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 1401 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 1402 | "dependencies": { 1403 | "delayed-stream": "~1.0.0" 1404 | }, 1405 | "engines": { 1406 | "node": ">= 0.8" 1407 | } 1408 | }, 1409 | "node_modules/commondir": { 1410 | "version": "1.0.1", 1411 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", 1412 | "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", 1413 | "dev": true 1414 | }, 1415 | "node_modules/concat-map": { 1416 | "version": "0.0.1", 1417 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1418 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 1419 | "dev": true, 1420 | "license": "MIT" 1421 | }, 1422 | "node_modules/cross-spawn": { 1423 | "version": "7.0.6", 1424 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", 1425 | "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 1426 | "dev": true, 1427 | "license": "MIT", 1428 | "dependencies": { 1429 | "path-key": "^3.1.0", 1430 | "shebang-command": "^2.0.0", 1431 | "which": "^2.0.1" 1432 | }, 1433 | "engines": { 1434 | "node": ">= 8" 1435 | } 1436 | }, 1437 | "node_modules/debug": { 1438 | "version": "4.3.4", 1439 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1440 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1441 | "dev": true, 1442 | "dependencies": { 1443 | "ms": "2.1.2" 1444 | }, 1445 | "engines": { 1446 | "node": ">=6.0" 1447 | }, 1448 | "peerDependenciesMeta": { 1449 | "supports-color": { 1450 | "optional": true 1451 | } 1452 | } 1453 | }, 1454 | "node_modules/deep-is": { 1455 | "version": "0.1.4", 1456 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 1457 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 1458 | "dev": true 1459 | }, 1460 | "node_modules/deepmerge": { 1461 | "version": "4.3.1", 1462 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", 1463 | "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", 1464 | "dev": true, 1465 | "engines": { 1466 | "node": ">=0.10.0" 1467 | } 1468 | }, 1469 | "node_modules/delayed-stream": { 1470 | "version": "1.0.0", 1471 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 1472 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 1473 | "engines": { 1474 | "node": ">=0.4.0" 1475 | } 1476 | }, 1477 | "node_modules/discord-api-types": { 1478 | "version": "0.37.83", 1479 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.83.tgz", 1480 | "integrity": "sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==", 1481 | "peer": true 1482 | }, 1483 | "node_modules/discord.js": { 1484 | "version": "14.16.3", 1485 | "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.16.3.tgz", 1486 | "integrity": "sha512-EPCWE9OkA9DnFFNrO7Kl1WHHDYFXu3CNVFJg63bfU7hVtjZGyhShwZtSBImINQRWxWP2tgo2XI+QhdXx28r0aA==", 1487 | "license": "Apache-2.0", 1488 | "peer": true, 1489 | "dependencies": { 1490 | "@discordjs/builders": "^1.9.0", 1491 | "@discordjs/collection": "1.5.3", 1492 | "@discordjs/formatters": "^0.5.0", 1493 | "@discordjs/rest": "^2.4.0", 1494 | "@discordjs/util": "^1.1.1", 1495 | "@discordjs/ws": "1.1.1", 1496 | "@sapphire/snowflake": "3.5.3", 1497 | "discord-api-types": "0.37.100", 1498 | "fast-deep-equal": "3.1.3", 1499 | "lodash.snakecase": "4.1.1", 1500 | "tslib": "^2.6.3", 1501 | "undici": "6.19.8" 1502 | }, 1503 | "engines": { 1504 | "node": ">=18" 1505 | }, 1506 | "funding": { 1507 | "url": "https://github.com/discordjs/discord.js?sponsor" 1508 | } 1509 | }, 1510 | "node_modules/discord.js/node_modules/discord-api-types": { 1511 | "version": "0.37.100", 1512 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.100.tgz", 1513 | "integrity": "sha512-a8zvUI0GYYwDtScfRd/TtaNBDTXwP5DiDVX7K5OmE+DRT57gBqKnwtOC5Ol8z0mRW8KQfETIgiB8U0YZ9NXiCA==", 1514 | "license": "MIT", 1515 | "peer": true 1516 | }, 1517 | "node_modules/es-module-lexer": { 1518 | "version": "1.5.0", 1519 | "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.0.tgz", 1520 | "integrity": "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==", 1521 | "dev": true 1522 | }, 1523 | "node_modules/esbuild": { 1524 | "version": "0.24.0", 1525 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", 1526 | "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", 1527 | "dev": true, 1528 | "hasInstallScript": true, 1529 | "license": "MIT", 1530 | "bin": { 1531 | "esbuild": "bin/esbuild" 1532 | }, 1533 | "engines": { 1534 | "node": ">=18" 1535 | }, 1536 | "optionalDependencies": { 1537 | "@esbuild/aix-ppc64": "0.24.0", 1538 | "@esbuild/android-arm": "0.24.0", 1539 | "@esbuild/android-arm64": "0.24.0", 1540 | "@esbuild/android-x64": "0.24.0", 1541 | "@esbuild/darwin-arm64": "0.24.0", 1542 | "@esbuild/darwin-x64": "0.24.0", 1543 | "@esbuild/freebsd-arm64": "0.24.0", 1544 | "@esbuild/freebsd-x64": "0.24.0", 1545 | "@esbuild/linux-arm": "0.24.0", 1546 | "@esbuild/linux-arm64": "0.24.0", 1547 | "@esbuild/linux-ia32": "0.24.0", 1548 | "@esbuild/linux-loong64": "0.24.0", 1549 | "@esbuild/linux-mips64el": "0.24.0", 1550 | "@esbuild/linux-ppc64": "0.24.0", 1551 | "@esbuild/linux-riscv64": "0.24.0", 1552 | "@esbuild/linux-s390x": "0.24.0", 1553 | "@esbuild/linux-x64": "0.24.0", 1554 | "@esbuild/netbsd-x64": "0.24.0", 1555 | "@esbuild/openbsd-arm64": "0.24.0", 1556 | "@esbuild/openbsd-x64": "0.24.0", 1557 | "@esbuild/sunos-x64": "0.24.0", 1558 | "@esbuild/win32-arm64": "0.24.0", 1559 | "@esbuild/win32-ia32": "0.24.0", 1560 | "@esbuild/win32-x64": "0.24.0" 1561 | } 1562 | }, 1563 | "node_modules/escape-string-regexp": { 1564 | "version": "4.0.0", 1565 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1566 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1567 | "dev": true, 1568 | "engines": { 1569 | "node": ">=10" 1570 | }, 1571 | "funding": { 1572 | "url": "https://github.com/sponsors/sindresorhus" 1573 | } 1574 | }, 1575 | "node_modules/eslint": { 1576 | "version": "9.15.0", 1577 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", 1578 | "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", 1579 | "dev": true, 1580 | "license": "MIT", 1581 | "dependencies": { 1582 | "@eslint-community/eslint-utils": "^4.2.0", 1583 | "@eslint-community/regexpp": "^4.12.1", 1584 | "@eslint/config-array": "^0.19.0", 1585 | "@eslint/core": "^0.9.0", 1586 | "@eslint/eslintrc": "^3.2.0", 1587 | "@eslint/js": "9.15.0", 1588 | "@eslint/plugin-kit": "^0.2.3", 1589 | "@humanfs/node": "^0.16.6", 1590 | "@humanwhocodes/module-importer": "^1.0.1", 1591 | "@humanwhocodes/retry": "^0.4.1", 1592 | "@types/estree": "^1.0.6", 1593 | "@types/json-schema": "^7.0.15", 1594 | "ajv": "^6.12.4", 1595 | "chalk": "^4.0.0", 1596 | "cross-spawn": "^7.0.5", 1597 | "debug": "^4.3.2", 1598 | "escape-string-regexp": "^4.0.0", 1599 | "eslint-scope": "^8.2.0", 1600 | "eslint-visitor-keys": "^4.2.0", 1601 | "espree": "^10.3.0", 1602 | "esquery": "^1.5.0", 1603 | "esutils": "^2.0.2", 1604 | "fast-deep-equal": "^3.1.3", 1605 | "file-entry-cache": "^8.0.0", 1606 | "find-up": "^5.0.0", 1607 | "glob-parent": "^6.0.2", 1608 | "ignore": "^5.2.0", 1609 | "imurmurhash": "^0.1.4", 1610 | "is-glob": "^4.0.0", 1611 | "json-stable-stringify-without-jsonify": "^1.0.1", 1612 | "lodash.merge": "^4.6.2", 1613 | "minimatch": "^3.1.2", 1614 | "natural-compare": "^1.4.0", 1615 | "optionator": "^0.9.3" 1616 | }, 1617 | "bin": { 1618 | "eslint": "bin/eslint.js" 1619 | }, 1620 | "engines": { 1621 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1622 | }, 1623 | "funding": { 1624 | "url": "https://eslint.org/donate" 1625 | }, 1626 | "peerDependencies": { 1627 | "jiti": "*" 1628 | }, 1629 | "peerDependenciesMeta": { 1630 | "jiti": { 1631 | "optional": true 1632 | } 1633 | } 1634 | }, 1635 | "node_modules/eslint-scope": { 1636 | "version": "8.2.0", 1637 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", 1638 | "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", 1639 | "dev": true, 1640 | "license": "BSD-2-Clause", 1641 | "dependencies": { 1642 | "esrecurse": "^4.3.0", 1643 | "estraverse": "^5.2.0" 1644 | }, 1645 | "engines": { 1646 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1647 | }, 1648 | "funding": { 1649 | "url": "https://opencollective.com/eslint" 1650 | } 1651 | }, 1652 | "node_modules/eslint-visitor-keys": { 1653 | "version": "3.4.3", 1654 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", 1655 | "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", 1656 | "dev": true, 1657 | "engines": { 1658 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1659 | }, 1660 | "funding": { 1661 | "url": "https://opencollective.com/eslint" 1662 | } 1663 | }, 1664 | "node_modules/eslint/node_modules/eslint-visitor-keys": { 1665 | "version": "4.2.0", 1666 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", 1667 | "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", 1668 | "dev": true, 1669 | "license": "Apache-2.0", 1670 | "engines": { 1671 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1672 | }, 1673 | "funding": { 1674 | "url": "https://opencollective.com/eslint" 1675 | } 1676 | }, 1677 | "node_modules/espree": { 1678 | "version": "10.3.0", 1679 | "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", 1680 | "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", 1681 | "dev": true, 1682 | "license": "BSD-2-Clause", 1683 | "dependencies": { 1684 | "acorn": "^8.14.0", 1685 | "acorn-jsx": "^5.3.2", 1686 | "eslint-visitor-keys": "^4.2.0" 1687 | }, 1688 | "engines": { 1689 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1690 | }, 1691 | "funding": { 1692 | "url": "https://opencollective.com/eslint" 1693 | } 1694 | }, 1695 | "node_modules/espree/node_modules/eslint-visitor-keys": { 1696 | "version": "4.2.0", 1697 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", 1698 | "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", 1699 | "dev": true, 1700 | "license": "Apache-2.0", 1701 | "engines": { 1702 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1703 | }, 1704 | "funding": { 1705 | "url": "https://opencollective.com/eslint" 1706 | } 1707 | }, 1708 | "node_modules/esquery": { 1709 | "version": "1.5.0", 1710 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", 1711 | "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", 1712 | "dev": true, 1713 | "dependencies": { 1714 | "estraverse": "^5.1.0" 1715 | }, 1716 | "engines": { 1717 | "node": ">=0.10" 1718 | } 1719 | }, 1720 | "node_modules/esrecurse": { 1721 | "version": "4.3.0", 1722 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 1723 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 1724 | "dev": true, 1725 | "license": "BSD-2-Clause", 1726 | "dependencies": { 1727 | "estraverse": "^5.2.0" 1728 | }, 1729 | "engines": { 1730 | "node": ">=4.0" 1731 | } 1732 | }, 1733 | "node_modules/estraverse": { 1734 | "version": "5.3.0", 1735 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 1736 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 1737 | "dev": true, 1738 | "engines": { 1739 | "node": ">=4.0" 1740 | } 1741 | }, 1742 | "node_modules/estree-walker": { 1743 | "version": "2.0.2", 1744 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", 1745 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", 1746 | "dev": true 1747 | }, 1748 | "node_modules/esutils": { 1749 | "version": "2.0.3", 1750 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 1751 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 1752 | "dev": true, 1753 | "license": "BSD-2-Clause", 1754 | "engines": { 1755 | "node": ">=0.10.0" 1756 | } 1757 | }, 1758 | "node_modules/fast-deep-equal": { 1759 | "version": "3.1.3", 1760 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 1761 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 1762 | }, 1763 | "node_modules/fast-json-stable-stringify": { 1764 | "version": "2.1.0", 1765 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 1766 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 1767 | "dev": true, 1768 | "license": "MIT" 1769 | }, 1770 | "node_modules/fast-levenshtein": { 1771 | "version": "2.0.6", 1772 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 1773 | "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 1774 | "dev": true 1775 | }, 1776 | "node_modules/file-entry-cache": { 1777 | "version": "8.0.0", 1778 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", 1779 | "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", 1780 | "dev": true, 1781 | "license": "MIT", 1782 | "dependencies": { 1783 | "flat-cache": "^4.0.0" 1784 | }, 1785 | "engines": { 1786 | "node": ">=16.0.0" 1787 | } 1788 | }, 1789 | "node_modules/find-up": { 1790 | "version": "5.0.0", 1791 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 1792 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1793 | "dev": true, 1794 | "dependencies": { 1795 | "locate-path": "^6.0.0", 1796 | "path-exists": "^4.0.0" 1797 | }, 1798 | "engines": { 1799 | "node": ">=10" 1800 | }, 1801 | "funding": { 1802 | "url": "https://github.com/sponsors/sindresorhus" 1803 | } 1804 | }, 1805 | "node_modules/flat-cache": { 1806 | "version": "4.0.1", 1807 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", 1808 | "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", 1809 | "dev": true, 1810 | "license": "MIT", 1811 | "dependencies": { 1812 | "flatted": "^3.2.9", 1813 | "keyv": "^4.5.4" 1814 | }, 1815 | "engines": { 1816 | "node": ">=16" 1817 | } 1818 | }, 1819 | "node_modules/flatted": { 1820 | "version": "3.3.2", 1821 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", 1822 | "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", 1823 | "dev": true, 1824 | "license": "ISC" 1825 | }, 1826 | "node_modules/follow-redirects": { 1827 | "version": "1.15.6", 1828 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", 1829 | "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", 1830 | "funding": [ 1831 | { 1832 | "type": "individual", 1833 | "url": "https://github.com/sponsors/RubenVerborgh" 1834 | } 1835 | ], 1836 | "engines": { 1837 | "node": ">=4.0" 1838 | }, 1839 | "peerDependenciesMeta": { 1840 | "debug": { 1841 | "optional": true 1842 | } 1843 | } 1844 | }, 1845 | "node_modules/form-data": { 1846 | "version": "4.0.0", 1847 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 1848 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 1849 | "dependencies": { 1850 | "asynckit": "^0.4.0", 1851 | "combined-stream": "^1.0.8", 1852 | "mime-types": "^2.1.12" 1853 | }, 1854 | "engines": { 1855 | "node": ">= 6" 1856 | } 1857 | }, 1858 | "node_modules/fsevents": { 1859 | "version": "2.3.3", 1860 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 1861 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1862 | "dev": true, 1863 | "hasInstallScript": true, 1864 | "optional": true, 1865 | "os": [ 1866 | "darwin" 1867 | ], 1868 | "engines": { 1869 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1870 | } 1871 | }, 1872 | "node_modules/function-bind": { 1873 | "version": "1.1.2", 1874 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 1875 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 1876 | "dev": true, 1877 | "funding": { 1878 | "url": "https://github.com/sponsors/ljharb" 1879 | } 1880 | }, 1881 | "node_modules/get-tsconfig": { 1882 | "version": "4.7.3", 1883 | "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", 1884 | "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", 1885 | "dev": true, 1886 | "dependencies": { 1887 | "resolve-pkg-maps": "^1.0.0" 1888 | }, 1889 | "funding": { 1890 | "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" 1891 | } 1892 | }, 1893 | "node_modules/glob-parent": { 1894 | "version": "6.0.2", 1895 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 1896 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 1897 | "dev": true, 1898 | "dependencies": { 1899 | "is-glob": "^4.0.3" 1900 | }, 1901 | "engines": { 1902 | "node": ">=10.13.0" 1903 | } 1904 | }, 1905 | "node_modules/globals": { 1906 | "version": "15.12.0", 1907 | "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz", 1908 | "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==", 1909 | "dev": true, 1910 | "license": "MIT", 1911 | "engines": { 1912 | "node": ">=18" 1913 | }, 1914 | "funding": { 1915 | "url": "https://github.com/sponsors/sindresorhus" 1916 | } 1917 | }, 1918 | "node_modules/has-flag": { 1919 | "version": "4.0.0", 1920 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1921 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1922 | "dev": true, 1923 | "engines": { 1924 | "node": ">=8" 1925 | } 1926 | }, 1927 | "node_modules/hasown": { 1928 | "version": "2.0.2", 1929 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 1930 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 1931 | "dev": true, 1932 | "dependencies": { 1933 | "function-bind": "^1.1.2" 1934 | }, 1935 | "engines": { 1936 | "node": ">= 0.4" 1937 | } 1938 | }, 1939 | "node_modules/ignore": { 1940 | "version": "5.3.2", 1941 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", 1942 | "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", 1943 | "dev": true, 1944 | "license": "MIT", 1945 | "engines": { 1946 | "node": ">= 4" 1947 | } 1948 | }, 1949 | "node_modules/import-fresh": { 1950 | "version": "3.3.0", 1951 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 1952 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 1953 | "dev": true, 1954 | "license": "MIT", 1955 | "dependencies": { 1956 | "parent-module": "^1.0.0", 1957 | "resolve-from": "^4.0.0" 1958 | }, 1959 | "engines": { 1960 | "node": ">=6" 1961 | }, 1962 | "funding": { 1963 | "url": "https://github.com/sponsors/sindresorhus" 1964 | } 1965 | }, 1966 | "node_modules/imurmurhash": { 1967 | "version": "0.1.4", 1968 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 1969 | "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 1970 | "dev": true, 1971 | "engines": { 1972 | "node": ">=0.8.19" 1973 | } 1974 | }, 1975 | "node_modules/is-core-module": { 1976 | "version": "2.13.1", 1977 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", 1978 | "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", 1979 | "dev": true, 1980 | "dependencies": { 1981 | "hasown": "^2.0.0" 1982 | }, 1983 | "funding": { 1984 | "url": "https://github.com/sponsors/ljharb" 1985 | } 1986 | }, 1987 | "node_modules/is-extglob": { 1988 | "version": "2.1.1", 1989 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1990 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1991 | "dev": true, 1992 | "engines": { 1993 | "node": ">=0.10.0" 1994 | } 1995 | }, 1996 | "node_modules/is-glob": { 1997 | "version": "4.0.3", 1998 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1999 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 2000 | "dev": true, 2001 | "dependencies": { 2002 | "is-extglob": "^2.1.1" 2003 | }, 2004 | "engines": { 2005 | "node": ">=0.10.0" 2006 | } 2007 | }, 2008 | "node_modules/is-module": { 2009 | "version": "1.0.0", 2010 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", 2011 | "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", 2012 | "dev": true 2013 | }, 2014 | "node_modules/is-reference": { 2015 | "version": "1.2.1", 2016 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", 2017 | "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", 2018 | "dev": true, 2019 | "dependencies": { 2020 | "@types/estree": "*" 2021 | } 2022 | }, 2023 | "node_modules/isexe": { 2024 | "version": "2.0.0", 2025 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 2026 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 2027 | "dev": true, 2028 | "license": "ISC" 2029 | }, 2030 | "node_modules/js-yaml": { 2031 | "version": "4.1.0", 2032 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 2033 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 2034 | "dev": true, 2035 | "license": "MIT", 2036 | "dependencies": { 2037 | "argparse": "^2.0.1" 2038 | }, 2039 | "bin": { 2040 | "js-yaml": "bin/js-yaml.js" 2041 | } 2042 | }, 2043 | "node_modules/json-buffer": { 2044 | "version": "3.0.1", 2045 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 2046 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 2047 | "dev": true, 2048 | "license": "MIT" 2049 | }, 2050 | "node_modules/json-schema-traverse": { 2051 | "version": "0.4.1", 2052 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 2053 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 2054 | "dev": true, 2055 | "license": "MIT" 2056 | }, 2057 | "node_modules/json-stable-stringify-without-jsonify": { 2058 | "version": "1.0.1", 2059 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 2060 | "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 2061 | "dev": true 2062 | }, 2063 | "node_modules/keyv": { 2064 | "version": "4.5.4", 2065 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 2066 | "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 2067 | "dev": true, 2068 | "license": "MIT", 2069 | "dependencies": { 2070 | "json-buffer": "3.0.1" 2071 | } 2072 | }, 2073 | "node_modules/levn": { 2074 | "version": "0.4.1", 2075 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 2076 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 2077 | "dev": true, 2078 | "dependencies": { 2079 | "prelude-ls": "^1.2.1", 2080 | "type-check": "~0.4.0" 2081 | }, 2082 | "engines": { 2083 | "node": ">= 0.8.0" 2084 | } 2085 | }, 2086 | "node_modules/locate-path": { 2087 | "version": "6.0.0", 2088 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 2089 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 2090 | "dev": true, 2091 | "dependencies": { 2092 | "p-locate": "^5.0.0" 2093 | }, 2094 | "engines": { 2095 | "node": ">=10" 2096 | }, 2097 | "funding": { 2098 | "url": "https://github.com/sponsors/sindresorhus" 2099 | } 2100 | }, 2101 | "node_modules/lodash": { 2102 | "version": "4.17.21", 2103 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 2104 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 2105 | "license": "MIT", 2106 | "peer": true 2107 | }, 2108 | "node_modules/lodash.merge": { 2109 | "version": "4.6.2", 2110 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 2111 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 2112 | "dev": true 2113 | }, 2114 | "node_modules/lodash.snakecase": { 2115 | "version": "4.1.1", 2116 | "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", 2117 | "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", 2118 | "peer": true 2119 | }, 2120 | "node_modules/magic-bytes.js": { 2121 | "version": "1.10.0", 2122 | "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", 2123 | "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==", 2124 | "peer": true 2125 | }, 2126 | "node_modules/magic-string": { 2127 | "version": "0.30.8", 2128 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", 2129 | "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", 2130 | "dev": true, 2131 | "dependencies": { 2132 | "@jridgewell/sourcemap-codec": "^1.4.15" 2133 | }, 2134 | "engines": { 2135 | "node": ">=12" 2136 | } 2137 | }, 2138 | "node_modules/mime-db": { 2139 | "version": "1.52.0", 2140 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 2141 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 2142 | "engines": { 2143 | "node": ">= 0.6" 2144 | } 2145 | }, 2146 | "node_modules/mime-types": { 2147 | "version": "2.1.35", 2148 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 2149 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 2150 | "dependencies": { 2151 | "mime-db": "1.52.0" 2152 | }, 2153 | "engines": { 2154 | "node": ">= 0.6" 2155 | } 2156 | }, 2157 | "node_modules/minimatch": { 2158 | "version": "3.1.2", 2159 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 2160 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 2161 | "dev": true, 2162 | "license": "ISC", 2163 | "dependencies": { 2164 | "brace-expansion": "^1.1.7" 2165 | }, 2166 | "engines": { 2167 | "node": "*" 2168 | } 2169 | }, 2170 | "node_modules/ms": { 2171 | "version": "2.1.2", 2172 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 2173 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 2174 | "dev": true 2175 | }, 2176 | "node_modules/natural-compare": { 2177 | "version": "1.4.0", 2178 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 2179 | "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", 2180 | "dev": true 2181 | }, 2182 | "node_modules/optionator": { 2183 | "version": "0.9.3", 2184 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", 2185 | "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", 2186 | "dev": true, 2187 | "dependencies": { 2188 | "@aashutoshrathi/word-wrap": "^1.2.3", 2189 | "deep-is": "^0.1.3", 2190 | "fast-levenshtein": "^2.0.6", 2191 | "levn": "^0.4.1", 2192 | "prelude-ls": "^1.2.1", 2193 | "type-check": "^0.4.0" 2194 | }, 2195 | "engines": { 2196 | "node": ">= 0.8.0" 2197 | } 2198 | }, 2199 | "node_modules/p-limit": { 2200 | "version": "3.1.0", 2201 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 2202 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 2203 | "dev": true, 2204 | "dependencies": { 2205 | "yocto-queue": "^0.1.0" 2206 | }, 2207 | "engines": { 2208 | "node": ">=10" 2209 | }, 2210 | "funding": { 2211 | "url": "https://github.com/sponsors/sindresorhus" 2212 | } 2213 | }, 2214 | "node_modules/p-locate": { 2215 | "version": "5.0.0", 2216 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 2217 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 2218 | "dev": true, 2219 | "dependencies": { 2220 | "p-limit": "^3.0.2" 2221 | }, 2222 | "engines": { 2223 | "node": ">=10" 2224 | }, 2225 | "funding": { 2226 | "url": "https://github.com/sponsors/sindresorhus" 2227 | } 2228 | }, 2229 | "node_modules/parent-module": { 2230 | "version": "1.0.1", 2231 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 2232 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 2233 | "dev": true, 2234 | "license": "MIT", 2235 | "dependencies": { 2236 | "callsites": "^3.0.0" 2237 | }, 2238 | "engines": { 2239 | "node": ">=6" 2240 | } 2241 | }, 2242 | "node_modules/path-exists": { 2243 | "version": "4.0.0", 2244 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 2245 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 2246 | "dev": true, 2247 | "engines": { 2248 | "node": ">=8" 2249 | } 2250 | }, 2251 | "node_modules/path-key": { 2252 | "version": "3.1.1", 2253 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 2254 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 2255 | "dev": true, 2256 | "license": "MIT", 2257 | "engines": { 2258 | "node": ">=8" 2259 | } 2260 | }, 2261 | "node_modules/path-parse": { 2262 | "version": "1.0.7", 2263 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 2264 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 2265 | "dev": true 2266 | }, 2267 | "node_modules/picomatch": { 2268 | "version": "2.3.1", 2269 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 2270 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 2271 | "dev": true, 2272 | "engines": { 2273 | "node": ">=8.6" 2274 | }, 2275 | "funding": { 2276 | "url": "https://github.com/sponsors/jonschlinkert" 2277 | } 2278 | }, 2279 | "node_modules/prelude-ls": { 2280 | "version": "1.2.1", 2281 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 2282 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 2283 | "dev": true, 2284 | "engines": { 2285 | "node": ">= 0.8.0" 2286 | } 2287 | }, 2288 | "node_modules/proxy-from-env": { 2289 | "version": "1.1.0", 2290 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 2291 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 2292 | }, 2293 | "node_modules/punycode": { 2294 | "version": "2.3.1", 2295 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 2296 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 2297 | "dev": true, 2298 | "license": "MIT", 2299 | "engines": { 2300 | "node": ">=6" 2301 | } 2302 | }, 2303 | "node_modules/resolve": { 2304 | "version": "1.22.8", 2305 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", 2306 | "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", 2307 | "dev": true, 2308 | "dependencies": { 2309 | "is-core-module": "^2.13.0", 2310 | "path-parse": "^1.0.7", 2311 | "supports-preserve-symlinks-flag": "^1.0.0" 2312 | }, 2313 | "bin": { 2314 | "resolve": "bin/resolve" 2315 | }, 2316 | "funding": { 2317 | "url": "https://github.com/sponsors/ljharb" 2318 | } 2319 | }, 2320 | "node_modules/resolve-from": { 2321 | "version": "4.0.0", 2322 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 2323 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 2324 | "dev": true, 2325 | "license": "MIT", 2326 | "engines": { 2327 | "node": ">=4" 2328 | } 2329 | }, 2330 | "node_modules/resolve-pkg-maps": { 2331 | "version": "1.0.0", 2332 | "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", 2333 | "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", 2334 | "dev": true, 2335 | "funding": { 2336 | "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" 2337 | } 2338 | }, 2339 | "node_modules/rollup": { 2340 | "version": "4.27.3", 2341 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.3.tgz", 2342 | "integrity": "sha512-SLsCOnlmGt9VoZ9Ek8yBK8tAdmPHeppkw+Xa7yDlCEhDTvwYei03JlWo1fdc7YTfLZ4tD8riJCUyAgTbszk1fQ==", 2343 | "dev": true, 2344 | "license": "MIT", 2345 | "dependencies": { 2346 | "@types/estree": "1.0.6" 2347 | }, 2348 | "bin": { 2349 | "rollup": "dist/bin/rollup" 2350 | }, 2351 | "engines": { 2352 | "node": ">=18.0.0", 2353 | "npm": ">=8.0.0" 2354 | }, 2355 | "optionalDependencies": { 2356 | "@rollup/rollup-android-arm-eabi": "4.27.3", 2357 | "@rollup/rollup-android-arm64": "4.27.3", 2358 | "@rollup/rollup-darwin-arm64": "4.27.3", 2359 | "@rollup/rollup-darwin-x64": "4.27.3", 2360 | "@rollup/rollup-freebsd-arm64": "4.27.3", 2361 | "@rollup/rollup-freebsd-x64": "4.27.3", 2362 | "@rollup/rollup-linux-arm-gnueabihf": "4.27.3", 2363 | "@rollup/rollup-linux-arm-musleabihf": "4.27.3", 2364 | "@rollup/rollup-linux-arm64-gnu": "4.27.3", 2365 | "@rollup/rollup-linux-arm64-musl": "4.27.3", 2366 | "@rollup/rollup-linux-powerpc64le-gnu": "4.27.3", 2367 | "@rollup/rollup-linux-riscv64-gnu": "4.27.3", 2368 | "@rollup/rollup-linux-s390x-gnu": "4.27.3", 2369 | "@rollup/rollup-linux-x64-gnu": "4.27.3", 2370 | "@rollup/rollup-linux-x64-musl": "4.27.3", 2371 | "@rollup/rollup-win32-arm64-msvc": "4.27.3", 2372 | "@rollup/rollup-win32-ia32-msvc": "4.27.3", 2373 | "@rollup/rollup-win32-x64-msvc": "4.27.3", 2374 | "fsevents": "~2.3.2" 2375 | } 2376 | }, 2377 | "node_modules/rollup-plugin-esbuild": { 2378 | "version": "6.1.1", 2379 | "resolved": "https://registry.npmjs.org/rollup-plugin-esbuild/-/rollup-plugin-esbuild-6.1.1.tgz", 2380 | "integrity": "sha512-CehMY9FAqJD5OUaE/Mi1r5z0kNeYxItmRO2zG4Qnv2qWKF09J2lTy5GUzjJR354ZPrLkCj4fiBN41lo8PzBUhw==", 2381 | "dev": true, 2382 | "license": "MIT", 2383 | "dependencies": { 2384 | "@rollup/pluginutils": "^5.0.5", 2385 | "debug": "^4.3.4", 2386 | "es-module-lexer": "^1.3.1", 2387 | "get-tsconfig": "^4.7.2" 2388 | }, 2389 | "engines": { 2390 | "node": ">=14.18.0" 2391 | }, 2392 | "peerDependencies": { 2393 | "esbuild": ">=0.18.0", 2394 | "rollup": "^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" 2395 | } 2396 | }, 2397 | "node_modules/shebang-command": { 2398 | "version": "2.0.0", 2399 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 2400 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 2401 | "dev": true, 2402 | "license": "MIT", 2403 | "dependencies": { 2404 | "shebang-regex": "^3.0.0" 2405 | }, 2406 | "engines": { 2407 | "node": ">=8" 2408 | } 2409 | }, 2410 | "node_modules/shebang-regex": { 2411 | "version": "3.0.0", 2412 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 2413 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 2414 | "dev": true, 2415 | "license": "MIT", 2416 | "engines": { 2417 | "node": ">=8" 2418 | } 2419 | }, 2420 | "node_modules/strip-json-comments": { 2421 | "version": "3.1.1", 2422 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 2423 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 2424 | "dev": true, 2425 | "license": "MIT", 2426 | "engines": { 2427 | "node": ">=8" 2428 | }, 2429 | "funding": { 2430 | "url": "https://github.com/sponsors/sindresorhus" 2431 | } 2432 | }, 2433 | "node_modules/supports-color": { 2434 | "version": "7.2.0", 2435 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 2436 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 2437 | "dev": true, 2438 | "dependencies": { 2439 | "has-flag": "^4.0.0" 2440 | }, 2441 | "engines": { 2442 | "node": ">=8" 2443 | } 2444 | }, 2445 | "node_modules/supports-preserve-symlinks-flag": { 2446 | "version": "1.0.0", 2447 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 2448 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 2449 | "dev": true, 2450 | "engines": { 2451 | "node": ">= 0.4" 2452 | }, 2453 | "funding": { 2454 | "url": "https://github.com/sponsors/ljharb" 2455 | } 2456 | }, 2457 | "node_modules/ts-mixer": { 2458 | "version": "6.0.4", 2459 | "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", 2460 | "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==", 2461 | "license": "MIT", 2462 | "peer": true 2463 | }, 2464 | "node_modules/tslib": { 2465 | "version": "2.8.1", 2466 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 2467 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 2468 | "license": "0BSD", 2469 | "peer": true 2470 | }, 2471 | "node_modules/type-check": { 2472 | "version": "0.4.0", 2473 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 2474 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 2475 | "dev": true, 2476 | "dependencies": { 2477 | "prelude-ls": "^1.2.1" 2478 | }, 2479 | "engines": { 2480 | "node": ">= 0.8.0" 2481 | } 2482 | }, 2483 | "node_modules/undici": { 2484 | "version": "6.19.8", 2485 | "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz", 2486 | "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==", 2487 | "license": "MIT", 2488 | "peer": true, 2489 | "engines": { 2490 | "node": ">=18.17" 2491 | } 2492 | }, 2493 | "node_modules/undici-types": { 2494 | "version": "6.13.0", 2495 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", 2496 | "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", 2497 | "peer": true 2498 | }, 2499 | "node_modules/uri-js": { 2500 | "version": "4.4.1", 2501 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 2502 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 2503 | "dev": true, 2504 | "license": "BSD-2-Clause", 2505 | "dependencies": { 2506 | "punycode": "^2.1.0" 2507 | } 2508 | }, 2509 | "node_modules/which": { 2510 | "version": "2.0.2", 2511 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 2512 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 2513 | "dev": true, 2514 | "license": "ISC", 2515 | "dependencies": { 2516 | "isexe": "^2.0.0" 2517 | }, 2518 | "bin": { 2519 | "node-which": "bin/node-which" 2520 | }, 2521 | "engines": { 2522 | "node": ">= 8" 2523 | } 2524 | }, 2525 | "node_modules/ws": { 2526 | "version": "8.18.0", 2527 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", 2528 | "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", 2529 | "peer": true, 2530 | "engines": { 2531 | "node": ">=10.0.0" 2532 | }, 2533 | "peerDependencies": { 2534 | "bufferutil": "^4.0.1", 2535 | "utf-8-validate": ">=5.0.2" 2536 | }, 2537 | "peerDependenciesMeta": { 2538 | "bufferutil": { 2539 | "optional": true 2540 | }, 2541 | "utf-8-validate": { 2542 | "optional": true 2543 | } 2544 | } 2545 | }, 2546 | "node_modules/yocto-queue": { 2547 | "version": "0.1.0", 2548 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 2549 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 2550 | "dev": true, 2551 | "engines": { 2552 | "node": ">=10" 2553 | }, 2554 | "funding": { 2555 | "url": "https://github.com/sponsors/sindresorhus" 2556 | } 2557 | } 2558 | } 2559 | } 2560 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@outwalk/discord-backup", 3 | "type": "module", 4 | "version": "0.8.2", 5 | "publishConfig": { 6 | "access": "public" 7 | }, 8 | "description": "A module for backing up and restoring discord servers.", 9 | "main": "dist/index.cjs", 10 | "module": "dist/index.js", 11 | "types": "types/index.d.ts", 12 | "engines": { 13 | "node": ">=16" 14 | }, 15 | "scripts": { 16 | "build": "rollup -c", 17 | "lint": "eslint src", 18 | "prepublishOnly": "npm run lint && npm run build" 19 | }, 20 | "keywords": [ 21 | "discord", 22 | "backup" 23 | ], 24 | "files": [ 25 | "dist", 26 | "types" 27 | ], 28 | "exports": { 29 | ".": { 30 | "types": "./types/index.d.ts", 31 | "import": "./dist/index.js", 32 | "require": "./dist/index.cjs" 33 | } 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/OutwalkStudios/discord-backup.git" 38 | }, 39 | "bugs": { 40 | "url": "https://github.com/OutwalkStudios/discord-backup/issues" 41 | }, 42 | "homepage": "https://github.com/OutwalkStudios/discord-backup#readme", 43 | "author": "Outwalk Studios (https://www.outwalkstudios.com/)", 44 | "license": "MIT", 45 | "devDependencies": { 46 | "@eslint/js": "^9.15.0", 47 | "@rollup/plugin-commonjs": "^28.0.1", 48 | "@rollup/plugin-json": "^6.1.0", 49 | "@rollup/plugin-node-resolve": "^15.3.0", 50 | "esbuild": "^0.24.0", 51 | "eslint": "^9.15.0", 52 | "globals": "^15.12.0", 53 | "rollup": "^4.27.3", 54 | "rollup-plugin-esbuild": "^6.1.1" 55 | }, 56 | "dependencies": { 57 | "axios": "^1.7.7", 58 | "bottleneck": "^2.19.5" 59 | }, 60 | "peerDependencies": { 61 | "discord.js": "^14.x.x" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | import commonjs from "@rollup/plugin-commonjs"; 3 | import json from "@rollup/plugin-json"; 4 | import esbuild from "rollup-plugin-esbuild"; 5 | import { builtinModules } from "module"; 6 | import fs from "fs"; 7 | 8 | const { dependencies } = JSON.parse(fs.readFileSync(new URL("./package.json", import.meta.url))); 9 | 10 | export default { 11 | input: "src/index.js", 12 | output: [ 13 | { file: "dist/index.js", format: "esm" }, 14 | { file: "dist/index.cjs", format: "cjs", exports: "default" }, 15 | ], 16 | plugins: [ 17 | resolve(), 18 | commonjs(), 19 | json(), 20 | esbuild({ target: "node16" }) 21 | ], 22 | external: builtinModules.concat(["discord.js", ...Object.keys(dependencies)]) 23 | }; -------------------------------------------------------------------------------- /src/functions/create.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { ChannelType } from "discord.js"; 3 | import { 4 | fetchChannelPermissions, 5 | fetchTextChannelData, 6 | fetchVoiceChannelData, 7 | fetchStageChannelData, 8 | logStatus 9 | } from "../utils"; 10 | 11 | /* Helper function to check if a channel should be excluded */ 12 | function shouldExcludeChannel(channel, doNotBackupList) { 13 | const channelId = channel.id; 14 | 15 | // If the channel is explicitly listed in the `doNotBackupList`, exclude it. 16 | if (doNotBackupList.includes(channelId)) { 17 | return true; 18 | } 19 | 20 | // If the channel is a category, exclude it if the category itself is listed or all of its children are listed. 21 | if (channel.type === ChannelType.GuildCategory && channel.children) { 22 | const childChannels = channel.children.cache.map(child => child.id); 23 | 24 | // Exclude the entire category if its ID is in the `doNotBackupList`. 25 | if (doNotBackupList.includes(channelId)) { 26 | return true; 27 | } 28 | 29 | // Exclude the category if all of its children are listed in the `doNotBackupList`. 30 | const isChildInDoNotBackup = childChannels.every(childId => doNotBackupList.includes(childId)); 31 | if (isChildInDoNotBackup) { 32 | return true; 33 | } 34 | } 35 | 36 | return false; // By default, do not exclude any channel. 37 | } 38 | 39 | /* Helper function to check if a channel should be included */ 40 | function shouldIncludeChannel(channel, toBackupList) { 41 | const channelId = channel.id; 42 | 43 | // Include if the channel or category is explicitly in the `toBackup` list. 44 | if (toBackupList.includes(channelId)) { 45 | return true; 46 | } 47 | 48 | // If the channel is a category, include it if any of its children are explicitly listed. 49 | if (channel.type === ChannelType.GuildCategory && channel.children) { 50 | const childChannels = channel.children.cache.map(child => child.id); 51 | const isChildInToBackup = childChannels.some(childId => toBackupList.includes(childId)); 52 | if (isChildInToBackup) { 53 | return true; 54 | } 55 | } 56 | 57 | // For individual channels, include their parent category if they are explicitly listed. 58 | if (channel.parent && toBackupList.includes(channelId)) { 59 | return true; 60 | } 61 | 62 | return false; // By default, do not include any channel. 63 | } 64 | 65 | /* returns an array with the banned members of the guild */ 66 | export async function getBans(guild, limiter, options) { 67 | 68 | const bans = await limiter.schedule({ id: "getBans::guild.bans.fetch" }, () => guild.bans.fetch()); 69 | const totalBans = bans.size; 70 | 71 | const result = []; 72 | 73 | let savedBans = 0; 74 | for (const ban of bans.values()) { 75 | const info = `Backed up Ban: User ID: ${ban.user.id}, Reason: ${ban.reason || "No reason provided"}`; 76 | savedBans++; 77 | await logStatus("Bans", savedBans, totalBans, options, info); 78 | 79 | // Add the processed ban to the result array 80 | result.push({ id: ban.user.id, reason: ban.reason }); 81 | } 82 | 83 | return result; 84 | } 85 | 86 | /* returns an array with the members of the guild */ 87 | export async function getMembers(guild, limiter, options) { 88 | 89 | const members = await limiter.schedule({ id: "getMembers::guild.members.fetch" }, () => guild.members.fetch()); 90 | const totalMembers = members.size; 91 | 92 | const result = []; 93 | 94 | let savedMembers = 0; 95 | for (const member of members.values()) { 96 | const info = `Backed up Member: ${member.user.username}#${member.user.discriminator} (ID: ${member.user.id})`; 97 | savedMembers++; 98 | await logStatus("Members", savedMembers, totalMembers, options, info); 99 | 100 | result.push({ 101 | userId: member.user.id, 102 | username: member.user.username, 103 | discriminator: member.user.discriminator, 104 | avatarUrl: member.user.avatarURL(), 105 | joinedTimestamp: member.joinedTimestamp, 106 | roles: member.roles.cache.map((role) => role.id), 107 | bot: member.user.bot 108 | }); 109 | } 110 | 111 | return result; 112 | } 113 | 114 | /* returns an array with the roles of the guild */ 115 | export async function getRoles(guild, limiter, options) { 116 | 117 | const roles = await limiter.schedule({ id: "getRoles::guild.roles.fetch" }, () => guild.roles.fetch()); 118 | 119 | // Filter out managed roles (roles created by bots or integrations) 120 | const filteredRoles = roles.filter((role) => !role.managed).sort((a, b) => b.position - a.position); 121 | const totalRoles = filteredRoles.size; 122 | 123 | const result = []; 124 | 125 | let savedRoles = 0; 126 | 127 | for (const role of filteredRoles.values()) { 128 | const info = `Backed up Role: ${role.name} (ID: ${role.id})`; 129 | savedRoles++; 130 | await logStatus("Roles", savedRoles, totalRoles, options, info); 131 | 132 | result.push({ 133 | oldId: role.id, 134 | name: role.name, 135 | color: role.hexColor, 136 | icon: role.iconURL(), 137 | hoist: role.hoist, 138 | permissions: role.permissions.bitfield.toString(), 139 | mentionable: role.mentionable, 140 | position: role.position, 141 | isEveryone: guild.id == role.id 142 | }); 143 | } 144 | 145 | return result; 146 | } 147 | 148 | /* returns an array with the emojis of the guild */ 149 | export async function getEmojis(guild, limiter, options) { 150 | 151 | const emojis = await limiter.schedule({ id: "getEmojis::guild.emojis.fetch" }, () => guild.emojis.fetch()); 152 | const totalEmojis = emojis.size; 153 | 154 | let savedEmojis = 0; 155 | const collectedEmojis = []; 156 | 157 | for (const emoji of emojis.values()) { 158 | const info = `Backed up Emoji: ${emoji.name} (ID: ${emoji.id})`; 159 | if (emojis.length >= 50) break; 160 | 161 | const data = { name: emoji.name }; 162 | 163 | if (options.saveImages && options.saveImages == "base64") { 164 | const response = await axios.get(emoji.imageURL(), { responseType: "arraybuffer" }); 165 | data.base64 = Buffer.from(response.data, "binary").toString("base64"); 166 | } else { 167 | data.url = emoji.imageURL(); 168 | } 169 | 170 | collectedEmojis.push(data); 171 | savedEmojis++; 172 | await logStatus("Emojis", savedEmojis, totalEmojis, options, info); 173 | } 174 | 175 | return collectedEmojis; 176 | } 177 | 178 | 179 | /* returns an array with the channels of the guild */ 180 | export async function getChannels(guild, limiter, options) { 181 | const channels = await limiter.schedule({ id: "getChannels::guild.channels.fetch" }, () => guild.channels.fetch()); 182 | const collectedChannels = { categories: [], others: [] }; 183 | let totalChannels = 0; 184 | let savedChannels = 0; 185 | 186 | const categories = channels 187 | .filter((channel) => channel.type == ChannelType.GuildCategory) 188 | .sort((a, b) => a.position - b.position) 189 | .toJSON(); 190 | 191 | // Calculate the total number of channels to be processed 192 | totalChannels = channels.filter( 193 | (channel) => channel.type !== ChannelType.GuildCategory 194 | ).size; 195 | 196 | for (let category of categories) { 197 | const categoryData = { name: category.name, permissions: fetchChannelPermissions(category), children: [] }; 198 | 199 | const children = category.children.cache.sort((a, b) => a.position - b.position).toJSON(); 200 | 201 | for (let child of children) { 202 | let channelData; 203 | const info = `Backed up Channel: ${child.name} (Category: ${category.name})`; 204 | 205 | if (child.type == ChannelType.GuildText || child.type == ChannelType.GuildAnnouncement) { 206 | channelData = await fetchTextChannelData(child, options, limiter); 207 | } else if (child.type == ChannelType.GuildVoice) { 208 | channelData = fetchVoiceChannelData(child); 209 | } else if (child.type == ChannelType.GuildStageVoice) { 210 | channelData = await fetchStageChannelData(child, options, limiter); 211 | } else { 212 | console.warn(`Unsupported channel type: ${child.type}`); 213 | } 214 | 215 | if (channelData) { 216 | channelData.oldId = child.id; 217 | categoryData.children.push(channelData); 218 | savedChannels++; 219 | await logStatus("Channels", savedChannels, totalChannels, options, info); 220 | } 221 | } 222 | 223 | collectedChannels.categories.push(categoryData); 224 | } 225 | 226 | const others = channels 227 | .filter((channel) => { 228 | return ( 229 | !channel.parent && 230 | channel.type != ChannelType.GuildCategory && 231 | channel.type != ChannelType.AnnouncementThread && 232 | channel.type != ChannelType.PrivateThread && 233 | channel.type != ChannelType.PublicThread 234 | ); 235 | }) 236 | .sort((a, b) => a.position - b.position) 237 | .toJSON(); 238 | 239 | for (let channel of others) { 240 | let channelData; 241 | const info = `Backed up Channel: ${channel.name}`; 242 | 243 | if (channel.type == ChannelType.GuildText || channel.type == ChannelType.GuildAnnouncement) { 244 | channelData = await fetchTextChannelData(channel, options, limiter); 245 | } else { 246 | channelData = fetchVoiceChannelData(channel); 247 | } 248 | if (channelData) { 249 | channelData.oldId = channel.id; 250 | collectedChannels.others.push(channelData); 251 | savedChannels++; 252 | await logStatus("Channels", savedChannels, totalChannels, options, info); 253 | } 254 | } 255 | 256 | return collectedChannels; 257 | } 258 | 259 | /* Helper function to fetch channels and exclude them based on doNotBackup */ 260 | export async function doNotBackupgetChannels(guild, limiter, options) { 261 | const channels = await limiter.schedule({ id: "getChannels::guild.channels.fetch" }, () => guild.channels.fetch()); 262 | const collectedChannels = { categories: [], others: [] }; 263 | let savedChannels = 0; 264 | 265 | const doNotBackup = options.doNotBackup || []; 266 | const doNotBackupList = doNotBackup.flatMap(item => item.channels || []); 267 | 268 | const categories = channels 269 | .filter((channel) => channel.type === ChannelType.GuildCategory) 270 | .sort((a, b) => a.position - b.position) 271 | .toJSON(); 272 | 273 | // Calculate the total number of channels to be backed up before the backup starts 274 | let totalChannels = 0; 275 | 276 | // Process categories and their children first 277 | for (let category of categories) { 278 | if (shouldExcludeChannel(category, doNotBackupList)) { 279 | continue; 280 | } 281 | 282 | const nonExcludedChildren = category.children.cache 283 | .filter((child) => !shouldExcludeChannel(child, doNotBackupList)) 284 | .sort((a, b) => a.position - b.position) 285 | .toJSON(); 286 | 287 | // Only count the category if it has non-excluded children 288 | if (nonExcludedChildren.length > 0) { 289 | totalChannels += nonExcludedChildren.length; 290 | } 291 | } 292 | 293 | // Process non-categorized channels 294 | const others = channels 295 | .filter((channel) => { 296 | const exclude = shouldExcludeChannel(channel, doNotBackupList); 297 | return ( 298 | !channel.parent && 299 | channel.type !== ChannelType.GuildCategory && 300 | !exclude 301 | ); 302 | }) 303 | .sort((a, b) => a.position - b.position) 304 | .toJSON(); 305 | 306 | totalChannels += others.length; 307 | 308 | // Backup logic for categories 309 | for (let category of categories) { 310 | if (shouldExcludeChannel(category, doNotBackupList)) continue; 311 | 312 | const nonExcludedChildren = category.children.cache 313 | .filter((child) => !shouldExcludeChannel(child, doNotBackupList)) 314 | .sort((a, b) => a.position - b.position) 315 | .toJSON(); 316 | 317 | if (nonExcludedChildren.length === 0) continue; 318 | 319 | const categoryData = { 320 | name: category.name, 321 | permissions: fetchChannelPermissions(category), 322 | children: [] 323 | }; 324 | 325 | for (let child of nonExcludedChildren) { 326 | let channelData; 327 | const info = `Backed up Channel: ${child.name} (Category: ${category.name})`; 328 | 329 | if (child.type === ChannelType.GuildText || child.type === ChannelType.GuildAnnouncement) { 330 | channelData = await fetchTextChannelData(child, options, limiter); 331 | } else if (child.type === ChannelType.GuildVoice) { 332 | channelData = fetchVoiceChannelData(child); 333 | } else if (child.type === ChannelType.GuildStageVoice) { 334 | channelData = await fetchStageChannelData(child, options, limiter); 335 | } else { 336 | console.warn(`[DEBUG] Unsupported channel type: ${child.type} for channel ${child.name}.`); 337 | } 338 | 339 | if (channelData) { 340 | channelData.oldId = child.id; 341 | categoryData.children.push(channelData); 342 | savedChannels++; // Increment only when a channel is backed up 343 | await logStatus("Channels", savedChannels, totalChannels, options, info); 344 | } 345 | } 346 | 347 | // Add category to the list if it has any non-excluded children. 348 | if (categoryData.children.length > 0) { 349 | collectedChannels.categories.push(categoryData); 350 | } 351 | } 352 | 353 | // Backup logic for non-categorized channels 354 | for (let channel of others) { 355 | let channelData; 356 | const info = `Backed up Channel: ${channel.name}`; 357 | 358 | if (channel.type === ChannelType.GuildText || channel.type === ChannelType.GuildAnnouncement) { 359 | channelData = await fetchTextChannelData(channel, options, limiter); 360 | } else if (channel.type === ChannelType.GuildVoice) { 361 | channelData = fetchVoiceChannelData(channel); 362 | } 363 | 364 | if (channelData) { 365 | channelData.oldId = channel.id; 366 | collectedChannels.others.push(channelData); 367 | savedChannels++; // Increment only when a channel is backed up 368 | await logStatus("Channels", savedChannels, totalChannels, options, info); 369 | } 370 | } 371 | 372 | return collectedChannels; 373 | } 374 | 375 | /* returns an array with the channels of the guild */ 376 | export async function toBackupgetChannels(guild, limiter, options) { 377 | const channels = await limiter.schedule({ id: "getChannels::guild.channels.fetch" }, () => guild.channels.fetch()); 378 | const collectedChannels = { categories: [], others: [] }; 379 | let totalChannels = 0; 380 | let savedChannels = 0; 381 | 382 | const toBackup = options.toBackup || []; 383 | const toBackupList = toBackup.flatMap(item => item.channels || []); 384 | 385 | const categories = channels 386 | .filter((channel) => channel.type === ChannelType.GuildCategory) 387 | .sort((a, b) => a.position - b.position) 388 | .toJSON(); 389 | 390 | // Calculate total channels before backup starts 391 | for (let category of categories) { 392 | const includeCategory = shouldIncludeChannel(category, toBackupList); 393 | 394 | if (!includeCategory) { 395 | continue; 396 | } 397 | 398 | // Count only child channels that are explicitly listed or if the entire category is included. 399 | const includedChildren = category.children.cache 400 | .filter((child) => shouldIncludeChannel(child, toBackupList) || toBackupList.includes(category.id)) 401 | .toJSON(); 402 | 403 | totalChannels += includedChildren.length; 404 | } 405 | 406 | // Calculate the number of non-categorized channels to be backed up 407 | const nonCategorizedChannels = channels 408 | .filter((channel) => { 409 | const include = shouldIncludeChannel(channel, toBackupList); 410 | return ( 411 | !channel.parent && 412 | channel.type !== ChannelType.GuildCategory && 413 | include 414 | ); 415 | }) 416 | .toJSON(); 417 | 418 | totalChannels += nonCategorizedChannels.length; 419 | 420 | // Process categories and their children 421 | for (let category of categories) { 422 | const includeCategory = shouldIncludeChannel(category, toBackupList); 423 | 424 | if (!includeCategory) { 425 | continue; 426 | } 427 | 428 | // Include only specified child channels or include all if the category itself is listed. 429 | const includedChildren = category.children.cache 430 | .filter((child) => shouldIncludeChannel(child, toBackupList) || toBackupList.includes(category.id)) 431 | .sort((a, b) => a.position - b.position) 432 | .toJSON(); 433 | 434 | const categoryData = { 435 | name: category.name, 436 | permissions: fetchChannelPermissions(category), 437 | children: [] 438 | }; 439 | 440 | for (let child of includedChildren) { 441 | let channelData; 442 | const info = `Backed up Channel: ${child.name} (Category: ${category.name})`; 443 | 444 | if (child.type === ChannelType.GuildText || child.type === ChannelType.GuildAnnouncement) { 445 | channelData = await fetchTextChannelData(child, options, limiter); 446 | } else if (child.type === ChannelType.GuildVoice) { 447 | channelData = fetchVoiceChannelData(child); 448 | } else if (child.type === ChannelType.GuildStageVoice) { 449 | channelData = await fetchStageChannelData(child, options, limiter); 450 | } else { 451 | console.warn(`[DEBUG] Unsupported channel type: ${child.type} for channel ${child.name}.`); 452 | } 453 | 454 | if (channelData) { 455 | channelData.oldId = child.id; 456 | categoryData.children.push(channelData); 457 | savedChannels++; 458 | await logStatus("Channels", savedChannels, totalChannels, options, info); 459 | } 460 | } 461 | 462 | // Only add the category if there are included children. 463 | if (categoryData.children.length > 0) { 464 | collectedChannels.categories.push(categoryData); 465 | } 466 | } 467 | 468 | // Process non-categorized channels 469 | for (let channel of nonCategorizedChannels) { 470 | let channelData; 471 | const info = `Backed up Channel: ${channel.name}`; 472 | 473 | if (channel.type === ChannelType.GuildText || channel.type === ChannelType.GuildAnnouncement) { 474 | channelData = await fetchTextChannelData(channel, options, limiter); 475 | } else if (channel.type === ChannelType.GuildVoice) { 476 | channelData = fetchVoiceChannelData(channel); 477 | } 478 | 479 | if (channelData) { 480 | channelData.oldId = channel.id; 481 | collectedChannels.others.push(channelData); 482 | savedChannels++; 483 | await logStatus("Channels", savedChannels, totalChannels, options, info); 484 | } 485 | } 486 | 487 | return collectedChannels; 488 | } 489 | 490 | /* returns an array with the guild's automoderation rules */ 491 | export async function getAutoModerationRules(guild, limiter, options) { 492 | 493 | const rules = await limiter.schedule({ id: "getAutoModerationRules::guild.autoModerationRules.fetch" }, () => guild.autoModerationRules.fetch({ cache: false })); 494 | const totalRules = rules.size; 495 | 496 | let savedRules = 0; 497 | const collectedRules = []; 498 | 499 | for (const rule of rules.values()) { 500 | const info = `Backed up AutoModeration Rule: ${rule.name} (ID: ${rule.id})`; 501 | const actions = []; 502 | 503 | for (const action of rule.actions) { 504 | const copyAction = JSON.parse(JSON.stringify(action)); 505 | 506 | if (copyAction.metadata.channelId) { 507 | const channel = guild.channels.cache.get(copyAction.metadata.channelId); 508 | 509 | if (channel) { 510 | copyAction.metadata.channelName = channel.name; 511 | actions.push(copyAction); 512 | } 513 | 514 | } else { 515 | actions.push(copyAction); 516 | } 517 | } 518 | 519 | /* filter out deleted roles and channels due to a potential bug with discord.js */ 520 | const exemptRoles = rule.exemptRoles.filter((role) => role != undefined); 521 | const exemptChannels = rule.exemptChannels.filter((channel) => channel != undefined); 522 | 523 | collectedRules.push({ 524 | name: rule.name, 525 | eventType: rule.eventType, 526 | triggerType: rule.triggerType, 527 | triggerMetadata: rule.triggerMetadata, 528 | actions: actions, 529 | enabled: rule.enabled, 530 | exemptRoles: exemptRoles.map((role) => ({ id: role.id, name: role.name })), 531 | exemptChannels: exemptChannels.map((channel) => ({ id: channel.id, name: channel.name })) 532 | }); 533 | 534 | savedRules++; 535 | await logStatus("Auto Moderation Rules", savedRules, totalRules, options, info); 536 | } 537 | 538 | return collectedRules; 539 | } 540 | 541 | export default { 542 | getBans, 543 | getMembers, 544 | getRoles, 545 | getEmojis, 546 | getChannels, 547 | doNotBackupgetChannels, 548 | toBackupgetChannels, 549 | getAutoModerationRules, 550 | }; 551 | -------------------------------------------------------------------------------- /src/functions/load.js: -------------------------------------------------------------------------------- 1 | import { GuildFeature, ChannelType } from "discord.js"; 2 | import { loadCategory, loadChannel, logStatus } from "../utils"; 3 | 4 | /* Helper function to check if a channel should be excluded */ 5 | function shouldExcludeChannel(channel, doNotLoad) { 6 | const channelId = channel.oldId; 7 | 8 | if (doNotLoad && doNotLoad.length > 0) { 9 | const doNotLoadList = doNotLoad.flatMap(item => item.channels || []); 10 | 11 | // For non-categories, exclude if the channel is in the doNotLoad list 12 | if (!channel.children) { 13 | return doNotLoadList.includes(channelId); 14 | } 15 | 16 | // If this is a category, exclude it if all of its children are in the doNotLoad list 17 | const childChannels = channel.children.map(child => child.oldId || child.id); 18 | const isChildInDoNotLoad = childChannels.every(childId => doNotLoadList.includes(childId)); 19 | return isChildInDoNotLoad; // Exclude category if all of its children are excluded 20 | } 21 | return false; // By default, do not exclude any channel 22 | } 23 | 24 | /* Helper function to check if a category should be excluded based on its children having the specified `parent_id`. */ 25 | function shouldExcludeCategory(category, doNotLoadList) { 26 | // Exclude the category if any of its child channels' parent_id matches an ID in the `doNotLoad` list 27 | const childExcluded = category.children.some(child => doNotLoadList.includes(child.parent_id)); 28 | return childExcluded; 29 | } 30 | 31 | /* restores the guild configuration */ 32 | export async function loadConfig(guild, backup, limiter, options) { 33 | 34 | const tasks = [ 35 | backup.name ? "Name" : null, 36 | backup.iconBase64 || backup.iconURL ? "Icon" : null, 37 | backup.splashBase64 || backup.splashURL ? "Splash" : null, 38 | backup.bannerBase64 || backup.bannerURL ? "Banner" : null, 39 | backup.verificationLevel ? "Verification Level" : null, 40 | backup.defaultMessageNotifications ? "Message Notifications" : null, 41 | backup.explicitContentFilter ? "Explicit Content Filter" : null 42 | ].filter(Boolean); 43 | 44 | let completedTasks = 0; 45 | const totalTasks = tasks.length; 46 | 47 | if (backup.name) { 48 | const info = `Restored Config: Set Name to ${backup.name}`; 49 | await limiter.schedule({ id: "loadConfig::guild.setName" }, () => guild.setName(backup.name)); 50 | completedTasks++; 51 | await logStatus("Config", completedTasks, totalTasks, options, info); 52 | } 53 | 54 | if (backup.iconBase64) { 55 | const info = "Restored Config: Set Icon (Base64)"; 56 | await limiter.schedule({ id: "loadConfig::guild.setIcon" }, () => guild.setIcon(Buffer.from(backup.iconBase64, "base64"))); 57 | completedTasks++; 58 | await logStatus("Config", completedTasks, totalTasks, options, info); 59 | } else if (backup.iconURL) { 60 | const info = "Restored Config: Setting Icon (URL)"; 61 | await limiter.schedule({ id: "loadConfig::guild.setIcon" }, () => guild.setIcon(backup.iconURL)); 62 | completedTasks++; 63 | await logStatus("Config", completedTasks, totalTasks, options, info); 64 | } 65 | 66 | if (backup.splashBase64) { 67 | const info = "Restored Config: Setting Splash (Base64)"; 68 | await limiter.schedule({ id: "loadConfig::guild.setSplash" }, () => guild.setSplash(Buffer.from(backup.splashBase64, "base64"))); 69 | completedTasks++; 70 | await logStatus("Config", completedTasks, totalTasks, options, info); 71 | } else if (backup.splashURL) { 72 | const info = "Restored Config: Setting Splash (URL)"; 73 | await limiter.schedule({ id: "loadConfig::guild.setSplash" }, () => guild.setSplash(backup.splashURL)); 74 | completedTasks++; 75 | await logStatus("Config", completedTasks, totalTasks, options, info); 76 | } 77 | 78 | if (backup.bannerBase64) { 79 | const info = "Restored Config: Setting Banner (Base64)"; 80 | await limiter.schedule({ id: "loadConfig::guild.setBanner" }, () => guild.setBanner(Buffer.from(backup.bannerBase64, "base64"))); 81 | completedTasks++; 82 | await logStatus("Config", completedTasks, totalTasks, options, info); 83 | } else if (backup.bannerURL) { 84 | const info = "Restored Config: Setting Banner (URL)"; 85 | await limiter.schedule({ id: "loadConfig::guild.setBanner" }, () => guild.setBanner(backup.bannerURL)); 86 | completedTasks++; 87 | await logStatus("Config", completedTasks, totalTasks, options, info); 88 | } 89 | 90 | if (backup.verificationLevel) { 91 | const info = `Restored Config: Setting Verification Level to ${backup.verificationLevel}`; 92 | await limiter.schedule({ id: "loadConfig::guild.setVerificationLevel" }, () => guild.setVerificationLevel(backup.verificationLevel)); 93 | completedTasks++; 94 | await logStatus("Config", completedTasks, totalTasks, options, info); 95 | } 96 | 97 | if (backup.defaultMessageNotifications) { 98 | const info = `Restored Config: Setting Default Message Notifications to ${backup.defaultMessageNotifications}`; 99 | await limiter.schedule({ id: "loadConfig::guild.setDefaultMessageNotifications" }, () => guild.setDefaultMessageNotifications(backup.defaultMessageNotifications)); 100 | completedTasks++; 101 | await logStatus("Config", completedTasks, totalTasks, options, info); 102 | } 103 | 104 | const changeableExplicitLevel = guild.features.includes(GuildFeature.Community); 105 | if (backup.explicitContentFilter && changeableExplicitLevel) { 106 | const info = `Restored Config: Setting Explicit Content Filter to ${backup.explicitContentFilter}`; 107 | await limiter.schedule({ id: "loadConfig::guild.setExplicitContentFilter" }, () => guild.setExplicitContentFilter(backup.explicitContentFilter)); 108 | completedTasks++; 109 | await logStatus("Config", completedTasks, totalTasks, options, info); 110 | } 111 | 112 | // Role and channel maps which will get populated later (internal use only): 113 | backup.roleMap = {}; 114 | backup.channelMap = {}; 115 | 116 | } 117 | 118 | /* restore the guild roles */ 119 | export async function loadRoles(guild, backup, limiter, options) { 120 | 121 | const totalRoles = backup.roles.length; 122 | let savedRoles = 0; 123 | 124 | for (let role of backup.roles) { 125 | try { 126 | if (role.isEveryone) { 127 | await limiter.schedule({ id: `loadRoles::guild.roles.edit::everyone` }, () => guild.roles.edit(guild.roles.everyone, { 128 | permissions: BigInt(role.permissions), 129 | mentionable: role.mentionable 130 | })); 131 | backup.roleMap[role.oldId] = guild.roles.everyone; 132 | } else { 133 | const createdRole = await limiter.schedule({ id: `loadRoles::guild.roles.create::${role.name}` }, () => guild.roles.create({ 134 | name: role.name, 135 | color: role.color, 136 | icon: role.icon, 137 | hoist: role.hoist, 138 | permissions: BigInt(role.permissions), 139 | mentionable: role.mentionable, 140 | position: role.position 141 | })); 142 | backup.roleMap[role.oldId] = createdRole; 143 | } 144 | savedRoles++; 145 | const info = `Restored Role: ${role.isEveryone ? "@everyone" : `${role.name} (ID: ${role.oldId})`}`; 146 | await logStatus("Roles", savedRoles, totalRoles, options, info); 147 | } catch (error) { 148 | console.error(error.message); 149 | } 150 | } 151 | 152 | } 153 | 154 | /* restore the guild channels */ 155 | export async function doNotLoadloadChannels(guild, backup, limiter, options) { 156 | const doNotLoad = options.doNotLoad || []; 157 | const doNotLoadList = doNotLoad.flatMap(item => item.channels || []); 158 | 159 | // Calculate the total number of channels to restore based on the doNotLoad list 160 | let totalChannels = 0; 161 | for (let category of backup.channels.categories) { 162 | const nonExcludedChildren = category.children.filter(child => !shouldExcludeChannel(child, doNotLoad)); 163 | if (!shouldExcludeCategory(category, doNotLoadList) && nonExcludedChildren.length > 0) { 164 | totalChannels += nonExcludedChildren.length; 165 | } 166 | } 167 | 168 | // Include non-categorized channels in the total 169 | totalChannels += backup.channels.others.filter(channel => !shouldExcludeChannel(channel, doNotLoad)).length; 170 | 171 | // Ensure totalChannels is at least 1 to avoid division by zero 172 | if (totalChannels === 0) { 173 | totalChannels = 1; 174 | } 175 | 176 | let savedChannels = 0; 177 | 178 | // Restore categories and their child channels 179 | for (let category of backup.channels.categories) { 180 | 181 | const nonExcludedChildren = category.children.filter(child => !shouldExcludeChannel(child, doNotLoad)); 182 | 183 | // Skip creating the category if all of its children are excluded 184 | if (shouldExcludeCategory(category, doNotLoadList) || nonExcludedChildren.length === 0) { 185 | continue; 186 | } 187 | 188 | const createdCategory = await loadCategory(category, guild, limiter); 189 | 190 | for (let channel of nonExcludedChildren) { 191 | try { 192 | const info = `Restored Channel: ${channel.name} (Category: ${category.name})`; 193 | const createdChannel = await loadChannel(channel, guild, createdCategory, options, limiter); 194 | if (createdChannel) { 195 | backup.channelMap[channel.oldId || channel.id] = createdChannel; 196 | savedChannels++; 197 | await logStatus("Channels", savedChannels, totalChannels, options, info); 198 | } 199 | } catch (error) { 200 | console.error(`[ERROR] Error restoring channel ${channel.name}: ${error.message}`); 201 | } 202 | } 203 | } 204 | 205 | // Restore non-categorized channels 206 | for (let channel of backup.channels.others) { 207 | if (shouldExcludeChannel(channel, doNotLoad)) { 208 | continue; 209 | } 210 | 211 | try { 212 | const info = `Restored Non-Categorized Channel: ${channel.name}`; 213 | const createdChannel = await loadChannel(channel, guild, null, options, limiter); 214 | if (createdChannel) { 215 | backup.channelMap[channel.oldId || channel.id] = createdChannel; 216 | savedChannels++; 217 | await logStatus("Channels", savedChannels, totalChannels, options, info); 218 | } 219 | } catch (error) { 220 | console.error(`[ERROR] Error restoring non-categorized channel ${channel.name}: ${error.message}`); 221 | } 222 | } 223 | 224 | // Ensure progress doesn't exceed the total number of channels 225 | if (savedChannels > totalChannels) { 226 | console.warn(`Saved channels (${savedChannels}) exceeded total channels (${totalChannels}). Resetting progress.`); 227 | savedChannels = totalChannels; 228 | await logStatus("Channels", savedChannels, totalChannels, options); 229 | } 230 | } 231 | 232 | /* restore the guild channels */ 233 | export async function toLoadloadChannels(guild, backup, limiter, options) { 234 | const toLoad = options.toLoad || []; 235 | const specifiedChannels = toLoad.flatMap(item => item.channels || []); 236 | 237 | let savedChannels = 0; 238 | 239 | // Helper function to check if a channel should be loaded 240 | function shouldLoadChannel(channelId) { 241 | const result = toLoad.includes("channels") || specifiedChannels.includes(channelId); 242 | return result; 243 | } 244 | 245 | // Helper function to check if a category should be loaded based on its children having the specified `parent_id` or if the category itself is specified. 246 | function shouldLoadCategory(categoryName) { 247 | const result = backup.channels.categories.some(category => 248 | category.name === categoryName && 249 | (category.children.some(child => specifiedChannels.includes(child.parent_id)) || specifiedChannels.includes(category.name)) 250 | ); 251 | return result; 252 | } 253 | 254 | // Calculate total restorable channels 255 | const totalChannels = backup.channels.categories.reduce((count, category) => { 256 | const relevantChildren = category.children.filter(child => shouldLoadChannel(child.oldId) || specifiedChannels.includes(child.parent_id)); 257 | if (shouldLoadCategory(category.name) || relevantChildren.length > 0) { 258 | return count + relevantChildren.length; 259 | } 260 | return count; 261 | }, 0) + backup.channels.others.filter(channel => shouldLoadChannel(channel.oldId)).length; 262 | 263 | // Restore categories and their child channels 264 | for (let category of backup.channels.categories) { 265 | 266 | // Check if the category should be loaded based on its children or if it itself is specified. 267 | const relevantChildren = category.children.filter(child => shouldLoadChannel(child.oldId) || specifiedChannels.includes(child.parent_id)); 268 | 269 | if (!shouldLoadCategory(category.name) && relevantChildren.length === 0) { 270 | continue; 271 | } 272 | 273 | const createdCategory = await loadCategory(category, guild, limiter); 274 | 275 | for (let channel of category.children) { 276 | try { 277 | // Check if this channel should be loaded based on the `parent_id` or if it's directly specified. 278 | if (!shouldLoadChannel(channel.oldId) && !relevantChildren.includes(channel)) { 279 | continue; 280 | } 281 | 282 | const info = `Restored Channel: ${channel.name} (Category: ${category.name})`; 283 | const createdChannel = await loadChannel(channel, guild, createdCategory, options, limiter); 284 | if (createdChannel) { 285 | backup.channelMap[channel.oldId] = createdChannel; 286 | savedChannels++; 287 | await logStatus("Channels", savedChannels, totalChannels, options, info); 288 | } 289 | } catch (error) { 290 | console.error(`Error restoring channel ${channel.name} under category ${category.name}: ${error.message}`); 291 | } 292 | } 293 | } 294 | 295 | // Restore non-categorized channels (channels without a parent) 296 | for (let channel of backup.channels.others) { 297 | try { 298 | 299 | // Check if this channel should be loaded based on `shouldLoadChannel`. 300 | if (!shouldLoadChannel(channel.oldId)) { 301 | continue; 302 | } 303 | 304 | const info = `Restored Channel: ${channel.name}`; 305 | const createdChannel = await loadChannel(channel, guild, null, options, limiter); 306 | if (createdChannel) { 307 | backup.channelMap[channel.oldId] = createdChannel; 308 | savedChannels++; 309 | await logStatus("Channels", savedChannels, totalChannels, options, info); 310 | } 311 | } catch (error) { 312 | console.error(`Error restoring non-categorized channel ${channel.name}: ${error.message}`); 313 | } 314 | } 315 | 316 | // Ensure progress doesn't exceed the total number of channels 317 | if (savedChannels > totalChannels) { 318 | console.warn(`Saved channels (${savedChannels}) exceeded total channels (${totalChannels}). Resetting progress.`); 319 | savedChannels = totalChannels; 320 | await logStatus("Channels", savedChannels, totalChannels, options); 321 | } 322 | } 323 | 324 | 325 | /* restore the automod rules */ 326 | export async function loadAutoModRules(guild, backup, limiter, options) { 327 | 328 | if (backup.autoModerationRules.length === 0) return; 329 | 330 | const roles = await limiter.schedule({ id: "loadAutoModRules::guild.roles.fetch" }, () => guild.roles.fetch()); 331 | const channels = await limiter.schedule({ id: "loadAutoModRules::guild.channels.fetch" }, () => guild.channels.fetch()); 332 | 333 | const autoModRules = await limiter.schedule({ id: "loadAutoModRules::guild.guild.autoModerationRules.fetch" }, () => guild.autoModerationRules.fetch({ cache: false })); 334 | const rules = backup.autoModerationRules.filter((rule) => !autoModRules.values().find((rule) => rule.triggerType == 5) || rule.triggerType != 5); 335 | const totalRules = rules.length; 336 | let savedRules = 0; 337 | 338 | for (const autoModRule of rules) { 339 | const info = `Restored AutoMod Rule: ${autoModRule.name} (ID: ${autoModRule.id})`; 340 | 341 | let actions = []; 342 | for (const action of autoModRule.actions) { 343 | let copyAction = JSON.parse(JSON.stringify(action)); 344 | if (action.metadata.channelName) { 345 | const filteredFirstChannel = channels.filter(channel => channel.name === action.metadata.channelName && backup.channelMap[action.metadata.channelId] === channel).first(); 346 | if (filteredFirstChannel) { 347 | copyAction.metadata.channel = filteredFirstChannel.id; 348 | copyAction.metadata.channelName = null; 349 | actions.push(copyAction); 350 | } 351 | } else { 352 | copyAction.metadata.channel = null; 353 | copyAction.metadata.channelName = null; 354 | actions.push(copyAction); 355 | } 356 | } 357 | 358 | const data = { 359 | name: autoModRule.name, 360 | eventType: autoModRule.eventType, 361 | triggerType: autoModRule.triggerType, 362 | triggerMetadata: autoModRule.triggerMetadata, 363 | actions: actions, 364 | enabled: autoModRule.enabled, 365 | exemptRoles: autoModRule.exemptRoles?.map((exemptRole) => { 366 | const filteredFirstRole = roles.filter(role => role.name === exemptRole.name && backup.roleMap[exemptRole.id] === role).first(); 367 | if (filteredFirstRole) return filteredFirstRole.id; 368 | }), 369 | exemptChannels: autoModRule.exemptChannels?.map((exemptChannel) => { 370 | const filteredFirstChannel = channels.filter(channel => channel.name === exemptChannel.name && backup.channelMap[exemptChannel.id] === channel).first(); 371 | if (filteredFirstChannel) return filteredFirstChannel.id; 372 | }), 373 | }; 374 | 375 | await limiter.schedule({ id: "loadAutoModRules::guild.autoModerationRules.create" }, () => guild.autoModerationRules.create(data)); 376 | savedRules++; 377 | await logStatus("AutoMod Rules", savedRules, totalRules, options, info); 378 | } 379 | 380 | } 381 | 382 | /* restore the afk configuration */ 383 | export async function loadAFk(guild, backup, limiter, options) { 384 | 385 | const totalAFKTasks = backup.afk ? 2 : 0; // 2 tasks: set AFK channel and timeout 386 | let completedAFKTasks = 0; 387 | 388 | if (backup.afk) { 389 | try { 390 | await limiter.schedule({ id: "loadAFK::guild.setAFKChannel" }, () => guild.setAFKChannel(guild.channels.cache.find((channel) => channel.name == backup.afk.name && channel.type == ChannelType.GuildVoice))); 391 | completedAFKTasks++; 392 | const infoChannel = `Set AFK Channel to: ${backup.afk.name}`; 393 | await logStatus("AFK Settings", completedAFKTasks, totalAFKTasks, options, infoChannel); 394 | 395 | await limiter.schedule({ id: "loadAFK::guild.setAFKTimeout" }, () => guild.setAFKTimeout(backup.afk.timeout)); 396 | completedAFKTasks++; 397 | const infoTimeout = `Set AFK Timeout to: ${backup.afk.timeout} seconds`; 398 | await logStatus("AFK Settings", completedAFKTasks, totalAFKTasks, options, infoTimeout); 399 | } catch (error) { 400 | console.error(error.message); 401 | } 402 | } 403 | 404 | } 405 | 406 | /* restore guild emojis */ 407 | export async function loadEmojis(guild, backup, limiter, options) { 408 | 409 | const totalEmojis = backup.emojis.length; 410 | let savedEmojis = 0; 411 | 412 | for (let emoji of backup.emojis) { 413 | try { 414 | const info = `Restored Emoji: ${emoji.name}`; 415 | if (emoji.url) { 416 | await limiter.schedule({ id: `loadEmojis::guild.emojis.create::${emoji.name}` }, () => guild.emojis.create({ name: emoji.name, attachment: emoji.url })); 417 | } else if (emoji.base64) { 418 | await limiter.schedule({ id: `loadEmojis::guild.emojis.create::${emoji.name}` }, () => guild.emojis.create({ name: emoji.name, attachment: Buffer.from(emoji.base64, "base64") })); 419 | } 420 | savedEmojis++; 421 | await logStatus("Emojis", savedEmojis, totalEmojis, options, info); 422 | } catch (error) { 423 | console.error(error.message); 424 | } 425 | } 426 | 427 | } 428 | 429 | /* restore guild bans */ 430 | export async function loadBans(guild, backup, limiter, options) { 431 | 432 | const totalBans = backup.bans.length; 433 | let savedBans = 0; 434 | 435 | for (let ban of backup.bans) { 436 | try { 437 | const info = `Restored Ban: User ID: ${ban.id}`; 438 | await limiter.schedule({ id: `loadBans::guild.members.ban::${ban.id}` }, () => guild.members.ban(ban.id, { reason: ban.reason })); 439 | savedBans++; 440 | await logStatus("Bans", savedBans, totalBans, options, info); 441 | } catch (error) { 442 | console.error(error.message); 443 | } 444 | } 445 | 446 | } 447 | 448 | /* restore embedChannel configuration */ 449 | export async function loadEmbedChannel(guild, backup, limiter, options) { 450 | 451 | const totalEmbedTasks = backup.widget.channel ? 1 : 0; 452 | let completedEmbedTasks = 0; 453 | 454 | if (backup.widget.channel) { 455 | try { 456 | const info = `Restored Embed Channel: ${backup.widget.channel}`; 457 | await limiter.schedule({ id: "loadEmbedChannel::guild.setWidgetSettings" }, () => guild.setWidgetSettings({ 458 | enabled: backup.widget.enabled, 459 | channel: guild.channels.cache.find((channel) => channel.name == backup.widget.channel) 460 | })); 461 | completedEmbedTasks++; 462 | await logStatus("Embed Channel", completedEmbedTasks, totalEmbedTasks, options, info); 463 | } catch (error) { 464 | console.error(error.message); 465 | } 466 | } 467 | 468 | } 469 | 470 | /* restore the guild settings (final part, which requires everything else already restored) */ 471 | export async function loadFinalSettings(guild, backup, limiter, options) { 472 | 473 | const totalFinalTasks = backup.systemChannel ? 2 : 0; // 2 tasks for system channel and boost bar 474 | let completedFinalTasks = 0; 475 | 476 | // System Channel: 477 | if (backup.systemChannel) { 478 | const channels = await limiter.schedule({ id: "loadFinalSettings::guild.channels.fetch" }, () => guild.channels.fetch()); 479 | const filteredFirstChannel = channels.filter(channel => channel.name === backup.systemChannel.name).first(); 480 | 481 | await limiter.schedule({ id: "loadFinalSettings::guild.setSystemChannel" }, () => guild.setSystemChannel(filteredFirstChannel)); 482 | completedFinalTasks++; 483 | const infoSystemChannel = `Restored System Channel: ${backup.systemChannel.name}`; 484 | await logStatus("Final Settings", completedFinalTasks, totalFinalTasks, options, infoSystemChannel); 485 | 486 | await limiter.schedule({ id: "loadFinalSettings::guild.setSystemChannelFlags" }, () => guild.setSystemChannelFlags(backup.systemChannel.flags)); 487 | completedFinalTasks++; 488 | const infoSystemChannelFlags = `Restored System Channel Flags for: ${backup.systemChannel.name}`; 489 | await logStatus("Final Settings", completedFinalTasks, totalFinalTasks, options, infoSystemChannelFlags); 490 | } 491 | 492 | // Boost Progress Bar: 493 | if (backup.premiumProgressBarEnabled) { 494 | const infoBoostBar = "Restored Premium Progress Bar"; 495 | await limiter.schedule({ id: "loadFinalSettings::guild.setPremiumProgressBarEnabled" }, () => guild.setPremiumProgressBarEnabled(backup.premiumProgressBarEnabled)); 496 | completedFinalTasks++; 497 | await logStatus("Final Settings", completedFinalTasks, totalFinalTasks, options, infoBoostBar); 498 | } 499 | 500 | } 501 | 502 | /* restore role assignments to members */ 503 | export async function assignRolesToMembers(guild, backup, limiter, options) { 504 | 505 | const members = await limiter.schedule({ id: "assignRolesToMembers::guild.members.fetch" }, () => guild.members.fetch()); 506 | const totalMembers = backup.members.length; 507 | let processedMembers = 0; 508 | 509 | for (let backupMember of backup.members) { 510 | if (!backupMember.bot) { // Ignore bots 511 | const member = members.get(backupMember.userId); 512 | if (member) { // Backed up member exists in our new guild 513 | const roles = backupMember.roles.map((oldRoleId) => { 514 | const newRole = backup.roleMap[oldRoleId]; 515 | return newRole ? newRole.id : null; 516 | }).filter(roleId => !member.roles.cache.has(roleId)); // Exclude roles the member already has 517 | 518 | if (roles.length > 0) { 519 | const info = `Restored Roles for Member: ${member.user.tag} (ID: ${member.user.id})`; 520 | await limiter.schedule({ id: `assignRolesToMembers::member.edit::${member.id}` }, () => member.edit({ roles: roles })); 521 | 522 | // Log the status update with the info 523 | await logStatus("Assigning Roles", processedMembers + 1, totalMembers, options, info); 524 | } 525 | } 526 | } 527 | processedMembers++; 528 | } 529 | 530 | } 531 | 532 | export default { 533 | loadConfig, 534 | loadRoles, 535 | toLoadloadChannels, 536 | doNotLoadloadChannels, 537 | loadAutoModRules, 538 | loadAFk, 539 | loadEmojis, 540 | loadBans, 541 | loadEmbedChannel, 542 | loadFinalSettings, 543 | assignRolesToMembers 544 | }; 545 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { SnowflakeUtil, IntentsBitField, GatewayIntentBits, GuildMFALevel } from "discord.js"; 2 | import Bottleneck from "bottleneck"; 3 | import axios from "axios"; 4 | import createFunctions from "./functions/create"; 5 | import loadFunctions from "./functions/load"; 6 | import { clearGuild } from "./utils"; 7 | import path from "path"; 8 | import url from "url"; 9 | import fs from "fs"; 10 | 11 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); 12 | 13 | let backups = `${__dirname}/backups`; 14 | if (!fs.existsSync(backups)) fs.mkdirSync(backups); 15 | 16 | /* checks if user has 2fa permissions for 2fa required requests, otherwise warns them */ 17 | function check2FA(options, guild, permission) { 18 | /* skip further processing when 2FA is not required */ 19 | if (guild.mfaLevel == GuildMFALevel.None) return true; 20 | 21 | /* log a warning when an action requires 2FA but 2FA has not been setup on the bot owner */ 22 | if (!guild.client.user.mfaEnabled && !options.ignore2FA) { 23 | console.log(`[WARNING] - 2FA is required by this server in order to backup ${permission}`); 24 | } 25 | 26 | return guild.client.user.mfaEnabled; 27 | } 28 | 29 | /* checks if a backup exists and returns its data */ 30 | async function getBackupData(backupId) { 31 | return new Promise((resolve, reject) => { 32 | const files = fs.readdirSync(backups); 33 | const file = files 34 | .filter((file) => file.split(".").pop() == "json") 35 | .find((file) => file == `${backupId}.json`); 36 | 37 | if (file) { 38 | const backupData = JSON.parse( 39 | fs.readFileSync(`${backups}${path.sep}${file}`) 40 | ); 41 | resolve(backupData); 42 | } else { 43 | reject("No backup found"); 44 | } 45 | }); 46 | } 47 | 48 | /* fetches a backup and returns the information about it */ 49 | async function fetch(backupId) { 50 | try { 51 | const backupData = await getBackupData(backupId); 52 | const size = fs.statSync(`${backups}${path.sep}${backupId}.json`).size; 53 | 54 | return { 55 | data: backupData, 56 | id: backupId, 57 | size: Number((size / 1024).toFixed(2)), 58 | }; 59 | } catch { 60 | throw new Error("No backup found."); 61 | } 62 | } 63 | 64 | /* creates a new backup and saves it to the storage */ 65 | async function create(guild, options = {}) { 66 | 67 | const intents = new IntentsBitField(guild.client.options.intents); 68 | if (!intents.has(GatewayIntentBits.Guilds)) 69 | throw new Error("GUILDS intent is required"); 70 | 71 | // Ensure `toBackup` and `doNotBackup` are not used together 72 | if (options.toBackup && options.doNotBackup) { 73 | throw new Error("You cannot use both 'toBackup' and 'doNotBackup' options at the same time."); 74 | } 75 | 76 | options = { 77 | backupId: null, 78 | maxMessagesPerChannel: 10, 79 | jsonSave: true, 80 | jsonBeautify: false, 81 | doNotBackup: [], 82 | toBackup: [], 83 | backupMembers: false, 84 | saveImages: true, 85 | speed: 250, 86 | concurrency: 45, 87 | verbose: false, 88 | ignore2FA: false, 89 | onStatusChange: null, 90 | ...options, 91 | }; 92 | 93 | const backup = { 94 | name: guild.name, 95 | verificationLevel: guild.verificationLevel, 96 | explicitContentFilter: guild.explicitContentFilter, 97 | systemChannel: guild.systemChannel 98 | ? { name: guild.systemChannel.name, flags: guild.systemChannelFlags } 99 | : null, 100 | premiumProgressBarEnabled: guild.premiumProgressBarEnabled, 101 | defaultMessageNotifications: guild.defaultMessageNotifications, 102 | afk: guild.afkChannel 103 | ? { name: guild.afkChannel.name, timeout: guild.afkTimeout } 104 | : null, 105 | widget: { 106 | enabled: guild.widgetEnabled, 107 | channel: guild.widgetChannel ? guild.widgetChannel.name : null, 108 | }, 109 | autoModerationRules: [], 110 | channels: { categories: [], others: [] }, 111 | roles: [], 112 | bans: [], 113 | emojis: [], 114 | members: [], 115 | createdTimestamp: Date.now(), 116 | messagesPerChannel: options.maxMessagesPerChannel, 117 | guildID: guild.id, 118 | id: options.backupId ?? SnowflakeUtil.generate(Date.now()), 119 | }; 120 | 121 | const limiter = new Bottleneck({ 122 | reservoir: 50, // Maximum number of requests 123 | reservoirRefreshAmount: 50, // Reset reservoir back to 50 124 | reservoirRefreshInterval: 1000, // Refresh every second (1000ms) 125 | maxConcurrent: options.concurrency, // Allow up to `concurrency` tasks at the same time 126 | minTime: options.speed // Control minimum delay between requests 127 | }); 128 | 129 | /* if verbose is enabled, log all tasks at executing and done stages */ 130 | if (options.verbose) { 131 | limiter.on("executing", (jobInfo) => { 132 | console.log(`Executing ${jobInfo.options.id}.`); 133 | }); 134 | 135 | limiter.on("done", (jobInfo) => { 136 | console.log(`Completed ${jobInfo.options.id}.`); 137 | }); 138 | } 139 | 140 | limiter.on("error", async (error) => { 141 | /* ignore errors where the request entity is too large */ 142 | if (error.message == "Request entity too large") return; 143 | 144 | console.error(`ERROR: ${error.message}`); 145 | }); 146 | 147 | limiter.on("failed", (error, jobInfo) => { 148 | /* ignore errors where the request entity is too large */ 149 | if (error.message == "Request entity too large") return; 150 | 151 | console.error(`Job Failed: ${error.message}\nID: ${jobInfo.options.id}`); 152 | }); 153 | 154 | if (check2FA(options, guild, "auto moderation rules")) { 155 | backup.autoModerationRules = await createFunctions.getAutoModerationRules(guild, limiter, options); 156 | } 157 | 158 | if (guild.iconURL()) { 159 | if (options && options.saveImages && options.saveImages == "base64") { 160 | const response = await axios.get(guild.iconURL({ dynamic: true }), { responseType: "arraybuffer" }); 161 | backup.iconBase64 = Buffer.from(response.data, "binary").toString("base64"); 162 | } 163 | 164 | backup.iconURL = guild.iconURL({ dynamic: true }); 165 | } 166 | 167 | if (guild.splashURL()) { 168 | if (options && options.saveImages && options.saveImages == "base64") { 169 | const response = await axios.get(guild.splashURL(), { responseType: "arraybuffer" }); 170 | backup.splashBase64 = Buffer.from(response.data, "binary").toString("base64"); 171 | } 172 | 173 | backup.splashURL = guild.splashURL(); 174 | } 175 | 176 | if (guild.bannerURL()) { 177 | if (options && options.saveImages && options.saveImages == "base64") { 178 | const response = await axios.get(guild.bannerURL(), { responseType: "arraybuffer" }); 179 | backup.bannerBase64 = Buffer.from(response.data, "binary").toString("base64"); 180 | } 181 | 182 | backup.bannerURL = guild.bannerURL(); 183 | } 184 | 185 | // Default behavior if neither toBackup nor doNotBackup is specified 186 | if ((!options.toBackup || options.toBackup.length === 0) && (!options.doNotBackup || options.doNotBackup.length === 0)) { 187 | // Backup everything by default 188 | if (check2FA(options, guild, "bans")) { 189 | backup.bans = await createFunctions.getBans(guild, limiter, options); 190 | } 191 | backup.roles = await createFunctions.getRoles(guild, limiter, options); 192 | backup.emojis = await createFunctions.getEmojis(guild, limiter, options); 193 | backup.channels = await createFunctions.getChannels(guild, limiter, options); 194 | if (options.backupMembers) { 195 | backup.members = await createFunctions.getMembers(guild, limiter, options); 196 | } 197 | } else if (options.doNotBackup.length > 0) { 198 | // Use doNotBackup to exclude backup of specific items. 199 | if (!options.doNotBackup.includes("bans")) { 200 | if (check2FA(options, guild, "bans")) { 201 | backup.bans = await createFunctions.getBans(guild, limiter, options); 202 | } 203 | } 204 | 205 | if (!options.doNotBackup.includes("roles")) { 206 | backup.roles = await createFunctions.getRoles(guild, limiter, options); 207 | } 208 | 209 | if (!options.doNotBackup.includes("emojis")) { 210 | backup.emojis = await createFunctions.getEmojis(guild, limiter, options); 211 | } 212 | 213 | if (!options.doNotBackup.includes("channels")) { 214 | backup.channels = await createFunctions.doNotBackupgetChannels(guild, limiter, options); 215 | } 216 | 217 | if (options.backupMembers) { 218 | backup.members = await createFunctions.getMembers(guild, limiter, options); 219 | } 220 | } else if (options.toBackup.length > 0) { 221 | // Use toBackup to backup specific items. 222 | const toBackupList = options.toBackup; 223 | 224 | if (toBackupList.includes("bans")) { 225 | if (check2FA(options, guild, "bans")) { 226 | backup.bans = await createFunctions.getBans(guild, limiter, options); 227 | } 228 | } 229 | 230 | if (toBackupList.includes("roles")) { 231 | backup.roles = await createFunctions.getRoles(guild, limiter, options); 232 | } 233 | 234 | if (toBackupList.includes("emojis")) { 235 | backup.emojis = await createFunctions.getEmojis(guild, limiter, options); 236 | } 237 | 238 | if (toBackupList.includes("channels")) { 239 | backup.channels = await createFunctions.getChannels(guild, limiter, options); 240 | } 241 | 242 | if (toBackupList.length > 0 && !toBackupList.includes("channels")) { 243 | backup.channels = await createFunctions.toBackupgetChannels(guild, limiter, options); 244 | } 245 | 246 | if (options && options.backupMembers) { 247 | backup.members = await createFunctions.getMembers(guild, limiter, options); 248 | } 249 | } 250 | 251 | if (!options || options.jsonSave == undefined || options.jsonSave) { 252 | const reviver = (key, value) => typeof value === "bigint" ? value.toString() : value; 253 | const backupJSON = options.jsonBeautify ? JSON.stringify(backup, reviver, 4) : JSON.stringify(backup, reviver); 254 | fs.writeFileSync(`${backups}${path.sep}${backup.id}.json`, backupJSON, "utf-8"); 255 | } 256 | 257 | return backup; 258 | } 259 | 260 | /* loads a backup for a guild */ 261 | async function load(backup, guild, options) { 262 | if (!guild) throw new Error("Invalid Guild!"); 263 | 264 | // Ensure `toLoad` and `doNotLoad` are not used together 265 | if (options.toLoad && options.doNotLoad) { 266 | throw new Error("You cannot use both 'toLoad' and 'doNotLoad' options at the same time."); 267 | } 268 | 269 | options = { 270 | clearGuildBeforeRestore: true, 271 | maxMessagesPerChannel: 10, 272 | speed: 250, 273 | concurrency: 45, 274 | doNotLoad: [], 275 | toLoad: [], 276 | verbose: false, 277 | onStatusChange: null, 278 | ...options, 279 | }; 280 | 281 | /* get the backup data from a several possible methods it could be passed into this method */ 282 | const isBackupFromFetch = backup.id && backup.size && backup.data; 283 | const backupData = typeof backup == "string" ? await getBackupData(backup) : isBackupFromFetch ? backup.data : backup; 284 | 285 | if (typeof options.speed != "number") { 286 | throw new Error("Speed option must be a string or number"); 287 | } 288 | 289 | const limiter = new Bottleneck({ 290 | reservoir: 50, // Maximum number of requests 291 | reservoirRefreshAmount: 50, // Reset reservoir back to 50 292 | reservoirRefreshInterval: 1000, // Refresh every second (1000ms) 293 | maxConcurrent: options.concurrency, // Allow up to `concurrency` tasks at the same time 294 | minTime: options.speed // Control minimum delay between requests 295 | }); 296 | 297 | /* if verbose is enabled, log all tasks at executing and done stages */ 298 | if (options.verbose) { 299 | limiter.on("executing", (jobInfo) => { 300 | console.log(`Executing ${jobInfo.options.id}.`); 301 | }); 302 | 303 | limiter.on("done", (jobInfo) => { 304 | console.log(`Completed ${jobInfo.options.id}.`); 305 | }); 306 | } 307 | 308 | limiter.on("error", async (error) => { 309 | /* ignore errors where the request entity is too large */ 310 | if (error.message == "Request entity too large") return; 311 | 312 | console.error(`ERROR: ${error.message}`); 313 | }); 314 | 315 | limiter.on("failed", (error, jobInfo) => { 316 | /* ignore errors where the request entity is too large */ 317 | if (error.message == "Request entity too large") return; 318 | 319 | console.error(`Job Failed: ${error.message}\nID: ${jobInfo.options.id}`); 320 | }); 321 | 322 | // Default behavior if neither toLoad nor doNotLoad is specified 323 | if ((!options.toLoad || options.toLoad.length === 0) && (!options.doNotLoad || options.doNotLoad.length === 0)) { 324 | // Load everything by default 325 | if (options.clearGuildBeforeRestore == undefined || options.clearGuildBeforeRestore) { 326 | await clearGuild(guild, limiter); 327 | } 328 | 329 | await Promise.all([ 330 | loadFunctions.loadConfig(guild, backupData, limiter, options), 331 | loadFunctions.loadBans(guild, backupData, limiter, options), 332 | ]); 333 | 334 | await loadFunctions.loadRoles(guild, backupData, limiter, options); 335 | await loadFunctions.doNotLoadloadChannels(guild, backupData, limiter, options); 336 | 337 | await Promise.all([ 338 | loadFunctions.loadAFk(guild, backupData, limiter, options), 339 | loadFunctions.loadEmbedChannel(guild, backupData, limiter, options), 340 | loadFunctions.loadAutoModRules(guild, backupData, limiter, options), 341 | loadFunctions.loadFinalSettings(guild, backupData, limiter, options), 342 | ]); 343 | 344 | await loadFunctions.assignRolesToMembers(guild, backupData, limiter, options); 345 | await loadFunctions.loadEmojis(guild, backupData, limiter, options); 346 | 347 | } else if (options.doNotLoad.length > 0) { 348 | // Use doNotLoad to exclude load of specific items. 349 | if (!options || !(options.doNotLoad || []).includes("main")) { 350 | if (options.clearGuildBeforeRestore == undefined || options.clearGuildBeforeRestore) { 351 | await clearGuild(guild, limiter); 352 | } 353 | 354 | await Promise.all([ 355 | loadFunctions.loadConfig(guild, backupData, limiter, options), 356 | loadFunctions.loadBans(guild, backupData, limiter, options), 357 | ]); 358 | } 359 | 360 | if (!options || !(options.doNotLoad || []).includes("roles")) { 361 | await loadFunctions.loadRoles(guild, backupData, limiter, options); 362 | } 363 | 364 | if (!options || !(options.doNotLoad || []).includes("channels")) { 365 | await loadFunctions.doNotLoadloadChannels(guild, backupData, limiter, options); 366 | } 367 | 368 | if (!options || !(options.doNotLoad || []).includes("main")) { 369 | await Promise.all([ 370 | loadFunctions.loadAFk(guild, backupData, limiter, options), 371 | loadFunctions.loadEmbedChannel(guild, backupData, limiter, options), 372 | loadFunctions.loadAutoModRules(guild, backupData, limiter, options), 373 | loadFunctions.loadFinalSettings(guild, backupData, limiter, options), 374 | ]); 375 | } 376 | 377 | if (!options || !(options.doNotLoad || []).includes("roleAssignments")) { 378 | await loadFunctions.assignRolesToMembers(guild, backupData, limiter, options); 379 | } 380 | 381 | if (!options || !(options.doNotLoad || []).includes("emojis")) { 382 | await loadFunctions.loadEmojis(guild, backupData, limiter, options); 383 | } 384 | 385 | } else if (options.toLoad.length > 0) { 386 | // Use toLoad to load specific items. 387 | const toLoadList = options.toLoad; 388 | 389 | if (toLoadList.includes("main")) { 390 | if (options.clearGuildBeforeRestore == undefined || options.clearGuildBeforeRestore) { 391 | await clearGuild(guild, limiter); 392 | } 393 | 394 | await Promise.all([ 395 | loadFunctions.loadConfig(guild, backupData, limiter, options), 396 | loadFunctions.loadBans(guild, backupData, limiter, options), 397 | ]); 398 | } 399 | 400 | if (toLoadList.includes("roles")) { 401 | await loadFunctions.loadRoles(guild, backupData, limiter, options); 402 | } 403 | 404 | if (toLoadList && toLoadList.length > 0) { 405 | await loadFunctions.toLoadloadChannels(guild, backupData, limiter, options); 406 | } 407 | 408 | if (toLoadList.includes("main")) { 409 | await Promise.all([ 410 | loadFunctions.loadAFk(guild, backupData, limiter, options), 411 | loadFunctions.loadEmbedChannel(guild, backupData, limiter, options), 412 | loadFunctions.loadAutoModRules(guild, backupData, limiter, options), 413 | loadFunctions.loadFinalSettings(guild, backupData, limiter, options), 414 | ]); 415 | } 416 | 417 | if (toLoadList.includes("roleAssignments")) { 418 | await loadFunctions.assignRolesToMembers(guild, backupData, limiter, options); 419 | } 420 | 421 | if (toLoadList.includes("emojis")) { 422 | await loadFunctions.loadEmojis(guild, backupData, limiter, options); 423 | } 424 | } 425 | 426 | return backupData; 427 | } 428 | 429 | /* removes a backup */ 430 | async function remove(backupId) { 431 | try { 432 | fs.unlinkSync(`${backups}${path.sep}${backupId}.json`); 433 | } catch { 434 | throw new Error("Backup not found"); 435 | } 436 | } 437 | 438 | /* returns the list of all backups */ 439 | function list() { 440 | const files = fs.readdirSync(backups); 441 | return files.map((file) => file.split(".")[0]); 442 | } 443 | 444 | /* change the storage path */ 445 | function setStorageFolder(pathname) { 446 | if (pathname.endsWith(path.sep)) pathname = pathname.substr(0, pathname.length - 1); 447 | 448 | backups = pathname; 449 | if (!fs.existsSync(backups)) fs.mkdirSync(backups); 450 | } 451 | 452 | export default { create, fetch, list, load, remove, setStorageFolder }; 453 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import { 2 | ChannelType, 3 | GuildFeature, 4 | GuildDefaultMessageNotifications, 5 | GuildExplicitContentFilter, 6 | GuildVerificationLevel, 7 | GuildSystemChannelFlags, 8 | OverwriteType, 9 | GuildPremiumTier 10 | } from "discord.js"; 11 | import axios from "axios"; 12 | 13 | const MAX_BITRATE_PER_TIER = { 14 | [GuildPremiumTier.None]: 64000, 15 | [GuildPremiumTier.Tier1]: 128000, 16 | [GuildPremiumTier.Tier2]: 256000, 17 | [GuildPremiumTier.Tier3]: 384000 18 | }; 19 | 20 | /** 21 | * Updates the status after each major step in the backup process. 22 | * Triggers an optionally user-defined callback for custom handling. 23 | */ 24 | export async function logStatus(step, currentStep, totalSteps, options, info = "") { 25 | const percentage = ((currentStep / totalSteps) * 100).toFixed(2) + "%"; 26 | const status = { 27 | step: step, 28 | progress: `${currentStep}/${totalSteps}`, 29 | percentage: percentage, 30 | info: info 31 | }; 32 | 33 | if (options.onStatusChange) { 34 | await options.onStatusChange(status); 35 | } 36 | } 37 | 38 | /* gets the permissions for a channel */ 39 | export function fetchChannelPermissions(channel) { 40 | const permissions = []; 41 | 42 | channel.permissionOverwrites.cache 43 | .filter((permission) => permission.type == OverwriteType.Role) 44 | .forEach((permission) => { 45 | const role = channel.guild.roles.cache.get(permission.id); 46 | if (role) { 47 | permissions.push({ 48 | roleName: role.name, 49 | allow: permission.allow.bitfield.toString(), 50 | deny: permission.deny.bitfield.toString() 51 | }); 52 | } 53 | }); 54 | 55 | return permissions; 56 | } 57 | 58 | /* fetches the voice channel data that is necessary for the backup */ 59 | export function fetchVoiceChannelData(channel) { 60 | return { 61 | type: ChannelType.GuildVoice, 62 | name: channel.name, 63 | bitrate: channel.bitrate, 64 | userLimit: channel.userLimit, 65 | parent: channel.parent ? channel.parent.name : null, 66 | parent_id: channel.parentId || null, 67 | permissions: fetchChannelPermissions(channel) 68 | }; 69 | } 70 | 71 | /* fetches the stage channel data that is necessary for the backup */ 72 | export async function fetchStageChannelData(channel, options, limiter) { 73 | const channelData = { 74 | id: channel.id, 75 | type: ChannelType.GuildStageVoice, 76 | name: channel.name, 77 | nsfw: channel.nsfw, 78 | rateLimitPerUser: channel.rateLimitPerUser, 79 | topic: channel.topic, 80 | bitrate: channel.bitrate, 81 | userLimit: channel.userLimit, 82 | parent: channel.parent ? channel.parent.name : null, 83 | parent_id: channel.parentId || null, 84 | permissions: fetchChannelPermissions(channel), 85 | messages: [] 86 | }; 87 | 88 | try { 89 | channelData.messages = await fetchChannelMessages(channel, options, limiter); 90 | return channelData; 91 | } catch { 92 | return channelData; 93 | } 94 | } 95 | 96 | /* fetches the messages from a channel */ 97 | export async function fetchChannelMessages(channel, options, limiter) { 98 | const messages = []; 99 | const messageCount = isNaN(options.maxMessagesPerChannel) ? 10 : options.maxMessagesPerChannel; 100 | const fetchOptions = { limit: (messageCount < 100) ? messageCount : 100 }; 101 | 102 | let lastMessageId; 103 | let fetchComplete = false; 104 | 105 | while (!fetchComplete) { 106 | if (lastMessageId) fetchOptions.before = lastMessageId; 107 | 108 | const fetched = await limiter.schedule({ id: `fetchChannelMessages::channel.messages.fetch::${channel.id}` }, () => channel.messages.fetch(fetchOptions)); 109 | if (fetched.size == 0) break; 110 | 111 | lastMessageId = fetched.last().id; 112 | 113 | await Promise.all(fetched.map(async (message) => { 114 | if (!message.author || messages.length >= messageCount) { 115 | fetchComplete = true; 116 | return; 117 | } 118 | 119 | // Avoid backing up very long messages (content length > 2000 characters) 120 | if (message.cleanContent.length > 2000) return; 121 | 122 | // Fetch and process attachments (base64 encoding for images) 123 | const files = await Promise.all(message.attachments.map(async (attachment) => { 124 | const fileExtension = attachment.url.split(".").pop().toLowerCase(); 125 | if (["png", "jpg", "jpeg", "jpe", "jif", "jfif", "jfi"].includes(fileExtension) && options.saveImages && options.saveImages == "base64") { 126 | const response = await axios.get(attachment.url, { responseType: "arraybuffer" }); 127 | const buffer = Buffer.from(response.data, "binary").toString("base64"); 128 | return { name: attachment.name, attachment: buffer }; 129 | } 130 | 131 | return { name: attachment.name, attachment: attachment.url }; 132 | })); 133 | 134 | // Store message and attachments together 135 | messages.push({ 136 | oldId: message.id, 137 | userId: message.author.id, 138 | username: message.author.username, 139 | avatar: message.author.displayAvatarURL(), 140 | content: message.cleanContent, 141 | embeds: message.embeds, 142 | components: message.components, 143 | files: files, 144 | pinned: message.pinned, 145 | sentAt: message.createdAt.toISOString() 146 | }); 147 | })); 148 | } 149 | 150 | // Ensure the messages are sorted by their creation time to maintain correct order. 151 | messages.sort((a, b) => new Date(a.sentAt) - new Date(b.sentAt)); 152 | 153 | return messages; 154 | } 155 | 156 | /* fetches the text channel data that is necessary for the backup */ 157 | export async function fetchTextChannelData(channel, options, limiter) { 158 | const channelData = { 159 | id: channel.id, 160 | type: channel.type, 161 | name: channel.name, 162 | nsfw: channel.nsfw, 163 | rateLimitPerUser: channel.type == ChannelType.GuildText ? channel.rateLimitPerUser : undefined, 164 | parent: channel.parent ? channel.parent.name : null, 165 | parent_id: channel.parentId || null, 166 | topic: channel.topic, 167 | permissions: fetchChannelPermissions(channel), 168 | messages: [], 169 | isNews: channel.type == ChannelType.GuildAnnouncement, 170 | threads: [] 171 | }; 172 | 173 | if (channel.threads.cache.size > 0) { 174 | channel.threads.cache.forEach(async (thread) => { 175 | const threadData = { 176 | id: thread.id, 177 | type: thread.type, 178 | name: thread.name, 179 | archived: thread.archived, 180 | autoArchiveDuration: thread.autoArchiveDuration, 181 | locked: thread.locked, 182 | rateLimitPerUser: thread.rateLimitPerUser, 183 | messages: [] 184 | }; 185 | 186 | try { 187 | threadData.messages = await fetchChannelMessages(thread, options, limiter); 188 | channelData.threads.push(threadData); 189 | } catch { 190 | channelData.threads.push(threadData); 191 | } 192 | }); 193 | } 194 | 195 | try { 196 | channelData.messages = await fetchChannelMessages(channel, options, limiter); 197 | return channelData; 198 | } catch { 199 | return channelData; 200 | } 201 | } 202 | 203 | /* creates a category for the guild */ 204 | export async function loadCategory(categoryData, guild, limiter) { 205 | const category = await limiter.schedule({ id: `loadCategory::guild.channels.create::${categoryData.name}` }, () => guild.channels.create({ name: categoryData.name, type: ChannelType.GuildCategory })); 206 | const finalPermissions = []; 207 | 208 | categoryData.permissions.forEach((permission) => { 209 | const role = guild.roles.cache.find((role) => role.name == permission.roleName); 210 | if (role) { 211 | finalPermissions.push({ 212 | id: role.id, 213 | allow: BigInt(permission.allow), 214 | deny: BigInt(permission.deny) 215 | }); 216 | } 217 | }); 218 | 219 | await limiter.schedule({ id: `loadCategory::category.permissionOverwrites.set::${category.name}` }, () => category.permissionOverwrites.set(finalPermissions)); 220 | return category; 221 | } 222 | 223 | /* creates a channel and returns it */ 224 | export async function loadChannel(channelData, guild, category, options, limiter) { 225 | const restoredThreads = new Set(); 226 | 227 | // Function to load messages into a channel 228 | const loadMessages = async (channel, messages, previousWebhook) => { 229 | const webhook = previousWebhook || await limiter.schedule( 230 | { id: `loadMessages::channel.createWebhook::${channel.id}.${channel.name}` }, 231 | () => channel.createWebhook({ name: "MessagesBackup", avatar: channel.client.user.displayAvatarURL() }) 232 | ); 233 | if (!webhook) return; 234 | 235 | // Filter out any messages that are thread names or empty content 236 | messages = messages.filter(message => message.content.length > 0 || message.embeds.length > 0 || message.files.length > 0); 237 | 238 | for (let message of messages) { 239 | if (message.content.length > 2000) continue; // Skip overly long messages 240 | 241 | try { 242 | let sent; 243 | 244 | // Check if the message `oldId` matches any thread `id` in the channel data 245 | const matchingThread = channelData.threads.find(thread => thread.id === message.oldId); 246 | 247 | if (matchingThread) { 248 | // Skip sending this message as it represents a thread title 249 | restoredThreads.add(matchingThread.id); // Mark thread as restored 250 | 251 | // Restore the thread at this moment instead of sending the message 252 | const thread = await limiter.schedule( 253 | { id: `loadChannel::channel.threads.create::${matchingThread.name}` }, 254 | () => channel.threads.create({ 255 | name: matchingThread.name, 256 | autoArchiveDuration: matchingThread.autoArchiveDuration 257 | }) 258 | ); 259 | 260 | // Restore the messages of the thread using the webhook 261 | await loadMessages(thread, matchingThread.messages, webhook); 262 | 263 | } else if (message.userId === channel.client.user.id) { 264 | // If the message was sent by the client user, restore as a normal message 265 | sent = await limiter.schedule( 266 | { id: `loadMessages::channel.send::${channel.id}.${channel.name}` }, 267 | () => channel.send({ 268 | content: message.content.length ? message.content : undefined, 269 | embeds: message.embeds, 270 | components: message.components, 271 | files: message.files, 272 | allowedMentions: options.allowedMentions 273 | }) 274 | ); 275 | } else { 276 | // Otherwise, send the message using the webhook 277 | sent = await limiter.schedule( 278 | { id: `loadMessages::webhook.send::${channel.id}.${channel.name}` }, 279 | () => webhook.send({ 280 | content: message.content.length ? message.content : undefined, 281 | username: message.username, 282 | avatarURL: message.avatar, 283 | embeds: message.embeds, 284 | components: message.components, 285 | files: message.files, 286 | allowedMentions: options.allowedMentions, 287 | threadId: channel.isThread() ? channel.id : undefined 288 | }) 289 | ); 290 | } 291 | 292 | // Pin the message if it was originally pinned 293 | if (message.pinned && sent) { 294 | await limiter.schedule( 295 | { id: `loadMessages::sent.pin::${channel.id}.${channel.name}` }, 296 | () => sent.pin() 297 | ); 298 | } 299 | } catch (error) { 300 | if (error.message === "Request entity too large") return; // Handle large entity errors 301 | console.error(error); 302 | } 303 | } 304 | 305 | return webhook; 306 | }; 307 | 308 | // Create the channel object 309 | const createOptions = { name: channelData.name, type: null, parent: category }; 310 | 311 | // Determine the type of the channel 312 | if (channelData.type == ChannelType.GuildText || channelData.type == ChannelType.GuildAnnouncement) { 313 | createOptions.topic = channelData.topic; 314 | createOptions.nsfw = channelData.nsfw; 315 | createOptions.rateLimitPerUser = channelData.rateLimitPerUser; 316 | createOptions.type = channelData.isNews && guild.features.includes("NEWS") ? ChannelType.GuildAnnouncement : ChannelType.GuildText; 317 | } 318 | 319 | else if (channelData.type == ChannelType.GuildVoice) { 320 | let bitrate = channelData.bitrate; 321 | const bitrates = Object.values(MAX_BITRATE_PER_TIER); 322 | 323 | /* make sure the bitrate set is allowed by the guild premium tier */ 324 | while (bitrate > MAX_BITRATE_PER_TIER[guild.premiumTier]) { 325 | bitrate = bitrates[guild.premiumTier]; 326 | } 327 | 328 | createOptions.bitrate = bitrate; 329 | createOptions.userLimit = channelData.userLimit; 330 | createOptions.type = channelData.type; 331 | } 332 | 333 | else if (channelData.type == ChannelType.GuildStageVoice) { 334 | let bitrate = channelData.bitrate; 335 | const bitrates = Object.values(MAX_BITRATE_PER_TIER); 336 | 337 | /* make sure the bitrate set is allowed by the guild premium tier */ 338 | while (bitrate > MAX_BITRATE_PER_TIER[guild.premiumTier]) { 339 | bitrate = bitrates[guild.premiumTier]; 340 | } 341 | 342 | createOptions.topic = channelData.topic; 343 | createOptions.nsfw = channelData.nsfw; 344 | createOptions.bitrate = bitrate; 345 | createOptions.userLimit = channelData.userLimit; 346 | createOptions.type = channelData.type; 347 | 348 | /* Stage channels require the server to have Community features. */ 349 | if (!guild.features.includes(GuildFeature.Community)) return null; 350 | createOptions.type = ChannelType.GuildStageVoice; 351 | } 352 | 353 | const channel = await limiter.schedule( 354 | { id: `loadChannel::guild.channels.create::${channelData.name}` }, 355 | () => guild.channels.create(createOptions) 356 | ); 357 | 358 | // Set channel permissions 359 | const finalPermissions = []; 360 | channelData.permissions.forEach(permission => { 361 | const role = guild.roles.cache.find(role => role.name == permission.roleName); 362 | if (role) { 363 | finalPermissions.push({ 364 | id: role.id, 365 | allow: BigInt(permission.allow), 366 | deny: BigInt(permission.deny) 367 | }); 368 | } 369 | }); 370 | await limiter.schedule( 371 | { id: `loadChannel::channel.permissionOverwrites.set::${channel.name}` }, 372 | () => channel.permissionOverwrites.set(finalPermissions) 373 | ); 374 | 375 | // Restore messages and threads in text channels 376 | if (channelData.type == ChannelType.GuildText) { 377 | let webhook; 378 | 379 | if (channelData.messages.length > 0) { 380 | webhook = await loadMessages(channel, channelData.messages); 381 | } 382 | 383 | if (channelData.threads.length > 0) { 384 | for (let threadData of channelData.threads) { 385 | if (restoredThreads.has(threadData.id)) continue; // Prevent restoring the thread multiple times 386 | 387 | const thread = await limiter.schedule( 388 | { id: `loadChannel::channel.threads.create::${threadData.name}` }, 389 | () => channel.threads.create({ 390 | name: threadData.name, 391 | autoArchiveDuration: threadData.autoArchiveDuration 392 | }) 393 | ); 394 | 395 | if (webhook) await loadMessages(thread, threadData.messages, webhook); 396 | } 397 | } 398 | } 399 | 400 | return channel; 401 | } 402 | 403 | /* delete all roles, channels, emojis, etc of a guild */ 404 | export async function clearGuild(guild, limiter) { 405 | const roles = guild.roles.cache.filter((role) => !role.managed && role.editable && role.id != guild.id); 406 | roles.forEach(async (role) => await limiter.schedule({ id: `clearGuild::role.delete::${role.id}` }, () => role.delete().catch((error) => console.error(`Error occurred while deleting roles: ${error.message}`)))); 407 | 408 | guild.channels.cache.forEach(async (channel) => { 409 | if (channel?.deletable) { 410 | await limiter.schedule({ id: `clearGuild::channel.delete::${channel.id}` }, () => channel.delete().catch((error) => console.error(`Error occurred while deleting channels: ${error.message}`))); 411 | } 412 | }); 413 | 414 | guild.emojis.cache.forEach(async (emoji) => await limiter.schedule({ id: `clearGuild::emoji.delete::${emoji.id}` }, () => emoji.delete().catch((error) => console.error(`Error occurred while deleting emojis: ${error.message}`)))); 415 | 416 | const webhooks = await limiter.schedule({ id: "clearGuild::guild.fetchWebhooks" }, () => guild.fetchWebhooks()); 417 | webhooks.forEach(async (webhook) => await limiter.schedule({ id: `clearGuild::webhook.delete::${webhook.id}` }, () => webhook.delete().catch((error) => console.error(`Error occurred while deleting webhooks: ${error.message}`)))); 418 | 419 | const bans = await limiter.schedule({ id: "clearGuild::guild.bans.fetch" }, () => guild.bans.fetch()); 420 | bans.forEach(async (ban) => await limiter.schedule({ id: `clearGuild::guild.members.unban::${ban.user.id}` }, () => guild.members.unban(ban.user).catch((error) => console.error(`Error occurred while deleting bans: ${error.message}`)))); 421 | 422 | await limiter.schedule({ id: "clearGuild::guild.setAFKChannel" }, () => guild.setAFKChannel(null)); 423 | await limiter.schedule({ id: "clearGuild::guild.setAFKTimeout" }, () => guild.setAFKTimeout(60 * 5)); 424 | await limiter.schedule({ id: "clearGuild::guild.setIcon" }, () => guild.setIcon(null)); 425 | await limiter.schedule({ id: "clearGuild::guild.setBanner" }, () => guild.setBanner(null)); 426 | await limiter.schedule({ id: "clearGuild::guild.setSplash" }, () => guild.setSplash(null)); 427 | await limiter.schedule({ id: "clearGuild::guild.setDefaultMessageNotifications" }, () => guild.setDefaultMessageNotifications(GuildDefaultMessageNotifications.OnlyMentions)); 428 | await limiter.schedule({ id: "clearGuild::guild.setWidgetSettings" }, () => guild.setWidgetSettings({ enabled: false, channel: null })); 429 | 430 | if (!guild.features.includes(GuildFeature.Community)) { 431 | await limiter.schedule({ id: "clearGuild::guild.setExplicitContentFilter" }, () => guild.setExplicitContentFilter(GuildExplicitContentFilter.Disabled)); 432 | await limiter.schedule({ id: "clearGuild::guild.setVerificationLevel" }, () => guild.setVerificationLevel(GuildVerificationLevel.None)); 433 | } 434 | 435 | await limiter.schedule({ id: "clearGuild::guild.setSystemChannel" }, () => guild.setSystemChannel(null)); 436 | await limiter.schedule({ id: "clearGuild::guild.setSystemChannelFlags" }, () => guild.setSystemChannelFlags([ 437 | GuildSystemChannelFlags.SuppressGuildReminderNotifications, 438 | GuildSystemChannelFlags.SuppressJoinNotifications, 439 | GuildSystemChannelFlags.SuppressPremiumSubscriptions 440 | ])); 441 | 442 | await limiter.schedule({ id: "clearGuild::guild.setPremiumProgressBarEnabled" }, () => guild.setPremiumProgressBarEnabled(false)); 443 | 444 | const rules = await limiter.schedule({ id: "clearGuild::guild.autoModerationRules.fetch" }, () => guild.autoModerationRules.fetch()); 445 | rules.forEach(async (rule) => await limiter.schedule({ id: `clearGuild::rule.delete::${rule.id}` }, () => rule.delete().catch((error) => console.error(`Error occurred while deleting automod rules: ${error.message}`)))); 446 | } -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | Guild, 3 | GuildVerificationLevel, 4 | GuildExplicitContentFilter, 5 | GuildDefaultMessageNotifications, 6 | Snowflake, 7 | TextBasedChannelTypes, 8 | VoiceBasedChannelTypes, 9 | ThreadChannelType, 10 | Embed, 11 | MessageComponent, 12 | ThreadAutoArchiveDuration, 13 | SystemChannelFlagsResolvable, 14 | AutoModerationRuleEventType, 15 | AutoModerationRuleTriggerType, 16 | AutoModerationTriggerMetadata, 17 | AutoModerationActionType 18 | } from "discord.js"; 19 | 20 | export declare interface CreateOptions { 21 | backupId?: string; 22 | maxMessagesPerChannel?: number; 23 | jsonSave?: boolean; 24 | jsonBeautify?: boolean; 25 | doNotBackup?: (string | { channels: string[] })[]; 26 | toBackup?: (string | { channels: string[] })[]; 27 | backupMembers?: boolean; 28 | saveImages?: boolean | string; 29 | speed?: number; 30 | concurrency?: number; 31 | verbose?: boolean; 32 | ignore2FA?: boolean; 33 | onStatusChange?: (status: BackupStatus) => void; 34 | } 35 | 36 | export declare interface LoadOptions { 37 | clearGuildBeforeRestore?: boolean; 38 | maxMessagesPerChannel?: number; 39 | speed?: number; 40 | concurrency?: number; 41 | verbose?: boolean; 42 | doNotLoad?: (string | { channels: string[] })[]; 43 | toLoad?: (string | { channels: string[] })[]; 44 | onStatusChange?: (status: BackupStatus) => void; 45 | } 46 | 47 | export declare interface SystemChannelData { 48 | name: string; 49 | flags: SystemChannelFlagsResolvable; 50 | } 51 | 52 | export declare interface AfkData { 53 | name: string; 54 | timeout: number; 55 | } 56 | 57 | export declare interface WidgetData { 58 | enabled: boolean; 59 | channel?: string; 60 | 61 | } 62 | 63 | export declare interface ChannelPermissionsData { 64 | roleName: string; 65 | allow: string; 66 | deny: string; 67 | } 68 | 69 | export declare interface BaseChannelData { 70 | oldId: string; 71 | type: TextBasedChannelTypes | VoiceBasedChannelTypes | ThreadChannelType; 72 | name: string; 73 | parent?: string; 74 | permissions: ChannelPermissionsData[]; 75 | } 76 | 77 | export declare interface MessageData { 78 | userId?: string; 79 | username: string; 80 | avatar?: string; 81 | content?: string; 82 | embeds?: Embed[]; 83 | components?: MessageComponent[]; 84 | files?: Object; 85 | pinned?: boolean; 86 | sentAt: string; 87 | } 88 | 89 | export declare interface ThreadChannelData { 90 | type: ThreadChannelType; 91 | name: string; 92 | archived: boolean; 93 | autoArchiveDuration: ThreadAutoArchiveDuration; 94 | locked: boolean; 95 | rateLimitPerUser: number; 96 | messages: MessageData[]; 97 | } 98 | 99 | export declare interface TextChannelData extends BaseChannelData { 100 | nsfw: boolean; 101 | parent?: string; 102 | topic?: string; 103 | rateLimitPerUser?: number; 104 | isNews: boolean; 105 | messages: MessageData[]; 106 | threads: ThreadChannelData[]; 107 | } 108 | 109 | export declare interface VoiceChannelData extends BaseChannelData { 110 | bitrate: number; 111 | userLimit: number; 112 | } 113 | 114 | export declare interface CategoryData { 115 | name: string; 116 | permissions: ChannelPermissionsData[]; 117 | children: Array; 118 | } 119 | 120 | export declare interface ChannelsData { 121 | categories: CategoryData[]; 122 | others: Array; 123 | } 124 | 125 | export declare interface RoleData { 126 | oldId: string; 127 | name: string; 128 | color: `#${string}`; 129 | icon: string; 130 | hoist: boolean; 131 | permissions: string; 132 | mentionable: boolean; 133 | position: number; 134 | isEveryone: boolean; 135 | } 136 | 137 | export declare interface BanData { 138 | id: Snowflake; 139 | reason: string; 140 | } 141 | 142 | export declare interface EmojiData { 143 | name: string; 144 | url?: string; 145 | base64?: string; 146 | } 147 | 148 | export declare interface MemberData { 149 | userId: string; 150 | username: string; 151 | discriminator: string; 152 | avatarUrl: string; 153 | joinedTimestamp: number; 154 | roles: string[]; 155 | bot: boolean; 156 | } 157 | 158 | export declare interface AutoModerationActionMetadataData { // Same as AutoModerationActionMetadata + channelName 159 | channelId?: Snowflake; 160 | channelName?: string; 161 | durationSeconds?: number; 162 | customMessage?: string; 163 | } 164 | 165 | export declare interface AutoModRuleActionData { 166 | type: AutoModerationActionType; 167 | metadata: AutoModerationActionMetadataData; 168 | } 169 | 170 | export declare interface ExemptRoleData { 171 | id: string; 172 | name: string; 173 | } 174 | 175 | export declare interface ExemptChannelData { 176 | id: string; 177 | name: string; 178 | } 179 | 180 | export declare interface AutoModRuleData { 181 | name: string; 182 | eventType: AutoModerationRuleEventType; 183 | triggerType: AutoModerationRuleTriggerType; 184 | triggerMetadata: AutoModerationTriggerMetadata; 185 | actions: AutoModRuleActionData[]; 186 | enabled: boolean; 187 | exemptRoles: ExemptRoleData[], 188 | exemptChannels: ExemptChannelData[], 189 | } 190 | 191 | export declare interface BackupData { 192 | name: string; 193 | iconURL?: string; 194 | iconBase64?: string; 195 | verificationLevel: GuildVerificationLevel; 196 | explicitContentFilter: GuildExplicitContentFilter; 197 | defaultMessageNotifications: GuildDefaultMessageNotifications | number; 198 | systemChannel?: SystemChannelData; 199 | afk?: AfkData; 200 | premiumProgressBarEnabled?: boolean; 201 | widget: WidgetData; 202 | splashURL?: string; 203 | splashBase64?: string; 204 | bannerURL?: string; 205 | bannerBase64?: string; 206 | autoModerationRules: AutoModRuleData[]; 207 | channels: ChannelsData; 208 | roles: RoleData[]; 209 | bans: BanData[]; 210 | emojis: EmojiData[]; 211 | members: MemberData[]; 212 | createdTimestamp: number; 213 | messagesPerChannel?: number; 214 | guildID: string; 215 | id: Snowflake; 216 | } 217 | 218 | export declare interface BackupInfo { 219 | data: BackupData, 220 | id: string, 221 | size: number 222 | } 223 | 224 | export declare function fetch(backupId: string): Promise; 225 | export declare function create(guild: Guild, options?: CreateOptions): Promise; 226 | export declare function load(backup: Object, guild: Guild, options?: LoadOptions): Promise; 227 | export declare function remove(backupId: string): Promise; 228 | export declare function list(): string[]; 229 | export declare function setStorageFolder(pathname: string): void; --------------------------------------------------------------------------------