@hunteroi/discord-server-generator
Version:
A framework to generate Discord guild categories, channels and roles, built with DiscordJS
312 lines (311 loc) • 12.6 kB
JavaScript
import EventEmitter from "node:events";
import { ChannelType, ForumChannel, GatewayIntentBits, GuildPremiumTier, NewsChannel, TextChannel, } from "discord.js";
import { ServerGeneratorManagerEvents } from "./ServerGeneratorManagerEvents.js";
/**
*
* @export
* @class ServerGeneratorManager
* @extends {EventEmitter}
*/
export class ServerGeneratorManager extends EventEmitter {
client;
options;
hasOptionalIntent = false;
constructor(client, options = {
reason: `Automated by ${client.user}`,
}) {
super();
if (!client.options.intents.has(GatewayIntentBits.Guilds)) {
throw new Error("GUILDS intent is required to use this package!");
}
if (!client.options.intents.has(GatewayIntentBits.GuildEmojisAndStickers)) {
console.warn("GUILD_EMOJIS_AND_STICKERS intent is optional for this package to work but could be a nice addition.");
}
else
this.hasOptionalIntent = true;
this.client = client;
this.options = options;
this.on(ServerGeneratorManagerEvents.channelCreate, this.#handleChannelCreate);
}
/**
* Generates a guild from scratch with the given options.
*
* @param {GuildResolvable} guild
* @param {GuildOptions} options
* @param {string?} [reason]
* @memberof ServerGeneratorManager
*/
async generate(guild, options, reason) {
if (!this.client.isReady()) {
throw new Error("You should call this method on client#ready");
}
const overallReason = reason ?? this.options.reason ?? "Automated by the bot";
const target = await this.client.guilds.fetch({ guild });
await this.#getCaches(target);
if (target.roles.highest !== target.roles.botRoleFor(this.client.user)) {
throw new Error("The bot's role has to be the highest in order to generate the whole guild from scratch.");
}
this.emit(ServerGeneratorManagerEvents.guildGenerate, target, options, overallReason);
await this.#deleteEverything(target, overallReason);
await this.#createEverything(target, options, overallReason);
this.emit(ServerGeneratorManagerEvents.guildGenerated, target, options, overallReason);
}
async #handleChannelCreate(channel, options, reason) {
if (options.isRulesChannel && channel.isTextBased()) {
await channel.guild.setRulesChannel(channel.id, reason);
}
if (options.isAFKChannel && channel.isVoiceBased()) {
await channel.guild.setAFKChannel(channel.id, reason);
}
}
async #getCaches(guild) {
if (this.hasOptionalIntent) {
await guild.emojis.fetch();
await guild.stickers.fetch();
}
await guild.roles.fetch();
await guild.channels.fetch();
}
async #deleteEverything(target, overallReason) {
if (this.hasOptionalIntent) {
await this.#delete(target.emojis.cache, ServerGeneratorManagerEvents.emojiDelete, overallReason);
await this.#delete(target.stickers.cache, ServerGeneratorManagerEvents.stickerDelete, overallReason);
}
await this.#delete(target.roles.cache.filter((r) => r !== target.roles.everyone && !r.managed), ServerGeneratorManagerEvents.roleDelete, overallReason);
await this.#delete(target.channels.cache.filter((c) => !c.isThread()), ServerGeneratorManagerEvents.channelDelete, overallReason);
}
async #createEverything(target, options, overallReason) {
if (this.hasOptionalIntent) {
await this.#createEmojis(target.emojis, options.emojis, overallReason);
await this.#createStickers(target.stickers, options.stickers, overallReason);
}
await this.#createRoles(target.roles, options.roles, overallReason);
await this.#createChannels(target.channels, options.rootChannels, null, overallReason);
await this.#createCategories(target.channels, options.channels, null, overallReason);
await target.edit({ ...options, reason: overallReason });
}
async #delete(collection, event, reason) {
if (!collection || !event)
return;
for (const entity of collection.values()) {
await entity.delete(reason);
this.emit(event, entity, reason);
}
}
async #createRoles(roles, roleOptions, reason) {
if (!roles || !roleOptions)
return;
for (const options of roleOptions) {
const role = await roles.create({ reason, ...options });
this.emit(ServerGeneratorManagerEvents.roleCreate, role, options, reason);
}
}
#isText(channel) {
return (channel.isTextBased() &&
(channel instanceof NewsChannel ||
channel instanceof TextChannel ||
channel instanceof ForumChannel));
}
async #createThreads(channels, threadOptions, textChannelId, reason) {
if (!channels || !textChannelId || !threadOptions)
return;
const textChannel = await channels.fetch(textChannelId);
if (!textChannel || !this.#isText(textChannel))
return;
for (const options of threadOptions) {
let thread;
if (textChannel instanceof ForumChannel) {
thread = await textChannel.threads.create({
reason,
...options,
});
}
else if (textChannel instanceof NewsChannel) {
thread = await textChannel.threads.create({
reason,
...options,
});
}
else {
thread = await textChannel.threads.create({
reason,
...options,
});
}
this.emit(ServerGeneratorManagerEvents.threadCreate, thread, options, reason);
}
}
async #createChannels(channels, channelOptions, parent, reason) {
if (!channels || !channelOptions)
return;
for (const options of channelOptions) {
const channel = await channels.create({
reason,
...options,
parent,
});
this.emit(ServerGeneratorManagerEvents.channelCreate, channel, options);
if (options.children && channel.isTextBased()) {
await this.#createThreads(channels, options.children, channel.id, reason);
}
}
}
async #createCategories(channels, channelOptions, parent, reason) {
if (!channels || !channelOptions)
return;
for (const options of channelOptions) {
const channel = await channels.create({
reason,
...options,
parent,
type: ChannelType.GuildCategory,
});
this.emit(ServerGeneratorManagerEvents.channelCreate, channel, options);
if (options.children &&
channel.type === ChannelType.GuildCategory) {
await this.#createChannels(channels, options.children, channel.id, reason);
}
}
}
async #createEmojis(emojis, emojiOptions, reason) {
if (!emojis || !emojiOptions)
return;
for (const options of emojiOptions) {
const emoji = await emojis.create({
reason,
...options,
});
this.emit(ServerGeneratorManagerEvents.emojiCreate, emoji, options, reason);
}
}
async #createStickers(stickers, stickerOptions, reason) {
if (!stickers || !stickerOptions)
return;
if (stickers.guild.premiumTier === GuildPremiumTier.None)
return;
for (const options of stickerOptions) {
const sticker = await stickers.create({
reason,
...options,
});
this.emit(ServerGeneratorManagerEvents.stickerCreate, sticker, options, reason);
}
}
}
/**
* Emitted when a guild is being generated by the manager.
* @event ServerGeneratorManager#guildGenerate
* @param {Guild} Guild
* @param {GuildOptions} options
* @param {string?} reason
* @example
* manager.on(ServerGeneratorManagerEvents.guildGenerate, (guild, options, reason) => {
* console.log(`Generating guild ${guild.id} for reason ${reason} with options`, options);
* });
*/
/**
* Emitted when a guild has just been generated by the manager.
* @event ServerGeneratorManager#guildGenerated
* @param {Guild} Guild
* @param {GuildOptions} options
* @param {string?} reason
* @example
* manager.on(ServerGeneratorManagerEvents.guildGenerated, (guild, options, reason) => {
* console.log(`Generated guild ${guild.id} for reason ${reason} with options`, options);
* });
*/
/**
* Emitted when an old role is being deleted by the manager when generating the guild.
* @event ServerGeneratorManager#roleDelete
* @param {Role} role
* @param {string?} reason
* @example
* manager.on(ServerGeneratorManagerEvents.roleDelete, (role, reason) => {
* console.log(`Role ${role.id} deleted for reason ${reason}`);
* });
*/
/**
* Emitted when an old channel is being deleted by the manager when generating the guild.
* @event ServerGeneratorManager#channelDelete
* @param {GuildChannel} channel
* @param {string?} reason
* @example
* manager.on(ServerGeneratorManagerEvents.channelDelete, (channel, reason) => {
* console.log(`Channel ${channel.id} deleted for reason ${reason}`);
* });
*/
/**
* Emitted when an old emoji is being deleted by the manager when generating the guild.
* @event ServerGeneratorManager#emojiDelete
* @param {GuildEmoji} emoji
* @param {string?} reason
* @example
* manager.on(ServerGeneratorManagerEvents.emojiDelete, (emoji, reason) => {
* console.log(`Emoji ${emoji.id} deleted for reason ${reason}`);
* });
*/
/**
* Emitted when an old sticker is being deleted by the manager when generating the guild.
* @event ServerGeneratorManager#stickerDelete
* @param {Sticker} sticker
* @param {string?} reason
* @example
* manager.on(ServerGeneratorManagerEvents.stickerDelete, (sticker, reason) => {
* console.log(`Sticker ${sticker.id} deleted for reason ${reason}`);
* });
*/
/**
* Emitted when a new channel is being created by the manager when generating the guild.
* @event ServerGeneratorManager#channelCreate
* @param { TextChannel | VoiceChannel | CategoryChannel | NewsChannel | StoreChannel | StageChannel} channel
* @param {CategoryOptions | GuildChannelOptions} options
* @param {string?} reason
* @example
* manager.on(ServerGeneratorManagerEvents.channelCreate, (channel, options, reason) => {
* console.log(`Channel ${channel.id} created for reason ${reason} with options`, options);
* });
*/
/**
* Emitted when a new thread is being created by the manager when generating the guild.
* @event ServerGeneratorManager#threadCreate
* @param {ThreadChannel} thread
* @param {ThreadOptions} options
* @param {string?} reason
* @example
* manager.on(ServerGeneratorManagerEvents.threadCreate, (thread, options, reason) => {
* console.log(`Thread ${thread.id} created for reason ${reason} with options`, options);
* });
*/
/**
* Emitted when a new role is being created by the manager when generating the guild.
* @event ServerGeneratorManager#roleCreate
* @param {Role} role
* @param {RoleOptions} options
* @param {string?} reason
* @example
* manager.on(ServerGeneratorManagerEvents.roleCreate, (role, options, reason) => {
* console.log(`Role ${role.id} created for reason ${reason} with options`, options);
* });
*/
/**
* Emitted when a new emoji is being created by the manager when generating the guild.
* @event ServerGeneratorManager#emojiCreate
* @param {GuildEmoji} emoji
* @param {EmojiOptions} options
* @param {string?} reason
* @example
* manager.on(ServerGeneratorManagerEvents.emojiCreate, (emoji, options, reason) => {
* console.log(`Emoji ${emoji.id} created for reason ${reason} with options`, options);
* });
*/
/**
* Emitted when a new sticker is being created by the manager when generating the guild.
* @event ServerGeneratorManager#stickerCreate
* @param {Sticker} sticker
* @param {StickerOptions} options
* @param {string?} reason
* @example
* manager.on(ServerGeneratorManagerEvents.stickerCreate, (sticker, options, reason) => {
* console.log(`Sticker ${sticker.id} created for reason ${reason} with options`, options);
* });
*/