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.

remove image ai

Luna 14301842 f31180ba

+8 -755
-2
app/(dynamic-assets)/sitemap.xml/route.ts
··· 15 15 }; 16 16 17 17 export async function GET() { 18 - const uploadIds = await fetch(`${process.env.NEXT_PUBLIC_API}/ai/sitemap`, fetchOptions).then((res) => res.json()) as string[]; 19 18 const guildIds = await fetch(`${process.env.NEXT_PUBLIC_API}/guilds`, fetchOptions).then((res) => res.json()) as string[]; 20 19 21 20 const sitemap = [ ··· 75 74 76 75 for (const page of docsMetadata.pages) sitemap.push({ url: getCanonicalUrl("docs", page.file.split(".")[0]), priority: 0.6 }); 77 76 for (const guildId of guildIds) sitemap.push({ url: getCanonicalUrl("leaderboard", guildId), priority: 0.5 }); 78 - for (const uploadId of uploadIds) sitemap.push({ url: getCanonicalUrl("ai-gallery", uploadId), priority: 0.4 }); 79 77 80 78 return new Response(` 81 79 <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+3 -3
app/(home)/faq.component.tsx
··· 37 37 }, 38 38 { 39 39 startContent: <HiCash />, 40 - title: "Is the /image Ai or Text to Speech free to use?", 40 + title: "Is Text to Speech free to use?", 41 41 content: ( 42 42 <div> 43 - Yes, the image Ai and Text to Speech are free to use. However, you might have to <LinkTag href="/vote">vote for Wamellow on top.gg</LinkTag> if you start using it a lot. 43 + Yes, Text to Speech is free to use. However, you might have to <LinkTag href="/vote">vote for Wamellow on top.gg</LinkTag> if you start using it a lot. 44 44 </div> 45 45 ) 46 46 }, ··· 120 120 title: "Which social platforms does Wamellow Notifications support?", 121 121 content: ( 122 122 <div itemProp="text"> 123 - Wamellow currently support YouTube videos, shorts and livestreams; Twitch live stream; and Bluesky (bsky.app) posts, reposts and quote-replies. 123 + Wamellow currently support YouTube videos, shorts and livestreams; Twitch live stream; Bluesky (bsky.app) posts, reposts and quote-replies; and Reddit posts. 124 124 </div> 125 125 ) 126 126 }
+3 -3
app/(home)/page.tsx
··· 96 96 97 97 <span className="text-lg font-medium max-w-[38rem] mb-4"> 98 98 Engage in leaderboards, starboards, and a welcoming atmosphere. 99 - Dive into /anime&apos;s, free /image Ai, and the power of Text-to-Speech. 99 + Dive into /anime&apos;s, free chat ai, and the power of Text-to-Speech. 100 100 Stay updated with dailyposts and receive social notifications! 101 101 </span> 102 102 ··· 374 374 <div> 375 375 <h2 className={styles.h2}>Watchin{"'"} Anime ❤️</h2> 376 376 377 - <Box className="flex flex-col md:flex-row-reverse gap-10 items-center"> 377 + <Box className="flex flex-col md:flex-row gap-10 items-center"> 378 378 <div className="md:w-1/2"> 379 379 <Badge 380 380 className="mb-2" ··· 431 431 <div> 432 432 <h2 className={styles.h2}>Fun leveling and leaderboards 🦄</h2> 433 433 434 - <Box className="flex flex-col md:flex-row gap-10 items-center"> 434 + <Box className="flex flex-col md:flex-row-reverse gap-10 items-center"> 435 435 <div className="md:w-1/2"> 436 436 <Badge 437 437 className="mb-2"
-1
app/[pathname]/page.tsx
··· 18 18 case "logout": return redirect("/login?logout=true"); 19 19 case "docs": 20 20 case "guides": return redirect("/docs/index"); 21 - case "ai": return redirect("/ai-gallery"); 22 21 case "tts": return redirect("https://youtube.com/watch?v=NS5fZ1ltovE"); 23 22 case "disable-commands": return redirect("https://youtube.com/watch?v=ehc0_whydu8"); 24 23 case "youtube": return redirect("https://www.youtube.com/channel/UClWBeVcz5LUmcCN1gHG_GCg");
-65
app/ai-gallery/(home)/filter.component.tsx
··· 1 - "use client"; 2 - 3 - import { useRouter } from "next/navigation"; 4 - import { useEffect, useState } from "react"; 5 - 6 - import Switch from "@/components/inputs/switch"; 7 - import { Button } from "@/components/ui/button"; 8 - import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; 9 - 10 - export function SearchFilter( 11 - { 12 - searchParams 13 - }: { 14 - searchParams: { 15 - page: string; 16 - model: string; 17 - nsfw: string; 18 - }; 19 - } 20 - ) { 21 - const router = useRouter(); 22 - 23 - const [isEmbedded, setEmbedded] = useState(false); 24 - 25 - useEffect(() => { 26 - setEmbedded(window.self !== window.top); 27 - }, []); 28 - 29 - if (isEmbedded) return <></>; 30 - 31 - return ( 32 - <Popover 33 - > 34 - <PopoverTrigger asChild> 35 - <Button> 36 - Search Filter 37 - </Button> 38 - </PopoverTrigger> 39 - 40 - <PopoverContent 41 - className="space-y-2" 42 - align="end" 43 - > 44 - <h4 className="font-medium leading-none">Search Options</h4> 45 - <div className="flex flex-col gap-2 w-full"> 46 - <Switch 47 - className="-mb-5" 48 - name="Show 18+" 49 - defaultState={searchParams.nsfw === "true"} 50 - onSave={(checked) => { 51 - const params = new URLSearchParams(searchParams); 52 - params.delete("nsfw"); 53 - 54 - if (!checked) params.append("nsfw", "true"); 55 - 56 - router.replace(`?${params.toString()}`); 57 - }} 58 - /> 59 - </div> 60 - </PopoverContent> 61 - 62 - </Popover> 63 - ); 64 - 65 - }
-112
app/ai-gallery/(home)/layout.tsx
··· 1 - import type { Metadata } from "next"; 2 - import { Montserrat } from "next/font/google"; 3 - import Image from "next/image"; 4 - import Link from "next/link"; 5 - import { HiUserAdd } from "react-icons/hi"; 6 - 7 - import { Footer } from "@/components/footer"; 8 - import { Button } from "@/components/ui/button"; 9 - import CommandPic from "@/public/image-command.webp"; 10 - import { cn } from "@/utils/cn"; 11 - import { getBaseUrl, getCanonicalUrl } from "@/utils/urls"; 12 - 13 - 14 - const montserrat = Montserrat({ subsets: ["latin"] }); 15 - 16 - interface Props { 17 - children: React.ReactNode; 18 - } 19 - 20 - export const revalidate = 3600; 21 - 22 - export const generateMetadata = (): Metadata => { 23 - 24 - const title = "Free /image Ai for Discord"; 25 - const description = "Summon the enchantment of AI generated images to your Discord server with our versatile /image command, featuring over 40+ distinct SD and 10+ SDXL models."; 26 - const url = getCanonicalUrl("ai-gallery"); 27 - 28 - return { 29 - title, 30 - description, 31 - alternates: { 32 - canonical: url 33 - }, 34 - openGraph: { 35 - title, 36 - description, 37 - type: "website", 38 - url, 39 - images: `${getBaseUrl()}/waya-v3.webp` 40 - }, 41 - twitter: { 42 - card: "summary", 43 - site: "wamellow.com", 44 - title, 45 - description, 46 - images: `${getBaseUrl()}/waya-v3.webp` 47 - } 48 - }; 49 - }; 50 - 51 - export default function RootLayout({ 52 - children 53 - }: Props) { 54 - 55 - return (<> 56 - 57 - <h1 className={cn(montserrat.className, "lg:text-5xl text-4xl font-bold dark:text-neutral-100 text-neutral-900 break-words mb-2 w-full")}> 58 - <span className="bg-gradient-to-r from-indigo-400 to-pink-400 bg-clip-text text-transparent h-20 break-keep">/image Ai</span> 59 - {" generated in "} 60 - <span className="underline decoration-blurple break-keep">Discord</span> 61 - </h1> 62 - 63 - {children} 64 - 65 - <Link 66 - href="/login?invite=true" 67 - prefetch={false} 68 - target="_blank" 69 - > 70 - <Image 71 - alt="/image command usage" 72 - className="w-full rounded-md shadow-md mt-12" 73 - height={580} 74 - src={CommandPic} 75 - width={1550} 76 - /> 77 - </Link> 78 - 79 - <div className="flex gap-2 m-4 w-full"> 80 - <Button 81 - asChild 82 - className="w-1/2 lg:w-fit text-lg font-medium" 83 - variant="secondary" 84 - > 85 - <Link 86 - prefetch={false} 87 - href="/login?invite=true" 88 - > 89 - <HiUserAdd /> 90 - <span className="block sm:hidden">Invite</span> 91 - <span className="hidden sm:block">Invite Wamellow</span> 92 - </Link> 93 - </Button> 94 - <Button 95 - asChild 96 - className="w-1/2 lg:w-fit text-lg" 97 - > 98 - <Link 99 - prefetch={false} 100 - href="/support" 101 - > 102 - <HiUserAdd /> 103 - <span className="block sm:hidden">Support</span> 104 - <span className="hidden sm:block">Join Support</span> 105 - </Link> 106 - </Button> 107 - </div> 108 - 109 - <Footer /> 110 - 111 - </>); 112 - }
-106
app/ai-gallery/(home)/page.tsx
··· 1 - import Image from "next/image"; 2 - import Link from "next/link"; 3 - 4 - import Notice from "@/components/notice"; 5 - import { HomeButton, ScreenMessage, SupportButton } from "@/components/screen-message"; 6 - import { Badge } from "@/components/ui/badge"; 7 - import SadWumpusPic from "@/public/sad-wumpus.gif"; 8 - 9 - import { getUploads } from "../api"; 10 - import { SearchFilter } from "./filter.component"; 11 - import Pagination from "./pagination.component"; 12 - 13 - interface Props { 14 - searchParams: Promise<{ 15 - page: string; 16 - model: string; 17 - nsfw: string; 18 - }>; 19 - } 20 - 21 - export const revalidate = 3600; 22 - 23 - export default async function Home({ searchParams }: Props) { 24 - const { page, model, nsfw } = await searchParams; 25 - 26 - const uploads = await getUploads({ 27 - page: parseInt(page || "1"), 28 - model: model, 29 - nsfw: nsfw === "true" 30 - }); 31 - 32 - if ("message" in uploads) { 33 - return ( 34 - <ScreenMessage 35 - top="0rem" 36 - title="Something went wrong on this page.." 37 - description={uploads.message} 38 - buttons={<> 39 - <HomeButton /> 40 - <SupportButton /> 41 - </>} 42 - > 43 - <Image src={SadWumpusPic} alt="" height={141} width={124} /> 44 - </ScreenMessage> 45 - ); 46 - } 47 - 48 - if (!uploads.results.length) { 49 - return <Notice message="No uploads were found for the specified model." />; 50 - } 51 - 52 - return (<> 53 - 54 - <div className="w-full mb-4 flex justify-between"> 55 - <div 56 - className="text-lg font-medium mb-2" 57 - > 58 - Images that were generated using the /image Ai in discord with Wamellow. 59 - </div> 60 - <SearchFilter searchParams={{ page, model, nsfw }} /> 61 - </div> 62 - 63 - <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 mb-4"> 64 - {uploads.results.map((upload) => ( 65 - <Link 66 - key={"upload-" + upload.id} 67 - className="bg-wamellow rounded-xl shadow-md relative duration-100 outline-violet-500 hover:outline" 68 - href={`/ai-gallery/${upload.id}`} 69 - target="_blank" 70 - > 71 - <Image 72 - alt="" 73 - className="rounded-xl" 74 - height={300} 75 - itemProp="image" 76 - loading="lazy" 77 - src={`https://r2.wamellow.com/ai-image/${upload.id}.webp`} 78 - width={300} 79 - /> 80 - 81 - <Badge 82 - className="absolute top-2 left-2 z-10 backdrop-blur-xl backdrop-brightness-50" 83 - variant="secondary" 84 - size="sm" 85 - > 86 - {upload.model} 87 - </Badge> 88 - 89 - <div className="p-3 flex gap-2"> 90 - <p className="truncate"> 91 - {upload.prompt} 92 - </p> 93 - </div> 94 - 95 - </Link> 96 - ))} 97 - </div> 98 - 99 - <Pagination 100 - key={model} 101 - searchParams={{ page, model }} 102 - pages={uploads.pagination.pages} 103 - /> 104 - 105 - </>); 106 - }
-36
app/ai-gallery/(home)/pagination.component.tsx
··· 1 - "use client"; 2 - 3 - import { Pagination as UiPagination } from "@nextui-org/react"; 4 - import { useRouter } from "next/navigation"; 5 - 6 - export default function Pagination( 7 - { 8 - searchParams, 9 - pages 10 - }: { 11 - searchParams: { page: string; model: string; }; 12 - pages: number; 13 - } 14 - ) { 15 - const router = useRouter(); 16 - 17 - return ( 18 - <UiPagination 19 - className="w-full" 20 - classNames={{ prev: "bg-wamellow", item: "bg-wamellow", next: "bg-wamellow" }} 21 - color="secondary" 22 - showControls 23 - total={pages} 24 - size="lg" 25 - page={parseInt(searchParams.page || "0")} 26 - onChange={(now) => { 27 - const params = new URLSearchParams(searchParams); 28 - params.delete("page"); 29 - params.append("page", now.toString()); 30 - 31 - router.push(`?${params.toString()}`, { scroll: false }); 32 - }} 33 - /> 34 - ); 35 - 36 - }
-155
app/ai-gallery/[uploadId]/layout.tsx
··· 1 - import type { Metadata } from "next"; 2 - import Image from "next/image"; 3 - import Link from "next/link"; 4 - import { HiArrowLeft, HiPlus } from "react-icons/hi"; 5 - 6 - import { Footer } from "@/components/footer"; 7 - import Notice from "@/components/notice"; 8 - import { ScreenMessage } from "@/components/screen-message"; 9 - import { Button } from "@/components/ui/button"; 10 - import { getPageAnalytics } from "@/lib/analytics"; 11 - import { getGuild } from "@/lib/api"; 12 - import SadWumpusPic from "@/public/sad-wumpus.gif"; 13 - import { truncate } from "@/utils/truncate"; 14 - import { getBaseUrl, getCanonicalUrl } from "@/utils/urls"; 15 - 16 - import { getUpload, getUploads } from "../api"; 17 - import Side from "./side.component"; 18 - 19 - export interface Props { 20 - params: Promise<{ uploadId: string; }>; 21 - children: React.ReactNode; 22 - } 23 - 24 - export const revalidate = 3600; 25 - 26 - export const generateMetadata = async ({ params }: Props): Promise<Metadata> => { 27 - const { uploadId } = await params; 28 - 29 - const upload = await getUpload(uploadId); 30 - const prompt = "prompt" in upload 31 - ? truncate(upload.prompt.split(" ").map((str) => str.replace(/^\w/, (char) => char.toUpperCase())).join(" "), 24) 32 - : null; 33 - 34 - const title = prompt ? `${prompt} - /image Ai` : "Free /image Ai for Discord"; 35 - const description = `Amazing AI generated images ${"model" in upload ? `using the ${upload.model}` : ""}, created using Wamellow's versatile /image command for Discord.`.replace(/ +/g, " "); 36 - const images = "id" in upload ? `https://r2.wamellow.com/ai-image/${upload.id}.webp` : `${getBaseUrl()}/waya-v3.webp`; 37 - const url = getCanonicalUrl("ai-gallery", uploadId); 38 - 39 - return { 40 - title, 41 - description, 42 - alternates: { 43 - canonical: url 44 - }, 45 - openGraph: { 46 - title, 47 - description, 48 - type: "website", 49 - url, 50 - images 51 - }, 52 - twitter: { 53 - card: "summary", 54 - site: "wamellow.com", 55 - title, 56 - description, 57 - images 58 - } 59 - }; 60 - }; 61 - 62 - export default async function RootLayout({ params, children }: Props) { 63 - const { uploadId } = await params; 64 - 65 - const upload = await getUpload(uploadId); 66 - 67 - const [guild, uploads, analytics] = await Promise.all([ 68 - "guildId" in upload ? getGuild(upload.guildId) : undefined, 69 - "model" in upload ? getUploads({ query: upload.prompt, nsfw: upload.nsfw }) : undefined, 70 - getPageAnalytics("/ai-gallery/" + uploadId) 71 - ]); 72 - 73 - return ( 74 - <div className="w-full space-y-12"> 75 - 76 - <div className="md:flex"> 77 - 78 - <div className="lg:w-3/4 md:w-2/3 w-full md:mr-6"> 79 - {"id" in upload ? 80 - children 81 - : 82 - <ScreenMessage 83 - top="4rem" 84 - title="Something went wrong!" 85 - description={upload.message} 86 - > 87 - <Image src={SadWumpusPic} alt="" height={141} width={124} /> 88 - </ScreenMessage> 89 - } 90 - </div> 91 - 92 - <div className="lg:w-1/4 md:w-1/3 mt-8 md:mt-0"> 93 - <Side 94 - upload={upload} 95 - guild={guild} 96 - analytics={analytics} 97 - /> 98 - </div> 99 - 100 - </div> 101 - 102 - <div> 103 - <h2 className="text-3xl font-bold mb-4 text-neutral-200">More like this /image</h2> 104 - <span className="relative bottom-3"> 105 - Images that are similiar to the one you{"'"}re viewing. 106 - </span> 107 - 108 - {uploads && "results" in uploads ? 109 - <div className="flex flex-wrap gap-4 mt-2"> 110 - {uploads.results 111 - .map((upload) => ( 112 - <Link 113 - key={upload.id} 114 - className="h-24 w-24" 115 - href={`/ai-gallery/${upload.id}`} 116 - > 117 - <Image 118 - alt={upload.prompt} 119 - className="rounded-lg" 120 - height={128} 121 - src={`https://r2.wamellow.com/ai-image/${upload.id}.webp`} 122 - width={128} 123 - /> 124 - </Link> 125 - ))} 126 - <Link 127 - className="h-24 w-24 border-2 dark:border-wamellow border-wamellow-100 p-4 flex justify-center items-center rounded-lg drop-shadow-md overflow-hidden relative duration-100 outline-violet-500 hover:outline" 128 - href="/login?invite=true" 129 - prefetch={false} 130 - target="_blank" 131 - > 132 - <HiPlus /> 133 - </Link> 134 - </div> 135 - : 136 - <Notice message={uploads?.message || "Something went wrong..."} /> 137 - } 138 - 139 - </div> 140 - 141 - <Button 142 - asChild 143 - className="!mt-5" 144 - > 145 - <Link href="/ai-gallery"> 146 - <HiArrowLeft /> 147 - View all Uploads 148 - </Link> 149 - </Button> 150 - 151 - <Footer className="mt-10" /> 152 - 153 - </div> 154 - ); 155 - }
-59
app/ai-gallery/[uploadId]/page.tsx
··· 1 - import Image from "next/image"; 2 - import Link from "next/link"; 3 - import { HiExternalLink } from "react-icons/hi"; 4 - 5 - import { Badge } from "@/components/ui/badge"; 6 - 7 - import { getUpload } from "../api"; 8 - 9 - export interface Props { 10 - params: Promise<{ uploadId: string; }>; 11 - } 12 - 13 - export const revalidate = 3600; 14 - 15 - export default async function Home({ params }: Props) { 16 - const { uploadId } = await params; 17 - 18 - const upload = await getUpload(uploadId); 19 - if (!upload || "status" in upload) return; 20 - 21 - const src = `https://r2.wamellow.com/ai-image/${upload.id}.webp`; 22 - 23 - return (<> 24 - <div className="relative"> 25 - <Image 26 - alt={upload.prompt} 27 - className="rounded-lg" 28 - height={1024} 29 - src={src} 30 - width={1024} 31 - /> 32 - 33 - <div className="relative md:absolute px-2 md:p-4 bottom-8 md:bottom-0 left-0 z-10 -mb-466md:mb-0"> 34 - <div className="bg-wamellow backdrop-blur-xl backdrop-brightness-50 rounded-lg overflow-hidden shadow-lg p-4"> 35 - <Badge 36 - className="mb-2" 37 - variant="secondary" 38 - radius="rounded" 39 - > 40 - {upload.model} 41 - </Badge> 42 - <div className="text-xl font-medium text-neutral-200">{upload.prompt}</div> 43 - <div className="text-medium">{upload.negativePrompt}</div> 44 - </div> 45 - </div> 46 - </div> 47 - 48 - 49 - <Link 50 - className="my-1 z-20 flex items-center gap-1" 51 - target="_blank" 52 - href={src} 53 - > 54 - Open original file 55 - <HiExternalLink /> 56 - </Link> 57 - 58 - </>); 59 - }
-175
app/ai-gallery/[uploadId]/side.component.tsx
··· 1 - "use client"; 2 - 3 - import { Accordion, AccordionItem } from "@nextui-org/react"; 4 - import Link from "next/link"; 5 - import { useCookies } from "next-client-cookies"; 6 - import { HiHand, HiUserGroup } from "react-icons/hi"; 7 - 8 - import Ad from "@/components/ad"; 9 - import ImageReduceMotion from "@/components/image-reduce-motion"; 10 - import { Share } from "@/components/share"; 11 - import { formatDate } from "@/components/time"; 12 - import { Badge } from "@/components/ui/badge"; 13 - import type { AnalyticsError, AnalyticsResponse } from "@/lib/analytics"; 14 - import type { ApiError, ApiV1GuildsGetResponse, ApiV1UploadGetResponse } from "@/typings"; 15 - import { getCanonicalUrl } from "@/utils/urls"; 16 - 17 - export default function Side({ 18 - upload, 19 - guild, 20 - analytics 21 - }: { 22 - upload: ApiV1UploadGetResponse | ApiError; 23 - guild: ApiV1GuildsGetResponse | ApiError | undefined; 24 - analytics: { results: AnalyticsResponse[]; } | AnalyticsError | undefined; 25 - }) { 26 - const cookies = useCookies(); 27 - 28 - return ( 29 - <div className="flex flex-col gap-3"> 30 - {"prompt" in upload && 31 - <Share 32 - title="Share image" 33 - url={getCanonicalUrl("ai-gallery", upload.id)} 34 - text={`${upload.author.username} created an #ai image with the #wamellow bot for #discord using ${upload.model}!`} 35 - /> 36 - } 37 - 38 - <Ad 39 - title="/image AI for free" 40 - description="Create your own amazing AI /image's with Wamellow for free in your Discord Server using the best SDXL models and 40+ more!" 41 - /> 42 - 43 - <Accordion 44 - variant="shadow" 45 - className="bg-wamellow" 46 - selectionMode="multiple" 47 - defaultExpandedKeys={["1"]} 48 - disableAnimation={cookies.get("reduceMotions") === "true"} 49 - > 50 - <AccordionItem 51 - key="1" 52 - aria-label="about" 53 - title="About" 54 - classNames={{ content: "mb-2 space-y-4" }} 55 - > 56 - 57 - <div className="flex items-center justify-between"> 58 - <span>Created</span> 59 - <Badge> 60 - {"createdAt" in upload ? 61 - formatDate(upload.createdAt as string, "en-US") 62 - : 63 - "unknown" 64 - } 65 - </Badge> 66 - </div> 67 - 68 - {analytics && "results" in analytics && 69 - <div className="flex items-center justify-between"> 70 - Views 71 - <Badge> 72 - {Array.isArray(analytics.results) && analytics.results.length ? 73 - (analytics.results[0].pageviews) 74 - : 75 - "unknown" 76 - } 77 - {" "} 78 - views 79 - </Badge> 80 - </div> 81 - } 82 - 83 - {"author" in upload && 84 - <div className="flex items-center justify-between"> 85 - Author 86 - <Badge className="flex"> 87 - <ImageReduceMotion 88 - className="rounded-full relative right-1" 89 - alt="uploader's avatar" 90 - url={"https://cdn.discordapp.com/avatars/" + upload.authorId + "/" + upload.author.avatar} 91 - size={16} 92 - /> 93 - 94 - {upload.author.username} 95 - 96 - {upload.author.bot && 97 - <Badge 98 - className="relative left-1" 99 - color="secondary" 100 - variant="flat" 101 - size="xs" 102 - > 103 - BOT 104 - </Badge> 105 - } 106 - </Badge> 107 - </div> 108 - } 109 - 110 - <div className="flex items-center justify-between"> 111 - Rating 112 - <Badge 113 - className="font-bold select-none" 114 - variant={"nsfw" in upload && upload.nsfw ? "destructive" : "default"} 115 - > 116 - {"nsfw" in upload && upload.nsfw 117 - ? <HiHand /> 118 - : <HiUserGroup /> 119 - } 120 - {"nsfw" in upload && upload.nsfw ? "Mature" : "Everyone"} 121 - </Badge> 122 - </div> 123 - 124 - </AccordionItem> 125 - </Accordion> 126 - 127 - <Accordion 128 - variant="shadow" 129 - className="bg-wamellow" 130 - selectionMode="multiple" 131 - disableAnimation={cookies.get("reduceMotions") === "true"} 132 - > 133 - <AccordionItem 134 - key="1" 135 - aria-label="command" 136 - title="Command" 137 - classNames={{ content: "mb-2 space-y-4" }} 138 - subtitle="Copy it and run it in Discord" 139 - > 140 - {"prompt" in upload && 141 - <span className="select-all"> 142 - /image query: {upload?.prompt} {upload?.negativePrompt && `negative-query: ${upload?.negativePrompt}`} model: {upload?.model} 143 - </span> 144 - } 145 - </AccordionItem> 146 - </Accordion> 147 - 148 - {guild && "id" in guild && 149 - <Link 150 - href={getCanonicalUrl("leaderboard", guild.id, "?utm_source=wamellow.com&utm_medium=ai-gallery")} 151 - target="_blank" 152 - className="flex gap-3 items-center border-2 border-wamellow p-3 rounded-lg shadow-md" 153 - > 154 - <ImageReduceMotion 155 - className="rounded-full h-12 w-12" 156 - alt="Server icon" 157 - url={`https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}`} 158 - size={56} 159 - /> 160 - 161 - <div> 162 - <div className="text-lg text-neutral-200 font-semibold truncate">{guild.name}</div> 163 - <div className="text-sm font-medium">View leaderboard</div> 164 - </div> 165 - </Link> 166 - } 167 - 168 - <div className="hidden xl:block"> 169 - The image has been generated by artificial intelligence (AI) and not by a human creator. Wamellow and its developers disclaim any responsibility for the content of the images and reserve all rights to the media. 170 - </div> 171 - 172 - </div> 173 - ); 174 - 175 - }
-36
app/ai-gallery/api.ts
··· 1 - import { defaultFetchOptions } from "@/lib/api"; 2 - import type { 3 - ApiError, 4 - ApiV1UploadGetResponse, 5 - ApiV1UploadsGetResponse 6 - } from "@/typings"; 7 - 8 - export async function getUploads(options?: Partial<{ 9 - model: string; 10 - nsfw: boolean; 11 - page: number; 12 - query: string; 13 - }>): Promise<ApiV1UploadsGetResponse | ApiError> { 14 - 15 - const params = new URLSearchParams(); 16 - if (options?.model) params.append("model", options.model); 17 - if (options?.nsfw) params.append("nsfw", options.nsfw.toString()); 18 - if (options?.page) params.append("page", (options.page - 1).toString()); 19 - if (options?.query) params.append("query", options.query); 20 - 21 - const res = await fetch( 22 - `${process.env.NEXT_PUBLIC_API}/ai?${params.toString()}`, 23 - defaultFetchOptions 24 - ); 25 - 26 - return res.json(); 27 - } 28 - 29 - export async function getUpload(uploadId: string): Promise<ApiV1UploadGetResponse | ApiError> { 30 - const res = await fetch( 31 - `${process.env.NEXT_PUBLIC_API}/ai/${uploadId}`, 32 - defaultFetchOptions 33 - ); 34 - 35 - return res.json(); 36 - }
+1 -1
app/layout.tsx
··· 42 42 export const generateMetadata = (): Metadata => { 43 43 44 44 const title = "Wamellow: Next-gen of Discord Bots & Apps"; 45 - const description = "Engage with leaderboards, starboards, and welcoming atmosphere. Dive into anime discussions, enjoy free /image AI and unleash the power of Text-To-Speech."; 45 + const description = "Engage with leaderboards, starboards, and welcoming atmosphere. Dive into anime discussions, enjoy free chat aI and unleash the power of Text-To-Speech."; 46 46 47 47 return { 48 48 metadataBase: new URL(getBaseUrl()),
+1 -1
app/login/route.ts
··· 20 20 21 21 const permissions = [ 22 22 PermissionFlagsBits.AddReactions, // greetings 23 - PermissionFlagsBits.AttachFiles, // /image, /tts, leaderboards 23 + PermissionFlagsBits.AttachFiles, // /tts, leaderboards 24 24 PermissionFlagsBits.BanMembers, // passport 25 25 PermissionFlagsBits.Connect, // /tts 26 26 PermissionFlagsBits.CreatePublicThreads, // nsfw moderation