diff --git a/pages/api/[...id].ts b/pages/api/[...id].ts index b408e5d..3cd6616 100644 --- a/pages/api/[...id].ts +++ b/pages/api/[...id].ts @@ -1,36 +1,46 @@ -import type { NextApiRequest, NextApiResponse } from 'next' -import axios from 'axios'; -import * as LanyardTypes from '../../src/LanyardTypes'; -import renderCard from '../../src/renderCard'; +import type { NextApiRequest, NextApiResponse } from "next"; +import axios from "axios"; +import renderCard from "../../src/renderCard"; +import { isSnowflake } from "../../src/snowflake"; type Data = { - id?: string | string[] - error?: any; -} + id?: string | string[]; + error?: any; +}; type Parameters = { - animated?: string; -} + animated?: string; +}; export default async function handler( - req: NextApiRequest, - res: NextApiResponse + req: NextApiRequest, + res: NextApiResponse, ) { + const params: Parameters = req.query, + userid = req.query.id[0]; - let params: Parameters = req.query, - userid = req.query.id[0], - lanyardData: any; + if (!isSnowflake(userid)) + return res.send({ + error: `Specify a valid Discord user ID! If everything looks correct and this still occurs, please contact @cnraddd on Twitter.`, + }); - res.setHeader("Content-Type", "image/svg+xml; charset=utf-8"); - res.setHeader("content-security-policy", "default-src 'none'; img-src * data:; style-src 'unsafe-inline'"); + let err: any; + const axiosRes = await axios + .get(`https://api.lanyard.rest/v1/users/${userid}`) + .catch((e) => (err = e)); - try { - lanyardData = await axios.get(`https://api.lanyard.rest/v1/users/${userid}`); - } catch (e) { - console.log(e) - res.send({ error: `Something went wrong! If everything looks correct and this still occurs, please contact @cnraddd on Twitter.` }) - } + if (err) console.log(err); + if (err || axiosRes.status != 200) + return res.send({ + error: `Something went wrong! If everything looks correct and this still occurs, please contact @cnraddd on Twitter.`, + }); - let svg = await renderCard(lanyardData.data, params); - res.status(200).send(svg as any); -} \ No newline at end of file + res.setHeader("Content-Type", "image/svg+xml; charset=utf-8"); + res.setHeader( + "content-security-policy", + "default-src 'none'; img-src * data:; style-src 'unsafe-inline'", + ); + + let svg = await renderCard(axiosRes.data, params); + res.status(200).send(svg as any); +} diff --git a/src/snowflake.ts b/src/snowflake.ts new file mode 100644 index 0000000..a75ffec --- /dev/null +++ b/src/snowflake.ts @@ -0,0 +1,47 @@ +interface DeconstructedSnowflake { + timestamp: number; + date: Date; + workerID: number; + processID: number; + increment: number; + binary: string; +} + +const EPOCH = 1420070400000; // Discord's EPOCH + +export function isSnowflake(snowflake: string): boolean { + const { timestamp } = deconstruct(snowflake); + if (timestamp > EPOCH && timestamp <= 3619093655551) { + return true; + } + return false; +} + +function deconstruct(snowflake: string): DeconstructedSnowflake { + const BINARY = idToBinary(snowflake).padStart(64, "0"); + return { + timestamp: parseInt(BINARY.substring(0, 42), 2) + EPOCH, + get date() { + return new Date(this.timestamp); + }, + workerID: parseInt(BINARY.substring(42, 47), 2), + processID: parseInt(BINARY.substring(47, 52), 2), + increment: parseInt(BINARY.substring(52, 64), 2), + binary: BINARY, + }; +} + +function idToBinary(snowflake: string): string { + let bin = ""; + let high = parseInt(snowflake.slice(0, -10)) || 0; + let low = parseInt(snowflake.slice(-10)); + while (low > 0 || high > 0) { + bin = String(low & 1) + bin; + low = Math.floor(low / 2); + if (high > 0) { + low += 5000000000 * (high % 2); + high = Math.floor(high / 2); + } + } + return bin; +}