discordio-backup
Version:
A complete framework to facilitate guild backup!
330 lines (329 loc) • 13.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.clearGuild = exports.loadChannel = exports.loadCategory = exports.fetchTextChannelData = exports.fetchChannelMessages = exports.fetchVoiceChannelData = exports.fetchChannelPermissions = void 0;
const node_fetch_1 = require("node-fetch");
const discord_js_1 = require("discord.js");
/**
* Gets the permissions for a channel
*/
function fetchChannelPermissions(channel) {
const permissions = [];
channel.permissionOverwrites.cache
.filter((p) => p.type === discord_js_1.OverwriteType.Role)
.forEach((perm) => {
// For each overwrites permission
const role = channel.guild.roles.cache.get(perm.id);
if (role) {
permissions.push({
roleName: role.name,
allow: perm.allow.bitfield.toString(),
deny: perm.deny.bitfield.toString()
});
}
});
return permissions;
}
exports.fetchChannelPermissions = fetchChannelPermissions;
/**
* Fetches the voice channel data that is necessary for the backup
*/
async function fetchVoiceChannelData(channel) {
return new Promise(async (resolve) => {
const channelData = {
type: discord_js_1.ChannelType.GuildVoice,
name: channel.name,
bitrate: channel.bitrate,
userLimit: channel.userLimit,
parent: channel.parent ? channel.parent.name : null,
permissions: fetchChannelPermissions(channel)
};
/* Return channel data */
resolve(channelData);
});
}
exports.fetchVoiceChannelData = fetchVoiceChannelData;
async function fetchChannelMessages(channel, options) {
let messages = [];
const messageCount = isNaN(options.maxMessagesPerChannel) ? 10 : options.maxMessagesPerChannel;
const fetchOptions = { limit: 100 };
let lastMessageId;
let fetchComplete = false;
while (!fetchComplete) {
if (lastMessageId) {
fetchOptions.before = lastMessageId;
}
const fetched = await channel.messages.fetch(fetchOptions);
if (fetched.size === 0) {
break;
}
lastMessageId = fetched.last().id;
await Promise.all(fetched.map(async (msg) => {
if (!msg.author || messages.length >= messageCount) {
fetchComplete = true;
return;
}
const files = await Promise.all(msg.attachments.map(async (a) => {
let attach = a.url;
if (a.url && ['png', 'jpg', 'jpeg', 'jpe', 'jif', 'jfif', 'jfi'].includes(a.url)) {
if (options.saveImages && options.saveImages === 'base64') {
attach = (await (0, node_fetch_1.default)(a.url).then((res) => res.buffer())).toString('base64');
}
}
return {
name: a.name,
attachment: attach
};
}));
messages.push({
username: msg.author.username,
avatar: msg.author.displayAvatarURL(),
content: msg.cleanContent,
embeds: msg.embeds,
files,
pinned: msg.pinned,
sentAt: msg.createdAt.toISOString()
});
}));
}
return messages;
}
exports.fetchChannelMessages = fetchChannelMessages;
/**
* Fetches the text channel data that is necessary for the backup
*/
async function fetchTextChannelData(channel, options) {
return new Promise(async (resolve) => {
const channelData = {
type: channel.type,
name: channel.name,
nsfw: channel.nsfw,
rateLimitPerUser: channel.type === discord_js_1.ChannelType.GuildText ? channel.rateLimitPerUser : undefined,
parent: channel.parent ? channel.parent.name : null,
topic: channel.topic,
permissions: fetchChannelPermissions(channel),
messages: [],
isNews: channel.type === discord_js_1.ChannelType.GuildNews,
threads: []
};
/* Fetch channel threads */
if (channel.threads.cache.size > 0) {
await Promise.all(channel.threads.cache.map(async (thread) => {
const threadData = {
type: thread.type,
name: thread.name,
archived: thread.archived,
autoArchiveDuration: thread.autoArchiveDuration,
locked: thread.locked,
rateLimitPerUser: thread.rateLimitPerUser,
messages: []
};
try {
threadData.messages = await fetchChannelMessages(thread, options);
/* Return thread data */
channelData.threads.push(threadData);
}
catch {
channelData.threads.push(threadData);
}
}));
}
/* Fetch channel messages */
try {
channelData.messages = await fetchChannelMessages(channel, options);
/* Return channel data */
resolve(channelData);
}
catch {
resolve(channelData);
}
});
}
exports.fetchTextChannelData = fetchTextChannelData;
/**
* Creates a category for the guild
*/
async function loadCategory(categoryData, guild) {
return new Promise((resolve) => {
guild.channels
.create({
name: categoryData.name,
type: discord_js_1.ChannelType.GuildCategory
})
.then(async (category) => {
// When the category is created
const finalPermissions = [];
categoryData.permissions.forEach((perm) => {
const role = guild.roles.cache.find((r) => r.name === perm.roleName);
if (role) {
finalPermissions.push({
id: role.id,
allow: BigInt(perm.allow),
deny: BigInt(perm.deny)
});
}
});
await category.permissionOverwrites.set(finalPermissions);
resolve(category); // Return the category
});
});
}
exports.loadCategory = loadCategory;
/**
* Create a channel and returns it
*/
async function loadChannel(channelData, guild, category, options) {
return new Promise(async (resolve) => {
const loadMessages = (channel, messages, previousWebhook) => {
return new Promise(async (resolve) => {
const webhook = previousWebhook ||
(await channel
.createWebhook({
name: 'MessagesBackup',
avatar: channel.client.user.displayAvatarURL()
})
.catch(() => { }));
if (!webhook)
return resolve();
messages = messages
.filter((m) => m.content.length > 0 || m.embeds.length > 0 || m.files.length > 0)
.reverse();
messages = messages.slice(messages.length - options.maxMessagesPerChannel);
for (const msg of messages) {
const sentMsg = await webhook
.send({
content: msg.content.length ? msg.content : undefined,
username: msg.username,
avatarURL: msg.avatar,
embeds: msg.embeds,
files: msg.files,
allowedMentions: options.allowedMentions,
threadId: channel.isThread() ? channel.id : undefined
})
.catch((err) => {
console.log(err.message);
});
if (msg.pinned && sentMsg)
await sentMsg.pin();
}
resolve(webhook);
});
};
const createOptions = {
name: channelData.name,
type: null,
parent: category
};
if (channelData.type === discord_js_1.ChannelType.GuildText || channelData.type === discord_js_1.ChannelType.GuildAnnouncement) {
createOptions.topic = channelData.topic;
createOptions.nsfw = channelData.nsfw;
createOptions.rateLimitPerUser = channelData.rateLimitPerUser;
createOptions.type =
channelData.isNews && guild.features.includes(discord_js_1.GuildFeature.News)
? discord_js_1.ChannelType.GuildAnnouncement
: discord_js_1.ChannelType.GuildText;
}
else if (channelData.type === discord_js_1.ChannelType.GuildVoice) {
// Downgrade bitrate
let bitrate = channelData.bitrate;
while (Math.min(bitrate, guild.maximumBitrate)) {
let bitrates = [64000, 128000, 256000, 384000];
bitrate = bitrates[guild.premiumTier];
}
createOptions.bitrate = bitrate;
createOptions.userLimit = channelData.userLimit;
createOptions.type = discord_js_1.ChannelType.GuildVoice;
}
guild.channels.create(createOptions).then(async (channel) => {
/* Update channel permissions */
const finalPermissions = [];
channelData.permissions.forEach((perm) => {
const role = guild.roles.cache.find((r) => r.name === perm.roleName);
if (role) {
finalPermissions.push({
id: role.id,
allow: BigInt(perm.allow),
deny: BigInt(perm.deny)
});
}
});
await channel.permissionOverwrites.set(finalPermissions);
if (channelData.type === discord_js_1.ChannelType.GuildText) {
/* Load messages */
let webhook;
if (channelData.messages.length > 0) {
webhook = await loadMessages(channel, channelData.messages).catch(() => { });
}
/* Load threads */
if (channelData.threads.length > 0) {
//&& guild.features.includes('THREADS_ENABLED')) {
await Promise.all(channelData.threads.map(async (threadData) => {
let autoArchiveDuration = threadData.autoArchiveDuration;
//if (!guild.features.includes('SEVEN_DAY_THREAD_ARCHIVE') && autoArchiveDuration === 10080) autoArchiveDuration = 4320;
//if (!guild.features.includes('THREE_DAY_THREAD_ARCHIVE') && autoArchiveDuration === 4320) autoArchiveDuration = 1440;
return channel.threads
.create({
name: threadData.name,
autoArchiveDuration
})
.then((thread) => {
if (!webhook)
return;
return loadMessages(thread, threadData.messages, webhook);
});
}));
}
return channel;
}
else {
resolve(channel); // Return the channel
}
});
});
}
exports.loadChannel = loadChannel;
/**
* Delete all roles, all channels, all emojis, etc... of a guild
*/
async function clearGuild(guild) {
guild.roles.cache
.filter((role) => !role.managed && role.editable && role.id !== guild.id)
.forEach((role) => {
role.delete().catch(() => { });
});
guild.channels.cache.forEach((channel) => {
channel.delete().catch(() => { });
});
guild.emojis.cache.forEach((emoji) => {
emoji.delete().catch(() => { });
});
const webhooks = await guild.fetchWebhooks();
webhooks.forEach((webhook) => {
webhook.delete().catch(() => { });
});
const bans = await guild.bans.fetch();
bans.forEach((ban) => {
guild.members.unban(ban.user).catch(() => { });
});
guild.setAFKChannel(null);
guild.setAFKTimeout(60 * 5);
guild.setIcon(null);
guild.setBanner(null).catch(() => { });
guild.setSplash(null).catch(() => { });
guild.setDefaultMessageNotifications(discord_js_1.GuildDefaultMessageNotifications.OnlyMentions);
guild.setWidgetSettings({
enabled: false,
channel: null
});
if (!guild.features.includes(discord_js_1.GuildFeature.Community)) {
guild.setExplicitContentFilter(discord_js_1.GuildExplicitContentFilter.Disabled);
guild.setVerificationLevel(discord_js_1.GuildVerificationLevel.None);
}
guild.setSystemChannel(null);
guild.setSystemChannelFlags([
discord_js_1.GuildSystemChannelFlags.SuppressGuildReminderNotifications,
discord_js_1.GuildSystemChannelFlags.SuppressJoinNotifications,
discord_js_1.GuildSystemChannelFlags.SuppressPremiumSubscriptions
]);
return;
}
exports.clearGuild = clearGuild;