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.
This commit is contained in:
Zane Helton 2025-06-28 22:09:56 -04:00 committed by Madeline
parent eec2a8ba85
commit 33fde3bc4a
4 changed files with 43 additions and 24 deletions

View File

@ -24,6 +24,7 @@ import { HTTPError } from "lambert-server";
const router: Router = Router(); const router: Router = Router();
const TemplateGuildProjection: (keyof Guild)[] = [ const TemplateGuildProjection: (keyof Guild)[] = [
"id",
"name", "name",
"description", "description",
"region", "region",
@ -32,7 +33,7 @@ const TemplateGuildProjection: (keyof Guild)[] = [
"explicit_content_filter", "explicit_content_filter",
"preferred_locale", "preferred_locale",
"afk_timeout", "afk_timeout",
"roles", // "roles",
// "channels", // "channels",
"afk_channel_id", "afk_channel_id",
"system_channel_id", "system_channel_id",
@ -85,6 +86,7 @@ router.post(
const guild = await Guild.findOneOrFail({ const guild = await Guild.findOneOrFail({
where: { id: guild_id }, where: { id: guild_id },
select: TemplateGuildProjection, select: TemplateGuildProjection,
relations: ["roles", "channels"],
}); });
const exists = await Template.findOne({ const exists = await Template.findOne({
where: { id: guild_id }, where: { id: guild_id },

View File

@ -61,6 +61,7 @@ router.post(
const guild = await Guild.createGuild({ const guild = await Guild.createGuild({
...body, ...body,
owner_id: req.user_id, owner_id: req.user_id,
template_guild_id: null,
}); });
const { autoJoin } = Config.get().guild; const { autoJoin } = Config.get().guild;

View File

@ -108,7 +108,7 @@ router.post(
// allowDiscordTemplates, // allowDiscordTemplates,
// allowRaws, // allowRaws,
} = Config.get().templates; } = Config.get().templates;
if (!enabled) if (!enabled) {
return res return res
.json({ .json({
code: 403, code: 403,
@ -116,13 +116,16 @@ router.post(
"Template creation & usage is disabled on this instance.", "Template creation & usage is disabled on this instance.",
}) })
.sendStatus(403); .sendStatus(403);
if (!allowTemplateCreation) }
if (!allowTemplateCreation) {
return res return res
.json({ .json({
code: 403, code: 403,
message: "Template creation is disabled on this instance.", message: "Template creation is disabled on this instance.",
}) })
.sendStatus(403); .sendStatus(403);
}
const { code } = req.params; const { code } = req.params;
const body = req.body as GuildTemplateCreateSchema; const body = req.body as GuildTemplateCreateSchema;
@ -138,29 +141,15 @@ router.post(
where: { code: code }, where: { code: code },
}); });
const guild_id = Snowflake.generate(); const guild = await Guild.createGuild({
const guild = await Guild.create({
...body,
...template.serialized_source_guild, ...template.serialized_source_guild,
id: guild_id, // body comes after the template
...body,
owner_id: req.user_id, owner_id: req.user_id,
premium_tier: 0, template_guild_id: template.source_guild_id,
}).save(); });
await Role.create({ await Member.addToGuild(req.user_id, guild.id);
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);
res.status(201).json({ id: guild.id }); res.status(201).json({ id: guild.id });
}, },

View File

@ -270,6 +270,9 @@ export class Guild extends BaseClass {
@Column({ nullable: true }) @Column({ nullable: true })
verification_level?: number; verification_level?: number;
/**
* DEPRECATED: Look at the new Guild onboarding screens.
*/
@Column({ type: "simple-json" }) @Column({ type: "simple-json" })
welcome_screen: GuildWelcomeScreen; welcome_screen: GuildWelcomeScreen;
@ -308,7 +311,9 @@ export class Guild extends BaseClass {
name?: string; name?: string;
icon?: string | null; icon?: string | null;
owner_id?: string; owner_id?: string;
roles?: Partial<Role>[];
channels?: Partial<Channel>[]; channels?: Partial<Channel>[];
template_guild_id: string | null;
}) { }) {
const guild_id = Snowflake.generate(); const guild_id = Snowflake.generate();
@ -364,10 +369,32 @@ export class Guild extends BaseClass {
flags: 0, // TODO? flags: 0, // TODO?
}).save(); }).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 = [ body.channels = [
{ id: "01", type: 0, name: "general", nsfw: false }, { id: "01", type: 0, name: "general", nsfw: false },
]; ];
}
const ids = new Map(); const ids = new Map();