From 86fa4a01d3098c9c3685595d80f572f45584ba7c Mon Sep 17 00:00:00 2001 From: kimpure Date: Wed, 20 May 2026 14:29:14 +0000 Subject: [PATCH] add supertonic style option --- .gitignore | 3 +- eslint.config.js | 0 packages/bot/commands/setNya.ts | 2 +- packages/bot/commands/setSupertonicStyle.ts | 29 ++++++++++++++++++ packages/bot/commands/setVoice.ts | 2 +- packages/bot/db.ts | 17 ++++++++++- packages/bot/events/readChannel.ts | 12 +++++--- packages/bot/tts.ts | 3 +- .../db/generated/prisma/internal/class.ts | 4 +-- .../prisma/internal/prismaNamespace.ts | 3 +- .../prisma/internal/prismaNamespaceBrowser.ts | 3 +- .../prisma/models/DiscordUserProfile.ts | 30 ++++++++++++++++++- packages/env.ts | 10 +++++++ .../migration.sql | 2 ++ prisma/schema.prisma | 1 + 15 files changed, 105 insertions(+), 16 deletions(-) create mode 100644 eslint.config.js create mode 100644 packages/bot/commands/setSupertonicStyle.ts create mode 100644 prisma/migrations/20260520141050_add_supertonic_style/migration.sql diff --git a/.gitignore b/.gitignore index 2845510..4c108ac 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,4 @@ cache db docker-compose.yml -target/ -test.wav \ No newline at end of file +target/ \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..e69de29 diff --git a/packages/bot/commands/setNya.ts b/packages/bot/commands/setNya.ts index c7d8bc1..e1f2049 100644 --- a/packages/bot/commands/setNya.ts +++ b/packages/bot/commands/setNya.ts @@ -12,7 +12,7 @@ export default defineCommand( }); const profile = await getUserProfile(interaction.user.id); - setUserNya(interaction.user.id, !profile.nya); + await setUserNya(interaction.user.id, !profile.nya); await interaction.editReply(profile.nya ? "냐앙..." : "냐앙!!"); } diff --git a/packages/bot/commands/setSupertonicStyle.ts b/packages/bot/commands/setSupertonicStyle.ts new file mode 100644 index 0000000..9aedcca --- /dev/null +++ b/packages/bot/commands/setSupertonicStyle.ts @@ -0,0 +1,29 @@ +import { ChatInputCommandInteraction, MessageFlags, SlashCommandSubcommandBuilder } from "discord.js"; +import { defineCommand } from "../command"; +import { setUserSupertonicStyle } from "../db"; +import { SUPERTONIC_STYLE_LIST } from "../../env"; + +export default defineCommand( + new SlashCommandSubcommandBuilder() + .setName("슈퍼토닉목소리") + .setDescription("예주의 슈퍼토닉 목소리 스타일을 설정해요") + .addStringOption(option => + option + .setName("style") + .setDescription("사용할수 있는 목소리들이에요") + .setRequired(true) + .addChoices( + ...SUPERTONIC_STYLE_LIST + ) + ), + async (interaction: ChatInputCommandInteraction): Promise => { + await interaction.deferReply({ + flags: [MessageFlags.Ephemeral] + }); + + const style = interaction.options.getString("voice") as string; + setUserSupertonicStyle(interaction.user.id, style); + + await interaction.editReply("예주의 목소리 스타일을 변경했어요!"); + } +) \ No newline at end of file diff --git a/packages/bot/commands/setVoice.ts b/packages/bot/commands/setVoice.ts index 0d99855..f56536d 100644 --- a/packages/bot/commands/setVoice.ts +++ b/packages/bot/commands/setVoice.ts @@ -24,7 +24,7 @@ export default defineCommand( }); const voice = interaction.options.getString("voice") as Voice; - setUserVoice(interaction.user.id, voice); + await setUserVoice(interaction.user.id, voice); await interaction.editReply("예주의 목소리를 변경했어요!"); } diff --git a/packages/bot/db.ts b/packages/bot/db.ts index 23474ab..8487932 100644 --- a/packages/bot/db.ts +++ b/packages/bot/db.ts @@ -42,6 +42,21 @@ export async function setUserNya(userId: string, nya: boolean): Promise { }); } +export async function setUserSupertonicStyle(userId: string, style: string): Promise { + await prisma.discordUserProfile.upsert({ + where: { + userId: userId + }, + update: { + userSupertonicStyle: style + }, + create: { + userId: userId, + userSupertonicStyle: style + } + }); +} + export async function setUserVoice(userId: string, voice: Voice): Promise { await prisma.discordUserProfile.upsert({ where: { @@ -52,7 +67,7 @@ export async function setUserVoice(userId: string, voice: Voice): Promise }, create: { userId: userId, - voice: voice + voice: voice } }); } diff --git a/packages/bot/events/readChannel.ts b/packages/bot/events/readChannel.ts index 5ad927e..0a987da 100644 --- a/packages/bot/events/readChannel.ts +++ b/packages/bot/events/readChannel.ts @@ -3,6 +3,7 @@ import { getUserProfile, hasGuildReadChannel } from "../db"; import { defineEvent } from "../event"; import { playVoice, PlayVoiceOptions } from "../tts"; import { Voice } from "../../db/generated/prisma/enums"; +import { SUPERTONIC_DEFAULT_VOICE } from "../../env"; export default defineEvent("messageCreate", async (message) => { if (message.author.bot) @@ -20,7 +21,7 @@ export default defineEvent("messageCreate", async (message) => { let content = message.cleanContent; let voice: Voice | null = null - let options: PlayVoiceOptions | undefined = undefined; + let options: PlayVoiceOptions = {}; let matched: RegExpMatchArray | null = null; if (content.startsWith("$t ")) { voice = "TypeCast"; @@ -28,15 +29,18 @@ export default defineEvent("messageCreate", async (message) => { voice = "Papago"; } else if (matched = content.match(/^\$s(\S*) /)) { voice = "Supertonic"; - let style: string | undefined = undefined if (matched[1].length) { - style = matched[1] + options.supertonicStyleId = matched[1] } - options = { supertonicStyleId: style }; } else if (content.match(/^\$\s/)) { return; } + if (profile.userSupertonicStyle.length) { + options.supertonicStyleId ??= profile.userSupertonicStyle; + } + options.supertonicStyleId ??= SUPERTONIC_DEFAULT_VOICE; + if (voice) { content = content.replace(/^\$\S+\s+/, "") } else { diff --git a/packages/bot/tts.ts b/packages/bot/tts.ts index aeb1a14..bf2386d 100644 --- a/packages/bot/tts.ts +++ b/packages/bot/tts.ts @@ -99,7 +99,6 @@ export async function playVoice( throw new Error(`Unknown voice type: ${voice}`); } - VoiceQueue.fromConnection(connection).enqueue( TTSModelBase.bufferToAudioResource(voiceBuffer) ); @@ -112,7 +111,7 @@ export async function playVoice( export async function skipCurrentVoice(guild: Guild): Promise { let connection = await getOrCreateVoiceConnection(guild); if (!connection) - throw new Error("Yaeju is not joined VoiceChat"); + throw new Error("YaejuNyang is not joined VoiceChat"); const vqueue = VoiceQueue.fromConnection(connection); if (vqueue.hasNext()) { diff --git a/packages/db/generated/prisma/internal/class.ts b/packages/db/generated/prisma/internal/class.ts index c827904..b53ef69 100644 --- a/packages/db/generated/prisma/internal/class.ts +++ b/packages/db/generated/prisma/internal/class.ts @@ -20,7 +20,7 @@ const config: runtime.GetPrismaClientConfig = { "clientVersion": "7.3.0", "engineVersion": "9d6ad21cbbceab97458517b147a6a09ff43aa735", "activeProvider": "postgresql", - "inlineSchema": "generator client {\n provider = \"prisma-client\"\n output = \"../packages/db/generated/prisma\"\n specifying = [\"prismaSchemaFolder\"]\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel DiscordUserProfile {\n id String @id @default(cuid())\n userId String @unique\n voice Voice @default(Papago)\n nya Boolean @default(false)\n canTypecast Boolean @default(false)\n}\n\nmodel DiscordGuildProfile {\n id String @id @default(cuid())\n guildId String @unique\n readChannel String[] @default([])\n}\n\nenum Voice {\n TypeCast\n Papago\n Supertonic\n}\n", + "inlineSchema": "generator client {\n provider = \"prisma-client\"\n output = \"../packages/db/generated/prisma\"\n specifying = [\"prismaSchemaFolder\"]\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel DiscordUserProfile {\n id String @id @default(cuid())\n userId String @unique\n voice Voice @default(Papago)\n nya Boolean @default(false)\n canTypecast Boolean @default(false)\n userSupertonicStyle String @default(\"\")\n}\n\nmodel DiscordGuildProfile {\n id String @id @default(cuid())\n guildId String @unique\n readChannel String[] @default([])\n}\n\nenum Voice {\n TypeCast\n Papago\n Supertonic\n}\n", "runtimeDataModel": { "models": {}, "enums": {}, @@ -28,7 +28,7 @@ const config: runtime.GetPrismaClientConfig = { } } -config.runtimeDataModel = JSON.parse("{\"models\":{\"DiscordUserProfile\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"voice\",\"kind\":\"enum\",\"type\":\"Voice\"},{\"name\":\"nya\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"canTypecast\",\"kind\":\"scalar\",\"type\":\"Boolean\"}],\"dbName\":null},\"DiscordGuildProfile\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"guildId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"readChannel\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}") +config.runtimeDataModel = JSON.parse("{\"models\":{\"DiscordUserProfile\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"voice\",\"kind\":\"enum\",\"type\":\"Voice\"},{\"name\":\"nya\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"canTypecast\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"userSupertonicStyle\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":null},\"DiscordGuildProfile\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"guildId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"readChannel\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}") async function decodeBase64AsWasm(wasmBase64: string): Promise { const { Buffer } = await import('node:buffer') diff --git a/packages/db/generated/prisma/internal/prismaNamespace.ts b/packages/db/generated/prisma/internal/prismaNamespace.ts index d86e830..223da22 100644 --- a/packages/db/generated/prisma/internal/prismaNamespace.ts +++ b/packages/db/generated/prisma/internal/prismaNamespace.ts @@ -597,7 +597,8 @@ export const DiscordUserProfileScalarFieldEnum = { userId: 'userId', voice: 'voice', nya: 'nya', - canTypecast: 'canTypecast' + canTypecast: 'canTypecast', + userSupertonicStyle: 'userSupertonicStyle' } as const export type DiscordUserProfileScalarFieldEnum = (typeof DiscordUserProfileScalarFieldEnum)[keyof typeof DiscordUserProfileScalarFieldEnum] diff --git a/packages/db/generated/prisma/internal/prismaNamespaceBrowser.ts b/packages/db/generated/prisma/internal/prismaNamespaceBrowser.ts index 3369162..e9e0bda 100644 --- a/packages/db/generated/prisma/internal/prismaNamespaceBrowser.ts +++ b/packages/db/generated/prisma/internal/prismaNamespaceBrowser.ts @@ -76,7 +76,8 @@ export const DiscordUserProfileScalarFieldEnum = { userId: 'userId', voice: 'voice', nya: 'nya', - canTypecast: 'canTypecast' + canTypecast: 'canTypecast', + userSupertonicStyle: 'userSupertonicStyle' } as const export type DiscordUserProfileScalarFieldEnum = (typeof DiscordUserProfileScalarFieldEnum)[keyof typeof DiscordUserProfileScalarFieldEnum] diff --git a/packages/db/generated/prisma/models/DiscordUserProfile.ts b/packages/db/generated/prisma/models/DiscordUserProfile.ts index 178e0ba..a41b792 100644 --- a/packages/db/generated/prisma/models/DiscordUserProfile.ts +++ b/packages/db/generated/prisma/models/DiscordUserProfile.ts @@ -30,6 +30,7 @@ export type DiscordUserProfileMinAggregateOutputType = { voice: $Enums.Voice | null nya: boolean | null canTypecast: boolean | null + userSupertonicStyle: string | null } export type DiscordUserProfileMaxAggregateOutputType = { @@ -38,6 +39,7 @@ export type DiscordUserProfileMaxAggregateOutputType = { voice: $Enums.Voice | null nya: boolean | null canTypecast: boolean | null + userSupertonicStyle: string | null } export type DiscordUserProfileCountAggregateOutputType = { @@ -46,6 +48,7 @@ export type DiscordUserProfileCountAggregateOutputType = { voice: number nya: number canTypecast: number + userSupertonicStyle: number _all: number } @@ -56,6 +59,7 @@ export type DiscordUserProfileMinAggregateInputType = { voice?: true nya?: true canTypecast?: true + userSupertonicStyle?: true } export type DiscordUserProfileMaxAggregateInputType = { @@ -64,6 +68,7 @@ export type DiscordUserProfileMaxAggregateInputType = { voice?: true nya?: true canTypecast?: true + userSupertonicStyle?: true } export type DiscordUserProfileCountAggregateInputType = { @@ -72,6 +77,7 @@ export type DiscordUserProfileCountAggregateInputType = { voice?: true nya?: true canTypecast?: true + userSupertonicStyle?: true _all?: true } @@ -153,6 +159,7 @@ export type DiscordUserProfileGroupByOutputType = { voice: $Enums.Voice nya: boolean canTypecast: boolean + userSupertonicStyle: string _count: DiscordUserProfileCountAggregateOutputType | null _min: DiscordUserProfileMinAggregateOutputType | null _max: DiscordUserProfileMaxAggregateOutputType | null @@ -182,6 +189,7 @@ export type DiscordUserProfileWhereInput = { voice?: Prisma.EnumVoiceFilter<"DiscordUserProfile"> | $Enums.Voice nya?: Prisma.BoolFilter<"DiscordUserProfile"> | boolean canTypecast?: Prisma.BoolFilter<"DiscordUserProfile"> | boolean + userSupertonicStyle?: Prisma.StringFilter<"DiscordUserProfile"> | string } export type DiscordUserProfileOrderByWithRelationInput = { @@ -190,6 +198,7 @@ export type DiscordUserProfileOrderByWithRelationInput = { voice?: Prisma.SortOrder nya?: Prisma.SortOrder canTypecast?: Prisma.SortOrder + userSupertonicStyle?: Prisma.SortOrder } export type DiscordUserProfileWhereUniqueInput = Prisma.AtLeast<{ @@ -201,6 +210,7 @@ export type DiscordUserProfileWhereUniqueInput = Prisma.AtLeast<{ voice?: Prisma.EnumVoiceFilter<"DiscordUserProfile"> | $Enums.Voice nya?: Prisma.BoolFilter<"DiscordUserProfile"> | boolean canTypecast?: Prisma.BoolFilter<"DiscordUserProfile"> | boolean + userSupertonicStyle?: Prisma.StringFilter<"DiscordUserProfile"> | string }, "id" | "userId"> export type DiscordUserProfileOrderByWithAggregationInput = { @@ -209,6 +219,7 @@ export type DiscordUserProfileOrderByWithAggregationInput = { voice?: Prisma.SortOrder nya?: Prisma.SortOrder canTypecast?: Prisma.SortOrder + userSupertonicStyle?: Prisma.SortOrder _count?: Prisma.DiscordUserProfileCountOrderByAggregateInput _max?: Prisma.DiscordUserProfileMaxOrderByAggregateInput _min?: Prisma.DiscordUserProfileMinOrderByAggregateInput @@ -223,6 +234,7 @@ export type DiscordUserProfileScalarWhereWithAggregatesInput = { voice?: Prisma.EnumVoiceWithAggregatesFilter<"DiscordUserProfile"> | $Enums.Voice nya?: Prisma.BoolWithAggregatesFilter<"DiscordUserProfile"> | boolean canTypecast?: Prisma.BoolWithAggregatesFilter<"DiscordUserProfile"> | boolean + userSupertonicStyle?: Prisma.StringWithAggregatesFilter<"DiscordUserProfile"> | string } export type DiscordUserProfileCreateInput = { @@ -231,6 +243,7 @@ export type DiscordUserProfileCreateInput = { voice?: $Enums.Voice nya?: boolean canTypecast?: boolean + userSupertonicStyle?: string } export type DiscordUserProfileUncheckedCreateInput = { @@ -239,6 +252,7 @@ export type DiscordUserProfileUncheckedCreateInput = { voice?: $Enums.Voice nya?: boolean canTypecast?: boolean + userSupertonicStyle?: string } export type DiscordUserProfileUpdateInput = { @@ -247,6 +261,7 @@ export type DiscordUserProfileUpdateInput = { voice?: Prisma.EnumVoiceFieldUpdateOperationsInput | $Enums.Voice nya?: Prisma.BoolFieldUpdateOperationsInput | boolean canTypecast?: Prisma.BoolFieldUpdateOperationsInput | boolean + userSupertonicStyle?: Prisma.StringFieldUpdateOperationsInput | string } export type DiscordUserProfileUncheckedUpdateInput = { @@ -255,6 +270,7 @@ export type DiscordUserProfileUncheckedUpdateInput = { voice?: Prisma.EnumVoiceFieldUpdateOperationsInput | $Enums.Voice nya?: Prisma.BoolFieldUpdateOperationsInput | boolean canTypecast?: Prisma.BoolFieldUpdateOperationsInput | boolean + userSupertonicStyle?: Prisma.StringFieldUpdateOperationsInput | string } export type DiscordUserProfileCreateManyInput = { @@ -263,6 +279,7 @@ export type DiscordUserProfileCreateManyInput = { voice?: $Enums.Voice nya?: boolean canTypecast?: boolean + userSupertonicStyle?: string } export type DiscordUserProfileUpdateManyMutationInput = { @@ -271,6 +288,7 @@ export type DiscordUserProfileUpdateManyMutationInput = { voice?: Prisma.EnumVoiceFieldUpdateOperationsInput | $Enums.Voice nya?: Prisma.BoolFieldUpdateOperationsInput | boolean canTypecast?: Prisma.BoolFieldUpdateOperationsInput | boolean + userSupertonicStyle?: Prisma.StringFieldUpdateOperationsInput | string } export type DiscordUserProfileUncheckedUpdateManyInput = { @@ -279,6 +297,7 @@ export type DiscordUserProfileUncheckedUpdateManyInput = { voice?: Prisma.EnumVoiceFieldUpdateOperationsInput | $Enums.Voice nya?: Prisma.BoolFieldUpdateOperationsInput | boolean canTypecast?: Prisma.BoolFieldUpdateOperationsInput | boolean + userSupertonicStyle?: Prisma.StringFieldUpdateOperationsInput | string } export type DiscordUserProfileCountOrderByAggregateInput = { @@ -287,6 +306,7 @@ export type DiscordUserProfileCountOrderByAggregateInput = { voice?: Prisma.SortOrder nya?: Prisma.SortOrder canTypecast?: Prisma.SortOrder + userSupertonicStyle?: Prisma.SortOrder } export type DiscordUserProfileMaxOrderByAggregateInput = { @@ -295,6 +315,7 @@ export type DiscordUserProfileMaxOrderByAggregateInput = { voice?: Prisma.SortOrder nya?: Prisma.SortOrder canTypecast?: Prisma.SortOrder + userSupertonicStyle?: Prisma.SortOrder } export type DiscordUserProfileMinOrderByAggregateInput = { @@ -303,6 +324,7 @@ export type DiscordUserProfileMinOrderByAggregateInput = { voice?: Prisma.SortOrder nya?: Prisma.SortOrder canTypecast?: Prisma.SortOrder + userSupertonicStyle?: Prisma.SortOrder } export type StringFieldUpdateOperationsInput = { @@ -325,6 +347,7 @@ export type DiscordUserProfileSelect export type DiscordUserProfileSelectCreateManyAndReturn = runtime.Types.Extensions.GetSelect<{ @@ -333,6 +356,7 @@ export type DiscordUserProfileSelectCreateManyAndReturn export type DiscordUserProfileSelectUpdateManyAndReturn = runtime.Types.Extensions.GetSelect<{ @@ -341,6 +365,7 @@ export type DiscordUserProfileSelectUpdateManyAndReturn export type DiscordUserProfileSelectScalar = { @@ -349,9 +374,10 @@ export type DiscordUserProfileSelectScalar = { voice?: boolean nya?: boolean canTypecast?: boolean + userSupertonicStyle?: boolean } -export type DiscordUserProfileOmit = runtime.Types.Extensions.GetOmit<"id" | "userId" | "voice" | "nya" | "canTypecast", ExtArgs["result"]["discordUserProfile"]> +export type DiscordUserProfileOmit = runtime.Types.Extensions.GetOmit<"id" | "userId" | "voice" | "nya" | "canTypecast" | "userSupertonicStyle", ExtArgs["result"]["discordUserProfile"]> export type $DiscordUserProfilePayload = { name: "DiscordUserProfile" @@ -362,6 +388,7 @@ export type $DiscordUserProfilePayload composites: {} } @@ -790,6 +817,7 @@ export interface DiscordUserProfileFieldRefs { readonly voice: Prisma.FieldRef<"DiscordUserProfile", 'Voice'> readonly nya: Prisma.FieldRef<"DiscordUserProfile", 'Boolean'> readonly canTypecast: Prisma.FieldRef<"DiscordUserProfile", 'Boolean'> + readonly userSupertonicStyle: Prisma.FieldRef<"DiscordUserProfile", 'String'> } diff --git a/packages/env.ts b/packages/env.ts index 6f90e4e..6981863 100644 --- a/packages/env.ts +++ b/packages/env.ts @@ -7,3 +7,13 @@ export const APPLICATION_ID = process.env.APPLICATION_ID as string; export const GUILD_ID = process.env.GUILD_ID as string; export const TYPECAST_TOKENS = (process.env.TYPECAST_TOKEN as string).split(","); export const DATABASE_URL = process.env.DATABASE_URL as string; +export const SUPERTONIC_DEFAULT_VOICE = (process.env.SUPERTONIC_DEFAULT_VOICE as string | undefined) ?? "Q1"; +export const SUPERTONIC_STYLE_LIST: { name: string, value: string }[] = (()=>{ + const defaultValue = [ + { name: "여성1", value: "F1" }, + ]; + try { + return JSON.parse(process.env.SUPERTONIC_STYLE_LIST ?? "null") as any ?? defaultValue + } catch {} + return defaultValue; +})(); diff --git a/prisma/migrations/20260520141050_add_supertonic_style/migration.sql b/prisma/migrations/20260520141050_add_supertonic_style/migration.sql new file mode 100644 index 0000000..e13c7b2 --- /dev/null +++ b/prisma/migrations/20260520141050_add_supertonic_style/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "DiscordUserProfile" ADD COLUMN "userSupertonicStyle" TEXT NOT NULL DEFAULT ''; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 94f112f..b0de47c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -14,6 +14,7 @@ model DiscordUserProfile { voice Voice @default(Papago) nya Boolean @default(false) canTypecast Boolean @default(false) + userSupertonicStyle String @default("") } model DiscordGuildProfile {