UNPKG

discord-backup

Version:

A complete framework to facilitate server backup using discord.js v14 with rate limiting and error handling

375 lines (303 loc) 13.8 kB
# Discord Backup [![downloadsBadge](https://img.shields.io/npm/dt/discord-backup?style=for-the-badge)](https://npmjs.com/discord-backup) [![versionBadge](https://img.shields.io/npm/v/discord-backup?style=for-the-badge)](https://npmjs.com/discord-backup) **Note**: this module now supports Discord.js v14.20+ with advanced rate limiting and error handling. Discord Backup is a powerful [Node.js](https://nodejs.org) module that allows you to easily manage discord server backups. * Unlimited backups! * Backup creation takes less than 10 seconds! * Even restores messages with webhooks! * And restores everything that is possible to restore (channels, roles, permissions, bans, emojis, name, icon, and more!) ## Changelog v3.4.0 ### New Features * ✅ **Discord.js v14.20+ Support** - Fully compatible with latest Discord.js * ✅ **Advanced Rate Limiting** - Built-in rate limiting to prevent API rate limit errors * ✅ **Enhanced Error Handling** - Better error catching and recovery mechanisms * ✅ **Memory Optimization** - Improved memory usage for large servers * ✅ **Retry Logic** - Automatic retry for failed operations with exponential backoff * ✅ **File Size Limits** - Automatic file size checking to prevent oversized attachments * ✅ **Emoji Limits** - Respects server premium tier emoji limits * ✅ **Sequential Processing** - Controlled message/role/channel creation to avoid rate limits **[How to migrate to v14.20.0](#technical-changes-v14200-migration)** ### Previous Changes * Supports base64 for emojis/icon/banner backup * New option to save backups in your own database * `backup#delete()` removed in favor of `backup#remove()` ## Installation ```js npm install --save discord-backup ``` ## Examples You can read this example bot on Github: [backups-bot](https://github.com/Androz2091/backups-bot) ### Create Create a backup for the server specified in the parameters! ```js /** * @param {Guild} [Guild] - The discord server you want to backup * @param {object} [options] - The backup options */ const backup = require("discord-backup"); backup.create(Guild, options).then((backupData) => { console.log(backupData.id); // NSJH2 }); ``` Click [here](#create-advanced) to learn more about **backup options**. ### Load Allows you to load a backup on a Discord server! ```js /** * @param {string} [backupID] - The ID of the backup that you want to load * @param {Guild} [Guild] - The discord server on which you want to load the backup */ const backup = require("discord-backup"); backup.load(backupID, Guild).then(() => { backup.remove(backupID); // When the backup is loaded, it's recommended to delete it }); ``` ### Fetch Fetches information from a backup ```js /** * @param {string} [backupID] - The ID of the backup to fetch */ const backup = require("discord-backup"); backup.fetch(backupID).then((backupInfos) => { console.log(backupInfos); /* { id: "BC5qo", size: 0.05 data: {BackupData} } */ }); ``` ### Remove **Warn**: once the backup is removed, it is impossible to recover it! ```js /** * @param {string} [backupID] - The ID of the backup to remove */ const backup = require("discord-backup"); backup.remove(backupID); ``` ### List **Note**: `backup#list()` simply returns an array of IDs, you must fetch the ID to get complete information. ```js const backup = require("discord-backup"); backup.list().then((backups) => { console.log(backups); // Expected Output [ "BC5qo", "Jdo91", ...] }); ``` ### SetStorageFolder Updates the storage folder to another ```js const backup = require("discord-backup"); backup.setStorageFolder(__dirname+"/backups/"); await backup.create(guild); // Backup created in ./backups/ backup.setStorageFolder(__dirname+"/my-backups/"); await backup.create(guild); // Backup created in ./my-backups/ ``` ## Advanced usage ### Create [advanced] You can use more options for backup creation: ```js const backup = require("discord-backup"); backup.create(guild, { maxMessagesPerChannel: 10, jsonSave: false, jsonBeautify: true, doNotBackup: [ "roles", "channels", "emojis", "bans" ], saveImages: "base64" }); ``` **maxMessagesPerChannel**: Maximum of messages to save in each channel. "0" won't save any messages. **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. **jsonBeautify**: Whether you want your json backup pretty formatted. **doNotBackup**: Things you don't want to backup. Available items are: `roles`, `channels`, `emojis`, `bans`. **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. ### Load [advanced] As you can see, you're able to load a backup from your own data instead of from an ID: ```js const backup = require("discord-backup"); backup.load(backupData, guild, { clearGuildBeforeRestore: true }); ``` **clearGuildBeforeRestore**: Whether to clear the guild (roles, channels, etc... will be deleted) before the backup restoration (recommended). **maxMessagesPerChannel**: Maximum of messages to restore in each channel. "0" won't restore any messages. ## Example Bot ```js // Load modules (Discord.js v14.20+ Compatible) const { Client, GatewayIntentBits, Events, EmbedBuilder } = require("discord.js"); const backup = require("discord-backup"); const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildBans, GatewayIntentBits.GuildEmojisAndStickers, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent ] }); const settings = { prefix: "b!", token: "YOURTOKEN" }; client.once(Events.ClientReady, () => { console.log(`✅ Bot ${client.user.tag} is ready!`); }); client.on(Events.MessageCreate, async message => { // This reads the first part of your message behind your prefix to see which command you want to use. let command = message.content.toLowerCase().slice(settings.prefix.length).split(" ")[0]; // These are the arguments behind the commands. let args = message.content.split(" ").slice(1); // If the message does not start with your prefix return. // If the user that types a message is a bot account return. // If the command comes from DM return. if (!message.content.startsWith(settings.prefix) || message.author.bot || !message.guild) return; if(command === "create"){ // Check member permissions (v14 syntax) if(!message.member.permissions.has("Administrator")){ return message.channel.send(":x: | You must be an administrator of this server to request a backup!"); } // Create the backup backup.create(message.guild, { jsonBeautify: true }).then((backupData) => { // And send informations to the backup owner message.author.send("The backup has been created! To load it, type this command on the server of your choice: `"+settings.prefix+"load "+backupData.id+"`!"); message.channel.send(":white_check_mark: Backup successfully created. The backup ID was sent in dm!"); }); } if(command === "load"){ // Check member permissions (v14 syntax) if(!message.member.permissions.has("Administrator")){ return message.channel.send(":x: | You must be an administrator of this server to load a backup!"); } let backupID = args[0]; if(!backupID){ return message.channel.send(":x: | You must specify a valid backup ID!"); } // Fetching the backup to know if it exists backup.fetch(backupID).then(async () => { // If the backup exists, request for confirmation message.channel.send(":warning: | When the backup is loaded, all the channels, roles, etc. will be replaced! Type `-confirm` to confirm!"); await message.channel.awaitMessages(m => (m.author.id === message.author.id) && (m.content === "-confirm"), { max: 1, time: 20000, errors: ["time"] }).catch((err) => { // if the author of the commands does not confirm the backup loading return message.channel.send(":x: | Time's up! Cancelled backup loading!"); }); // When the author of the command has confirmed that he wants to load the backup on his server message.author.send(":white_check_mark: | Start loading the backup!"); // Load the backup backup.load(backupID, message.guild).then(() => { // When the backup is loaded, delete them from the server backup.remove(backupID); }).catch((err) => { // If an error occurred return message.author.send(":x: | Sorry, an error occurred... Please check that I have administrator permissions!"); }); }).catch((err) => { console.log(err); // if the backup wasn't found return message.channel.send(":x: | No backup found for `"+backupID+"`!"); }); } if(command === "infos"){ let backupID = args[0]; if(!backupID){ return message.channel.send(":x: | You must specify a valid backup ID!"); } // Fetch the backup backup.fetch(backupID).then((backupInfos) => { const date = new Date(backupInfos.data.createdTimestamp); const yyyy = date.getFullYear().toString(), mm = (date.getMonth()+1).toString(), dd = date.getDate().toString(); const formatedDate = `${yyyy}/${(mm[1]?mm:"0"+mm[0])}/${(dd[1]?dd:"0"+dd[0])}`; let embed = new EmbedBuilder() .setAuthor({ name: "Backup Informations" }) // Display the backup ID .addFields( { name: "Backup ID", value: backupInfos.id, inline: false }, // Displays the server from which this backup comes { name: "Server ID", value: backupInfos.data.guildID, inline: false }, // Display the size (in mb) of the backup { name: "Size", value: `${backupInfos.size} kb`, inline: false }, // Display when the backup was created { name: "Created at", value: formatedDate, inline: false } ) .setColor("#FF0000"); message.channel.send({ embeds: [embed] }); }).catch((err) => { // if the backup wasn't found return message.channel.send(":x: | No backup found for `"+backupID+"`!"); }); } }); //Your secret token to log the bot in. (never share this to anyone!) client.login(settings.token); ``` ## Restored things Here are all things that can be restored with `discord-backup`: * Server icon * Server banner * Server region * Server splash * Server verification level * Server explicit content filter * Server default message notifications * Server embed channel * Server bans (with reasons) * Server emojis * Server AFK (channel and timeout) * Server channels (with permissions, type, nsfw, messages, etc...) * Server roles (with permissions, color, etc...) Example of things that can't be restored: * Server logs * Server invitations * Server vanity url ## Technical Changes (v14.20.0 Migration) ### 🔧 Channel Type Updates * Updated deprecated `ChannelType.GuildNewsThread` → `ChannelType.AnnouncementThread` * Updated deprecated `ChannelType.GuildPrivateThread` → `ChannelType.PrivateThread` * Updated deprecated `ChannelType.GuildPublicThread` → `ChannelType.PublicThread` * Updated `ChannelType.GuildNews` → `ChannelType.GuildAnnouncement` ### ⚡ Rate Limiting Implementation * Added `withRetry()` function with exponential backoff (3 retries max) * Sequential role creation with 250ms delays * Sequential emoji creation with 500ms delays * Sequential ban operations with 1000ms delays * Message sending with 1000ms delays between messages ### 🛡️ Error Handling Improvements * Proper TypeScript error typing (`error: any`) * Graceful degradation for failed operations * Rate limit detection and automatic waiting * Permission error immediate failing * Memory-safe attachment processing (8MB limit) ### 📱 Memory Optimizations * Message limit enforcement (max 1000 per channel) * Attachment size checking (8MB max) * Emoji size limits (256KB max for base64) * Embed field limiting (10 embeds, 25 fields each) * Premium tier emoji limits enforcement ### 🔄 Discord.js v14 Compatibility * Updated discriminator handling (fallback to '0') * New intent requirements documentation * Fixed `ChannelNotCached` errors in test files * Updated embed structure for v14 * Modern async/await patterns throughout ### 🧪 Test File Enhancements * Comprehensive test bot with all backup operations * Channel reference management for guild clearing * Fallback message sending mechanisms * Advanced error handling for Discord API issues * Rate limit-aware testing procedures ### 💻 Code Quality Improvements * Removed all `console.warn` statements for production readiness * Fixed TSLint errors and improved TypeScript compliance * Updated array type declarations (`Array<T>` → `T[]`) * Proper const/let usage throughout codebase * Enhanced comment formatting and documentation