discord-backup
Version:
A complete framework to facilitate server backup using discord.js v14 with rate limiting and error handling
240 lines (239 loc) • 9.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.setStorageFolder = exports.list = exports.remove = exports.load = exports.create = exports.fetch = void 0;
const discord_js_1 = require("discord.js");
const node_fetch_1 = require("node-fetch");
const path_1 = require("path");
const fs_1 = require("fs");
const promises_1 = require("fs/promises");
const createMaster = require("./create");
const loadMaster = require("./load");
const utilMaster = require("./util");
let backups = `${__dirname}/backups`;
if (!(0, fs_1.existsSync)(backups)) {
(0, fs_1.mkdirSync)(backups);
}
/**
* Checks if a backup exists and returns its data
*/
const getBackupData = async (backupID) => {
return new Promise(async (resolve, reject) => {
const files = await (0, promises_1.readdir)(backups); // Read "backups" directory
// Try to get the json file
const file = files.filter((f) => f.split('.').pop() === 'json').find((f) => f === `${backupID}.json`);
if (file) {
// If the file exists
const backupData = require(`${backups}${path_1.sep}${file}`);
// Returns backup informations
resolve(backupData);
}
else {
// If no backup was found, return an error message
reject('No backup found');
}
});
};
/**
* Fetches a backyp and returns the information about it
*/
const fetch = (backupID) => {
return new Promise(async (resolve, reject) => {
getBackupData(backupID)
.then((backupData) => {
const size = (0, fs_1.statSync)(`${backups}${path_1.sep}${backupID}.json`).size; // Gets the size of the file using fs
const backupInfos = {
data: backupData,
id: backupID,
size: Number((size / 1024).toFixed(2))
};
// Returns backup informations
resolve(backupInfos);
})
.catch(() => {
reject('No backup found');
});
});
};
exports.fetch = fetch;
/**
* Creates a new backup and saves it to the storage
*/
const create = async (guild, options = {
backupID: null,
maxMessagesPerChannel: 10,
jsonSave: true,
jsonBeautify: true,
doNotBackup: [],
backupMembers: false,
saveImages: ''
}) => {
return new Promise(async (resolve, reject) => {
const intents = new discord_js_1.IntentsBitField(guild.client.options.intents);
if (!intents.has(discord_js_1.IntentsBitField.Flags.Guilds))
return reject('Guilds intent is required');
try {
const backupData = {
name: guild.name,
verificationLevel: guild.verificationLevel,
explicitContentFilter: guild.explicitContentFilter,
defaultMessageNotifications: guild.defaultMessageNotifications,
afk: guild.afkChannel ? { name: guild.afkChannel.name, timeout: guild.afkTimeout } : null,
widget: {
enabled: guild.widgetEnabled,
channel: guild.widgetChannel ? guild.widgetChannel.name : null
},
channels: { categories: [], others: [] },
roles: [],
bans: [],
emojis: [],
members: [],
createdTimestamp: Date.now(),
guildID: guild.id,
id: options.backupID ?? discord_js_1.SnowflakeUtil.generate().toString()
};
if (guild.iconURL()) {
if (options && options.saveImages && options.saveImages === 'base64') {
backupData.iconBase64 = (await (0, node_fetch_1.default)(guild.iconURL()).then((res) => res.buffer())).toString('base64');
}
backupData.iconURL = guild.iconURL();
}
if (guild.splashURL()) {
if (options && options.saveImages && options.saveImages === 'base64') {
backupData.splashBase64 = (await (0, node_fetch_1.default)(guild.splashURL()).then((res) => res.buffer())).toString('base64');
}
backupData.splashURL = guild.splashURL();
}
if (guild.bannerURL()) {
if (options && options.saveImages && options.saveImages === 'base64') {
backupData.bannerBase64 = (await (0, node_fetch_1.default)(guild.bannerURL()).then((res) => res.buffer())).toString('base64');
}
backupData.bannerURL = guild.bannerURL();
}
if (options && options.backupMembers) {
// Backup members
backupData.members = await createMaster.getMembers(guild);
}
if (!options || !(options.doNotBackup || []).includes('bans')) {
// Backup bans
backupData.bans = await createMaster.getBans(guild);
}
if (!options || !(options.doNotBackup || []).includes('roles')) {
// Backup roles
backupData.roles = await createMaster.getRoles(guild);
}
if (!options || !(options.doNotBackup || []).includes('emojis')) {
// Backup emojis
backupData.emojis = await createMaster.getEmojis(guild, options);
}
if (!options || !(options.doNotBackup || []).includes('channels')) {
// Backup channels
backupData.channels = await createMaster.getChannels(guild, options);
}
if (!options || options.jsonSave === undefined || options.jsonSave) {
// Convert Object to JSON
const backupJSON = options.jsonBeautify
? JSON.stringify(backupData, null, 4)
: JSON.stringify(backupData);
// Save the backup
await (0, promises_1.writeFile)(`${backups}${path_1.sep}${backupData.id}.json`, backupJSON, 'utf-8');
}
// Returns ID
resolve(backupData);
}
catch (e) {
return reject(e);
}
});
};
exports.create = create;
/**
* Loads a backup for a guild
*/
const load = async (backup, guild, options = {
clearGuildBeforeRestore: true,
maxMessagesPerChannel: 10
}) => {
return new Promise(async (resolve, reject) => {
if (!guild) {
return reject('Invalid guild');
}
try {
const backupData = typeof backup === 'string' ? await getBackupData(backup) : backup;
try {
if (options.clearGuildBeforeRestore === undefined || options.clearGuildBeforeRestore) {
// Clear the guild
await utilMaster.clearGuild(guild);
}
await Promise.all([
// Restore guild configuration
loadMaster.loadConfig(guild, backupData),
// Restore guild roles
loadMaster.loadRoles(guild, backupData),
// Restore guild channels
loadMaster.loadChannels(guild, backupData, options),
// Restore afk channel and timeout
loadMaster.loadAFK(guild, backupData),
// Restore guild emojis
loadMaster.loadEmojis(guild, backupData),
// Restore guild bans
loadMaster.loadBans(guild, backupData),
// Restore embed channel
loadMaster.loadEmbedChannel(guild, backupData)
]);
}
catch (e) {
return reject(e);
}
// Then return the backup data
return resolve(backupData);
}
catch (e) {
return reject('No backup found');
}
});
};
exports.load = load;
/**
* Removes a backup
*/
const remove = async (backupID) => {
return new Promise((resolve, reject) => {
try {
require(`${backups}${path_1.sep}${backupID}.json`);
(0, fs_1.unlinkSync)(`${backups}${path_1.sep}${backupID}.json`);
resolve();
}
catch (error) {
reject('Backup not found');
}
});
};
exports.remove = remove;
/**
* Returns the list of all backup
*/
const list = async () => {
const files = await (0, promises_1.readdir)(backups); // Read "backups" directory
return files.map((f) => f.split('.')[0]);
};
exports.list = list;
/**
* Change the storage path
*/
const setStorageFolder = (path) => {
if (path.endsWith(path_1.sep)) {
path = path.substr(0, path.length - 1);
}
backups = path;
if (!(0, fs_1.existsSync)(backups)) {
(0, fs_1.mkdirSync)(backups);
}
};
exports.setStorageFolder = setStorageFolder;
exports.default = {
create: exports.create,
fetch: exports.fetch,
list: exports.list,
load: exports.load,
remove: exports.remove
};