From 8939cc84927be36cd9b3f5c94fa8825d46ae591c Mon Sep 17 00:00:00 2001 From: Fleny Date: Mon, 16 Feb 2026 18:50:42 +0100 Subject: [PATCH] fix!: Community invites updates (#4724) * Update permissions for community invites Use payload_json for invite creation with file upload The TODO in INVITE_CREATE is to be done after #4436 gets merged as it splits the transformer logic between gateway and the normal invite object, placing the event transformation code in the event itself * Community invites breaking changes --- packages/bot/src/handlers/invites/INVITE_CREATE.ts | 1 + packages/bot/src/transformers/invite.ts | 1 + packages/bot/src/transformers/types.ts | 2 +- packages/rest/src/manager.ts | 10 ++-------- packages/rest/src/types.ts | 8 ++++---- packages/types/src/discord/gateway.ts | 2 ++ packages/types/src/discord/invite.ts | 2 +- packages/types/src/discordeno/channel.ts | 2 -- 8 files changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/bot/src/handlers/invites/INVITE_CREATE.ts b/packages/bot/src/handlers/invites/INVITE_CREATE.ts index 7243c9752..90d0e70df 100644 --- a/packages/bot/src/handlers/invites/INVITE_CREATE.ts +++ b/packages/bot/src/handlers/invites/INVITE_CREATE.ts @@ -6,5 +6,6 @@ export async function handleInviteCreate(bot: Bot, data: DiscordGatewayPayload, const payload = data.d as DiscordInviteCreate; + // TODO: Add role_ids, the transformer should be kept for the Invite type, not for the gateway event bot.events.inviteCreate(bot.transformers.invite(bot, payload, { shardId })); } diff --git a/packages/bot/src/transformers/invite.ts b/packages/bot/src/transformers/invite.ts index bb9049e40..63bbf1486 100644 --- a/packages/bot/src/transformers/invite.ts +++ b/packages/bot/src/transformers/invite.ts @@ -35,6 +35,7 @@ export function transformInvite(bot: Bot, payload: DiscordInviteCreate | Discord invite.expiresAt = Date.parse(payload.expires_at); } if (props.flags && payload.flags) invite.flags = new ToggleBitfield(payload.flags); + // @ts-expect-error TODO: Partials if (props.roles && payload.roles) invite.roles = payload.roles.map((role) => bot.transformers.role(bot, role)); } else { if (props.channelId && payload.channel_id) invite.channelId = bot.transformers.snowflake(payload.channel_id); diff --git a/packages/bot/src/transformers/types.ts b/packages/bot/src/transformers/types.ts index 3fc613c3f..205d82480 100644 --- a/packages/bot/src/transformers/types.ts +++ b/packages/bot/src/transformers/types.ts @@ -1144,7 +1144,7 @@ export interface Invite { /** * The roles assigned to the user upon accepting the invite */ - roles?: Role[]; + roles?: Pick[]; } export interface Member { diff --git a/packages/rest/src/manager.ts b/packages/rest/src/manager.ts index 14ef47481..1906462dd 100644 --- a/packages/rest/src/manager.ts +++ b/packages/rest/src/manager.ts @@ -815,17 +815,11 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage return await rest.post(rest.routes.channels.invites(channelId), { body, reason }); } - // When we have to upload a file, we need to use FormData, and each field has to be appended individually + // When we have to upload a file, we need to use FormData, and all other fields need to be part of the payload_json field. const form = new FormData(); - for (const [key, value] of Object.entries(body)) { - if (key !== 'targetUsersFile' && key !== 'roleIds') { - form.append(camelToSnakeCase(key), rest.changeToDiscordFormat(value).toString()); - } - } - + form.append('payload_json', JSON.stringify(rest.changeToDiscordFormat({ ...body, targetUsersFile: undefined }))); form.append('target_users_file', body.targetUsersFile); - if (body.roleIds) form.append('role_ids', body.roleIds.map((x) => x.toString()).join(',')); return await rest.post(rest.routes.channels.invites(channelId), { body: form, reason }); }, diff --git a/packages/rest/src/types.ts b/packages/rest/src/types.ts index 00dab276d..cb23da8f8 100644 --- a/packages/rest/src/types.ts +++ b/packages/rest/src/types.ts @@ -814,10 +814,10 @@ export interface RestManager { * Gets the users allowed to see and accept this invite. * * @param inviteCode - The invite code of the invite to update. - * @returns CSV file with a single column `Users` containing the user IDs. + * @returns CSV file containing the user IDs with the header `user_id` and each user ID from the original file * * @remarks - * Requires the `MANAGE_GUILD` permission. + * Requires called to be the inviter, or have `MANAGE_GUILD` permission, or have `VIEW_AUDIT_LOG` permission. * * @see {@link https://docs.discord.com/developers/resources/invite#get-target-users} */ @@ -829,7 +829,7 @@ export interface RestManager { * @param targetUsersFile - A CSV file with a single column of user IDs for all the users able to accept this invite * * @remarks - * Requires the `MANAGE_GUILD` permission. + * Requires the caller to be the inviter or have the `MANAGE_GUILD` permission. * * Uploading a file with invalid user IDs will result in a 400 with the invalid IDs described. * @@ -843,7 +843,7 @@ export interface RestManager { * @returns An object containing the status of the target users job. * * @remarks - * Requires the `MANAGE_GUILD` permission. + * Requires the caller to be the inviter, or have `MANAGE_GUILD` permission, or have `VIEW_AUDIT_LOG` permission. * * @see {@link https://docs.discord.com/developers/resources/invite#get-target-users-job-status} */ diff --git a/packages/types/src/discord/gateway.ts b/packages/types/src/discord/gateway.ts index db5cd2bea..04bb8de5a 100644 --- a/packages/types/src/discord/gateway.ts +++ b/packages/types/src/discord/gateway.ts @@ -709,6 +709,8 @@ export interface DiscordInviteCreate { uses: number; /** The expiration date of this invite. */ expires_at: string; + /** the role ID(s) for roles in the guild given to the users that accept this invite */ + role_ids?: string[]; } /** https://docs.discord.com/developers/events/gateway-events#invite-delete-invite-delete-event-fields */ diff --git a/packages/types/src/discord/invite.ts b/packages/types/src/discord/invite.ts index 2be994c55..9ef0ce8b7 100644 --- a/packages/types/src/discord/invite.ts +++ b/packages/types/src/discord/invite.ts @@ -42,7 +42,7 @@ export interface DiscordInvite { /** * The roles assigned to the user upon accepting the invite */ - roles?: DiscordRole[]; + roles?: Pick[]; } /** https://docs.discord.com/developers/resources/invite#invite-object-invite-types */ diff --git a/packages/types/src/discordeno/channel.ts b/packages/types/src/discordeno/channel.ts index 9421fb3fc..f45b47fa3 100644 --- a/packages/types/src/discordeno/channel.ts +++ b/packages/types/src/discordeno/channel.ts @@ -304,8 +304,6 @@ export interface CreateChannelInvite { * A csv file with a single column of user IDs for all the users able to accept this invite * * @remarks - * Requires the `MANAGE_GUILD` permission. - * * Uploading a file with invalid user IDs will result in a 400 with the invalid IDs described. */ targetUsersFile?: Blob;