Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/Form/Label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'}>
Expand Down
5 changes: 4 additions & 1 deletion components/Form/Selects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
31 changes: 30 additions & 1 deletion pages/addbot.tsx
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values={values.enforcements ?? ([] as string[])}

values.enforcements 가 possibly undefined or null 인가요?

Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -57,6 +57,7 @@ const AddBot: NextPage<AddBotProps> = ({ logged, user, csrfToken, theme }) => {
- 어떤
- 기능
- 있나요?`,
enforcements: [],
_csrf: csrfToken,
_captcha: 'captcha',
}
Expand Down Expand Up @@ -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>
Expand Down
47 changes: 25 additions & 22 deletions pages/api/v2/bots/[id]/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,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 })
Expand All @@ -289,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,
},
{
Expand All @@ -302,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,
}
)
Expand Down
76 changes: 60 additions & 16 deletions pages/bots/[id]/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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,
Expand All @@ -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}
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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'
Expand All @@ -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> = 필수 항목
Expand All @@ -320,7 +365,6 @@ const ManageBotPage: NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme }
<i className='far fa-save' /> 저장
</>
</Button>

</Form>
)}
</Formik>
Expand Down
20 changes: 17 additions & 3 deletions pages/bots/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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' /> 초대하기
Expand Down
6 changes: 6 additions & 0 deletions types/index.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
19 changes: 19 additions & 0 deletions utils/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Loading