/* eslint-disable @next/next/no-img-element */ import { ImageResponse } from "next/og"; import sharp from "sharp"; import { elementBackgroundColor } from "@/lib/catalog/element"; import { rarityGradient } from "@/lib/catalog/rarity"; import { isCustomTravelerKey } from "@/lib/catalog/travelers"; import { getCachedPublicTeamExport } from "@/lib/cache/public"; import { imageContentDisposition } from "@/lib/guides/export-image"; const anuphanFonts = Promise.all( [ { subset: "latin", weight: 400 as const }, { subset: "thai", weight: 400 as const }, { subset: "latin", weight: 700 as const }, { subset: "thai", weight: 700 as const }, ].map(async ({ subset, weight }) => { const response = await fetch( `https://unpkg.com/@fontsource/anuphan@5.3.0/files/anuphan-${subset}-${weight}-normal.woff`, ); if (!response.ok) throw new Error(`Unable to load Anuphan ${subset} ${weight}`); return { name: "Anuphan", data: await response.arrayBuffer(), weight, style: "normal" as const, }; }), ).catch(() => []); function amount(value: number): string { return new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format( value, ); } async function pngDataUrl(url: string): Promise { try { const response = await fetch(url); if (!response.ok) return null; const png = await sharp(Buffer.from(await response.arrayBuffer())) .resize(300, 300, { fit: "cover" }) .png() .toBuffer(); return `data:image/png;base64,${png.toString("base64")}`; } catch { return null; } } export async function GET( _request: Request, context: RouteContext<"/api/teams/[teamId]/image">, ) { const { teamId } = await context.params; const team = await getCachedPublicTeamExport(teamId); if (!team) return new Response("ไม่พบทีม", { status: 404 }); const total = team.members.reduce( (sum, member) => sum + Number(member.damage), 0, ); const seconds = Number(team.rotationSeconds); const dps = seconds > 0 ? total / seconds : 0; const [fonts, memberAssets] = await Promise.all([ anuphanFonts, Promise.all( team.members.map(async (member) => { const [character, weapon, artifact, secondArtifact] = await Promise.all([ pngDataUrl(member.character.imageUrl), member.weapon ? pngDataUrl(member.weapon.imageUrl) : null, member.artifact ? pngDataUrl(member.artifact.imageUrl) : null, member.secondArtifact ? pngDataUrl(member.secondArtifact.imageUrl) : null, ]); return { character, weapon, artifact, secondArtifact }; }), ), ]); return new ImageResponse(
{team.guideName}
{team.title} · ระยะเวลาโรเทชั่น{" "} {seconds.toLocaleString(undefined, { maximumFractionDigits: 2 })} วิ
{team.members.map((member, index) => { const damage = Number(member.damage); const percent = total > 0 ? (damage / total) * 100 : 0; return (
{memberAssets[index].character ? ( ) : (
{member.character.name.slice(0, 1)}
)}
C{member.constellation}
{member.weapon && memberAssets[index].weapon ? (
R{member.refinement}
) : null} {member.artifact && memberAssets[index].artifact ? (
) : null} {member.secondArtifact && memberAssets[index].secondArtifact ? (
) : null}
{member.character.name}
{amount(damage)}
{percent.toFixed(1)}%
); })}
{team.combo}
DPS: {amount(dps)} | DPR: {amount(total)}
By YT เกนชินไม่ใช่เกมมือถือ
, { width: 1080, height: 1350, fonts: fonts.length ? fonts : undefined, headers: { "Cache-Control": "no-store", "Content-Disposition": imageContentDisposition(team.title, "team"), "Content-Type": "image/png", }, }, ); }