diff --git a/package.json b/package.json index db25b6d5..0b5f2835 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "discord-backup", - "version": "3.2.1", + "version": "3.3.0", "description": "A complete framework to facilitate server backup using discord.js v12", "main": "lib/index.js", "files": [ @@ -31,7 +31,7 @@ }, "homepage": "https://github.com/Androz2091/discord-backup#readme", "dependencies": { - "discord.js": "^13.6.0" + "discord.js": "^14.2.0" }, "devDependencies": { "@types/node": "^17.0.30", diff --git a/src/create.ts b/src/create.ts index abedba6b..fc1a9a77 100644 --- a/src/create.ts +++ b/src/create.ts @@ -8,7 +8,7 @@ import type { TextChannelData, VoiceChannelData } from './types'; -import type { CategoryChannel, Collection, Guild, GuildChannel, Snowflake, TextChannel, ThreadChannel, VoiceChannel } from 'discord.js'; +import type { CategoryChannel, ChannelType, Collection, Guild, GuildChannel, Snowflake, TextChannel, ThreadChannel, VoiceChannel } from 'discord.js'; import nodeFetch from 'node-fetch'; import { fetchChannelPermissions, fetchTextChannelData, fetchVoiceChannelData } from './util'; import { MemberData } from './types/MemberData'; @@ -112,7 +112,7 @@ export async function getChannels(guild: Guild, options: CreateOptions) { }; // Gets the list of the categories and sort them by position const categories = (guild.channels.cache - .filter((ch) => ch.type === 'GUILD_CATEGORY') as Collection) + .filter((ch) => ch.type === ChannelType.GuildCategory) as Collection) .sort((a, b) => a.position - b.position) .toJSON() as CategoryChannel[]; for (const category of categories) { @@ -125,7 +125,7 @@ export async function getChannels(guild: Guild, options: CreateOptions) { const children = category.children.sort((a, b) => a.position - b.position).toJSON(); for (const child of children) { // For each child channel - if (child.type === 'GUILD_TEXT'|| child.type === 'GUILD_NEWS') { + if (child.type === ChannelType.GuildText || child.type === ChannelType.GuildNews) { const channelData: TextChannelData = await fetchTextChannelData(child as TextChannel, options); // Gets the channel data categoryData.children.push(channelData); // And then push the child in the categoryData } else { @@ -138,15 +138,15 @@ export async function getChannels(guild: Guild, options: CreateOptions) { // Gets the list of the other channels (that are not in a category) and sort them by position const others = (guild.channels.cache .filter((ch) => { - return !ch.parent && ch.type !== 'GUILD_CATEGORY' - && ch.type !== 'GUILD_STORE' // there is no way to restore store channels, ignore them - && ch.type !== 'GUILD_NEWS_THREAD' && ch.type !== 'GUILD_PRIVATE_THREAD' && ch.type !== 'GUILD_PUBLIC_THREAD' // threads will be saved with fetchTextChannelData + return !ch.parent && ch.type !== ChannelType.GuildCategory + //&& ch.type !== 'GUILD_STORE' // there is no way to restore store channels, ignore them + && ch.type !== ChannelType.GuildNewsThread && ch.type !== ChannelType.GuildPrivateThread && ch.type !== ChannelType.GuildPublicThread // threads will be saved with fetchTextChannelData }) as Collection>) .sort((a, b) => a.position - b.position) .toJSON(); for (const channel of others) { // For each channel - if (channel.type === 'GUILD_TEXT' || channel.type === 'GUILD_NEWS') { + if (channel.type === ChnanelType.GuildText || channel.type === ChannelType.GuildNews) { const channelData: TextChannelData = await fetchTextChannelData(channel as TextChannel, options); // Gets the channel data channels.others.push(channelData); // Update channels object } else { diff --git a/src/index.ts b/src/index.ts index 9baa00aa..3d838109 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import type { BackupData, BackupInfos, CreateOptions, LoadOptions } from './types/'; import type { Guild } from 'discord.js'; -import { SnowflakeUtil, Intents } from 'discord.js'; +import { SnowflakeUtil, IntentsBitField } from 'discord.js'; import nodeFetch from 'node-fetch'; import { sep } from 'path'; @@ -76,8 +76,8 @@ export const create = async ( ) => { return new Promise(async (resolve, reject) => { - const intents = new Intents(guild.client.options.intents); - if (!intents.has('GUILDS')) return reject('GUILDS intent is required'); + const intents = new IntentsBitField(guild.client.options.intents); + if (!intents.has(IntentsBitField.Flags.Guilds)) return reject('Guilds intent is required'); try { const backupData: BackupData = { diff --git a/src/load.ts b/src/load.ts index b1bef9a8..0dc4ac9a 100644 --- a/src/load.ts +++ b/src/load.ts @@ -1,5 +1,5 @@ import type { BackupData, LoadOptions } from './types'; -import type { Emoji, Guild, GuildChannel, Role, VoiceChannel } from 'discord.js'; +import type { ChannelType, Emoji, Guild, GuildFeature, GuildChannel, Role, VoiceChannel } from 'discord.js'; import { loadCategory, loadChannel } from './util'; /** @@ -31,7 +31,7 @@ export const loadConfig = (guild: Guild, backupData: BackupData): Promise => { const afkPromises: Promise[] = []; if (backupData.afk) { - afkPromises.push(guild.setAFKChannel(guild.channels.cache.find((ch) => ch.name === backupData.afk.name && ch.type === 'GUILD_VOICE') as VoiceChannel)); + afkPromises.push(guild.setAFKChannel(guild.channels.cache.find((ch) => ch.name === backupData.afk.name && ch.type === ChannelType.GuildVoice) as VoiceChannel)); afkPromises.push(guild.setAFKTimeout(backupData.afk.timeout)); } return Promise.all(afkPromises); diff --git a/src/util.ts b/src/util.ts index d970c4df..f1651f1c 100644 --- a/src/util.ts +++ b/src/util.ts @@ -11,8 +11,12 @@ import type { import type { CategoryChannel, ChannelLogsQueryOptions, + ChannelType, Collection, Guild, + GuildFeature, + GuildDefaultMessageNotifications, + GuildSystemChannelFlags, GuildChannelCreateOptions, Message, OverwriteData, @@ -28,10 +32,10 @@ import type { import nodeFetch from 'node-fetch'; const MaxBitratePerTier: Record = { - NONE: 64000, - TIER_1: 128000, - TIER_2: 256000, - TIER_3: 384000 + None: 64000, + Tier1: 128000, + Tier2: 256000, + Tier3: 384000 }; /** @@ -61,7 +65,7 @@ export function fetchChannelPermissions(channel: TextChannel | VoiceChannel | Ca export async function fetchVoiceChannelData(channel: VoiceChannel) { return new Promise(async (resolve) => { const channelData: VoiceChannelData = { - type: 'GUILD_VOICE', + type: ChannelType.GuildVoice, name: channel.name, bitrate: channel.bitrate, userLimit: channel.userLimit, @@ -129,12 +133,12 @@ export async function fetchTextChannelData(channel: TextChannel | NewsChannel, o type: channel.type, name: channel.name, nsfw: channel.nsfw, - rateLimitPerUser: channel.type === 'GUILD_TEXT' ? channel.rateLimitPerUser : undefined, + rateLimitPerUser: channel.type === ChannelType.GuildText ? channel.rateLimitPerUser : undefined, parent: channel.parent ? channel.parent.name : null, topic: channel.topic, permissions: fetchChannelPermissions(channel), messages: [], - isNews: channel.type === 'GUILD_NEWS', + isNews: channel.type === ChannelType.GuildNews, threads: [] }; /* Fetch channel threads */ @@ -176,7 +180,7 @@ export async function fetchTextChannelData(channel: TextChannel | NewsChannel, o export async function loadCategory(categoryData: CategoryData, guild: Guild) { return new Promise((resolve) => { guild.channels.create(categoryData.name, { - type: 'GUILD_CATEGORY' + type: ChannelType.GuildCategory }).then(async (category) => { // When the category is created const finalPermissions: OverwriteData[] = []; @@ -209,7 +213,8 @@ export async function loadChannel( const loadMessages = (channel: TextChannel | ThreadChannel, messages: MessageData[], previousWebhook?: Webhook): Promise => { return new Promise(async (resolve) => { - const webhook = previousWebhook || await (channel as TextChannel).createWebhook('MessagesBackup', { + const webhook = previousWebhook || await (channel as TextChannel).createWebhook({ + name: 'MessagesBackup', avatar: channel.client.user.displayAvatarURL() }).catch(() => {}); if (!webhook) return resolve(); @@ -238,16 +243,17 @@ export async function loadChannel( } const createOptions: GuildChannelCreateOptions = { + name: channelData.name, type: null, parent: category }; - if (channelData.type === 'GUILD_TEXT' || channelData.type === 'GUILD_NEWS') { + if (channelData.type === ChannelType.GuildText || channelData.type === ChannelType.GuildNews) { createOptions.topic = (channelData as TextChannelData).topic; createOptions.nsfw = (channelData as TextChannelData).nsfw; createOptions.rateLimitPerUser = (channelData as TextChannelData).rateLimitPerUser; createOptions.type = - (channelData as TextChannelData).isNews && guild.features.includes('NEWS') ? 'GUILD_NEWS' : 'GUILD_TEXT'; - } else if (channelData.type === 'GUILD_VOICE') { + (channelData as TextChannelData).isNews && guild.features.includes(GuildFeature.News) ? ChannelType.GuildNews : ChannelType.GuildText; + } else if (channelData.type === ChannelType.GuildVoice) { // Downgrade bitrate let bitrate = (channelData as VoiceChannelData).bitrate; const bitrates = Object.values(MaxBitratePerTier); @@ -256,9 +262,9 @@ export async function loadChannel( } createOptions.bitrate = bitrate; createOptions.userLimit = (channelData as VoiceChannelData).userLimit; - createOptions.type = 'GUILD_VOICE'; + createOptions.type = ChannelType.GuildVoice; } - guild.channels.create(channelData.name, createOptions).then(async (channel) => { + guild.channels.create(createOptions).then(async (channel) => { /* Update channel permissions */ const finalPermissions: OverwriteData[] = []; channelData.permissions.forEach((perm) => { @@ -272,7 +278,7 @@ export async function loadChannel( } }); await channel.permissionOverwrites.set(finalPermissions); - if (channelData.type === 'GUILD_TEXT') { + if (channelData.type === ChannelType.GuildText) { /* Load messages */ let webhook: Webhook|void; if ((channelData as TextChannelData).messages.length > 0) { @@ -282,8 +288,8 @@ export async function loadChannel( if ((channelData as TextChannelData).threads.length > 0) { //&& guild.features.includes('THREADS_ENABLED')) { await Promise.all((channelData as TextChannelData).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; + //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 as TextChannel).threads.create({ name: threadData.name, autoArchiveDuration @@ -329,16 +335,16 @@ export async function clearGuild(guild: Guild) { guild.setIcon(null); guild.setBanner(null).catch(() => {}); guild.setSplash(null).catch(() => {}); - guild.setDefaultMessageNotifications('ONLY_MENTIONS'); + guild.setDefaultMessageNotifications(GuildDefaultMessageNotifications.OnlyMentions); guild.setWidgetSettings({ enabled: false, channel: null }); - if (!guild.features.includes('COMMUNITY')) { - guild.setExplicitContentFilter('DISABLED'); - guild.setVerificationLevel('NONE'); + if (!guild.features.includes(GuildFeature.Community)) { + guild.setExplicitContentFilter(GuildExplicitContentFilter.Disabled); + guild.setVerificationLevel(GuildVerificationLevel.None); } guild.setSystemChannel(null); - guild.setSystemChannelFlags(['SUPPRESS_GUILD_REMINDER_NOTIFICATIONS', 'SUPPRESS_JOIN_NOTIFICATIONS', 'SUPPRESS_PREMIUM_SUBSCRIPTIONS']); + guild.setSystemChannelFlags([GuildSystemChannelFlags.SuppressGuildReminderNotifications, GuildSystemChannelFlags.SuppressJoinNotifications, GuildSystemChannelFlags.SuppressPremiumSubscriptions]); return; }