From b6e8e994609b09191e29a05f6aa6d3a58b0e6dd1 Mon Sep 17 00:00:00 2001 From: Junseo Park Date: Wed, 24 Feb 2021 22:49:14 +0900 Subject: [PATCH] feat: added widget --- pages/api/widget/bots/[type]/[id].ts | 56 ++++++++++++++++++++++++++++ utils/Constants.ts | 18 ++++++++- utils/Yup.ts | 20 ++++++++++ 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 pages/api/widget/bots/[type]/[id].ts diff --git a/pages/api/widget/bots/[type]/[id].ts b/pages/api/widget/bots/[type]/[id].ts new file mode 100644 index 0000000..fc44abb --- /dev/null +++ b/pages/api/widget/bots/[type]/[id].ts @@ -0,0 +1,56 @@ +import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next' + +import ResponseWrapper from '@utils/ResponseWrapper' +import { WidgetOptionsSchema } from '@utils/Yup' + +import { badgen } from 'badgen' +import { get } from '@utils/Query' +import { formatNumber } from '@utils/Tools' +import { BotBadgeType, DiscordEnpoints } from '@utils/Constants' + +const Widget: NextApiHandler = async(req: ApiRequest, res: NextApiResponse) => { + const { id: param, type, style='flat', scale=1 } = req.query + const splitted = param.split('.') + + const validated = await WidgetOptionsSchema.validate({ + id: splitted.slice(0, splitted.length - 1).join('.'), + ext: splitted[splitted.length - 1], + style, + type, + scale + }).then(el=> el).catch(e=> { + ResponseWrapper(res, { code: 400, errors: e.errors }) + return null + }) + + if(!validated) return + + const data = await get.bot.load(validated.id) + + if(!data) return ResponseWrapper(res, { code: 404, message: '존재하지 않는 봇입니다.' }) + console.log(data) + const userImage = !data.avatar ? null : await get.images.user.load(DiscordEnpoints.CDN.user(data.id, data.avatar, { format: 'png', size: 128 })) + console.log(userImage) + const img = userImage || await get.images.user.load(DiscordEnpoints.CDN.default(data.tag, { format: 'png', size: 128 })) + res.setHeader('content-type', 'image/svg+xml; charset=utf-8') + const badgeData = { + ...BotBadgeType(data)[type], + style: validated.style, + scale: validated.scale, + icon: `data:image/png;base64,${img.toString('base64')}` + } + + res.send(badgen(badgeData)) + +} + +interface ApiRequest extends NextApiRequest { + query: { + type: string + id: string + style?: string + scale?: string + } +} + +export default Widget \ No newline at end of file diff --git a/utils/Constants.ts b/utils/Constants.ts index ab58008..023febf 100644 --- a/utils/Constants.ts +++ b/utils/Constants.ts @@ -1,5 +1,5 @@ -import { ImageOptions, KoreanbotsImageOptions } from '@types' -import { makeImageURL } from './Tools' +import { Bot, ImageOptions, KoreanbotsImageOptions } from '@types' +import { formatNumber, makeImageURL } from './Tools' export const Status = { online: { @@ -129,6 +129,20 @@ export const BASE_URLs = { api: 'https://discord.com/api', cdn: 'https://cdn.discordapp.com' } + +export const BotBadgeType = (data: Bot) => { + return { + servers: { + label: '서버수', + status: formatNumber(data.servers) + }, + votes: { + label: '하트', + status: `${formatNumber(data.votes)}` + } + } +} + export const DiscordEnpoints = { Token: BASE_URLs.api + '/oauth2/token', Me: BASE_URLs.api + '/v8/users/@me', diff --git a/utils/Yup.ts b/utils/Yup.ts index 2528585..d7eaca6 100644 --- a/utils/Yup.ts +++ b/utils/Yup.ts @@ -38,6 +38,25 @@ interface ImageOptions { type ext = 'webp' | 'png' | 'gif' type ImageSize = '128' | '256' | '512' +export const WidgetOptionsSchema: Yup.SchemaOf = Yup.object({ + id: Yup.string().required(), + ext: Yup.mixed().oneOf(['svg']).required(), + type: Yup.mixed().oneOf(['votes', 'servers']).required(), + scale: Yup.number().positive().min(0.5).max(3).required(), + style: Yup.mixed<'flat'|'classic'>().oneOf(['flat', 'classic']).default('flat') +}) + +interface WidgetOptions { + id: string + ext: widgetExt + type: widgetType + scale: number + style: 'flat' | 'classic' +} + +type widgetType = 'votes' | 'servers' +type widgetExt = 'svg' + export const PageCount = Yup.number().integer().positive().required() export const OauthCallbackSchema: Yup.SchemaOf = Yup.object({ @@ -111,4 +130,5 @@ export const ManageBotSchema = Yup.object({ owners: Yup.array(Yup.string()).min(1, '최소 한 명의 소유자는 입력해주세요.').max(10, '소유자는 최대 10명까지만 가능합니다.').unique('소유자 아이디는 중복될 수 없습니다.').required('소유자는 필수 항목입니다.'), _csrf: Yup.string().required() }) + export default Yup