Compare commits

..

10 Commits

Author SHA1 Message Date
skinmaker1345
90b62e41b6 fix: typo 2025-07-03 21:53:47 +09:00
skinmaker1345
5260f85c03 refactor: do not filter keys to display enforcements 2025-07-03 21:52:19 +09:00
skinmaker1345
3e1e357802 refactor: make botEnforcements more maintainable 2025-07-03 21:41:40 +09:00
skinmaker1345
baeef4fd16 fix: typo 2025-07-03 21:41:40 +09:00
skinmaker1345
43fdf826af chore: add license related enforcements 2025-07-03 21:41:40 +09:00
skinmaker1345
e3aea944bf fix: do not send vanity log when no changes are made 2025-07-03 21:41:40 +09:00
skinmaker1345
221a19ce64 chore: specify enforcement description 2025-07-03 21:41:40 +09:00
skinmaker1345
2b2b2907a9 feat: add alert page 2025-07-03 21:41:40 +09:00
skinmaker1345
901c3a37b7 feat: add enforcements field 2025-07-03 21:41:40 +09:00
SKINMAKER
843e4137cb
fix: remove possiblity of duplicated instance of DataLoader (#688) 2025-06-30 08:55:44 +09:00
8 changed files with 41 additions and 27 deletions

View File

@ -368,8 +368,8 @@ const AddBot: NextPage<AddBotProps> = ({ logged, user, csrfToken, theme }) => {
> >
<Selects <Selects
options={Object.entries(botEnforcements).map(([k, v]) => ({ options={Object.entries(botEnforcements).map(([k, v]) => ({
label: k, label: v.label,
value: v, value: k,
}))} }))}
handleChange={(value) => { handleChange={(value) => {
setFieldValue( setFieldValue(

View File

@ -78,7 +78,6 @@ const ManageBotPage: NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme }
) )
return <Forbidden /> return <Forbidden />
const isPerkAvailable = checkBotFlag(bot.flags, 'trusted') || checkBotFlag(bot.flags, 'partnered') const isPerkAvailable = checkBotFlag(bot.flags, 'trusted') || checkBotFlag(bot.flags, 'partnered')
console.log(bot.enforcements)
return ( return (
<Container paddingTop className='pb-10 pt-5'> <Container paddingTop className='pb-10 pt-5'>
<NextSeo title={`${bot.name} 수정하기`} description='봇의 정보를 수정합니다.' /> <NextSeo title={`${bot.name} 수정하기`} description='봇의 정보를 수정합니다.' />
@ -341,8 +340,8 @@ const ManageBotPage: NextPage<ManageBotProps> = ({ bot, user, csrfToken, theme }
> >
<Selects <Selects
options={Object.entries(botEnforcements).map(([k, v]) => ({ options={Object.entries(botEnforcements).map(([k, v]) => ({
label: k, label: v.label,
value: v, value: k,
}))} }))}
handleChange={(value) => { handleChange={(value) => {
setFieldValue( setFieldValue(

View File

@ -9,9 +9,9 @@ import Tooltip from 'rc-tooltip'
import { SnowflakeUtil } from 'discord.js' import { SnowflakeUtil } from 'discord.js'
import { ParsedUrlQuery } from 'querystring' import { ParsedUrlQuery } from 'querystring'
import { Bot, BotEnforcementKeys, ResponseProps, Theme, User } from '@types' 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 { get } from '@utils/Query'
import Day from '@utils/Day' import Day from '@utils/Day'
import { ReportSchema } from '@utils/Yup' import { ReportSchema } from '@utils/Yup'
@ -120,15 +120,8 @@ const Bots: NextPage<BotsProps> = ({ data, desc, date, user, theme, csrfToken })
<Message type='warning'> <Message type='warning'>
<h2 className='text-lg font-extrabold'> .</h2> <h2 className='text-lg font-extrabold'> .</h2>
<p> <p>
{Object.entries({ {data.enforcements.map((i) => (
JOIN_ENFORCED: <li key={i}>{botEnforcements[i].description}</li>
'봇의 핵심 기능은 봇의 디스코드 서버에 참여해야 사용할 수 있습니다.',
JOIN_PARTIALLY_ENFORCED:
'봇의 일부 명령어는 봇의 디스코드 서버에 참여해야 사용할 수 있습니다.',
})
.filter((el) => data.enforcements.includes(el[0] as BotEnforcementKeys))
.map(([k, v]) => (
<li key={k}>{v}</li>
))} ))}
</p> </p>
</Message> </Message>

2
types/global.d.ts vendored
View File

@ -2,6 +2,7 @@
import * as Yup from 'yup' import * as Yup from 'yup'
import { Client } from 'discord.js' import { Client } from 'discord.js'
import NotificationManager from '@utils/NotificationManager' import NotificationManager from '@utils/NotificationManager'
import { CacheManager } from '@utils/Query'
declare global { declare global {
interface Window { interface Window {
@ -14,6 +15,7 @@ declare global {
var kodl: Client var kodl: Client
var serverlist: Client var serverlist: Client
var notification: NotificationManager var notification: NotificationManager
var get: CacheManager
interface Navigator { interface Navigator {
standalone?: boolean standalone?: boolean
} }

View File

@ -34,7 +34,7 @@ export interface Bot {
owners: User[] | string[] owners: User[] | string[]
} }
export type BotEnforcementKeys = ValueOf<typeof botEnforcements> export type BotEnforcementKeys = keyof typeof botEnforcements
export interface RawGuild { export interface RawGuild {
id: string id: string

View File

@ -121,10 +121,22 @@ export const botCategoryDescription = {
} }
export const botEnforcements = { export const botEnforcements = {
'서버 참여가 필요한 기능이 있습니다': 'JOIN_PARTIALLY_ENFORCED', JOIN_PARTIALLY_ENFORCED: {
'서버 참여 없이는 봇의 핵심 기능을 사용할 수 없습니다': 'JOIN_ENFORCED', label: '서버 참여가 필요한 기능이 있습니다',
'유료 구매가 필요한 기능이 있습니다': 'LICENSE_PARTIALLY_ENFORCED', description: '봇의 일부 명령어는 봇의 디스코드 서버에 참여해야 사용할 수 있습니다.',
'유료 구매 없이는 봇의 핵심 기능을 사용할 수 없습니다': 'LICENSE_ENFORCED', },
JOIN_ENFORCED: {
label: '서버 참여 없이는 봇의 핵심 기능을 사용할 수 없습니다',
description: '봇의 핵심 기능은 봇의 디스코드 서버에 참여해야 사용할 수 있습니다.',
},
LICENSE_PARTIALLY_ENFORCED: {
label: '유료 구매가 필요한 기능이 있습니다',
description: '봇의 일부 명령어는 유료 구매가 필요합니다.',
},
LICENSE_ENFORCED: {
label: '유료 구매 없이는 봇의 핵심 기능을 사용할 수 없습니다',
description: '유료 구매 없이는 봇의 핵심 기능을 사용할 수 없습니다.',
},
} as const } as const
export const botCategoryIcon = { export const botCategoryIcon = {

View File

@ -103,7 +103,7 @@ async function getBot(id: string, topLevel = true): Promise<Bot> {
res.owners = JSON.parse(res.owners) res.owners = JSON.parse(res.owners)
res.banner = res.banner ? camoUrl(res.banner) : null res.banner = res.banner ? camoUrl(res.banner) : null
res.bg = res.bg ? camoUrl(res.bg) : null res.bg = res.bg ? camoUrl(res.bg) : null
res.enforcements = JSON.parse(res.enforcements ?? '"[]"') res.enforcements = JSON.parse(res.enforcements ?? '[]')
if (discordBot.flags.bitfield & UserFlags.BotHTTPInteractions) { if (discordBot.flags.bitfield & UserFlags.BotHTTPInteractions) {
res.status = 'online' res.status = 'online'
} else if (botMember) { } else if (botMember) {
@ -465,7 +465,7 @@ async function getBotSubmit(id: string, date: number): Promise<SubmittedBot> {
.where({ id, date }) .where({ id, date })
if (res.length === 0) return null if (res.length === 0) return null
res[0].category = JSON.parse(res[0].category) res[0].category = JSON.parse(res[0].category)
res[0].enforcements = JSON.parse(res[0].enforcements || '"[]"') res[0].enforcements = JSON.parse(res[0].enforcements || '[]')
res[0].owner = await get.user.load(res[0].owner) res[0].owner = await get.user.load(res[0].owner)
return res[0] return res[0]
} }
@ -1208,7 +1208,7 @@ async function viewBot(id: string) {
await Bots.findByIdAndUpdate(id, { $push: { viewMetrix: { count: 0 } } }, { upsert: true }) await Bots.findByIdAndUpdate(id, { $push: { viewMetrix: { count: 0 } } }, { upsert: true })
} }
export const get = { const _get = {
discord: { discord: {
user: new DataLoader( user: new DataLoader(
async (ids: string[]) => async (ids: string[]) =>
@ -1423,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 = { export const update = {
assignToken, assignToken,
resetBotToken, resetBotToken,

View File

@ -175,7 +175,7 @@ export const AddBotSubmitSchema: Yup.SchemaOf<AddBotSubmit> = Yup.object({
.min(100, '봇 설명은 최소 100자여야합니다.') .min(100, '봇 설명은 최소 100자여야합니다.')
.max(1500, '봇 설명은 최대 1500자여야합니다.') .max(1500, '봇 설명은 최대 1500자여야합니다.')
.required('봇 설명은 필수 항목입니다.'), .required('봇 설명은 필수 항목입니다.'),
enforcements: Yup.array(Yup.string().oneOf(Object.values(botEnforcements))), enforcements: Yup.array(Yup.string().oneOf(Object.keys(botEnforcements))),
_csrf: Yup.string().required(), _csrf: Yup.string().required(),
_captcha: Yup.string().required(), _captcha: Yup.string().required(),
}) })
@ -306,7 +306,7 @@ export function getManageBotSchema(perkAvailable = false) {
.min(100, '봇 설명은 최소 100자여야합니다.') .min(100, '봇 설명은 최소 100자여야합니다.')
.max(1500, '봇 설명은 최대 1500자여야합니다.') .max(1500, '봇 설명은 최대 1500자여야합니다.')
.required('봇 설명은 필수 항목입니다.'), .required('봇 설명은 필수 항목입니다.'),
enforcements: Yup.array(Yup.string().oneOf(Object.values(botEnforcements))), enforcements: Yup.array(Yup.string().oneOf(Object.keys(botEnforcements))),
_csrf: Yup.string().required(), _csrf: Yup.string().required(),
} }