Split content in playVoice instead of readChannel handler

This commit is contained in:
kimpure 2026-05-21 14:36:20 +00:00
parent d77f3694a6
commit d4f8013d94
No known key found for this signature in database
4 changed files with 67 additions and 24 deletions

View file

@ -64,10 +64,7 @@ export default defineEvent("messageCreate", async (message) => {
} }
} }
for (const text of content.split("\n")) { await playVoice(guild, profile, voice, content, options);
await playVoice(guild, profile, voice, text, options);
}
} catch(err) { } catch(err) {
message.reply("말이 꼬이네요 ㅜ.ㅜ"); message.reply("말이 꼬이네요 ㅜ.ㅜ");
console.log("playVoice failed. ", err); console.log("playVoice failed. ", err);

View file

@ -68,40 +68,48 @@ export async function playVoice(
throw new Error(`the user ${profile.userId} is can't use typecast voice`); throw new Error(`the user ${profile.userId} is can't use typecast voice`);
} }
let voiceBuffer: Buffer let voiceBufferList: Buffer[]
if (voice == "TypeCast") { if (voice == "TypeCast") {
const content = TTSTypecastModel.instance.ttsify(text); const content = TTSTypecastModel.instance.ttsify(text);
if (!content.length) if (!content.length)
throw new Error("Empty content"); throw new Error("Empty content");
voiceBuffer = await TTSTypecastModel.instance.getMemcachedVoice( voiceBufferList = await Promise.all(content.split("\n").map(
TTSTypecastModel.instance.createRequestId(content) (content) => TTSTypecastModel.instance.getMemcachedVoice(
); TTSTypecastModel.instance.createRequestId(content)
)
));
} else if (voice == "Supertonic") { } else if (voice == "Supertonic") {
const content = TTSSupertonicModel.instance.ttsify(text); const content = TTSSupertonicModel.instance.ttsify(text);
if (!content.length) if (!content.length)
throw new Error("Empty content"); throw new Error("Empty content");
voiceBuffer = await TTSSupertonicModel.instance.getMemcachedVoice( voiceBufferList = await Promise.all(content.split("\n").map(
TTSSupertonicModel.instance.createRequestId(content, options?.supertonicStyleId) (content) => TTSSupertonicModel.instance.getMemcachedVoice(
); TTSSupertonicModel.instance.createRequestId(content, options?.supertonicStyleId)
)
));
} else if (voice == "Papago") { } else if (voice == "Papago") {
const content = TTSPapagoModel.instance.ttsify(text); const content = TTSPapagoModel.instance.ttsify(text);
if (!content.length) if (!content.length)
throw new Error("Empty content"); throw new Error("Empty content");
voiceBuffer = await TTSPapagoModel.instance.getMemcachedVoice( voiceBufferList = await Promise.all(content.split("\n").map(
TTSPapagoModel.instance.createRequestId(content) (content) => TTSPapagoModel.instance.getMemcachedVoice(
); TTSPapagoModel.instance.createRequestId(content)
)
));
} else { } else {
throw new Error(`Unknown voice type: ${voice}`); throw new Error(`Unknown voice type: ${voice}`);
} }
VoiceQueue.fromConnection(connection).enqueue( for (const voiceBuffer of voiceBufferList) {
TTSModelBase.bufferToAudioResource(voiceBuffer) VoiceQueue.fromConnection(connection).enqueue(
); TTSModelBase.bufferToAudioResource(voiceBuffer)
);
}
} catch(err) { } catch(err) {
OutputHandler.errorLog("[PlayVoice Error]", err); OutputHandler.errorLog("[PlayVoice Error]", err);
throw new Error(err as any); throw new Error(err as any);

View file

@ -44,6 +44,7 @@ export default class IntegerKorean {
} }
static convertFromString(num: string): string { static convertFromString(num: string): string {
num = num.replace(/,/g, "");
let isNegative = false; let isNegative = false;
if (num.startsWith("-")) { if (num.startsWith("-")) {
num = num.slice(1, -1); num = num.slice(1, -1);

View file

@ -25,6 +25,11 @@ export const SymbolMap = {
"~": "물결표", "~": "물결표",
"\\": "역슬래쉬", "\\": "역슬래쉬",
"♡": "하트 ", "♡": "하트 ",
"|": "",
">": "",
"<": "",
":": "콜론",
";": "세미콜론"
}; };
export const VersionPostfix = { export const VersionPostfix = {
"a": "알파", "a": "알파",
@ -120,7 +125,19 @@ export const SIPrefix = {
"zi": "제비", "zi": "제비",
"y": "요타", "y": "요타",
"yi": "요비", "yi": "요비",
} };
export const LiterPrefix = {
"m": "밀리",
"": "",
};
export const MeterPrefix = {
"m": "밀리",
"c": "센치",
"": "",
"k": "킬로",
};
export function processDots(input: string): string { export function processDots(input: string): string {
return input.replace(/[\.,]+$/, "") return input.replace(/[\.,]+$/, "")
@ -136,6 +153,8 @@ export function saferKorean(input: string): string {
element as keyof typeof IsolatedSymbolMap element as keyof typeof IsolatedSymbolMap
]).join("") ]).join("")
)) ))
.replace(/\s\|\|\s/g, " 오얼 ")
.replace(/\s\&\&\s/g, " 엔드 ")
// Process codeblock // Process codeblock
.replace(/\`\`\`([\s\S]*?)\`\`\`/g, (_, content: string)=>{ .replace(/\`\`\`([\s\S]*?)\`\`\`/g, (_, content: string)=>{
@ -153,7 +172,6 @@ export function saferKorean(input: string): string {
// Process koreans // Process koreans
.replace(/ㅋ{2,}/g, (content) => "크".repeat(content.length)) .replace(/ㅋ{2,}/g, (content) => "크".repeat(content.length))
.replace(/[아ㅏ]{3,}/g, "아아아") .replace(/[아ㅏ]{3,}/g, "아아아")
.replace(/https\S+/g, "링크")
.replace(/ㅌ{2,}/g, "틔틔") .replace(/ㅌ{2,}/g, "틔틔")
.replace(/ㄷ{2,}/g, "덜덜") .replace(/ㄷ{2,}/g, "덜덜")
.replace(/(ㅊㅋ)+/g, (content: string) => { .replace(/(ㅊㅋ)+/g, (content: string) => {
@ -173,12 +191,24 @@ export function saferKorean(input: string): string {
.replace(/[ㄱ-ㅎㄲㄸㅃㅆㅉ]/g, (char: string) => ChoseongMap[char as keyof typeof ChoseongMap]) .replace(/[ㄱ-ㅎㄲㄸㅃㅆㅉ]/g, (char: string) => ChoseongMap[char as keyof typeof ChoseongMap])
// Process number, unit // Process number, unit
.replace(/(\d+)([kKMmgGtTpP][iI]?)[bB]/g, (_, num: string, mod: string) => { .replace(/([\d,]+)([kKMmgGtTpP][iI]?)[bB]/g, (_, num: string, mod: string) => {
// 10kib => 10키비바이트 // 10kib => 키비바이트
num = IntegerKorean.convertFromString(num); num = IntegerKorean.convertFromString(num);
mod = SIPrefix[mod.toLowerCase() as keyof typeof SIPrefix]; mod = SIPrefix[mod.toLowerCase() as keyof typeof SIPrefix];
return `${num} ${mod}바이트 `; 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)=>{ .replace(/([\d\.]+)\s*([개살시평명])/g, (_, num: string, postfix: string)=>{
// 10명 => 열명 // 10명 => 열명
if (num.includes(".")) { if (num.includes(".")) {
@ -194,7 +224,6 @@ export function saferKorean(input: string): string {
.replace(/[\d,]+/g, (num: string) => { .replace(/[\d,]+/g, (num: string) => {
// 1,000 원 => 천원 // 1,000 원 => 천원
if (!num.includes(",")) return num; if (!num.includes(",")) return num;
num = num.replace(/,/g, "");
return IntegerKorean.convertFromString(num); return IntegerKorean.convertFromString(num);
}) })
.replace(/(v?)([\d\.]+)([ab]?)/g, (_, suffix: string, num: string, postfix: string) => { .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, "제곱미터")
.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 // Process symbol
.replace(/[\%\^\&\*\#\@\.\-\+\_\=\/\\♡\$]/g, (t) => ( .replace(/[\%\^\&\*\#\@\.\-\+\_\=\/\\♡\$\|\:\;\>\<]/g, (t) => (
SymbolMap[t as keyof typeof SymbolMap] SymbolMap[t as keyof typeof SymbolMap]
)) ))
.replace(/([\?\!]+)/g, (_, content: string) => content[0]) .replace(/([\?\!]+)/g, (_, content: string) => content[0])
.replace(/\s+/g, " ") .replace(/[ \t\f\r]+/g, " ")
} }