diff --git a/packages/bot/events/readChannel.ts b/packages/bot/events/readChannel.ts index 7912aa0..e271cd6 100644 --- a/packages/bot/events/readChannel.ts +++ b/packages/bot/events/readChannel.ts @@ -64,10 +64,7 @@ export default defineEvent("messageCreate", async (message) => { } } - for (const text of content.split("\n")) { - await playVoice(guild, profile, voice, text, options); - } - + await playVoice(guild, profile, voice, content, options); } catch(err) { message.reply("말이 꼬이네요 ㅜ.ㅜ"); console.log("playVoice failed. ", err); diff --git a/packages/bot/tts.ts b/packages/bot/tts.ts index bf2386d..0f65d5a 100644 --- a/packages/bot/tts.ts +++ b/packages/bot/tts.ts @@ -68,40 +68,48 @@ export async function playVoice( throw new Error(`the user ${profile.userId} is can't use typecast voice`); } - let voiceBuffer: Buffer + let voiceBufferList: Buffer[] if (voice == "TypeCast") { const content = TTSTypecastModel.instance.ttsify(text); if (!content.length) throw new Error("Empty content"); - voiceBuffer = await TTSTypecastModel.instance.getMemcachedVoice( - TTSTypecastModel.instance.createRequestId(content) - ); + voiceBufferList = await Promise.all(content.split("\n").map( + (content) => TTSTypecastModel.instance.getMemcachedVoice( + TTSTypecastModel.instance.createRequestId(content) + ) + )); } else if (voice == "Supertonic") { const content = TTSSupertonicModel.instance.ttsify(text); if (!content.length) throw new Error("Empty content"); - voiceBuffer = await TTSSupertonicModel.instance.getMemcachedVoice( - TTSSupertonicModel.instance.createRequestId(content, options?.supertonicStyleId) - ); + voiceBufferList = await Promise.all(content.split("\n").map( + (content) => TTSSupertonicModel.instance.getMemcachedVoice( + TTSSupertonicModel.instance.createRequestId(content, options?.supertonicStyleId) + ) + )); } else if (voice == "Papago") { const content = TTSPapagoModel.instance.ttsify(text); if (!content.length) throw new Error("Empty content"); - voiceBuffer = await TTSPapagoModel.instance.getMemcachedVoice( - TTSPapagoModel.instance.createRequestId(content) - ); + voiceBufferList = await Promise.all(content.split("\n").map( + (content) => TTSPapagoModel.instance.getMemcachedVoice( + TTSPapagoModel.instance.createRequestId(content) + ) + )); } else { throw new Error(`Unknown voice type: ${voice}`); } - VoiceQueue.fromConnection(connection).enqueue( - TTSModelBase.bufferToAudioResource(voiceBuffer) - ); + for (const voiceBuffer of voiceBufferList) { + VoiceQueue.fromConnection(connection).enqueue( + TTSModelBase.bufferToAudioResource(voiceBuffer) + ); + } } catch(err) { OutputHandler.errorLog("[PlayVoice Error]", err); throw new Error(err as any); diff --git a/packages/utils/integerKorean.ts b/packages/utils/integerKorean.ts index 7ac0347..2cbee34 100644 --- a/packages/utils/integerKorean.ts +++ b/packages/utils/integerKorean.ts @@ -44,6 +44,7 @@ export default class IntegerKorean { } static convertFromString(num: string): string { + num = num.replace(/,/g, ""); let isNegative = false; if (num.startsWith("-")) { num = num.slice(1, -1); diff --git a/packages/utils/saferKorean.ts b/packages/utils/saferKorean.ts index 8134adb..a46ecbc 100644 --- a/packages/utils/saferKorean.ts +++ b/packages/utils/saferKorean.ts @@ -25,6 +25,11 @@ export const SymbolMap = { "~": "물결표", "\\": "역슬래쉬", "♡": "하트 ", + "|": "", + ">": "", + "<": "", + ":": "콜론", + ";": "세미콜론" }; export const VersionPostfix = { "a": "알파", @@ -120,7 +125,19 @@ export const SIPrefix = { "zi": "제비", "y": "요타", "yi": "요비", -} +}; + +export const LiterPrefix = { + "m": "밀리", + "": "", +}; + +export const MeterPrefix = { + "m": "밀리", + "c": "센치", + "": "", + "k": "킬로", +}; export function processDots(input: string): string { return input.replace(/[\.,]+$/, "") @@ -136,6 +153,8 @@ export function saferKorean(input: string): string { element as keyof typeof IsolatedSymbolMap ]).join("") )) + .replace(/\s\|\|\s/g, " 오얼 ") + .replace(/\s\&\&\s/g, " 엔드 ") // Process codeblock .replace(/\`\`\`([\s\S]*?)\`\`\`/g, (_, content: string)=>{ @@ -153,7 +172,6 @@ export function saferKorean(input: string): string { // Process koreans .replace(/ㅋ{2,}/g, (content) => "크".repeat(content.length)) .replace(/[아ㅏ]{3,}/g, "아아아") - .replace(/https\S+/g, "링크") .replace(/ㅌ{2,}/g, "틔틔") .replace(/ㄷ{2,}/g, "덜덜") .replace(/(ㅊㅋ)+/g, (content: string) => { @@ -173,12 +191,24 @@ export function saferKorean(input: string): string { .replace(/[ㄱ-ㅎㄲㄸㅃㅆㅉ]/g, (char: string) => ChoseongMap[char as keyof typeof ChoseongMap]) // Process number, unit - .replace(/(\d+)([kKMmgGtTpP][iI]?)[bB]/g, (_, num: string, mod: string) => { - // 10kib => 10키비바이트 + .replace(/([\d,]+)([kKMmgGtTpP][iI]?)[bB]/g, (_, num: string, mod: string) => { + // 10kib => 십키비바이트 num = IntegerKorean.convertFromString(num); mod = SIPrefix[mod.toLowerCase() as keyof typeof SIPrefix]; return `${num} ${mod}바이트 `; }) + .replace(/([\d,]+)([mck]?)m/g, (_, num: string, mod: string) => { + // 10m => 십미터 + num = IntegerKorean.convertFromString(num); + mod = MeterPrefix[mod as keyof typeof MeterPrefix]; + return `${num} ${mod}미터`; + }) + .replace(/([\d,]+)([m]?)l/g, (_, num: string, mod: string) => { + // 10l => 십리터 + num = IntegerKorean.convertFromString(num); + mod = LiterPrefix[mod as keyof typeof LiterPrefix]; + return `${num} ${mod}리터`; + }) .replace(/([\d\.]+)\s*([개살시평명])/g, (_, num: string, postfix: string)=>{ // 10명 => 열명 if (num.includes(".")) { @@ -194,7 +224,6 @@ export function saferKorean(input: string): string { .replace(/[\d,]+/g, (num: string) => { // 1,000 원 => 천원 if (!num.includes(",")) return num; - num = num.replace(/,/g, ""); return IntegerKorean.convertFromString(num); }) .replace(/(v?)([\d\.]+)([ab]?)/g, (_, suffix: string, num: string, postfix: string) => { @@ -231,10 +260,18 @@ export function saferKorean(input: string): string { .replace(/㎡/g, "제곱미터") .replace(/㎢/g, "제곱킬로미터") + // Process link + .replace(/[hH][tT]{2}[pP][sS]?:\/\/(\S+)/g, (_, url: string) => { + if (url.startsWith("tenor.com/view")) { + return "움짤"; + } + return "링크"; + }) + // Process symbol - .replace(/[\%\^\&\*\#\@\.\-\+\_\=\/\\♡\$]/g, (t) => ( + .replace(/[\%\^\&\*\#\@\.\-\+\_\=\/\\♡\$\|\:\;\>\<]/g, (t) => ( SymbolMap[t as keyof typeof SymbolMap] )) .replace(/([\?\!]+)/g, (_, content: string) => content[0]) - .replace(/\s+/g, " ") + .replace(/[ \t\f\r]+/g, " ") }