The weeb for the next gen discord boat - Wamellow wamellow.com
bot discord
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: permission alerts to greeting pages (#36)

authored by

Luna Seemann and committed by
GitHub
99278579 ee17bae1

+104 -55
+12
app/dashboard/[guildId]/greeting/farewell/page.tsx
··· 14 14 import { transformer } from "@/utils/bitfields"; 15 15 import { cn } from "@/utils/cn"; 16 16 import { createSelectableItems } from "@/utils/create-selectable-items"; 17 + import { PermissionFlagsBits } from "discord-api-types/v10"; 17 18 import Image from "next/image"; 18 19 import Link from "next/link"; 19 20 import { useParams } from "next/navigation"; 20 21 import { HiArrowLeft, HiChat, HiExternalLink } from "react-icons/hi"; 22 + 23 + import { PermissionAlert } from "../permissions"; 21 24 22 25 export default function Home() { 23 26 const guild = guildStore((g) => g); ··· 37 40 </div> 38 41 ); 39 42 43 + const channel = guild?.channels?.find((channel) => channel.id === data.channelId); 44 + 40 45 return (<> 41 46 <Head guildId={params.guildId as string} /> 47 + 48 + {data.deleteAfter && 49 + <PermissionAlert 50 + channel={channel} 51 + permissions={[PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.ReadMessageHistory]} 52 + /> 53 + } 42 54 43 55 <InputSwitch 44 56 label="Enable Farewell"
+75
app/dashboard/[guildId]/greeting/permissions.tsx
··· 1 + import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; 2 + import type { ApiV1GuildsChannelsGetResponse } from "@/typings"; 3 + import { PermissionFlagsBits } from "discord-api-types/v10"; 4 + import { HiExclamation } from "react-icons/hi"; 5 + 6 + export function PermissionAlert( 7 + { channel, permissions }: { channel?: ApiV1GuildsChannelsGetResponse; permissions: bigint[]; } 8 + ) { 9 + if (channel?.permissions === undefined || !permissions?.length) return null; 10 + 11 + const missingPermission = permissions.find((permission) => ( 12 + (BigInt(channel.permissions) & permission) !== permission 13 + )); 14 + 15 + if (missingPermission === undefined) { 16 + return null; 17 + } 18 + 19 + const permissionName = 20 + Object.entries(PermissionFlagsBits).find(([, value]) => value === missingPermission)?.[0] ?? 21 + missingPermission.toString(); 22 + 23 + return ( 24 + <Alert> 25 + <HiExclamation /> 26 + <AlertTitle>Missing Permissions</AlertTitle> 27 + <AlertDescription> 28 + Wamellow needs the {permissionName} permission to complete actions. 29 + </AlertDescription> 30 + </Alert> 31 + ); 32 + } 33 + 34 + const getMissingPermissionContext = ( 35 + channels: ApiV1GuildsChannelsGetResponse[], 36 + permissions: bigint[] 37 + ): { missingPermissionName: string | null; affectedChannelNames: string[]; } => { 38 + for (const permission of permissions) { 39 + const channelsMissingPermission = channels.filter((channel) => ( 40 + (BigInt(channel?.permissions ?? 0) & permission) !== permission 41 + )); 42 + 43 + if (!channelsMissingPermission.length) continue; 44 + 45 + return { 46 + missingPermissionName: Object.entries(PermissionFlagsBits).find(([, value]) => value === permission)?.[0] ?? permission.toString(), 47 + affectedChannelNames: channelsMissingPermission.map((channel) => channel?.name ?? "Unnamed channel") 48 + }; 49 + 50 + } 51 + 52 + return { missingPermissionName: null, affectedChannelNames: [] }; 53 + }; 54 + 55 + export function PermissionsAlert( 56 + { channels, permissions }: { channels: ApiV1GuildsChannelsGetResponse[]; permissions: bigint[]; } 57 + ) { 58 + if (!channels?.length || !permissions?.length) return null; 59 + 60 + const { missingPermissionName, affectedChannelNames } = getMissingPermissionContext(channels, permissions); 61 + 62 + if (!missingPermissionName || !affectedChannelNames.length) { 63 + return null; 64 + } 65 + 66 + return ( 67 + <Alert> 68 + <HiExclamation /> 69 + <AlertTitle>Missing Permissions</AlertTitle> 70 + <AlertDescription> 71 + Wamellow needs the {missingPermissionName} permission to complete actions in {affectedChannelNames.join(", ")}. 72 + </AlertDescription> 73 + </Alert> 74 + ); 75 + }
+12
app/dashboard/[guildId]/greeting/welcome/page.tsx
··· 16 16 import { transformer } from "@/utils/bitfields"; 17 17 import { cn } from "@/utils/cn"; 18 18 import { createSelectableEmojiItems, createSelectableItems } from "@/utils/create-selectable-items"; 19 + import { PermissionFlagsBits } from "discord-api-types/v10"; 19 20 import Image from "next/image"; 20 21 import Link from "next/link"; 21 22 import { useParams } from "next/navigation"; 22 23 import { HiArrowLeft, HiChat, HiExternalLink } from "react-icons/hi"; 24 + 25 + import { PermissionAlert } from "../permissions"; 23 26 24 27 export default function Home() { 25 28 const guild = guildStore((g) => g); ··· 39 42 </div> 40 43 ); 41 44 45 + const channel = guild?.channels?.find((channel) => channel.id === data.channelId); 46 + 42 47 return (<> 43 48 <Head guildId={params.guildId as string} /> 49 + 50 + {(data.deleteAfterLeave || data.deleteAfter) && 51 + <PermissionAlert 52 + channel={channel} 53 + permissions={[PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.ReadMessageHistory]} 54 + /> 55 + } 44 56 45 57 <InputSwitch 46 58 label="Enable Welcome"
-52
typings.ts
··· 511 511 uses: number; 512 512 users: number; 513 513 snapshot: string; 514 - } 515 - 516 - export enum PermissionFlagsBits { 517 - CreateInstantInvite = 0x00_00_00_00_00_00_00_01, 518 - KickMembers = 0x00_00_00_00_00_00_00_02, 519 - BanMembers = 0x00_00_00_00_00_00_00_04, 520 - Administrator = 0x00_00_00_00_00_00_00_08, 521 - ManageChannels = 0x00_00_00_00_00_00_00_10, 522 - ManageGuild = 0x00_00_00_00_00_00_00_20, 523 - AddReactions = 0x00_00_00_00_00_00_00_40, 524 - ViewAuditLog = 0x00_00_00_00_00_00_00_80, 525 - PrioritySpeaker = 0x00_00_00_00_00_00_01_00, 526 - Stream = 0x00_00_00_00_00_00_02_00, 527 - ViewChannel = 0x00_00_00_00_00_00_04_00, 528 - SendMessages = 0x00_00_00_00_00_00_08_00, 529 - SendTtsMessages = 0x00_00_00_00_00_00_10_00, 530 - ManageMessages = 0x00_00_00_00_00_00_20_00, 531 - EmbedLinks = 0x00_00_00_00_00_00_40_00, 532 - AttachFiles = 0x00_00_00_00_00_00_80_00, 533 - ReadMessageHistory = 0x00_00_00_00_00_01_00_00, 534 - MentionEveryone = 0x00_00_00_00_00_02_00_00, 535 - UseExternalEmojis = 0x00_00_00_00_00_04_00_00, 536 - ViewGuildInsights = 0x00_00_00_00_00_08_00_00, 537 - Connect = 0x00_00_00_00_00_10_00_00, 538 - Speak = 0x00_00_00_00_00_20_00_00, 539 - MuteMembers = 0x00_00_00_00_00_40_00_00, 540 - DeafenMembers = 0x00_00_00_00_00_80_00_00, 541 - MoveMembers = 0x00_00_00_00_01_00_00_00, 542 - UseVad = 0x00_00_00_00_02_00_00_00, 543 - ChangeNickname = 0x00_00_00_00_04_00_00_00, 544 - ManageNicknames = 0x00_00_00_00_08_00_00_00, 545 - ManageRoles = 0x00_00_00_00_10_00_00_00, 546 - ManageWebhooks = 0x00_00_00_00_20_00_00_00, 547 - ManageGuildExpressions = 0x00_00_00_00_40_00_00_00, 548 - UseApplicationCommands = 0x00_00_00_00_80_00_00_00, 549 - RequestToSpeak = 0x00_00_00_01_00_00_00_00, 550 - ManageEvents = 0x00_00_00_02_00_00_00_00, 551 - ManageThreads = 0x00_00_00_04_00_00_00_00, 552 - CreatePublicThreads = 0x00_00_00_08_00_00_00_00, 553 - CreatePrivateThreads = 0x00_00_00_10_00_00_00_00, 554 - UseExternalStickers = 0x00_00_00_20_00_00_00_00, 555 - SendMessagesInThreads = 0x00_00_00_40_00_00_00_00, 556 - UseEmbeddedActivities = 0x00_00_00_80_00_00_00_00, 557 - ModerateMembers = 0x00_00_01_00_00_00_00_00, 558 - ViewCreatorMonetizationAnalytics = 0x00_00_02_00_00_00_00_00, 559 - UseSoundboard = 0x00_00_04_00_00_00_00_00, 560 - CreateGuildExpressions = 0x00_00_08_00_00_00_00_00, 561 - CreateEvents = 0x00_00_10_00_00_00_00_00, 562 - UseExternalSounds = 0x00_00_20_00_00_00_00_00, 563 - SendVoiceMessages = 0x00_00_40_00_00_00_00_00, 564 - SendPolls = 0x00_02_00_00_00_00_00_00, 565 - UseExternalApps = 0x00_04_00_00_00_00_00_00 566 514 }
+5 -3
utils/create-selectable-items.tsx
··· 1 - import { type ApiV1GuildsChannelsGetResponse, type ApiV1GuildsEmojisGetResponse, type ApiV1GuildsRolesGetResponse, PermissionFlagsBits } from "@/typings"; 2 - import { ChannelType } from "discord-api-types/v10"; 1 + import type { ApiV1GuildsChannelsGetResponse, ApiV1GuildsEmojisGetResponse, ApiV1GuildsRolesGetResponse } from "@/typings"; 2 + import { ChannelType, PermissionFlagsBits } from "discord-api-types/v10"; 3 3 import Image from "next/image"; 4 4 import { HiAtSymbol, HiHashtag, HiMenuAlt2, HiNewspaper, HiVolumeUp } from "react-icons/hi"; 5 5 6 6 type Item = ApiV1GuildsChannelsGetResponse | ApiV1GuildsRolesGetResponse; 7 7 type PermissionNames = keyof typeof PermissionFlagsBits | "RoleHirachy"; 8 8 9 + const zero = BigInt(0); 10 + 9 11 function parsePermissions(permissions: number, required: PermissionNames[]) { 10 12 if (permissions === -1 && required.includes("RoleHirachy")) return ["Role is above Wamellow"]; 11 13 12 14 return required 13 15 .filter((perm) => perm !== "RoleHirachy") 14 - .map((perm) => (permissions & PermissionFlagsBits[perm]) === 0 ? perm : false) 16 + .map((perm) => (BigInt(permissions) & PermissionFlagsBits[perm]) === zero ? perm : false) 15 17 .filter(Boolean); 16 18 } 17 19