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

Configure Feed

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

add dashboard automoderation

Luna 280de8e6 df4ae9c4

+123 -178
+2 -2
app/dashboard/[guildId]/layout.tsx
··· 189 189 icon: <HiPaperAirplane className="rotate-45" /> 190 190 }, 191 191 { 192 - name: "NSFW Moderation", 193 - value: "/nsfw-image-scanning", 192 + name: "Moderation", 193 + value: "/moderation", 194 194 icon: <HiEye /> 195 195 }, 196 196 {
+115
app/dashboard/[guildId]/moderation/page.tsx
··· 1 + "use client"; 2 + 3 + import { ChannelType } from "discord-api-types/v10"; 4 + import Image from "next/image"; 5 + import { useParams } from "next/navigation"; 6 + import { useCallback } from "react"; 7 + import { HiViewGridAdd } from "react-icons/hi"; 8 + import { useQuery, useQueryClient } from "react-query"; 9 + 10 + import { guildStore } from "@/common/guilds"; 11 + import MultiSelectMenu from "@/components/inputs/multi-select-menu"; 12 + import Switch from "@/components/inputs/switch"; 13 + import { ScreenMessage } from "@/components/screen-message"; 14 + import { cacheOptions, getData } from "@/lib/api"; 15 + import SadWumpusPic from "@/public/sad-wumpus.gif"; 16 + import { type ApiV1GuildsModulesAutomodGetResponse, AutomodType } from "@/typings"; 17 + import { createSelectableItems } from "@/utils/create-selectable-items"; 18 + 19 + const AUTOMOD_TYPES = Object 20 + .values(AutomodType) 21 + .filter((type) => typeof type === "string"); 22 + 23 + export default function Home() { 24 + const guild = guildStore((g) => g); 25 + const params = useParams(); 26 + 27 + const url = `/guilds/${params.guildId}/modules/automod` as const; 28 + const queryClient = useQueryClient(); 29 + 30 + const { data, isLoading, error } = useQuery( 31 + url, 32 + () => getData<ApiV1GuildsModulesAutomodGetResponse>(url), 33 + { 34 + enabled: !!params.guildId, 35 + ...cacheOptions 36 + } 37 + ); 38 + 39 + const enabled = data && !("message" in data) && Object.values(data.status).some(Boolean); 40 + 41 + const edit = useCallback( 42 + <K extends keyof ApiV1GuildsModulesAutomodGetResponse>(key: K, value: ApiV1GuildsModulesAutomodGetResponse[K]) => { 43 + if (!data || "message" in data) return; 44 + 45 + queryClient.setQueryData<ApiV1GuildsModulesAutomodGetResponse>(url, () => ({ 46 + ...data, 47 + [key]: value 48 + })); 49 + }, 50 + [data] 51 + ); 52 + 53 + if (error || (data && "message" in data)) { 54 + return ( 55 + <ScreenMessage 56 + top="0rem" 57 + title="Something went wrong on this page.." 58 + description={ 59 + (data && "message" in data ? data.message : `${error}`) 60 + || "An unknown error occurred."} 61 + href={`/dashboard/${guild?.id}`} 62 + button="Go back to overview" 63 + icon={<HiViewGridAdd />} 64 + > 65 + <Image src={SadWumpusPic} alt="" height={141} width={124} /> 66 + </ScreenMessage> 67 + ); 68 + } 69 + 70 + if (isLoading || !data) return <></>; 71 + 72 + return (<> 73 + {AUTOMOD_TYPES.map((type) => ( 74 + <Switch 75 + key={type} 76 + name={`Block ${type.replace(/^\w/, (c) => c.toUpperCase())}`} 77 + description={`Prevent ${type.replace(/s$/, "")} links from being sent.`} 78 + url={`${url}/${type}`} 79 + dataName="enabled" 80 + defaultState={data.status[type] || false} 81 + onSave={(value) => { 82 + data.status[type] = value; 83 + edit("status", data.status); 84 + }} 85 + /> 86 + ))} 87 + 88 + <div className="lg:flex gap-3"> 89 + <div className="lg:w-1/2"> 90 + <MultiSelectMenu 91 + name="Whitelist channels" 92 + url={url} 93 + dataName="whitelistChannelIds" 94 + items={createSelectableItems(guild?.channels, [], [ChannelType.GuildCategory, ChannelType.GuildText, ChannelType.GuildAnnouncement])} 95 + description="Select channels where messages should not be moderated in." 96 + defaultState={data.whitelistChannelIds} 97 + max={50} 98 + disabled={!enabled} 99 + /> 100 + </div> 101 + <div className="lg:w-1/2"> 102 + <MultiSelectMenu 103 + name="Whitelist roles" 104 + url={url} 105 + dataName="whitelistRoleIds" 106 + items={createSelectableItems(guild?.roles)} 107 + description="Select roles by who messages should not be moderated for." 108 + defaultState={data.whitelistRoleIds} 109 + max={20} 110 + disabled={!enabled} 111 + /> 112 + </div> 113 + </div> 114 + </>); 115 + }
-164
app/dashboard/[guildId]/nsfw-image-scanning/page.tsx
··· 1 - "use client"; 2 - 3 - import { Code } from "@nextui-org/react"; 4 - import { ChannelType } from "discord-api-types/v10"; 5 - import Image from "next/image"; 6 - import { useParams } from "next/navigation"; 7 - import { useState } from "react"; 8 - import { HiViewGridAdd } from "react-icons/hi"; 9 - import { useQuery } from "react-query"; 10 - 11 - import { guildStore } from "@/common/guilds"; 12 - import MultiSelectMenu from "@/components/inputs/multi-select-menu"; 13 - import SelectMenu from "@/components/inputs/select-menu"; 14 - import Slider from "@/components/inputs/slider-input"; 15 - import Switch from "@/components/inputs/switch"; 16 - import Notice, { NoticeType } from "@/components/notice"; 17 - import { ScreenMessage } from "@/components/screen-message"; 18 - import { cacheOptions, getData } from "@/lib/api"; 19 - import SadWumpusPic from "@/public/sad-wumpus.gif"; 20 - import type { ApiError, ApiV1GuildsModulesNsfwModerationGetResponse } from "@/typings"; 21 - import { createSelectableItems } from "@/utils/create-selectable-items"; 22 - 23 - export default function Home() { 24 - const guild = guildStore((g) => g); 25 - const params = useParams(); 26 - 27 - const url = `/guilds/${params.guildId}/modules/nsfw-image-scanning` as const; 28 - 29 - const [data, setData] = useState<ApiV1GuildsModulesNsfwModerationGetResponse | ApiError>(); 30 - 31 - const { isLoading, error } = useQuery( 32 - url, 33 - () => getData<ApiV1GuildsModulesNsfwModerationGetResponse>(url), 34 - { 35 - enabled: !!params.guildId, 36 - onSuccess: (d) => setData(d), 37 - ...cacheOptions, 38 - refetchOnMount: true 39 - } 40 - ); 41 - 42 - const handleSwitchToggle = (enabled: boolean) => { 43 - if (!data) return; 44 - const updatedLocalData = { ...data, enabled }; 45 - setData(updatedLocalData); 46 - }; 47 - 48 - if (error || (data && "message" in data)) { 49 - return ( 50 - <ScreenMessage 51 - top="0rem" 52 - title="Something went wrong on this page.." 53 - description={ 54 - (data && "message" in data ? data.message : `${error}`) 55 - || "An unknown error occurred."} 56 - href={`/dashboard/${guild?.id}`} 57 - button="Go back to overview" 58 - icon={<HiViewGridAdd />} 59 - > 60 - <Image src={SadWumpusPic} alt="" height={141} width={124} /> 61 - </ScreenMessage> 62 - ); 63 - } 64 - 65 - if (isLoading || !data) return <></>; 66 - 67 - return (<> 68 - <Notice 69 - type={NoticeType.Info} 70 - message="Images can be false positives or false negatives. This does not replace human moderation." 71 - /> 72 - 73 - <Switch 74 - name="NSFW image moderation enabled." 75 - url={url} 76 - dataName="enabled" 77 - defaultState={data.enabled || false} 78 - disabled={false} 79 - onSave={handleSwitchToggle} 80 - /> 81 - 82 - <div className="lg:flex gap-3"> 83 - <div className="lg:w-1/2"> 84 - <SelectMenu 85 - name="Logging channel" 86 - url={url} 87 - dataName="logChannelId" 88 - items={createSelectableItems(guild?.channels)} 89 - description="Select the channel where the logs should be send into." 90 - defaultState={data.logChannelId} 91 - disabled={!data.enabled} 92 - /> 93 - </div> 94 - <div className="lg:w-1/2"> 95 - <SelectMenu 96 - name="Punishment" 97 - url={url} 98 - dataName="punishment" 99 - items={[ 100 - { 101 - name: "Nothing", 102 - value: 0 103 - }, 104 - { 105 - name: "Ban member", 106 - value: 1 107 - }, 108 - { 109 - name: "Kick member", 110 - value: 2 111 - }, 112 - { 113 - name: "Delete message", 114 - value: 3 115 - } 116 - ]} 117 - description="Select what should happen if a user sets images containing nsfw." 118 - defaultState={data.punishment} 119 - disabled={!data.enabled} 120 - /> 121 - </div> 122 - </div> 123 - 124 - <div className="lg:flex gap-3"> 125 - <div className="lg:w-1/2"> 126 - <MultiSelectMenu 127 - name="Whitelist channels" 128 - url={url} 129 - dataName="whitelistChannelIds" 130 - items={createSelectableItems(guild?.channels, [], [ChannelType.GuildCategory, ChannelType.GuildText, ChannelType.GuildAnnouncement])} 131 - description="Select channels where images should not be moderated in." 132 - defaultState={data.whitelistChannelIds} 133 - max={500} 134 - disabled={!data.enabled} 135 - /> 136 - </div> 137 - <div className="lg:w-1/2"> 138 - <MultiSelectMenu 139 - name="Whitelist roles" 140 - url={url} 141 - dataName="whitelistRoleIds" 142 - items={createSelectableItems(guild?.roles)} 143 - description="Select roles by who images should not be moderated for." 144 - defaultState={data.whitelistRoleIds} 145 - max={500} 146 - disabled={!data.enabled} 147 - /> 148 - </div> 149 - </div> 150 - 151 - <Slider 152 - name="Threshold" 153 - description="The threshold at which an image should be considered NSFW; low values are sensitive, high values are lax." 154 - url={url} 155 - dataName="threshold" 156 - defaultState={data.threshold || 0.5} 157 - /> 158 - 159 - <span className="mb-2" > 160 - Members with the <Code color="secondary">Manage Messages</Code> permission bypass the NSFW image scanning automatically. <br /> 161 - </span> 162 - 163 - </>); 164 - }
+6 -12
typings.ts
··· 329 329 createdAt: Date; 330 330 } 331 331 332 - export interface ApiV1GuildsModulesNsfwModerationGetResponse { 333 - enabled: boolean; 334 - logChannelId: string | null; 335 - /** 336 - * @example 337 - * 0 - Nothing 338 - * 1 - Ban 339 - * 2 - Kick 340 - * 3 - Delete message 341 - */ 342 - punishment: 0 | 1 | 2 | 3; 343 - threshold: number; 332 + export enum AutomodType { 333 + BlockInvites = "invites", 334 + BlockTwitter = "twitter" 335 + } 344 336 337 + export interface ApiV1GuildsModulesAutomodGetResponse { 338 + status: Record<AutomodType, boolean>; 345 339 whitelistChannelIds: string[]; 346 340 whitelistRoleIds: string[]; 347 341 }