diff --git a/.env.example b/.env.example index 21aae90..25f8efe 100644 --- a/.env.example +++ b/.env.example @@ -4,3 +4,6 @@ PORT=8080 REDIS_URL=redis://username:password@localhost:6379 REDIS_TTL=3600 # seconds + +# if you wish to get discord badges +DISCORD_TOKEN=discord_bot_token diff --git a/README.md b/README.md index ab230e9..5084653 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,9 @@ REDIS_URL=redis://username:password@localhost:6379 # Value is in seconds REDIS_TTL=3600 + +#only use this if you want to show discord badges +DISCORD_TOKEN=discord_bot_token ``` ## Endpoint diff --git a/config/discordBadges.ts b/config/discordBadges.ts new file mode 100644 index 0000000..7e0e258 --- /dev/null +++ b/config/discordBadges.ts @@ -0,0 +1,87 @@ +export const discordBadges = { + // User badges + HYPESQUAD: 2 << 2, + HYPESQUAD_ONLINE_HOUSE_1: 2 << 6, + HYPESQUAD_ONLINE_HOUSE_2: 2 << 7, + HYPESQUAD_ONLINE_HOUSE_3: 2 << 8, + + STAFF: 2 << 0, + PARTNER: 2 << 1, + CERTIFIED_MODERATOR: 2 << 18, + + VERIFIED_DEVELOPER: 2 << 17, + ACTIVE_DEVELOPER: 2 << 22, + + PREMIUM_EARLY_SUPPORTER: 2 << 9, + + BUG_HUNTER_LEVEL_1: 2 << 3, + BUG_HUNTER_LEVEL_2: 2 << 14, + + // Bot badges + SUPPORTS_COMMANDS: 2 << 23, + USES_AUTOMOD: 2 << 24, +}; + +export const discordBadgeDetails = { + HYPESQUAD: { + tooltip: "HypeSquad Events", + icon: "/public/badges/discord/HYPESQUAD.svg", + }, + HYPESQUAD_ONLINE_HOUSE_1: { + tooltip: "HypeSquad Bravery", + icon: "/public/badges/discord/HYPESQUAD_ONLINE_HOUSE_1.svg", + }, + HYPESQUAD_ONLINE_HOUSE_2: { + tooltip: "HypeSquad Brilliance", + icon: "/public/badges/discord/HYPESQUAD_ONLINE_HOUSE_2.svg", + }, + HYPESQUAD_ONLINE_HOUSE_3: { + tooltip: "HypeSquad Balance", + icon: "/public/badges/discord/HYPESQUAD_ONLINE_HOUSE_3.svg", + }, + + STAFF: { + tooltip: "Discord Staff", + icon: "/public/badges/discord/STAFF.svg", + }, + PARTNER: { + tooltip: "Discord Partner", + icon: "/public/badges/discord/PARTNER.svg", + }, + CERTIFIED_MODERATOR: { + tooltip: "Certified Moderator", + icon: "/public/badges/discord/CERTIFIED_MODERATOR.svg", + }, + + VERIFIED_DEVELOPER: { + tooltip: "Verified Bot Developer", + icon: "/public/badges/discord/VERIFIED_DEVELOPER.svg", + }, + ACTIVE_DEVELOPER: { + tooltip: "Active Developer", + icon: "/public/badges/discord/ACTIVE_DEVELOPER.svg", + }, + + PREMIUM_EARLY_SUPPORTER: { + tooltip: "Premium Early Supporter", + icon: "/public/badges/discord/PREMIUM_EARLY_SUPPORTER.svg", + }, + + BUG_HUNTER_LEVEL_1: { + tooltip: "Bug Hunter (Level 1)", + icon: "/public/badges/discord/BUG_HUNTER_LEVEL_1.svg", + }, + BUG_HUNTER_LEVEL_2: { + tooltip: "Bug Hunter (Level 2)", + icon: "/public/badges/discord/BUG_HUNTER_LEVEL_2.svg", + }, + + SUPPORTS_COMMANDS: { + tooltip: "Supports Commands", + icon: "/public/badges/discord/SUPPORTS_COMMANDS.svg", + }, + USES_AUTOMOD: { + tooltip: "Uses AutoMod", + icon: "/public/badges/discord/USES_AUTOMOD.svg", + }, +}; diff --git a/config/environment.ts b/config/environment.ts index 03a3107..6b056d6 100644 --- a/config/environment.ts +++ b/config/environment.ts @@ -9,11 +9,6 @@ export const redisTtl: number = process.env.REDIS_TTL ? Number.parseInt(process.env.REDIS_TTL, 10) : 60 * 60 * 1; // 1 hour -// not sure the point ? -// function getClientModBadgesUrl(userId: string): string { -// return `https://cdn.jsdelivr.net/gh/Equicord/ClientModBadges-API@main/users/${userId}.json`; -// } - export const badgeServices: badgeURLMap[] = [ { service: "Vencord", @@ -31,8 +26,18 @@ export const badgeServices: badgeURLMap[] = [ service: "ReviewDb", url: "https://manti.vendicated.dev/api/reviewdb/badges", }, - // { - // service: "ClientMods", - // url: getClientModBadgesUrl, - // } + { + service: "Enmity", + url: (userId: string) => ({ + user: `https://raw.githubusercontent.com/enmity-mod/badges/main/${userId}.json`, + badge: (id: string) => + `https://raw.githubusercontent.com/enmity-mod/badges/main/data/${id}.json`, + }), + }, + { + service: "Discord", + url: (userId: string) => `https://discord.com/api/v10/users/${userId}`, + }, ]; + +export const botToken: string | undefined = process.env.DISCORD_TOKEN; diff --git a/public/badges/discord/ACTIVE_DEVELOPER.svg b/public/badges/discord/ACTIVE_DEVELOPER.svg new file mode 100644 index 0000000..80aa677 --- /dev/null +++ b/public/badges/discord/ACTIVE_DEVELOPER.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/badges/discord/BUG_HUNTER_LEVEL_1.svg b/public/badges/discord/BUG_HUNTER_LEVEL_1.svg new file mode 100644 index 0000000..ca75a4e --- /dev/null +++ b/public/badges/discord/BUG_HUNTER_LEVEL_1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/badges/discord/BUG_HUNTER_LEVEL_2.svg b/public/badges/discord/BUG_HUNTER_LEVEL_2.svg new file mode 100644 index 0000000..1c80182 --- /dev/null +++ b/public/badges/discord/BUG_HUNTER_LEVEL_2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/badges/discord/CERTIFIED_MODERATOR.svg b/public/badges/discord/CERTIFIED_MODERATOR.svg new file mode 100644 index 0000000..6f634b2 --- /dev/null +++ b/public/badges/discord/CERTIFIED_MODERATOR.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/badges/discord/HYPESQUAD.svg b/public/badges/discord/HYPESQUAD.svg new file mode 100644 index 0000000..85bec04 --- /dev/null +++ b/public/badges/discord/HYPESQUAD.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/badges/discord/HYPESQUAD_ONLINE_HOUSE_1.svg b/public/badges/discord/HYPESQUAD_ONLINE_HOUSE_1.svg new file mode 100644 index 0000000..91fd024 --- /dev/null +++ b/public/badges/discord/HYPESQUAD_ONLINE_HOUSE_1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/badges/discord/HYPESQUAD_ONLINE_HOUSE_2.svg b/public/badges/discord/HYPESQUAD_ONLINE_HOUSE_2.svg new file mode 100644 index 0000000..d0713bb --- /dev/null +++ b/public/badges/discord/HYPESQUAD_ONLINE_HOUSE_2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/badges/discord/HYPESQUAD_ONLINE_HOUSE_3.svg b/public/badges/discord/HYPESQUAD_ONLINE_HOUSE_3.svg new file mode 100644 index 0000000..01e4805 --- /dev/null +++ b/public/badges/discord/HYPESQUAD_ONLINE_HOUSE_3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/badges/discord/NITRO.svg b/public/badges/discord/NITRO.svg new file mode 100644 index 0000000..98b54ab --- /dev/null +++ b/public/badges/discord/NITRO.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/badges/discord/PARTNER.svg b/public/badges/discord/PARTNER.svg new file mode 100644 index 0000000..35facaf --- /dev/null +++ b/public/badges/discord/PARTNER.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/badges/discord/PREMIUM_EARLY_SUPPORTER.svg b/public/badges/discord/PREMIUM_EARLY_SUPPORTER.svg new file mode 100644 index 0000000..8cd0cda --- /dev/null +++ b/public/badges/discord/PREMIUM_EARLY_SUPPORTER.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/badges/discord/STAFF.svg b/public/badges/discord/STAFF.svg new file mode 100644 index 0000000..d65b724 --- /dev/null +++ b/public/badges/discord/STAFF.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/badges/discord/SUPPORTS_COMMANDS.svg b/public/badges/discord/SUPPORTS_COMMANDS.svg new file mode 100644 index 0000000..55e0c7b --- /dev/null +++ b/public/badges/discord/SUPPORTS_COMMANDS.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/badges/discord/USES_AUTOMOD.svg b/public/badges/discord/USES_AUTOMOD.svg new file mode 100644 index 0000000..e220934 --- /dev/null +++ b/public/badges/discord/USES_AUTOMOD.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/badges/discord/VERIFIED_DEVELOPER.svg b/public/badges/discord/VERIFIED_DEVELOPER.svg new file mode 100644 index 0000000..4ec174f --- /dev/null +++ b/public/badges/discord/VERIFIED_DEVELOPER.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/helpers/badges.ts b/src/helpers/badges.ts index 892233d..68d5ce4 100644 --- a/src/helpers/badges.ts +++ b/src/helpers/badges.ts @@ -1,10 +1,12 @@ -import { badgeServices, redisTtl } from "@config/environment"; +import { discordBadgeDetails, discordBadges } from "@config/discordBadges"; +import { badgeServices, botToken, redisTtl } from "@config/environment"; import { fetch, redis } from "bun"; export async function fetchBadges( userId: string, services: string[], options?: FetchBadgesOptions, + request?: Request, ): Promise { const { nocache = false, separated = false } = options ?? {}; const results: Record = {}; @@ -32,23 +34,23 @@ export async function fetchBadges( } } - let url: string; - if (typeof entry.url === "function") { - url = entry.url(userId); - } else { - url = entry.url; - } + const result: Badge[] = []; try { - const res = await fetch(url); - if (!res.ok) return; - const data = await res.json(); - - const result: Badge[] = []; + let url: string | { user: string; badge: (id: string) => string }; + if (typeof entry.url === "function") { + url = entry.url(userId); + } else { + url = entry.url; + } switch (serviceKey) { case "vencord": case "equicord": { + const res = await fetch(url as string); + if (!res.ok) break; + + const data = await res.json(); const userBadges = data[userId]; if (Array.isArray(userBadges)) { for (const b of userBadges) { @@ -62,6 +64,10 @@ export async function fetchBadges( } case "nekocord": { + const res = await fetch(url as string); + if (!res.ok) break; + + const data = await res.json(); const userBadgeIds = data.users?.[userId]?.badges; if (Array.isArray(userBadgeIds)) { for (const id of userBadgeIds) { @@ -78,6 +84,10 @@ export async function fetchBadges( } case "reviewdb": { + const res = await fetch(url as string); + if (!res.ok) break; + + const data = await res.json(); for (const b of data) { if (b.discordID === userId) { result.push({ @@ -88,6 +98,69 @@ export async function fetchBadges( } break; } + + case "enmity": { + if ( + typeof url !== "object" || + typeof url.user !== "string" || + typeof url.badge !== "function" + ) + break; + + const userRes = await fetch(url.user); + if (!userRes.ok) break; + + const badgeIds: string[] = await userRes.json(); + if (!Array.isArray(badgeIds)) break; + + await Promise.all( + badgeIds.map(async (id) => { + const badgeRes = await fetch(url.badge(id)); + if (!badgeRes.ok) return; + + const badge = await badgeRes.json(); + if (!badge?.name || !badge?.url?.dark) return; + + result.push({ + tooltip: badge.name, + badge: badge.url.dark, + }); + }), + ); + break; + } + + case "discord": { + if (!botToken) break; + + const res = await fetch(url as string, { + headers: { + Authorization: `Bot ${botToken}`, + }, + }); + if (!res.ok) break; + + const data = await res.json(); + + if (data.avatar.startsWith("a_")) { + result.push({ + tooltip: "Discord Nitro", + badge: `${request ? new URL(request.url).origin : ""}/public/badges/discord/NITRO.svg`, + }); + } + + for (const [flag, bitwise] of Object.entries(discordBadges)) { + if (data.flags & bitwise) { + const badge = + discordBadgeDetails[flag as keyof typeof discordBadgeDetails]; + result.push({ + tooltip: badge.tooltip, + badge: `${request ? new URL(request.url).origin : ""}${badge.icon}`, + }); + } + } + break; + } } if (result.length > 0) { diff --git a/src/routes/[id].ts b/src/routes/[id].ts index af6a621..4b8421e 100644 --- a/src/routes/[id].ts +++ b/src/routes/[id].ts @@ -58,10 +58,15 @@ async function handler(request: ExtendedRequest): Promise { validServices = badgeServices.map((b) => b.service); } - const badges: BadgeResult = await fetchBadges(userId, validServices, { - nocache: cache !== "true", - separated: seperated === "true", - }); + const badges: BadgeResult = await fetchBadges( + userId, + validServices, + { + nocache: cache !== "true", + separated: seperated === "true", + }, + request, + ); if (badges instanceof Error) { return Response.json( diff --git a/types/badge.d.ts b/types/badge.d.ts index 04b58c9..0731370 100644 --- a/types/badge.d.ts +++ b/types/badge.d.ts @@ -9,3 +9,14 @@ interface FetchBadgesOptions { nocache?: boolean; separated?: boolean; } + +type badgeURLMap = { + service: string; + url: + | string + | ((userId: string) => string) + | ((userId: string) => { + user: string; + badge: (id: string) => string; + }); +}; diff --git a/types/config.d.ts b/types/config.d.ts index 2d583ee..57584ed 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -3,8 +3,3 @@ type Environment = { host: string; development: boolean; }; - -type badgeURLMap = { - service: string; - url: string | ((userId: string) => string); -};