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

add leaderboard open graph img

Changed files
+86 -6
app
leaderboard
[guildId]
open-graph.png
assets
public
+1 -1
app/leaderboard/[guildId]/api.ts
··· 1 1 import { ApiV1GuildsGetResponse, ApiV1GuildsModulesLeaderboardGetResponse, ApiV1GuildsTopmembersGetResponse, ApiV1GuildsTopmembersPaginationGetResponse } from "@/typings"; 2 2 3 - export async function getGuild(guildId: string): Promise<ApiV1GuildsGetResponse> { 3 + export async function getGuild(guildId: string): Promise<ApiV1GuildsGetResponse | undefined> { 4 4 const res = await fetch(`${process.env.NEXT_PUBLIC_API}/guilds/${guildId}`, { 5 5 headers: { Authorization: process.env.API_SECRET as string }, 6 6 next: { revalidate: 60 * 60 }
+10 -5
app/leaderboard/[guildId]/layout.tsx
··· 12 12 import { getDesign, getGuild, getPagination } from "./api"; 13 13 import Side from "./side.component"; 14 14 15 - interface LeaderboardProps { 15 + export interface LeaderboardProps { 16 16 params: { guildId: string }, 17 17 children: React.ReactNode; 18 18 } ··· 25 25 const guild = await getGuild(params.guildId); 26 26 27 27 const title = `${guild?.name || "Unknown"}'s Leaderboard`; 28 - const description = `Effortlessly discover the most active chatters, voice timers, and acknowledge top inviters. Explore the vibrant community dynamics of the ${guild?.name || "unknown"} discord server right from your web browser.`; 28 + const description = `Discover the most active chatters, voice timers, and top inviters. ${guild?.name ? `Explore the community of the ${guild.name} discord server right from your web browser.` : ""}`; 29 29 const url = getCanonicalUrl("leaderboard", params.guildId); 30 30 31 31 return { ··· 39 39 description, 40 40 url, 41 41 type: "website", 42 - images: guild?.icon ? `https://cdn.discordapp.com/icons/${guild?.id}/${guild?.icon}.webp?size=256` : "/discord.png" 42 + images: { 43 + url: `https://4099-2001-871-21c-d364-74bb-b3f8-c7d7-1ffe.ngrok-free.app/leaderboard/${params.guildId}/open-graph.png`, 44 + width: 1200, 45 + height: 630, 46 + type: "image/png" 47 + } 43 48 }, 44 49 twitter: { 45 - card: "summary", 50 + card: "summary_large_image", 46 51 title, 47 52 description 48 53 }, 49 - robots: guild.name ? "index, follow" : "noindex" 54 + robots: guild?.name ? "index, follow" : "noindex" 50 55 }; 51 56 }; 52 57
+75
app/leaderboard/[guildId]/open-graph.png/route.tsx
··· 1 + /* eslint-disable @next/next/no-img-element */ 2 + 3 + import { readFile } from "fs/promises"; 4 + import { ImageResponse } from "next/og"; 5 + import { NextRequest } from "next/server"; 6 + 7 + import { getGuild, getTopMembers } from "../api"; 8 + import { LeaderboardProps } from "../layout"; 9 + 10 + // export const revalidate = 3600; // 1 hour 11 + 12 + export async function GET( 13 + request: NextRequest, 14 + { params }: LeaderboardProps 15 + ) { 16 + const guild = await getGuild(params.guildId); 17 + const members = await getTopMembers(params.guildId, { page: 1, type: "messages" }); 18 + 19 + const intl = new Intl.NumberFormat("en", { notation: "standard" }); 20 + 21 + return new ImageResponse( 22 + ( 23 + <div tw="bg-[#0d0f11] p-18 flex flex-col w-full h-full text-6xl text-white"> 24 + <div tw="flex mb-6"> 25 + <span tw="text-3xl bg-violet-400/75 opacity-80 pt-2 px-4 rounded-xl w-min" style={{ fontWeight: 500 }}>Leaderboard</span> 26 + </div> 27 + <div tw="flex mb-2 items-center"> 28 + {guild?.icon && <img src={`https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.png`} tw="h-20 w-20 rounded-2xl relative bottom-3 mr-5" alt="" />} 29 + <div style={{ fontWeight: 800, fontSize: "5rem" }}>{guild?.name || "unknown"}</div> 30 + </div> 31 + <div tw="text-4xl text-gray-500" style={{ fontWeight: 500 }}>Explore the vibrant community dynamics</div> 32 + 33 + <div tw="flex justify-between mt-42"> 34 + {members.slice(0, 3).map((member) => ( 35 + <div key={member.id} tw="flex flex-col"> 36 + <div tw="flex mb-2 text-5xl" style={{ fontWeight: 600 }}>@{member.username}</div> 37 + <div tw="text-2xl text-gray-400 flex text-3xl" style={{ fontWeight: 500 }}>{intl.format(member.activity.messages)} messages</div> 38 + </div> 39 + ))} 40 + </div> 41 + 42 + </div> 43 + ), 44 + { 45 + width: 1200, 46 + height: 630, 47 + fonts: [ 48 + { 49 + name: "Poppins", 50 + data: await readFile(process.cwd() + "/assets/Poppins-Regular.ttf"), 51 + style: "normal", 52 + weight: 400 53 + }, 54 + { 55 + name: "Poppins", 56 + data: await readFile(process.cwd() + "/assets/Poppins-Medium.ttf"), 57 + style: "normal", 58 + weight: 500 59 + }, 60 + { 61 + name: "Poppins", 62 + data: await readFile(process.cwd() + "/assets/Poppins-SemiBold.ttf"), 63 + style: "normal", 64 + weight: 600 65 + }, 66 + { 67 + name: "Poppins", 68 + data: await readFile(process.cwd() + "/assets/Poppins-ExtraBold.ttf"), 69 + style: "normal", 70 + weight: 800 71 + } 72 + ] 73 + } 74 + ); 75 + }
assets/Poppins-ExtraBold.ttf

This is a binary file and will not be displayed.

assets/Poppins-Medium.ttf

This is a binary file and will not be displayed.

assets/Poppins-Regular.ttf

This is a binary file and will not be displayed.

assets/Poppins-SemiBold.ttf

This is a binary file and will not be displayed.

public/shiggy.webm

This is a binary file and will not be displayed.