diff --git a/src/discord/send.permissions.ts b/src/discord/send.permissions.ts index a360622f35d..ae09e4c29fe 100644 --- a/src/discord/send.permissions.ts +++ b/src/discord/send.permissions.ts @@ -10,6 +10,8 @@ import { normalizeDiscordToken } from "./token.js"; const PERMISSION_ENTRIES = Object.entries(PermissionFlagsBits).filter( ([, value]) => typeof value === "bigint", ); +const ALL_PERMISSIONS = PERMISSION_ENTRIES.reduce((acc, [, value]) => acc | value, 0n); +const ADMINISTRATOR_BIT = PermissionFlagsBits.Administrator; type DiscordClientOpts = { token?: string; @@ -68,6 +70,10 @@ function bitfieldToPermissions(bitfield: bigint) { .toSorted(); } +function hasAdministrator(bitfield: bigint) { + return (bitfield & ADMINISTRATOR_BIT) === ADMINISTRATOR_BIT; +} + export function isThreadChannelType(channelType?: number) { return ( channelType === ChannelType.GuildNewsThread || @@ -121,6 +127,17 @@ export async function fetchChannelPermissionsDiscord( } } + if (hasAdministrator(base)) { + return { + channelId, + guildId, + permissions: bitfieldToPermissions(ALL_PERMISSIONS), + raw: ALL_PERMISSIONS.toString(), + isDm: false, + channelType, + }; + } + let permissions = base; const overwrites = "permission_overwrites" in channel ? (channel.permission_overwrites ?? []) : []; diff --git a/src/discord/send.sends-basic-channel-messages.test.ts b/src/discord/send.sends-basic-channel-messages.test.ts index a649822adee..1e2ddeaf39c 100644 --- a/src/discord/send.sends-basic-channel-messages.test.ts +++ b/src/discord/send.sends-basic-channel-messages.test.ts @@ -447,6 +447,34 @@ describe("fetchChannelPermissionsDiscord", () => { expect(res.permissions).toContain("SendMessages"); expect(res.isDm).toBe(false); }); + + it("treats Administrator as all permissions despite overwrites", async () => { + const { rest, getMock } = makeRest(); + getMock + .mockResolvedValueOnce({ + id: "chan1", + guild_id: "guild1", + permission_overwrites: [ + { + id: "guild1", + deny: PermissionFlagsBits.ViewChannel.toString(), + allow: "0", + }, + ], + }) + .mockResolvedValueOnce({ id: "bot1" }) + .mockResolvedValueOnce({ + id: "guild1", + roles: [{ id: "guild1", permissions: PermissionFlagsBits.Administrator.toString() }], + }) + .mockResolvedValueOnce({ roles: [] }); + const res = await fetchChannelPermissionsDiscord("chan1", { + rest, + token: "t", + }); + expect(res.permissions).toContain("Administrator"); + expect(res.permissions).toContain("ViewChannel"); + }); }); describe("readMessagesDiscord", () => {