From 33fde3bc4a338dccd8707a7712763535862aa037 Mon Sep 17 00:00:00 2001 From: Zane Helton Date: Sat, 28 Jun 2025 22:09:56 -0400 Subject: [PATCH] Fix the creation of guilds from templates The biggest hold-up was missing fields (`premium_tier`, `welcome_screen`, etc.) but it looks like someone has provided a helpful function called `createGuild(...)` to provide sensible default values. This commit fixes the errors related to creating a guild from a template. I've also refactored the code to include roles and channels in the template. To make sure that the @everyone role is cloned correctly, when creating the guild from a template, we check if the role's ID matches the template's `source_guild_id`. If it does, we set the @everyone role to the new guild's ID. --- src/api/routes/guilds/#guild_id/templates.ts | 4 ++- src/api/routes/guilds/index.ts | 1 + src/api/routes/guilds/templates/index.ts | 33 +++++++------------- src/util/entities/Guild.ts | 29 ++++++++++++++++- 4 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/api/routes/guilds/#guild_id/templates.ts b/src/api/routes/guilds/#guild_id/templates.ts index d6f12992..a9c6182f 100644 --- a/src/api/routes/guilds/#guild_id/templates.ts +++ b/src/api/routes/guilds/#guild_id/templates.ts @@ -24,6 +24,7 @@ import { HTTPError } from "lambert-server"; const router: Router = Router(); const TemplateGuildProjection: (keyof Guild)[] = [ + "id", "name", "description", "region", @@ -32,7 +33,7 @@ const TemplateGuildProjection: (keyof Guild)[] = [ "explicit_content_filter", "preferred_locale", "afk_timeout", - "roles", + // "roles", // "channels", "afk_channel_id", "system_channel_id", @@ -85,6 +86,7 @@ router.post( const guild = await Guild.findOneOrFail({ where: { id: guild_id }, select: TemplateGuildProjection, + relations: ["roles", "channels"], }); const exists = await Template.findOne({ where: { id: guild_id }, diff --git a/src/api/routes/guilds/index.ts b/src/api/routes/guilds/index.ts index 545beb18..a740e60b 100644 --- a/src/api/routes/guilds/index.ts +++ b/src/api/routes/guilds/index.ts @@ -61,6 +61,7 @@ router.post( const guild = await Guild.createGuild({ ...body, owner_id: req.user_id, + template_guild_id: null, }); const { autoJoin } = Config.get().guild; diff --git a/src/api/routes/guilds/templates/index.ts b/src/api/routes/guilds/templates/index.ts index d6de87f9..cff8f778 100644 --- a/src/api/routes/guilds/templates/index.ts +++ b/src/api/routes/guilds/templates/index.ts @@ -108,7 +108,7 @@ router.post( // allowDiscordTemplates, // allowRaws, } = Config.get().templates; - if (!enabled) + if (!enabled) { return res .json({ code: 403, @@ -116,13 +116,16 @@ router.post( "Template creation & usage is disabled on this instance.", }) .sendStatus(403); - if (!allowTemplateCreation) + } + + if (!allowTemplateCreation) { return res .json({ code: 403, message: "Template creation is disabled on this instance.", }) .sendStatus(403); + } const { code } = req.params; const body = req.body as GuildTemplateCreateSchema; @@ -138,29 +141,15 @@ router.post( where: { code: code }, }); - const guild_id = Snowflake.generate(); - - const guild = await Guild.create({ - ...body, + const guild = await Guild.createGuild({ ...template.serialized_source_guild, - id: guild_id, + // body comes after the template + ...body, owner_id: req.user_id, - premium_tier: 0, - }).save(); + template_guild_id: template.source_guild_id, + }); - await Role.create({ - id: guild_id, - guild_id: guild_id, - color: 0, - hoist: false, - managed: true, - mentionable: true, - name: "@everyone", - permissions: BigInt("2251804225").toString(), // TODO: where did this come from? - position: 0, - }).save(); - - await Member.addToGuild(req.user_id, guild_id); + await Member.addToGuild(req.user_id, guild.id); res.status(201).json({ id: guild.id }); }, diff --git a/src/util/entities/Guild.ts b/src/util/entities/Guild.ts index 4e49ce9b..30cbb573 100644 --- a/src/util/entities/Guild.ts +++ b/src/util/entities/Guild.ts @@ -270,6 +270,9 @@ export class Guild extends BaseClass { @Column({ nullable: true }) verification_level?: number; + /** + * DEPRECATED: Look at the new Guild onboarding screens. + */ @Column({ type: "simple-json" }) welcome_screen: GuildWelcomeScreen; @@ -308,7 +311,9 @@ export class Guild extends BaseClass { name?: string; icon?: string | null; owner_id?: string; + roles?: Partial[]; channels?: Partial[]; + template_guild_id: string | null; }) { const guild_id = Snowflake.generate(); @@ -364,10 +369,32 @@ export class Guild extends BaseClass { flags: 0, // TODO? }).save(); - if (!body.channels || !body.channels.length) + // create custom roles if provided + if (body.roles && body.roles.length) { + await Promise.all( + body.roles?.map((role) => { + new Promise((resolve) => { + Role.create({ + ...role, + guild_id, + id: + // role.id === body.template_guild_id indicates that this is the @everyone role + role.id === body.template_guild_id + ? guild_id + : Snowflake.generate(), + }) + .save() + .then(resolve); + }); + }), + ); + } + + if (!body.channels || !body.channels.length) { body.channels = [ { id: "01", type: 0, name: "general", nsfw: false }, ]; + } const ids = new Map();