mirror of
https://github.com/koreanbots/core.git
synced 2025-12-15 06:10:22 +00:00
Compare commits
6 Commits
0f5893e815
...
7bc7c7c902
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bc7c7c902 | ||
|
|
8a96148245 | ||
|
|
218e7909b3 | ||
|
|
843e4137cb | ||
|
|
9aaf66018f | ||
|
|
a2adbf116e |
@ -21,7 +21,7 @@ const Label: React.FC<LabelProps> = ({
|
||||
<span className='align-text-top text-base font-semibold text-red-500'> *</span>
|
||||
)}
|
||||
</h3>
|
||||
{labelDesc}
|
||||
<span className='whitespace-pre-line'>{labelDesc}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className={short ? 'col-span-1' : 'col-span-3'}>
|
||||
|
||||
@ -109,7 +109,10 @@ const Select: React.FC<SelectProps> = ({
|
||||
onChange={handleChange}
|
||||
onBlur={handleTouch}
|
||||
noOptionsMessage={() => '검색 결과가 없습니다.'}
|
||||
value={values.map((el) => ({ label: el, value: el }))}
|
||||
value={values.map((el) => ({
|
||||
label: Object.values(options).find(({ value }) => value === el)?.label || el,
|
||||
value: el,
|
||||
}))}
|
||||
components={{
|
||||
MultiValue,
|
||||
MultiValueRemove,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "koreanbots",
|
||||
"version": "2.11.0",
|
||||
"version": "2.12.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@ -42,7 +42,7 @@
|
||||
"knex": "^2.4.0",
|
||||
"mongoose": "6.13.6",
|
||||
"mysql": "2.18.1",
|
||||
"next": "^14.2.26",
|
||||
"next": "^14.2.30",
|
||||
"next-connect": "0.10.1",
|
||||
"next-pwa": "^5.6.0",
|
||||
"next-seo": "^6.1.0",
|
||||
|
||||
@ -10,7 +10,7 @@ import HCaptcha from '@hcaptcha/react-hcaptcha'
|
||||
import { get } from '@utils/Query'
|
||||
import { cleanObject, parseCookie, redirectTo } from '@utils/Tools'
|
||||
import { AddBotSubmit, AddBotSubmitSchema } from '@utils/Yup'
|
||||
import { botCategories, botCategoryDescription, library } from '@utils/Constants'
|
||||
import { botCategories, botCategoryDescription, botEnforcements, library } from '@utils/Constants'
|
||||
import { getToken } from '@utils/Csrf'
|
||||
import Fetch from '@utils/Fetch'
|
||||
import { ResponseProps, SubmittedBot, Theme, User } from '@types'
|
||||
@ -57,6 +57,7 @@ const AddBot: NextPage<AddBotProps> = ({ logged, user, csrfToken, theme }) => {
|
||||
- 어떤
|
||||
- 기능
|
||||
- 있나요?`,
|
||||
enforcements: [],
|
||||
_csrf: csrfToken,
|
||||
_captcha: 'captcha',
|
||||
}
|
||||
@ -356,6 +357,34 @@ const AddBot: NextPage<AddBotProps> = ({ logged, user, csrfToken, theme }) => {
|
||||
</Segment>
|
||||
</Label>
|
||||
<Divider />
|
||||
<Label
|
||||
For='enforcements'
|
||||
label='필수 고지 내용'
|
||||
labelDesc='내용에 해당하는 경우 필수로 선택해야 합니다.'
|
||||
required
|
||||
error={
|
||||
errors.enforcements && touched.enforcements ? (errors.enforcements as string) : null
|
||||
}
|
||||
>
|
||||
<Selects
|
||||
options={Object.entries(botEnforcements).map(([k, v]) => ({
|
||||
label: v.label,
|
||||
value: k,
|
||||
}))}
|
||||
handleChange={(value) => {
|
||||
setFieldValue(
|
||||
'enforcements',
|
||||
value.map((v) => v.value)
|
||||
)
|
||||
}}
|
||||
handleTouch={() => setFieldTouched('enforcements', true)}
|
||||
values={values.enforcements ?? ([] as string[])}
|
||||
setValues={(value) => {
|
||||
setFieldValue('enforcements', value)
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<Divider />
|
||||
<p className='mb-5 mt-2 text-base'>
|
||||
<span className='font-semibold text-red-500'> *</span> = 필수 항목
|
||||
</p>
|
||||
|
||||
@ -39,6 +39,10 @@ const patchLimiter = rateLimit({
|
||||
})
|
||||
const Bots = RequestHandler()
|
||||
.get(async (req: GetApiRequest, res) => {
|
||||
const auth = req.headers.authorization
|
||||
? await get.BotAuthorization(req.headers.authorization)
|
||||
: await get.Authorization(req.cookies.token)
|
||||
if (!auth) return ResponseWrapper(res, { code: 401 })
|
||||
const bot = await get.bot.load(req.query.id)
|
||||
if (!bot) return ResponseWrapper(res, { code: 404, message: '존재하지 않는 봇입니다.' })
|
||||
else {
|
||||
@ -200,7 +204,7 @@ const Bots = RequestHandler()
|
||||
|
||||
const isPerkAvailable =
|
||||
checkBotFlag(bot.flags, 'partnered') || checkBotFlag(bot.flags, 'trusted')
|
||||
|
||||
|
||||
const userInfo = await get.user.load(user)
|
||||
if (
|
||||
['reported', 'blocked', 'archived'].includes(bot.state) &&
|
||||
@ -219,7 +223,8 @@ const Bots = RequestHandler()
|
||||
const csrfValidated = checkToken(req, res, req.body._csrf)
|
||||
if (!csrfValidated) return
|
||||
|
||||
const validated: ManageBot = await getManageBotSchema(isPerkAvailable).validate(req.body, { abortEarly: false })
|
||||
const validated: ManageBot = await getManageBotSchema(isPerkAvailable)
|
||||
.validate(req.body, { abortEarly: false })
|
||||
.then((el) => el)
|
||||
.catch((e) => {
|
||||
ResponseWrapper(res, { code: 400, errors: e.errors })
|
||||
@ -241,28 +246,29 @@ const Bots = RequestHandler()
|
||||
errors: ['다른 커스텀 URL로 다시 시도해주세요.'],
|
||||
})
|
||||
}
|
||||
|
||||
await webhookClients.internal.noticeLog.send({
|
||||
embeds: [
|
||||
{
|
||||
title: '한디리 커스텀 URL 변경',
|
||||
description: `봇: ${bot.name} - <@${bot.id}> ([${bot.id}](${KoreanbotsEndPoints.URL.bot(
|
||||
bot.id
|
||||
)}))`,
|
||||
fields: [
|
||||
{
|
||||
name: '이전',
|
||||
value: bot.vanity || '없음',
|
||||
},
|
||||
{
|
||||
name: '이후',
|
||||
value: validated.vanity || '없음',
|
||||
},
|
||||
],
|
||||
color: Colors.Blue,
|
||||
},
|
||||
],
|
||||
})
|
||||
if (validated.vanity !== bot.vanity) {
|
||||
await webhookClients.internal.noticeLog.send({
|
||||
embeds: [
|
||||
{
|
||||
title: '한디리 커스텀 URL 변경',
|
||||
description: `봇: ${bot.name} - <@${bot.id}> ([${
|
||||
bot.id
|
||||
}](${KoreanbotsEndPoints.URL.bot(bot.id)}))`,
|
||||
fields: [
|
||||
{
|
||||
name: '이전',
|
||||
value: bot.vanity || '없음',
|
||||
},
|
||||
{
|
||||
name: '이후',
|
||||
value: validated.vanity || '없음',
|
||||
},
|
||||
],
|
||||
color: Colors.Blue,
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
}
|
||||
const result = await update.bot(req.query.id, validated)
|
||||
if (result === 0) return ResponseWrapper(res, { code: 400 })
|
||||
@ -284,6 +290,7 @@ const Bots = RequestHandler()
|
||||
category: JSON.stringify(bot.category),
|
||||
vanity: bot.vanity,
|
||||
banner: bot.banner,
|
||||
enforcements: JSON.stringify(bot.enforcements),
|
||||
bg: bot.bg,
|
||||
},
|
||||
{
|
||||
@ -297,6 +304,7 @@ const Bots = RequestHandler()
|
||||
category: JSON.stringify(validated.category),
|
||||
vanity: validated.vanity,
|
||||
banner: validated.banner,
|
||||
enforcements: JSON.stringify(validated.enforcements),
|
||||
bg: validated.bg,
|
||||
}
|
||||
)
|
||||
|
||||
@ -4,7 +4,11 @@ import ResponseWrapper from '@utils/ResponseWrapper'
|
||||
|
||||
import { Bot, List } from '@types'
|
||||
|
||||
const NewList = RequestHandler().get(async (_req, res) => {
|
||||
const NewList = RequestHandler().get(async (req, res) => {
|
||||
const auth = req.headers.authorization
|
||||
? await get.BotAuthorization(req.headers.authorization)
|
||||
: await get.Authorization(req.cookies.token)
|
||||
if (!auth) return ResponseWrapper(res, { code: 401 })
|
||||
const result = await get.list.new.load(1)
|
||||
return ResponseWrapper<List<Bot>>(res, { code: 200, data: result })
|
||||
})
|
||||
|
||||
@ -6,6 +6,10 @@ import { Bot, List } from '@types'
|
||||
import Yup from '@utils/Yup'
|
||||
|
||||
const VotesList = RequestHandler().get(async (req, res) => {
|
||||
const auth = req.headers.authorization
|
||||
? await get.BotAuthorization(req.headers.authorization)
|
||||
: await get.Authorization(req.cookies.token)
|
||||
if (!auth) return ResponseWrapper(res, { code: 401 })
|
||||
const page = await Yup.number()
|
||||
.positive()
|
||||
.integer()
|
||||
|
||||
@ -8,6 +8,10 @@ import { SearchQuerySchema } from '@utils/Yup'
|
||||
import { Bot, Server, List } from '@types'
|
||||
|
||||
const Search = RequestHandler().get(async (req: ApiRequest, res) => {
|
||||
const auth = req.headers.authorization
|
||||
? await get.BotAuthorization(req.headers.authorization)
|
||||
: await get.Authorization(req.cookies.token)
|
||||
if (!auth) return ResponseWrapper(res, { code: 401 })
|
||||
const validated = await SearchQuerySchema.validate({ q: req.query.q || req.query.query, page: 1 })
|
||||
.then((el) => el)
|
||||
.catch((e) => {
|
||||
|
||||
@ -8,6 +8,10 @@ import { SearchQuerySchema } from '@utils/Yup'
|
||||
import { Bot, List } from '@types'
|
||||
|
||||
const SearchBots = RequestHandler().get(async (req: ApiRequest, res: NextApiResponse) => {
|
||||
const auth = req.headers.authorization
|
||||
? await get.BotAuthorization(req.headers.authorization)
|
||||
: await get.Authorization(req.cookies.token)
|
||||
if (!auth) return ResponseWrapper(res, { code: 401 })
|
||||
const validated = await SearchQuerySchema.validate({
|
||||
q: req.query.q || req.query.query,
|
||||
page: req.query.page,
|
||||
|
||||
@ -8,6 +8,10 @@ import { SearchQuerySchema } from '@utils/Yup'
|
||||
import { Server, List } from '@types'
|
||||
|
||||
const SearchServers = RequestHandler().get(async (req: ApiRequest, res: NextApiResponse) => {
|
||||
const auth = req.headers.authorization
|
||||
? await get.BotAuthorization(req.headers.authorization)
|
||||
: await get.Authorization(req.cookies.token)
|
||||
if (!auth) return ResponseWrapper(res, { code: 401 })
|
||||
const validated = await SearchQuerySchema.validate({
|
||||
q: req.query.q || req.query.query,
|
||||
page: req.query.page,
|
||||
|
||||
@ -36,6 +36,10 @@ const patchLimiter = rateLimit({
|
||||
})
|
||||
const Servers = RequestHandler()
|
||||
.get(async (req: GetApiRequest, res) => {
|
||||
const auth = req.headers.authorization
|
||||
? await get.BotAuthorization(req.headers.authorization)
|
||||
: await get.Authorization(req.cookies.token)
|
||||
if (!auth) return ResponseWrapper(res, { code: 401 })
|
||||
const server = await get.server.load(req.query.id)
|
||||
if (!server) return ResponseWrapper(res, { code: 404, message: '존재하지 않는 서버 입니다.' })
|
||||
else {
|
||||
|
||||
@ -5,7 +5,10 @@ import ResponseWrapper from '@utils/ResponseWrapper'
|
||||
import RequestHandler from '@utils/RequestHandler'
|
||||
|
||||
const Users = RequestHandler().get(async (req: ApiRequest, res) => {
|
||||
console.log(req.query)
|
||||
const auth = req.headers.authorization
|
||||
? await get.BotAuthorization(req.headers.authorization)
|
||||
: await get.Authorization(req.cookies.token)
|
||||
if (!auth) return ResponseWrapper(res, { code: 401 })
|
||||
const user = await get.user.load(req.query?.id)
|
||||
if (!user) return ResponseWrapper(res, { code: 404, message: '존재하지 않는 유저 입니다.' })
|
||||
else return ResponseWrapper(res, { code: 200, data: user })
|
||||
|
||||
@ -9,9 +9,16 @@ import { ParsedUrlQuery } from 'querystring'
|
||||
import { getJosaPicker } from 'josa'
|
||||
|
||||
import { get } from '@utils/Query'
|
||||
import { checkBotFlag, checkUserFlag, cleanObject, makeBotURL, parseCookie, redirectTo } from '@utils/Tools'
|
||||
import {
|
||||
checkBotFlag,
|
||||
checkUserFlag,
|
||||
cleanObject,
|
||||
makeBotURL,
|
||||
parseCookie,
|
||||
redirectTo,
|
||||
} from '@utils/Tools'
|
||||
import { ManageBot, getManageBotSchema } from '@utils/Yup'
|
||||
import { botCategories, botCategoryDescription, library } from '@utils/Constants'
|
||||
import { botCategories, botCategoryDescription, botEnforcements, library } from '@utils/Constants'
|
||||
import { Bot, Theme, User } from '@types'
|
||||
import { getToken } from '@utils/Csrf'
|
||||
import Fetch from '@utils/Fetch'
|
||||
@ -82,6 +89,7 @@ const ManageBotPage: NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme }
|
||||
prefix: bot.prefix,
|
||||
library: bot.lib,
|
||||
category: bot.category,
|
||||
enforcements: bot.enforcements,
|
||||
intro: bot.intro,
|
||||
desc: bot.desc,
|
||||
website: bot.web,
|
||||
@ -98,8 +106,12 @@ const ManageBotPage: NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme }
|
||||
>
|
||||
{({ errors, touched, values, setFieldTouched, setFieldValue }) => (
|
||||
<Form>
|
||||
<div className='text-center md:flex md:text-left'>
|
||||
<DiscordAvatar userID={bot.id} className='mx-auto rounded-full md:mx-1' hash={bot.avatar}/>
|
||||
<div className='text-ceznter md:flex md:text-left'>
|
||||
<DiscordAvatar
|
||||
userID={bot.id}
|
||||
className='mx-auto rounded-full md:mx-1'
|
||||
hash={bot.avatar}
|
||||
/>
|
||||
<div className='px-8 py-6 md:w-2/3'>
|
||||
<h1 className='text-3xl font-bold'>
|
||||
{bot.name}#{bot.tag}
|
||||
@ -165,7 +177,11 @@ const ManageBotPage: NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme }
|
||||
error={errors.category && touched.category ? (errors.category as string) : null}
|
||||
>
|
||||
<Selects
|
||||
options={botCategories.map((el) => ({ label: el, value: el, description: botCategoryDescription[el] }))}
|
||||
options={botCategories.map((el) => ({
|
||||
label: el,
|
||||
value: el,
|
||||
description: botCategoryDescription[el],
|
||||
}))}
|
||||
handleChange={(value) => {
|
||||
setFieldValue(
|
||||
'category',
|
||||
@ -273,24 +289,26 @@ const ManageBotPage: NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme }
|
||||
<Markdown text={values.desc} />
|
||||
</Segment>
|
||||
</Label>
|
||||
{
|
||||
isPerkAvailable && (
|
||||
<>
|
||||
{isPerkAvailable && (
|
||||
<>
|
||||
<Divider />
|
||||
<h2 className='pt-2 text-2xl font-semibold text-koreanbots-green'>신뢰된 봇 특전 설정</h2>
|
||||
<span className='mt-1 text-sm text-gray-400'>신뢰된 봇의 혜택을 만나보세요. (커스텀 URL과 배너/배경 이미지는 이용약관 및 가이드라인을 준수해야하며 위반 시 신뢰된 봇 자격이 박탈될 수 있습니다.)</span>
|
||||
<h2 className='pt-2 text-2xl font-semibold text-koreanbots-green'>
|
||||
신뢰된 봇 특전 설정
|
||||
</h2>
|
||||
<span className='mt-1 text-sm text-gray-400'>
|
||||
신뢰된 봇의 혜택을 만나보세요. (커스텀 URL과 배너/배경 이미지는 이용약관 및
|
||||
가이드라인을 준수해야하며 위반 시 신뢰된 봇 자격이 박탈될 수 있습니다.)
|
||||
</span>
|
||||
<Label
|
||||
For='vanity'
|
||||
label='한디리 커스텀 URL'
|
||||
labelDesc='고유한 커스텀 URL을 설정해주세요.'
|
||||
error={errors.vanity && touched.vanity ? errors.vanity : null}
|
||||
|
||||
>
|
||||
<div className='flex items-center'>
|
||||
koreanbots.dev/bots/
|
||||
<Input name='vanity' placeholder='koreanbots' />
|
||||
</div>
|
||||
|
||||
</Label>
|
||||
<Label
|
||||
For='banner'
|
||||
@ -308,9 +326,36 @@ const ManageBotPage: NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme }
|
||||
>
|
||||
<Input name='bg' placeholder='https://koreanbots.dev/logo.png' />
|
||||
</Label>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)}
|
||||
<Divider />
|
||||
<Label
|
||||
For='enforcements'
|
||||
label='필수 고지 내용'
|
||||
labelDesc='내용에 해당하는 경우 필수로 선택해야 합니다.'
|
||||
required
|
||||
error={
|
||||
errors.enforcements && touched.enforcements ? (errors.enforcements as string) : null
|
||||
}
|
||||
>
|
||||
<Selects
|
||||
options={Object.entries(botEnforcements).map(([k, v]) => ({
|
||||
label: v.label,
|
||||
value: k,
|
||||
}))}
|
||||
handleChange={(value) => {
|
||||
setFieldValue(
|
||||
'enforcements',
|
||||
value.map((v) => v.value)
|
||||
)
|
||||
}}
|
||||
handleTouch={() => setFieldTouched('enforcements', true)}
|
||||
values={values.enforcements ?? ([] as string[])}
|
||||
setValues={(value) => {
|
||||
setFieldValue('enforcements', value)
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<Divider />
|
||||
<p className='mb-5 mt-2 text-base'>
|
||||
<span className='font-semibold text-red-500'> *</span> = 필수 항목
|
||||
@ -320,7 +365,6 @@ const ManageBotPage: NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme }
|
||||
<i className='far fa-save' /> 저장
|
||||
</>
|
||||
</Button>
|
||||
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
@ -11,7 +11,7 @@ import { SnowflakeUtil } from 'discord.js'
|
||||
import { ParsedUrlQuery } from 'querystring'
|
||||
import { Bot, ResponseProps, Theme, User } from '@types'
|
||||
|
||||
import { git, KoreanbotsEndPoints, reportCats, Status } from '@utils/Constants'
|
||||
import { botEnforcements, git, KoreanbotsEndPoints, reportCats, Status } from '@utils/Constants'
|
||||
import { get } from '@utils/Query'
|
||||
import Day from '@utils/Day'
|
||||
import { ReportSchema } from '@utils/Yup'
|
||||
@ -116,13 +116,27 @@ const Bots: NextPage<BotsProps> = ({ data, desc, date, user, theme, csrfToken })
|
||||
로 문의해주세요.
|
||||
</p>
|
||||
</Message>
|
||||
) : data.enforcements.length > 0 ? (
|
||||
<Message type='warning'>
|
||||
<h2 className='text-lg font-extrabold'>이 봇은 기능에 제한을 두고 있습니다.</h2>
|
||||
<p>
|
||||
{data.enforcements.map((i) => (
|
||||
<li key={i}>{botEnforcements[i].description}</li>
|
||||
))}
|
||||
</p>
|
||||
</Message>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
<div className='w-full lg:flex'>
|
||||
<div className='w-full text-center lg:w-2/12'>
|
||||
<DiscordAvatar userID={data.id} size={256} className='w-full rounded-full' hash={data.avatar}/>
|
||||
<DiscordAvatar
|
||||
userID={data.id}
|
||||
size={256}
|
||||
className='w-full rounded-full'
|
||||
hash={data.avatar}
|
||||
/>
|
||||
</div>
|
||||
<div className='w-full grow px-5 py-12 text-center lg:w-5/12 lg:text-left'>
|
||||
<Tag
|
||||
@ -158,7 +172,7 @@ const Bots: NextPage<BotsProps> = ({ data, desc, date, user, theme, csrfToken })
|
||||
</p>
|
||||
</div>
|
||||
<div className='w-full lg:w-1/4'>
|
||||
{(data.state === 'ok' && !checkBotFlag(data.flags, 'private')) && (
|
||||
{data.state === 'ok' && !checkBotFlag(data.flags, 'private') && (
|
||||
<LongButton newTab href={`/bots/${router.query.id}/invite`}>
|
||||
<h4 className='whitespace-nowrap'>
|
||||
<i className='fas fa-user-plus text-discord-blurple' /> 초대하기
|
||||
|
||||
2
types/global.d.ts
vendored
2
types/global.d.ts
vendored
@ -2,6 +2,7 @@
|
||||
import * as Yup from 'yup'
|
||||
import { Client } from 'discord.js'
|
||||
import NotificationManager from '@utils/NotificationManager'
|
||||
import { CacheManager } from '@utils/Query'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@ -14,6 +15,7 @@ declare global {
|
||||
var kodl: Client
|
||||
var serverlist: Client
|
||||
var notification: NotificationManager
|
||||
var get: CacheManager
|
||||
interface Navigator {
|
||||
standalone?: boolean
|
||||
}
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { botEnforcements } from '@utils/Constants'
|
||||
import type { GuildFeature } from 'discord.js'
|
||||
import type { IncomingMessage } from 'http'
|
||||
import type { NextPageContext } from 'next'
|
||||
|
||||
export type Nullable<T> = T | null
|
||||
|
||||
export type ValueOf<T> = T[keyof T]
|
||||
|
||||
export interface Bot {
|
||||
id: string
|
||||
name: string
|
||||
@ -25,11 +28,14 @@ export interface Bot {
|
||||
url: string | null
|
||||
discord: string | null
|
||||
vanity: string | null
|
||||
enforcements: BotEnforcementKeys[]
|
||||
bg: string
|
||||
banner: string
|
||||
owners: User[] | string[]
|
||||
}
|
||||
|
||||
export type BotEnforcementKeys = keyof typeof botEnforcements
|
||||
|
||||
export interface RawGuild {
|
||||
id: string
|
||||
name: string
|
||||
|
||||
@ -120,6 +120,25 @@ export const botCategoryDescription = {
|
||||
마인크래프트: '게임 "마인크래프트"에 관련된 기능을 다룹니다.',
|
||||
}
|
||||
|
||||
export const botEnforcements = {
|
||||
JOIN_PARTIALLY_ENFORCED: {
|
||||
label: '서버 참여가 필요한 기능이 있습니다',
|
||||
description: '봇의 일부 명령어는 봇의 디스코드 서버에 참여해야 사용할 수 있습니다.',
|
||||
},
|
||||
JOIN_ENFORCED: {
|
||||
label: '서버 참여 없이는 봇의 핵심 기능을 사용할 수 없습니다',
|
||||
description: '봇의 핵심 기능은 봇의 디스코드 서버에 참여해야 사용할 수 있습니다.',
|
||||
},
|
||||
LICENSE_PARTIALLY_ENFORCED: {
|
||||
label: '유료 구매가 필요한 기능이 있습니다',
|
||||
description: '봇의 일부 명령어는 유료 구매가 필요합니다.',
|
||||
},
|
||||
LICENSE_ENFORCED: {
|
||||
label: '유료 구매 없이는 봇의 핵심 기능을 사용할 수 없습니다',
|
||||
description: '유료 구매 없이는 봇의 핵심 기능을 사용할 수 없습니다.',
|
||||
},
|
||||
} as const
|
||||
|
||||
export const botCategoryIcon = {
|
||||
관리: 'fas fa-cogs',
|
||||
뮤직: 'fas fa-music',
|
||||
|
||||
@ -62,6 +62,7 @@ async function getBot(id: string, topLevel = true): Promise<Bot> {
|
||||
'bots.status',
|
||||
'bots.trusted',
|
||||
'bots.partnered',
|
||||
'bots.enforcements',
|
||||
'bots.discord',
|
||||
'bots.state',
|
||||
'bots.vanity',
|
||||
@ -102,7 +103,7 @@ async function getBot(id: string, topLevel = true): Promise<Bot> {
|
||||
res.owners = JSON.parse(res.owners)
|
||||
res.banner = res.banner ? camoUrl(res.banner) : null
|
||||
res.bg = res.bg ? camoUrl(res.bg) : null
|
||||
|
||||
res.enforcements = JSON.parse(res.enforcements ?? '[]')
|
||||
if (discordBot.flags.bitfield & UserFlags.BotHTTPInteractions) {
|
||||
res.status = 'online'
|
||||
} else if (botMember) {
|
||||
@ -448,6 +449,7 @@ async function getBotSubmit(id: string, date: number): Promise<SubmittedBot> {
|
||||
'id',
|
||||
'date',
|
||||
'category',
|
||||
'enforcements',
|
||||
'lib',
|
||||
'prefix',
|
||||
'intro',
|
||||
@ -463,6 +465,7 @@ async function getBotSubmit(id: string, date: number): Promise<SubmittedBot> {
|
||||
.where({ id, date })
|
||||
if (res.length === 0) return null
|
||||
res[0].category = JSON.parse(res[0].category)
|
||||
res[0].enforcements = JSON.parse(res[0].enforcements || '[]')
|
||||
res[0].owner = await get.user.load(res[0].owner)
|
||||
return res[0]
|
||||
}
|
||||
@ -474,6 +477,7 @@ async function getBotSubmits(id: string): Promise<SubmittedBot[]> {
|
||||
'id',
|
||||
'date',
|
||||
'category',
|
||||
'enforcements',
|
||||
'lib',
|
||||
'prefix',
|
||||
'intro',
|
||||
@ -492,6 +496,7 @@ async function getBotSubmits(id: string): Promise<SubmittedBot[]> {
|
||||
res = await Promise.all(
|
||||
res.map(async (el) => {
|
||||
el.category = JSON.parse(el.category)
|
||||
el.enforcements = JSON.parse(el.enforcements)
|
||||
el.owner = owner
|
||||
return el
|
||||
})
|
||||
@ -636,6 +641,7 @@ async function submitBot(
|
||||
git: data.git,
|
||||
url: data.url,
|
||||
category: JSON.stringify(data.category),
|
||||
enforcements: JSON.stringify(data.enforcements),
|
||||
discord: data.discord,
|
||||
state: 0,
|
||||
})
|
||||
@ -745,6 +751,7 @@ async function updateBot(id: string, data: ManageBot): Promise<number> {
|
||||
intro: data.intro,
|
||||
desc: data.desc,
|
||||
vanity: data.vanity,
|
||||
enforcements: JSON.stringify(data.enforcements),
|
||||
banner: data.banner,
|
||||
bg: data.bg,
|
||||
})
|
||||
@ -1134,6 +1141,7 @@ async function approveBotSubmission(id: string, date: number) {
|
||||
'id',
|
||||
'date',
|
||||
'category',
|
||||
'enforcements',
|
||||
'lib',
|
||||
'prefix',
|
||||
'intro',
|
||||
@ -1160,6 +1168,7 @@ async function approveBotSubmission(id: string, date: number) {
|
||||
web: data.web,
|
||||
git: data.git,
|
||||
category: data.category,
|
||||
enforcements: data.enforcements,
|
||||
discord: data.discord,
|
||||
token: sign({ id }),
|
||||
})
|
||||
@ -1199,7 +1208,7 @@ async function viewBot(id: string) {
|
||||
await Bots.findByIdAndUpdate(id, { $push: { viewMetrix: { count: 0 } } }, { upsert: true })
|
||||
}
|
||||
|
||||
export const get = {
|
||||
const _get = {
|
||||
discord: {
|
||||
user: new DataLoader(
|
||||
async (ids: string[]) =>
|
||||
@ -1414,6 +1423,14 @@ export const get = {
|
||||
},
|
||||
}
|
||||
|
||||
export type CacheManager = typeof _get
|
||||
|
||||
if (!global.get) {
|
||||
global.get = _get
|
||||
}
|
||||
|
||||
export const get = global.get
|
||||
|
||||
export const update = {
|
||||
assignToken,
|
||||
resetBotToken,
|
||||
|
||||
@ -3,6 +3,7 @@ import YupKorean from 'yup-locales-ko'
|
||||
import { ListType } from '@types'
|
||||
import {
|
||||
botCategories,
|
||||
botEnforcements,
|
||||
library,
|
||||
reportCats,
|
||||
reservedVanityBypass,
|
||||
@ -174,6 +175,7 @@ export const AddBotSubmitSchema: Yup.SchemaOf<AddBotSubmit> = Yup.object({
|
||||
.min(100, '봇 설명은 최소 100자여야합니다.')
|
||||
.max(1500, '봇 설명은 최대 1500자여야합니다.')
|
||||
.required('봇 설명은 필수 항목입니다.'),
|
||||
enforcements: Yup.array(Yup.string().oneOf(Object.keys(botEnforcements))),
|
||||
_csrf: Yup.string().required(),
|
||||
_captcha: Yup.string().required(),
|
||||
})
|
||||
@ -190,6 +192,7 @@ export interface AddBotSubmit {
|
||||
category: string | string[]
|
||||
intro: string
|
||||
desc: string
|
||||
enforcements: string[]
|
||||
_csrf: string
|
||||
_captcha: string
|
||||
}
|
||||
@ -303,6 +306,7 @@ export function getManageBotSchema(perkAvailable = false) {
|
||||
.min(100, '봇 설명은 최소 100자여야합니다.')
|
||||
.max(1500, '봇 설명은 최대 1500자여야합니다.')
|
||||
.required('봇 설명은 필수 항목입니다.'),
|
||||
enforcements: Yup.array(Yup.string().oneOf(Object.keys(botEnforcements))),
|
||||
_csrf: Yup.string().required(),
|
||||
}
|
||||
|
||||
@ -349,6 +353,7 @@ export interface ManageBot {
|
||||
desc: string
|
||||
vanity: string
|
||||
banner: string
|
||||
enforcements: string[]
|
||||
bg: string
|
||||
_csrf: string
|
||||
}
|
||||
|
||||
108
yarn.lock
108
yarn.lock
@ -3187,10 +3187,10 @@
|
||||
dependencies:
|
||||
sparse-bitfield "^3.0.3"
|
||||
|
||||
"@next/env@14.2.26":
|
||||
version "14.2.26"
|
||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.26.tgz#5d55f72d2edb7246607c78f61e7d3ff21516bc2e"
|
||||
integrity sha512-vO//GJ/YBco+H7xdQhzJxF7ub3SUwft76jwaeOyVVQFHCi5DCnkP16WHB+JBylo4vOKPoZBlR94Z8xBxNBdNJA==
|
||||
"@next/env@14.2.30":
|
||||
version "14.2.30"
|
||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.30.tgz#f955b57975751584722b6b0a2a8cf2bdcc4ffae3"
|
||||
integrity sha512-KBiBKrDY6kxTQWGzKjQB7QirL3PiiOkV7KW98leHFjtVRKtft76Ra5qSA/SL75xT44dp6hOcqiiJ6iievLOYug==
|
||||
|
||||
"@next/eslint-plugin-next@13.4.7":
|
||||
version "13.4.7"
|
||||
@ -3199,50 +3199,50 @@
|
||||
dependencies:
|
||||
glob "7.1.7"
|
||||
|
||||
"@next/swc-darwin-arm64@14.2.26":
|
||||
version "14.2.26"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.26.tgz#84b31a22149b2c49f5c5b29cddd7acb3a84d7e1c"
|
||||
integrity sha512-zDJY8gsKEseGAxG+C2hTMT0w9Nk9N1Sk1qV7vXYz9MEiyRoF5ogQX2+vplyUMIfygnjn9/A04I6yrUTRTuRiyQ==
|
||||
"@next/swc-darwin-arm64@14.2.30":
|
||||
version "14.2.30"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.30.tgz#8179a35a068bc6f43a9ab6439875f6e330d02e52"
|
||||
integrity sha512-EAqfOTb3bTGh9+ewpO/jC59uACadRHM6TSA9DdxJB/6gxOpyV+zrbqeXiFTDy9uV6bmipFDkfpAskeaDcO+7/g==
|
||||
|
||||
"@next/swc-darwin-x64@14.2.26":
|
||||
version "14.2.26"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.26.tgz#50a5eb37972d313951f76f36f1f0b7100d063ebd"
|
||||
integrity sha512-U0adH5ryLfmTDkahLwG9sUQG2L0a9rYux8crQeC92rPhi3jGQEY47nByQHrVrt3prZigadwj/2HZ1LUUimuSbg==
|
||||
"@next/swc-darwin-x64@14.2.30":
|
||||
version "14.2.30"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.30.tgz#87c08d805c0546a73c25a0538a81f8b5f43bd0e9"
|
||||
integrity sha512-TyO7Wz1IKE2kGv8dwQ0bmPL3s44EKVencOqwIY69myoS3rdpO1NPg5xPM5ymKu7nfX4oYJrpMxv8G9iqLsnL4A==
|
||||
|
||||
"@next/swc-linux-arm64-gnu@14.2.26":
|
||||
version "14.2.26"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.26.tgz#c4278c157623b05886e37ff17194811aca1c2d00"
|
||||
integrity sha512-SINMl1I7UhfHGM7SoRiw0AbwnLEMUnJ/3XXVmhyptzriHbWvPPbbm0OEVG24uUKhuS1t0nvN/DBvm5kz6ZIqpg==
|
||||
"@next/swc-linux-arm64-gnu@14.2.30":
|
||||
version "14.2.30"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.30.tgz#eed26d87d96d9ef6fffbde98ceed2c75108a9911"
|
||||
integrity sha512-I5lg1fgPJ7I5dk6mr3qCH1hJYKJu1FsfKSiTKoYwcuUf53HWTrEkwmMI0t5ojFKeA6Vu+SfT2zVy5NS0QLXV4Q==
|
||||
|
||||
"@next/swc-linux-arm64-musl@14.2.26":
|
||||
version "14.2.26"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.26.tgz#5751132764b7a1f13a5a3fe447b03d564eb29705"
|
||||
integrity sha512-s6JaezoyJK2DxrwHWxLWtJKlqKqTdi/zaYigDXUJ/gmx/72CrzdVZfMvUc6VqnZ7YEvRijvYo+0o4Z9DencduA==
|
||||
"@next/swc-linux-arm64-musl@14.2.30":
|
||||
version "14.2.30"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.30.tgz#54b38b43c8acf3d3e0b71ae208a0bfca5a9b8563"
|
||||
integrity sha512-8GkNA+sLclQyxgzCDs2/2GSwBc92QLMrmYAmoP2xehe5MUKBLB2cgo34Yu242L1siSkwQkiV4YLdCnjwc/Micw==
|
||||
|
||||
"@next/swc-linux-x64-gnu@14.2.26":
|
||||
version "14.2.26"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.26.tgz#74312cac45704762faa73e0880be6549027303af"
|
||||
integrity sha512-FEXeUQi8/pLr/XI0hKbe0tgbLmHFRhgXOUiPScz2hk0hSmbGiU8aUqVslj/6C6KA38RzXnWoJXo4FMo6aBxjzg==
|
||||
"@next/swc-linux-x64-gnu@14.2.30":
|
||||
version "14.2.30"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.30.tgz#0ee0419da4dc1211a4c925b0841419cd07aa6c59"
|
||||
integrity sha512-8Ly7okjssLuBoe8qaRCcjGtcMsv79hwzn/63wNeIkzJVFVX06h5S737XNr7DZwlsbTBDOyI6qbL2BJB5n6TV/w==
|
||||
|
||||
"@next/swc-linux-x64-musl@14.2.26":
|
||||
version "14.2.26"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.26.tgz#5d96464d71d2000ec704e650a1a86bb9d73f760d"
|
||||
integrity sha512-BUsomaO4d2DuXhXhgQCVt2jjX4B4/Thts8nDoIruEJkhE5ifeQFtvW5c9JkdOtYvE5p2G0hcwQ0UbRaQmQwaVg==
|
||||
"@next/swc-linux-x64-musl@14.2.30":
|
||||
version "14.2.30"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.30.tgz#e88463d8c10dd600087b062f2dea59a515cd66f6"
|
||||
integrity sha512-dBmV1lLNeX4mR7uI7KNVHsGQU+OgTG5RGFPi3tBJpsKPvOPtg9poyav/BYWrB3GPQL4dW5YGGgalwZ79WukbKQ==
|
||||
|
||||
"@next/swc-win32-arm64-msvc@14.2.26":
|
||||
version "14.2.26"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.26.tgz#859472b532b11499b8f5c2237f54401456286913"
|
||||
integrity sha512-5auwsMVzT7wbB2CZXQxDctpWbdEnEW/e66DyXO1DcgHxIyhP06awu+rHKshZE+lPLIGiwtjo7bsyeuubewwxMw==
|
||||
"@next/swc-win32-arm64-msvc@14.2.30":
|
||||
version "14.2.30"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.30.tgz#6975cbbab74d519b06d93210ed86cd4f3dbc1c4d"
|
||||
integrity sha512-6MMHi2Qc1Gkq+4YLXAgbYslE1f9zMGBikKMdmQRHXjkGPot1JY3n5/Qrbg40Uvbi8//wYnydPnyvNhI1DMUW1g==
|
||||
|
||||
"@next/swc-win32-ia32-msvc@14.2.26":
|
||||
version "14.2.26"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.26.tgz#e52e9bd0c43b7a469b03eda6d7a07c3d0c28f549"
|
||||
integrity sha512-GQWg/Vbz9zUGi9X80lOeGsz1rMH/MtFO/XqigDznhhhTfDlDoynCM6982mPCbSlxJ/aveZcKtTlwfAjwhyxDpg==
|
||||
"@next/swc-win32-ia32-msvc@14.2.30":
|
||||
version "14.2.30"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.30.tgz#08ad4de2e082bc6b07d41099b4310daec7885748"
|
||||
integrity sha512-pVZMnFok5qEX4RT59mK2hEVtJX+XFfak+/rjHpyFh7juiT52r177bfFKhnlafm0UOSldhXjj32b+LZIOdswGTg==
|
||||
|
||||
"@next/swc-win32-x64-msvc@14.2.26":
|
||||
version "14.2.26"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.26.tgz#6f42a3ae16ae15c5c5e36efa9b7e291c86ab1275"
|
||||
integrity sha512-2rdB3T1/Gp7bv1eQTTm9d1Y1sv9UuJ2LAwOE0Pe2prHKe32UNscj7YS13fRB37d0GAiGNR+Y7ZcW8YjDI8Ns0w==
|
||||
"@next/swc-win32-x64-msvc@14.2.30":
|
||||
version "14.2.30"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.30.tgz#94d3ddcc1e97572a0514a6180c8e3bb415e1dc98"
|
||||
integrity sha512-4KCo8hMZXMjpTzs3HOqOGYYwAXymXIy7PEPAXNEcEOyKqkjiDlECumrWziy+JEF0Oi4ILHGxzgQ3YiMGG2t/Lg==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
@ -8355,12 +8355,12 @@ next-seo@^6.1.0:
|
||||
resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-6.1.0.tgz#b60b06958cc77e7ed56f0a61b2d6cd0afed88ebb"
|
||||
integrity sha512-iMBpFoJsR5zWhguHJvsoBDxDSmdYTHtnVPB1ij+CD0NReQCP78ZxxbdL9qkKIf4oEuZEqZkrjAQLB0bkII7RYA==
|
||||
|
||||
next@^14.2.26:
|
||||
version "14.2.26"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-14.2.26.tgz#b918b3fc5c55e1a67aada1347907675713687721"
|
||||
integrity sha512-b81XSLihMwCfwiUVRRja3LphLo4uBBMZEzBBWMaISbKTwOmq3wPknIETy/8000tr7Gq4WmbuFYPS7jOYIf+ZJw==
|
||||
next@^14.2.30:
|
||||
version "14.2.30"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-14.2.30.tgz#7b7288859794574067f65d6e2ea98822f2173006"
|
||||
integrity sha512-+COdu6HQrHHFQ1S/8BBsCag61jZacmvbuL2avHvQFbWa2Ox7bE+d8FyNgxRLjXQ5wtPyQwEmk85js/AuaG2Sbg==
|
||||
dependencies:
|
||||
"@next/env" "14.2.26"
|
||||
"@next/env" "14.2.30"
|
||||
"@swc/helpers" "0.5.5"
|
||||
busboy "1.6.0"
|
||||
caniuse-lite "^1.0.30001579"
|
||||
@ -8368,15 +8368,15 @@ next@^14.2.26:
|
||||
postcss "8.4.31"
|
||||
styled-jsx "5.1.1"
|
||||
optionalDependencies:
|
||||
"@next/swc-darwin-arm64" "14.2.26"
|
||||
"@next/swc-darwin-x64" "14.2.26"
|
||||
"@next/swc-linux-arm64-gnu" "14.2.26"
|
||||
"@next/swc-linux-arm64-musl" "14.2.26"
|
||||
"@next/swc-linux-x64-gnu" "14.2.26"
|
||||
"@next/swc-linux-x64-musl" "14.2.26"
|
||||
"@next/swc-win32-arm64-msvc" "14.2.26"
|
||||
"@next/swc-win32-ia32-msvc" "14.2.26"
|
||||
"@next/swc-win32-x64-msvc" "14.2.26"
|
||||
"@next/swc-darwin-arm64" "14.2.30"
|
||||
"@next/swc-darwin-x64" "14.2.30"
|
||||
"@next/swc-linux-arm64-gnu" "14.2.30"
|
||||
"@next/swc-linux-arm64-musl" "14.2.30"
|
||||
"@next/swc-linux-x64-gnu" "14.2.30"
|
||||
"@next/swc-linux-x64-musl" "14.2.30"
|
||||
"@next/swc-win32-arm64-msvc" "14.2.30"
|
||||
"@next/swc-win32-ia32-msvc" "14.2.30"
|
||||
"@next/swc-win32-x64-msvc" "14.2.30"
|
||||
|
||||
node-addon-api@^6.1.0:
|
||||
version "6.1.0"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user