mirror of
https://github.com/koreanbots/core.git
synced 2025-12-15 06:10:22 +00:00
Fix things for next release (#447)
* feat: updated bot name * fix: blocked bot shown * chore: updated api-docs commit hash * feat: showing build info * fix: number not formatted * feat: parsing image tag * docs: updated issue template
This commit is contained in:
parent
e9d7cc38f9
commit
0759e35f2c
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -19,14 +19,13 @@ assignees: ''
|
|||||||
|
|
||||||
> 어떤 문제가 발생하나요?
|
> 어떤 문제가 발생하나요?
|
||||||
|
|
||||||
## 클라이언트 버전
|
## 빌드 버전
|
||||||
|
|
||||||
> 클라이언트 버전을 알려주세요!
|
> 클라이언트의 빌드 버전을 알려주세요!
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
클라이언트 버전을 가져오실 줄 모르신다면 아래 링크를 참고해주세요
|
https://koreanbots.dev/debug
|
||||||
|
에서 확인하실 수 있습니다.
|
||||||
https://github.com/koreanbots/docs/blob/master/version.md
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## 사양
|
## 사양
|
||||||
|
|||||||
@ -12,8 +12,12 @@ WORKDIR /usr/src/app
|
|||||||
ARG NEXT_PUBLIC_SENTRY_DSN
|
ARG NEXT_PUBLIC_SENTRY_DSN
|
||||||
ARG SENTRY_DSN
|
ARG SENTRY_DSN
|
||||||
ARG SENTRY_AUTH_TOKEN
|
ARG SENTRY_AUTH_TOKEN
|
||||||
|
ARG SOURCE_COMMIT
|
||||||
|
ARG TAG
|
||||||
|
|
||||||
ENV NEXT_PUBLIC_SENTRY_DSN $NEXT_PUBLIC_SENTRY_DSN
|
ENV NEXT_PUBLIC_SENTRY_DSN $NEXT_PUBLIC_SENTRY_DSN
|
||||||
|
ENV NEXT_PUBLIC_SOURCE_COMMIT $SOURCE_COMMIT
|
||||||
|
ENV NEXT_PUBLIC_TAG $TAG
|
||||||
ENV SENTRY_DSN $SENTRY_DSN
|
ENV SENTRY_DSN $SENTRY_DSN
|
||||||
ENV SENTRY_AUTH_TOKEN $SENTRY_AUTH_TOKEN
|
ENV SENTRY_AUTH_TOKEN $SENTRY_AUTH_TOKEN
|
||||||
ENV SENTRY_ORG koreanbots
|
ENV SENTRY_ORG koreanbots
|
||||||
|
|||||||
2
api-docs
2
api-docs
@ -1 +1 @@
|
|||||||
Subproject commit fcc3fb57a2bce58703acb6a2e9be4cfe98929427
|
Subproject commit f7d36b42ac19c91a150580309025f439faa5cbbf
|
||||||
@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
docker build --build-arg SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN --build-arg NEXT_PUBLIC_SENTRY_DSN=$NEXT_PUBLIC_SENTRY_DSN --build-arg SENTRY_DSN=$SENTRY_DSN -f $DOCKERFILE_PATH -t $IMAGE_NAME .
|
docker build --build-arg SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN --build-arg NEXT_PUBLIC_SENTRY_DSN=$NEXT_PUBLIC_SENTRY_DSN --build-arg SENTRY_DSN=$SENTRY_DSN --build-arg SOURCE_COMMIT=$SOURCE_COMMIT --build-arg TAG=$IMAGE_NAME -f $DOCKERFILE_PATH -t $IMAGE_NAME .
|
||||||
@ -6,9 +6,10 @@ import { useEffect, useState } from 'react'
|
|||||||
import { DefaultSeo } from 'next-seo'
|
import { DefaultSeo } from 'next-seo'
|
||||||
import { GlobalHotKeys } from 'react-hotkeys'
|
import { GlobalHotKeys } from 'react-hotkeys'
|
||||||
import NProgress from 'nprogress'
|
import NProgress from 'nprogress'
|
||||||
|
import Package from '../package.json'
|
||||||
|
|
||||||
import Logger from '@utils/Logger'
|
import Logger from '@utils/Logger'
|
||||||
import { handlePWA, parseCookie, systemTheme } from '@utils/Tools'
|
import { handlePWA, parseCookie, parseDockerhubTag, systemTheme } from '@utils/Tools'
|
||||||
import { DESCRIPTION, shortcutKeyMap, THEME_COLOR, TITLE } from '@utils/Constants'
|
import { DESCRIPTION, shortcutKeyMap, THEME_COLOR, TITLE } from '@utils/Constants'
|
||||||
import { Theme } from '@types'
|
import { Theme } from '@types'
|
||||||
|
|
||||||
@ -39,6 +40,7 @@ const KoreanbotsApp = ({ Component, pageProps, err, cookie }: KoreanbotsProps):
|
|||||||
'%c' + 'KOREANBOTS',
|
'%c' + 'KOREANBOTS',
|
||||||
'color: #3366FF; -webkit-text-stroke: 2px black; font-size: 72px; font-weight: bold;'
|
'color: #3366FF; -webkit-text-stroke: 2px black; font-size: 72px; font-weight: bold;'
|
||||||
)
|
)
|
||||||
|
Logger.debug(`[BUILD INFO] Tag: ${parseDockerhubTag(process.env.NEXT_PUBLIC_TAG)}, Version: v${Package.version}, Hash: ${process.env.NEXT_PUBLIC_SOURCE_COMMIT}`)
|
||||||
console.log(
|
console.log(
|
||||||
'%c' + '이곳에 코드를 붙여넣으면 공격자에게 엑세스 토큰을 넘겨줄 수 있습니다!!',
|
'%c' + '이곳에 코드를 붙여넣으면 공격자에게 엑세스 토큰을 넘겨줄 수 있습니다!!',
|
||||||
'color: #ff0000; font-size: 20px; font-weight: bold;'
|
'color: #ff0000; font-size: 20px; font-weight: bold;'
|
||||||
|
|||||||
@ -27,6 +27,7 @@ const Tag = dynamic(() => import('@components/Tag'))
|
|||||||
const Segment = dynamic(() => import('@components/Segment'))
|
const Segment = dynamic(() => import('@components/Segment'))
|
||||||
const Advertisement = dynamic(() => import('@components/Advertisement'))
|
const Advertisement = dynamic(() => import('@components/Advertisement'))
|
||||||
const Login = dynamic(() => import('@components/Login'))
|
const Login = dynamic(() => import('@components/Login'))
|
||||||
|
const Message = dynamic(() => import('@components/Message'))
|
||||||
|
|
||||||
const VoteBot: NextPage<VoteBotProps> = ({ data, user, theme, csrfToken }) => {
|
const VoteBot: NextPage<VoteBotProps> = ({ data, user, theme, csrfToken }) => {
|
||||||
const [ votingStatus, setVotingStatus ] = useState(0)
|
const [ votingStatus, setVotingStatus ] = useState(0)
|
||||||
@ -58,39 +59,46 @@ const VoteBot: NextPage<VoteBotProps> = ({ data, user, theme, csrfToken }) => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}} />
|
}} />
|
||||||
<Advertisement />
|
{
|
||||||
<Link href={makeBotURL(data)}>
|
data.state === 'blocked' ? <div className='pb-40'>
|
||||||
<a className='text-blue-500 hover:opacity-80'><i className='fas fa-arrow-left mt-3 mb-3' /> <strong>{data.name}</strong>{getJosaPicker('로')(data.name)} 돌아가기</a>
|
<Message type='error'>
|
||||||
</Link>
|
<h2 className='text-lg font-black'>해당 봇은 관리자에 의해 삭제되었습니다.</h2>
|
||||||
<Segment className='mb-16 py-8'>
|
</Message>
|
||||||
<div className='text-center'>
|
</div> : <>
|
||||||
<DiscordAvatar userID={data.id} className='mx-auto w-52 h-52 bg-white mb-4' />
|
<Advertisement />
|
||||||
<Tag text={<span><i className='fas fa-heart text-red-600' /> {data.votes}</span>} dark />
|
<Link href={makeBotURL(data)}>
|
||||||
<h1 className='text-3xl font-bold mt-3'>{data.name}</h1>
|
<a className='text-blue-500 hover:opacity-80'><i className='fas fa-arrow-left mt-3 mb-3' /> <strong>{data.name}</strong>{getJosaPicker('로')(data.name)} 돌아가기</a>
|
||||||
<h4 className='text-md mt-1'>12시간마다 다시 투표하실 수 있습니다.</h4>
|
</Link>
|
||||||
<div className='inline-block mt-2'>
|
<Segment className='mb-16 py-8'>
|
||||||
{
|
<div className='text-center'>
|
||||||
votingStatus === 0 ? <Button onClick={()=> setVotingStatus(1)}>
|
<DiscordAvatar userID={data.id} className='mx-auto w-52 h-52 bg-white mb-4' />
|
||||||
<><i className='far fa-heart text-red-600'/> 하트 추가</>
|
<Tag text={<span><i className='fas fa-heart text-red-600' /> {data.votes}</span>} dark />
|
||||||
</Button>
|
<h1 className='text-3xl font-bold mt-3'>{data.name}</h1>
|
||||||
: votingStatus === 1 ? <Captcha dark={theme === 'dark'} onVerify={async (key) => {
|
<h4 className='text-md mt-1'>12시간마다 다시 투표하실 수 있습니다.</h4>
|
||||||
const res = await Fetch<{ retryAfter: number }|unknown>(`/bots/${data.id}/vote`, { method: 'POST', body: JSON.stringify({ _csrf: csrfToken, _captcha: key }) })
|
<div className='inline-block mt-2'>
|
||||||
setResult(res)
|
{
|
||||||
setVotingStatus(2)
|
votingStatus === 0 ? <Button onClick={()=> setVotingStatus(1)}>
|
||||||
}}
|
<><i className='far fa-heart text-red-600'/> 하트 추가</>
|
||||||
/>
|
</Button>
|
||||||
: result.code === 200 ? <h2 className='text-2xl font-bold'>해당 봇에 투표했습니다!</h2>
|
: votingStatus === 1 ? <Captcha dark={theme === 'dark'} onVerify={async (key) => {
|
||||||
: result.code === 429 ? <>
|
const res = await Fetch<{ retryAfter: number }|unknown>(`/bots/${data.id}/vote`, { method: 'POST', body: JSON.stringify({ _csrf: csrfToken, _captcha: key }) })
|
||||||
<h2 className='text-2xl font-bold'>이미 해당 봇에 투표하였습니다.</h2>
|
setResult(res)
|
||||||
<h4 className='text-md mt-1'>{Day(+new Date() + result.data?.retryAfter).fromNow()} 다시 투표하실 수 있습니다.</h4>
|
setVotingStatus(2)
|
||||||
</>
|
}}
|
||||||
: <p>{result.message}</p>
|
/>
|
||||||
}
|
: result.code === 200 ? <h2 className='text-2xl font-bold'>해당 봇에 투표했습니다!</h2>
|
||||||
</div>
|
: result.code === 429 ? <>
|
||||||
|
<h2 className='text-2xl font-bold'>이미 해당 봇에 투표하였습니다.</h2>
|
||||||
|
<h4 className='text-md mt-1'>{Day(+new Date() + result.data?.retryAfter).fromNow()} 다시 투표하실 수 있습니다.</h4>
|
||||||
|
</>
|
||||||
|
: <p>{result.message}</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</Segment>
|
</Segment>
|
||||||
<Advertisement />
|
<Advertisement /></>
|
||||||
|
}
|
||||||
</Container>
|
</Container>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import { useFormik } from 'formik'
|
import { useFormik } from 'formik'
|
||||||
|
|
||||||
import { checkBrowser } from '@utils/Tools'
|
|
||||||
|
|
||||||
const Container = dynamic(()=> import('@components/Container'))
|
const Container = dynamic(()=> import('@components/Container'))
|
||||||
const Divider = dynamic(()=> import('@components/Divider'))
|
const Divider = dynamic(()=> import('@components/Divider'))
|
||||||
const Segment = dynamic(()=> import('@components/Segment'))
|
const Segment = dynamic(()=> import('@components/Segment'))
|
||||||
|
|
||||||
import Package from '../package.json'
|
import Package from '../package.json'
|
||||||
import Markdown from '@components/Markdown'
|
import Markdown from '@components/Markdown'
|
||||||
|
import { parseDockerhubTag } from '@utils/Tools'
|
||||||
|
|
||||||
const ClientInfo = ():JSX.Element => {
|
const ClientInfo = ():JSX.Element => {
|
||||||
const formik = useFormik({
|
const formik = useFormik({
|
||||||
@ -29,44 +28,18 @@ https://github.com/koreanbots
|
|||||||
},
|
},
|
||||||
onSubmit: ()=>{ alert('Pong') }
|
onSubmit: ()=>{ alert('Pong') }
|
||||||
})
|
})
|
||||||
return <Container paddingTop className='mb-10'>
|
return <Container paddingTop className='pb-10'>
|
||||||
<h1 className='text-4xl font-bold mb-3 mt-3'>개발자모드</h1>
|
<h1 className='text-4xl font-bold mb-3 mt-3'>개발자모드</h1>
|
||||||
<h2 className='text-3xl font-semibold mb-4'>정보들</h2>
|
<h2 className='text-3xl font-semibold mb-4'>정보들</h2>
|
||||||
<Segment>
|
<Segment>
|
||||||
<div className='markdown-body text-black dark:text-white'>
|
<div className='markdown-body text-black dark:text-white'>
|
||||||
<h1>빌드정보</h1>
|
<h1>빌드정보</h1>
|
||||||
<ul className='list-disc'>
|
<ul className='list-disc'>
|
||||||
<li>버전: <code>{Package.version}</code></li>
|
<li>Tag: <code>{parseDockerhubTag(process.env.NEXT_PUBLIC_TAG)}</code></li>
|
||||||
|
<li>Version: <code>v{Package.version}</code></li>
|
||||||
<li>
|
<li>Hash: <code>{process.env.NEXT_PUBLIC_SOURCE_COMMIT}</code></li>
|
||||||
해시: <a href={`https://github.com/koreanbots/koreanbots/commit/${process.env.NEXT_PUBLIC_COMMIT_HASH}`}><code>{process.env.NEXT_PUBLIC_COMMIT_HASH}</code></a>
|
|
||||||
</li>
|
|
||||||
<li>브랜치: <code>{process.env.NEXT_PUBLIC_BRANCH}</code></li>
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h1>클라이언트 정보</h1>
|
|
||||||
<h2>브라우저</h2>
|
|
||||||
<code>{checkBrowser()}</code>
|
|
||||||
<h2>User-Agent</h2>
|
|
||||||
<pre>{navigator.userAgent}</pre>
|
|
||||||
{/* <h2>Darkmode</h2>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Theme</th>
|
|
||||||
<th>System Cached</th>
|
|
||||||
<th>System</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>{theme}</td>
|
|
||||||
<td>{localStorage.detected}</td>
|
|
||||||
<td>{systemTheme}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table> */}
|
|
||||||
</div>
|
</div>
|
||||||
</Segment>
|
</Segment>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|||||||
@ -107,7 +107,7 @@ const Users: NextPage<UserProps> = ({ user, data }) => {
|
|||||||
{data.bots.length === 0 ? <h2 className='text-xl'>소유한 봇이 없습니다.</h2> :
|
{data.bots.length === 0 ? <h2 className='text-xl'>소유한 봇이 없습니다.</h2> :
|
||||||
<ResponsiveGrid>
|
<ResponsiveGrid>
|
||||||
{
|
{
|
||||||
(data.bots as Bot[]).map((bot: Bot) => (
|
(data.bots as Bot[]).filter(el => el.state !== 'blocked').map((bot: Bot) => (
|
||||||
<BotCard key={bot.id} bot={bot} />
|
<BotCard key={bot.id} bot={bot} />
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { DiscordEnpoints } from '../utils/Constants'
|
|||||||
import { checkUserFlag, formatNumber } from '../utils/Tools'
|
import { checkUserFlag, formatNumber } from '../utils/Tools'
|
||||||
|
|
||||||
test('format Number', () => {
|
test('format Number', () => {
|
||||||
|
expect(formatNumber(995)).toBe('1천')
|
||||||
expect(formatNumber(1000)).toBe('1천')
|
expect(formatNumber(1000)).toBe('1천')
|
||||||
expect(formatNumber(33333)).toBe('3.3만')
|
expect(formatNumber(33333)).toBe('3.3만')
|
||||||
expect(formatNumber(600)).toBe('600')
|
expect(formatNumber(600)).toBe('600')
|
||||||
|
|||||||
@ -57,14 +57,14 @@ async function getBot(id: string, owners=true):Promise<Bot> {
|
|||||||
res[0].status = discordBot.presence?.activities?.find(r => r.type === 'STREAMING') ? 'streaming' : discordBot.presence?.status || null
|
res[0].status = discordBot.presence?.activities?.find(r => r.type === 'STREAMING') ? 'streaming' : discordBot.presence?.status || null
|
||||||
delete res[0].trusted
|
delete res[0].trusted
|
||||||
delete res[0].partnered
|
delete res[0].partnered
|
||||||
if (owners)
|
if (owners) {
|
||||||
{
|
|
||||||
res[0].owners = await Promise.all(
|
res[0].owners = await Promise.all(
|
||||||
res[0].owners.map(async (u: string) => await get._rawUser.load(u))
|
res[0].owners.map(async (u: string) => await get._rawUser.load(u))
|
||||||
)
|
)
|
||||||
res[0].owners = res[0].owners.filter((el: User | null) => el).map((row: User) => ({ ...row }))
|
res[0].owners = res[0].owners.filter((el: User | null) => el).map((row: User) => ({ ...row }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await knex('bots').update({ name: discordBot.username }).where({ id })
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,28 +95,31 @@ async function getBotList(type: ListType, page = 1, query?: string):Promise<BotL
|
|||||||
let res: { id: string }[]
|
let res: { id: string }[]
|
||||||
let count:string|number
|
let count:string|number
|
||||||
if (type === 'VOTE') {
|
if (type === 'VOTE') {
|
||||||
count = (await knex('bots').count())[0]['count(*)']
|
count = (await knex('bots').whereNot({ state: 'blocked' }).count())[0]['count(*)']
|
||||||
res = await knex('bots')
|
res = await knex('bots')
|
||||||
.orderBy('votes', 'desc')
|
.orderBy('votes', 'desc')
|
||||||
.orderBy('servers', 'desc')
|
.orderBy('servers', 'desc')
|
||||||
.limit(16)
|
.limit(16)
|
||||||
.offset(((page ? Number(page) : 1) - 1) * 16)
|
.offset(((page ? Number(page) : 1) - 1) * 16)
|
||||||
.select(['id'])
|
.select(['id'])
|
||||||
|
.whereNot({ state: 'blocked' })
|
||||||
} else if (type === 'TRUSTED') {
|
} else if (type === 'TRUSTED') {
|
||||||
count = (
|
count = (
|
||||||
await knex('bots')
|
await knex('bots')
|
||||||
.where({ trusted: true })
|
.where({ trusted: true })
|
||||||
.count()
|
.count()
|
||||||
|
.whereNot({ state: 'blocked' })
|
||||||
)[0]['count(*)']
|
)[0]['count(*)']
|
||||||
res = await knex('bots')
|
res = await knex('bots').whereNot({ state: 'blocked' })
|
||||||
.where({ trusted: true })
|
.where({ trusted: true })
|
||||||
.orderByRaw('RAND()')
|
.orderByRaw('RAND()')
|
||||||
.limit(16)
|
.limit(16)
|
||||||
.offset(((page ? Number(page) : 1) - 1) * 16)
|
.offset(((page ? Number(page) : 1) - 1) * 16)
|
||||||
.select(['id'])
|
.select(['id'])
|
||||||
|
.whereNot({ state: 'blocked' })
|
||||||
} else if (type === 'NEW') {
|
} else if (type === 'NEW') {
|
||||||
count = (
|
count = (
|
||||||
await knex('bots')
|
await knex('bots').whereNot({ state: 'blocked' })
|
||||||
.count()
|
.count()
|
||||||
)[0]['count(*)']
|
)[0]['count(*)']
|
||||||
res = await knex('bots')
|
res = await knex('bots')
|
||||||
@ -124,14 +127,15 @@ async function getBotList(type: ListType, page = 1, query?: string):Promise<BotL
|
|||||||
.limit(16)
|
.limit(16)
|
||||||
.offset(((page ? Number(page) : 1) - 1) * 16)
|
.offset(((page ? Number(page) : 1) - 1) * 16)
|
||||||
.select(['id'])
|
.select(['id'])
|
||||||
|
.whereNot({ state: 'blocked' })
|
||||||
} else if (type === 'PARTNERED') {
|
} else if (type === 'PARTNERED') {
|
||||||
count = (
|
count = (
|
||||||
await knex('bots')
|
await knex('bots')
|
||||||
.where({ partnered: true })
|
.where({ partnered: true }).andWhereNot({ state: 'blocked' })
|
||||||
.count()
|
.count()
|
||||||
)[0]['count(*)']
|
)[0]['count(*)']
|
||||||
res = await knex('bots')
|
res = await knex('bots')
|
||||||
.where({ partnered: true })
|
.where({ partnered: true }).andWhereNot({ state: 'blocked' })
|
||||||
.orderByRaw('RAND()')
|
.orderByRaw('RAND()')
|
||||||
.limit(16)
|
.limit(16)
|
||||||
.offset(((page ? Number(page) : 1) - 1) * 16)
|
.offset(((page ? Number(page) : 1) - 1) * 16)
|
||||||
@ -141,11 +145,11 @@ async function getBotList(type: ListType, page = 1, query?: string):Promise<BotL
|
|||||||
if (!categories.includes(query)) throw new Error('알 수 없는 카테고리입니다.')
|
if (!categories.includes(query)) throw new Error('알 수 없는 카테고리입니다.')
|
||||||
count = (
|
count = (
|
||||||
await knex('bots')
|
await knex('bots')
|
||||||
.where('category', 'like', `%${decodeURI(query)}%`)
|
.where('category', 'like', `%${decodeURI(query)}%`).andWhereNot({ state: 'blocked' })
|
||||||
.count()
|
.count()
|
||||||
)[0]['count(*)']
|
)[0]['count(*)']
|
||||||
res = await knex('bots')
|
res = await knex('bots')
|
||||||
.where('category', 'like', `%${decodeURI(query)}%`)
|
.where('category', 'like', `%${decodeURI(query)}%`).andWhereNot({ state: 'blocked' })
|
||||||
.orderBy('votes', 'desc')
|
.orderBy('votes', 'desc')
|
||||||
.orderBy('servers', 'desc')
|
.orderBy('servers', 'desc')
|
||||||
.limit(16)
|
.limit(16)
|
||||||
|
|||||||
@ -27,11 +27,12 @@ export function formatNumber(value: number):string {
|
|||||||
if(!value) return '0'
|
if(!value) return '0'
|
||||||
const suffixes = ['', '만', '억', '조','해']
|
const suffixes = ['', '만', '억', '조','해']
|
||||||
const suffixNum = Math.floor((''+value).length/4)
|
const suffixNum = Math.floor((''+value).length/4)
|
||||||
let shortValue:string|number = parseFloat((suffixNum != 0 ? (value / Math.pow(10000,suffixNum)) : value).toPrecision(2))
|
let shortValue: string|number = parseFloat((suffixNum != 0 ? (value / Math.pow(10000, suffixNum)) : value).toPrecision(2))
|
||||||
if (shortValue % 1 != 0) {
|
if (shortValue % 1 != 0) {
|
||||||
shortValue = shortValue.toFixed(1)
|
shortValue = shortValue.toFixed(1)
|
||||||
}
|
}
|
||||||
if(suffixNum === 1 && shortValue < 1) return Number(shortValue) * 10 + '천'
|
if(suffixNum === 1 && shortValue < 1) return Number(shortValue) * 10 + '천'
|
||||||
|
else if(shortValue === 1000) return '1천'
|
||||||
return shortValue+suffixes[suffixNum]
|
return shortValue+suffixes[suffixNum]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,4 +190,8 @@ export function getRandom<T=unknown>(arr: T[]): T {
|
|||||||
return arr[Math.floor(Math.random() * arr.length)]
|
return arr[Math.floor(Math.random() * arr.length)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parseDockerhubTag(imageTag: string) {
|
||||||
|
return imageTag?.split('/').pop().split(':').pop()
|
||||||
|
}
|
||||||
|
|
||||||
export * from './ShowdownExtensions'
|
export * from './ShowdownExtensions'
|
||||||
Loading…
x
Reference in New Issue
Block a user