From 4de5b48128c0bf5fa044150876d7b89b1c7cc907 Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Fri, 21 Aug 2026 23:03:48 +0900 Subject: [PATCH 01/13] =?UTF-8?q?feat:=20TTS=E8=A8=AD=E5=AE=9A=E3=82=92?= =?UTF-8?q?=E5=AE=9F=E8=A3=85=E3=81=97=E3=82=B0=E3=83=AD=E3=83=BC=E3=83=90?= =?UTF-8?q?=E3=83=AB=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yuito Akatsuki (Tani Yutaka) --- .../command/general/tts/ttsSet/voice.go | 56 ++++- .../interaction/messageComponent/tts_voice.go | 211 ++++++++++-------- .../bot/handlers/interaction/registry.go | 4 +- src/internal/bot/ttsutil/voice_picker.go | 62 +++-- 4 files changed, 225 insertions(+), 108 deletions(-) diff --git a/src/internal/bot/handlers/interaction/command/general/tts/ttsSet/voice.go b/src/internal/bot/handlers/interaction/command/general/tts/ttsSet/voice.go index 5621479c..2b645588 100644 --- a/src/internal/bot/handlers/interaction/command/general/tts/ttsSet/voice.go +++ b/src/internal/bot/handlers/interaction/command/general/tts/ttsSet/voice.go @@ -2,6 +2,7 @@ package ttsSet import ( "fmt" + "log/slog" "time" "unibot/internal" "unibot/internal/bot/ttsutil" @@ -14,6 +15,13 @@ func LoadVoiceCommandContext() discord.ApplicationCommandOptionSubCommand { return discord.ApplicationCommandOptionSubCommand{ Name: "voice", Description: "読み上げの話者を設定します", + Options: []discord.ApplicationCommandOption{ + discord.ApplicationCommandOptionBool{ + Name: "global", + Description: "グローバルに設定するか (デフォルト: false)", + Required: false, + }, + }, } } @@ -23,6 +31,7 @@ func Voice(ctx *internal.BotContext) func(data discord.SlashCommandInteractionDa speakers, err := ttsutil.FetchSpeakers(ctx) if err != nil { + slog.ErrorContext(e.Ctx, "failed to fetch tts speaker list", slog.Any("err", err)) responseEmbed := discord.Embed{ Title: "エラー", Description: "話者情報の取得に失敗しました。", @@ -59,10 +68,51 @@ func Voice(ctx *internal.BotContext) func(data discord.SlashCommandInteractionDa return err } - currentSpeakerID := ttsutil.GetCurrentSpeakerID(ctx, e.User().ID.String()) - content, components := ttsutil.BuildVoiceMessage(0, pages, currentSpeakerID) + global := false + if value, ok := data.Options["global"]; ok { + global = value.Bool() + } + + currentSpeakerID, err := ttsutil.GetCurrentSpeakerID(ctx, e.User().ID, global, e.GuildID()) + if err != nil { + slog.ErrorContext(e.Ctx, "failed to fetch tts settings", slog.Any("err", err)) + responseEmbed := discord.Embed{ + Title: "エラー", + Description: "設定の取得に失敗しました。", + Color: config.Colors.Error, + Footer: &discord.EmbedFooter{ + Text: fmt.Sprintf("Requested by %s", e.User().Username), + IconURL: e.User().EffectiveAvatarURL(), + }, + Timestamp: func() *time.Time { + t := time.Now() + return &t + }(), + } + _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(responseEmbed).WithEphemeral(true)) + return err + } + embed, components, err := ttsutil.BuildVoiceMessage(ctx, 0, pages, currentSpeakerID, global) + if err != nil { + slog.ErrorContext(e.Ctx, "failed to generate response message", slog.Any("err", err)) + responseEmbed := discord.Embed{ + Title: "エラー", + Description: "メッセージの生成に失敗しました。", + Color: config.Colors.Error, + Footer: &discord.EmbedFooter{ + Text: fmt.Sprintf("Requested by %s", e.User().Username), + IconURL: e.User().EffectiveAvatarURL(), + }, + Timestamp: func() *time.Time { + t := time.Now() + return &t + }(), + } + _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(responseEmbed).WithEphemeral(true)) + return err + } - _, err = e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithContent(content).WithComponents(components...)) + _, err = e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(*embed).WithComponents(components...)) return err } } diff --git a/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go b/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go index 78c6640f..2eb75312 100644 --- a/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go +++ b/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go @@ -2,10 +2,12 @@ package messageComponent import ( "fmt" + "log/slog" "strconv" "time" "unibot/internal" "unibot/internal/bot/ttsutil" + "unibot/internal/query" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/handler" @@ -14,51 +16,58 @@ import ( // HandleTTSSetVoice は話者選択のセレクトメニューを処理します func HandleTTSSetVoice(ctx *internal.BotContext) func(data discord.SelectMenuInteractionData, e *handler.ComponentEvent) error { return func(_ discord.SelectMenuInteractionData, e *handler.ComponentEvent) error { - /* - config := ctx.Config - values := e.StringSelectMenuInteractionData().Values - if len(values) == 0 { - slog.WarnContext(e.Ctx, "no speaker selected", slog.String("outcome", "invalid_input")) - _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ - Title: "エラー", - Description: "話者が選択されていません。もう一度お試しください。", - Color: config.Colors.Error, - Footer: &discord.EmbedFooter{ - Text: fmt.Sprintf("Requested by %s", e.User().Username), - IconURL: e.User().EffectiveAvatarURL(), - }, - Timestamp: func() *time.Time { - t := time.Now() - return &t - }(), - }).WithFlags(discord.MessageFlagEphemeral)) - return err - } + config := ctx.Config + values := e.StringSelectMenuInteractionData().Values + if len(values) == 0 { + slog.WarnContext(e.Ctx, "no speaker selected", slog.String("outcome", "invalid_input")) + _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ + Title: "エラー", + Description: "話者が選択されていません。もう一度お試しください。", + Color: config.Colors.Error, + Footer: &discord.EmbedFooter{ + Text: fmt.Sprintf("Requested by %s", e.User().Username), + IconURL: e.User().EffectiveAvatarURL(), + }, + Timestamp: func() *time.Time { + t := time.Now() + return &t + }(), + }).WithFlags(discord.MessageFlagEphemeral)) + return err + } - speakerID := values[0] - if !ttsutil.IsSpeakerIDValid(ctx, speakerID) { - _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ - Title: "エラー", - Description: "不正な話者IDが選択されました。もう一度お試しください。", - Color: config.Colors.Error, - Footer: &discord.EmbedFooter{ - Text: fmt.Sprintf("Requested by %s", e.User().Username), - IconURL: e.User().EffectiveAvatarURL(), - }, - Timestamp: func() *time.Time { - t := time.Now() - return &t - }(), - }).WithFlags(discord.MessageFlagEphemeral)) - return err + speakerIDstring := values[0] + speakerIDraw, err := strconv.Atoi(speakerIDstring) + speakerID := int32(speakerIDraw) + if !ttsutil.IsSpeakerIDValid(ctx, speakerIDstring) || err != nil { + if err != nil { + slog.ErrorContext(e.Ctx, "failed to parse speaker id", slog.Any("err", err)) } - memberID := e.Member().User.ID.String() + _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ + Title: "エラー", + Description: "不正な話者IDが選択されました。もう一度お試しください。", + Color: config.Colors.Error, + Footer: &discord.EmbedFooter{ + Text: fmt.Sprintf("Requested by %s", e.User().Username), + IconURL: e.User().EffectiveAvatarURL(), + }, + Timestamp: func() *time.Time { + t := time.Now() + return &t + }(), + }).WithFlags(discord.MessageFlagEphemeral)) + return err + } + userID := e.Member().User.ID - memberRepo := repository.NewMemberRepository(ctx.DB) - if err := memberRepo.Create(memberID); err != nil { + modeValue := e.Vars["global"] + if modeValue == "global" { + setting, err := query.TtsUserPreference.Where(query.TtsUserPreference.UserID.Eq(int64(userID))).First() + if err != nil { + slog.ErrorContext(e.Ctx, "failed to fetch user tts preference", slog.Any("any", err)) _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ Title: "エラー", - Description: "メンバー情報の作成に失敗しました。", + Description: "TTS個人設定の取得に失敗しました。", Color: config.Colors.Error, Footer: &discord.EmbedFooter{ Text: fmt.Sprintf("Requested by %s", e.User().Username), @@ -72,12 +81,13 @@ func HandleTTSSetVoice(ctx *internal.BotContext) func(data discord.SelectMenuInt return err } - repo := repository.NewTTSPersonalSettingRepository(ctx.DB) - setting, err := repo.GetByMember(memberID) - if err != nil { + setting.UserID = int64(userID) + setting.SpeakerID = speakerID + if err := query.TtsUserPreference.Save(setting); err != nil { + slog.ErrorContext(e.Ctx, "failed save user tts preference", slog.Any("err", err)) _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ Title: "エラー", - Description: "TTS個人設定の取得に失敗しました。", + Description: "TTS個人設定の更新に失敗しました。", Color: config.Colors.Error, Footer: &discord.EmbedFooter{ Text: fmt.Sprintf("Requested by %s", e.User().Username), @@ -90,16 +100,14 @@ func HandleTTSSetVoice(ctx *internal.BotContext) func(data discord.SelectMenuInt }).WithFlags(discord.MessageFlagEphemeral)) return err } - - if setting == nil { - defaultSetting := repository.DefaultTTSPersonalSetting - setting = &defaultSetting - setting.MemberID = memberID - setting.SpeakerID = speakerID - if err := repo.Create(setting); err != nil { + } else { + { + setting, err := query.TtsMemberPreference.Where(query.TtsMemberPreference.UserID.Eq(int64(userID))).First() + if err != nil { + slog.ErrorContext(e.Ctx, "failed to fetch member tts preference", slog.Any("any", err)) _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ Title: "エラー", - Description: "TTS個人設定の作成に失敗しました。", + Description: "TTS個人設定の取得に失敗しました。", Color: config.Colors.Error, Footer: &discord.EmbedFooter{ Text: fmt.Sprintf("Requested by %s", e.User().Username), @@ -112,9 +120,11 @@ func HandleTTSSetVoice(ctx *internal.BotContext) func(data discord.SelectMenuInt }).WithFlags(discord.MessageFlagEphemeral)) return err } - } else { - setting.SpeakerID = speakerID - if err := repo.Update(setting); err != nil { + + setting.UserID = int64(userID) + setting.SpeakerID = &speakerID + if err := query.TtsMemberPreference.Save(setting); err != nil { + slog.ErrorContext(e.Ctx, "failed save member tts preference", slog.Any("err", err)) _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ Title: "エラー", Description: "TTS個人設定の更新に失敗しました。", @@ -131,24 +141,23 @@ func HandleTTSSetVoice(ctx *internal.BotContext) func(data discord.SelectMenuInt return err } } + } - label := ttsutil.ResolveSpeakerLabel(ctx, speakerID) - _, err = e.UpdateInteractionResponse(discord.NewMessageUpdate().WithEmbeds(discord.Embed{ - Title: "話者設定を更新しました", - Description: "選択した話者: " + label, - Color: config.Colors.Success, - Footer: &discord.EmbedFooter{ - Text: fmt.Sprintf("Requested by %s", e.User().Username), - IconURL: e.User().EffectiveAvatarURL(), - }, - Timestamp: func() *time.Time { - t := time.Now() - return &t - }(), - })) - return err - */ - return nil + label := ttsutil.ResolveSpeakerLabel(ctx, speakerIDstring) + _, err = e.UpdateInteractionResponse(discord.NewMessageUpdate().WithEmbeds(discord.Embed{ + Title: "話者設定を更新しました", + Description: "選択した話者: " + label, + Color: config.Colors.Success, + Footer: &discord.EmbedFooter{ + Text: fmt.Sprintf("Requested by %s", e.User().Username), + IconURL: e.User().EffectiveAvatarURL(), + }, + Timestamp: func() *time.Time { + t := time.Now() + return &t + }(), + })) + return err } } @@ -157,6 +166,8 @@ func HandleTTSSetVoicePage(ctx *internal.BotContext) func(data discord.ButtonInt return func(_ discord.ButtonInteractionData, e *handler.ComponentEvent) error { pageIndexString := e.Vars["pageIndex"] pageIndex, err := strconv.Atoi(pageIndexString) + globalModeString := e.Vars["global"] + isGlobal, err := strconv.ParseBool(globalModeString) if err != nil { _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ Title: "エラー", @@ -210,23 +221,47 @@ func HandleTTSSetVoicePage(ctx *internal.BotContext) func(data discord.ButtonInt return err } - memberID := e.Member().User.ID.String() - currentSpeakerID := ttsutil.GetCurrentSpeakerID(ctx, memberID) - content, components := ttsutil.BuildVoiceMessage(pageIndex, pages, currentSpeakerID) + memberID := e.Member().User.ID + currentSpeakerID, err := ttsutil.GetCurrentSpeakerID(ctx, memberID, isGlobal, e.GuildID()) + if err != nil { + slog.ErrorContext(e.Ctx, "failed to fetch tts settings", slog.Any("err", err)) + responseEmbed := discord.Embed{ + Title: "エラー", + Description: "設定の取得に失敗しました。", + Color: ctx.Config.Colors.Error, + Footer: &discord.EmbedFooter{ + Text: fmt.Sprintf("Requested by %s", e.User().Username), + IconURL: e.User().EffectiveAvatarURL(), + }, + Timestamp: func() *time.Time { + t := time.Now() + return &t + }(), + } + _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(responseEmbed).WithEphemeral(true)) + return err + } + embed, components, err := ttsutil.BuildVoiceMessage(ctx, pageIndex, pages, currentSpeakerID, isGlobal) + if err != nil { + slog.ErrorContext(e.Ctx, "failed to generate response message", slog.Any("err", err)) + responseEmbed := discord.Embed{ + Title: "エラー", + Description: "メッセージの生成に失敗しました。", + Color: ctx.Config.Colors.Error, + Footer: &discord.EmbedFooter{ + Text: fmt.Sprintf("Requested by %s", e.User().Username), + IconURL: e.User().EffectiveAvatarURL(), + }, + Timestamp: func() *time.Time { + t := time.Now() + return &t + }(), + } + _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(responseEmbed).WithEphemeral(true)) + return err + } - _, err = e.UpdateInteractionResponse(discord.NewMessageUpdate().WithEmbeds(discord.Embed{ - Title: "話者を選択してください", - Description: content, - Color: ctx.Config.Colors.Primary, - Footer: &discord.EmbedFooter{ - Text: fmt.Sprintf("Requested by %s", e.User().Username), - IconURL: e.User().EffectiveAvatarURL(), - }, - Timestamp: func() *time.Time { - t := time.Now() - return &t - }(), - }).WithComponents(components...)) + _, err = e.UpdateInteractionResponse(discord.NewMessageUpdate().WithEmbeds(*embed).WithComponents(components...)) return err } } diff --git a/src/internal/bot/handlers/interaction/registry.go b/src/internal/bot/handlers/interaction/registry.go index 3acd2f5d..e36d69f9 100644 --- a/src/internal/bot/handlers/interaction/registry.go +++ b/src/internal/bot/handlers/interaction/registry.go @@ -79,14 +79,14 @@ func RegistHandler(r *handler.Mux, ctxData *internal.BotContext) { r.Use(DeferReplyMiddleware(ctxData, true, true)) r.SelectMenuComponent("/", messageComponent.HandleTTSDictRemove(ctxData)) }) - r.Route("/tts_set_voice_select", func(r handler.Router) { + r.Route("/tts_set_voice_select/{global}", func(r handler.Router) { r.Use(DeferReplyMiddleware(ctxData, true, true)) r.SelectMenuComponent("/", messageComponent.HandleTTSSetVoice(ctxData)) }) // button r.Route("/tts_set_voice_page/{pageIndex}", func(r handler.Router) { r.Use(DeferReplyMiddleware(ctxData, true, true)) - r.ButtonComponent("/", messageComponent.HandleTTSSetVoicePage(ctxData)) + r.ButtonComponent("/{global}", messageComponent.HandleTTSSetVoicePage(ctxData)) }) } diff --git a/src/internal/bot/ttsutil/voice_picker.go b/src/internal/bot/ttsutil/voice_picker.go index eb1cb704..5e0a43c3 100644 --- a/src/internal/bot/ttsutil/voice_picker.go +++ b/src/internal/bot/ttsutil/voice_picker.go @@ -3,12 +3,15 @@ package ttsutil import ( "context" "fmt" + "strconv" "sync" "time" "unibot/internal" "unibot/internal/api/voicevox" + "unibot/internal/query" "github.com/disgoorg/disgo/discord" + "github.com/disgoorg/snowflake/v2" ) const ( @@ -123,10 +126,10 @@ func BuildSpeakerPages(speakers []voicevox.Speaker, perPage int) []SpeakerPage { return pages } -func BuildVoiceMessage(pageIndex int, pages []SpeakerPage, currentSpeakerID string) (string, []discord.LayoutComponent) { +func BuildVoiceMessage(ctx *internal.BotContext, pageIndex int, pages []SpeakerPage, currentSpeakerID int32, global bool) (*discord.Embed, []discord.LayoutComponent, error) { maxPage := len(pages) if maxPage == 0 { - return "話者情報が取得できませんでした。", []discord.LayoutComponent{} + return nil, []discord.LayoutComponent{}, fmt.Errorf("page length is 0") } if pageIndex < 0 { pageIndex = 0 @@ -135,13 +138,35 @@ func BuildVoiceMessage(pageIndex int, pages []SpeakerPage, currentSpeakerID stri pageIndex = maxPage - 1 } - content := fmt.Sprintf("話者を選択してください。\n現在の話者ID: %s\nページ %d/%d", currentSpeakerID, pageIndex+1, maxPage) + content := &discord.Embed{ + Title: "読み上げに使用する話者を選択してください", + Fields: []discord.EmbedField{ + { + Name: "現在の話者ID", + Value: strconv.Itoa(int(currentSpeakerID)), + }, + { + Name: "ページ数", + Value: fmt.Sprintf("%d/%d", pageIndex+1, maxPage), + }, + }, + Color: ctx.Config.Colors.Primary, + Timestamp: func() *time.Time { + t := time.Now() + return &t + }(), + } + + globalModeText := "false" + if global { + globalModeText = "true" + } components := []discord.LayoutComponent{ discord.ActionRowComponent{ Components: []discord.InteractiveComponent{ discord.NewStringSelectMenu( - VoiceSelectCustomID, + fmt.Sprint("%s/%s", VoiceSelectCustomID, globalModeText), "話者を選択してください", ).SetOptions(pages[pageIndex].Options...), }, @@ -149,8 +174,8 @@ func BuildVoiceMessage(pageIndex int, pages []SpeakerPage, currentSpeakerID stri } if maxPage > 1 { - prevID := fmt.Sprintf("%s/%d", VoicePageCustomIDPrefix, pageIndex-1) - nextID := fmt.Sprintf("%s/%d", VoicePageCustomIDPrefix, pageIndex+1) + prevID := fmt.Sprintf("%s/%d/%s", VoicePageCustomIDPrefix, pageIndex-1, globalModeText) + nextID := fmt.Sprintf("%s/%d/%s", VoicePageCustomIDPrefix, pageIndex+1, globalModeText) components = append(components, discord.ActionRowComponent{ Components: []discord.InteractiveComponent{ discord.ButtonComponent{ @@ -169,19 +194,26 @@ func BuildVoiceMessage(pageIndex int, pages []SpeakerPage, currentSpeakerID stri }) } - return content, components + return content, components, nil } -func GetCurrentSpeakerID(ctx *internal.BotContext, memberID string) string { - /* - repo := repository.NewTTSPersonalSettingRepository(ctx.DB) - setting, err := repo.GetByMember(memberID) +func GetCurrentSpeakerID(ctx *internal.BotContext, memberID snowflake.ID, isGlobal bool, guildID *snowflake.ID) (int32, error) { + if isGlobal { + setting, err := query.TtsUserPreference.Where(query.TtsUserPreference.UserID.Eq(int64(memberID))).First() if err != nil || setting == nil { - return repository.DefaultTTSPersonalSetting.SpeakerID + return 0, nil + } + return setting.SpeakerID, nil + } else { + if guildID != nil { + return 0, fmt.Errorf("guild id not set") } - return setting.SpeakerID - */ - return "0" + setting, err := query.TtsMemberPreference.Where(query.TtsMemberPreference.UserID.Eq(int64(memberID))).First() + if err != nil || setting == nil || setting.SpeakerID == nil { + return 0, nil + } + return *setting.SpeakerID, nil + } } func ResolveSpeakerLabel(ctx *internal.BotContext, speakerID string) string { From b174435f2a8ca8df881e31e5f05516f77da255b2 Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Fri, 21 Aug 2026 23:08:41 +0900 Subject: [PATCH 02/13] =?UTF-8?q?fix:=20fmt.Sprint=E3=82=92fmt.Sprintf?= =?UTF-8?q?=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yuito Akatsuki (Tani Yutaka) --- src/internal/bot/ttsutil/voice_picker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/bot/ttsutil/voice_picker.go b/src/internal/bot/ttsutil/voice_picker.go index 5e0a43c3..ee3cc035 100644 --- a/src/internal/bot/ttsutil/voice_picker.go +++ b/src/internal/bot/ttsutil/voice_picker.go @@ -166,7 +166,7 @@ func BuildVoiceMessage(ctx *internal.BotContext, pageIndex int, pages []SpeakerP discord.ActionRowComponent{ Components: []discord.InteractiveComponent{ discord.NewStringSelectMenu( - fmt.Sprint("%s/%s", VoiceSelectCustomID, globalModeText), + fmt.Sprintf("%s/%s", VoiceSelectCustomID, globalModeText), "話者を選択してください", ).SetOptions(pages[pageIndex].Options...), }, From 452c71679d8050395fad4d33068d184a6ca7cc6e Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Sat, 22 Aug 2026 00:23:57 +0900 Subject: [PATCH 03/13] =?UTF-8?q?fix:=20DM=E3=81=A7=E3=81=AE=E3=82=B3?= =?UTF-8?q?=E3=83=9E=E3=83=B3=E3=83=89=E5=AE=9F=E8=A1=8C=E6=99=82=E3=81=AB?= =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yuito Akatsuki (Tani Yutaka) --- .../command/general/tts/ttsSet/voice.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/internal/bot/handlers/interaction/command/general/tts/ttsSet/voice.go b/src/internal/bot/handlers/interaction/command/general/tts/ttsSet/voice.go index 2b645588..dcb19503 100644 --- a/src/internal/bot/handlers/interaction/command/general/tts/ttsSet/voice.go +++ b/src/internal/bot/handlers/interaction/command/general/tts/ttsSet/voice.go @@ -29,6 +29,24 @@ func Voice(ctx *internal.BotContext) func(data discord.SlashCommandInteractionDa return func(data discord.SlashCommandInteractionData, e *handler.CommandEvent) error { config := ctx.Config + if _, ok := e.Guild(); ok { + responseEmbed := discord.Embed{ + Title: "エラー", + Description: "DMで実行することはできません。", + Color: config.Colors.Error, + Footer: &discord.EmbedFooter{ + Text: fmt.Sprintf("Requested by %s", e.User().Username), + IconURL: e.User().EffectiveAvatarURL(), + }, + Timestamp: func() *time.Time { + t := time.Now() + return &t + }(), + } + _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(responseEmbed).WithEphemeral(true)) + return err + } + speakers, err := ttsutil.FetchSpeakers(ctx) if err != nil { slog.ErrorContext(e.Ctx, "failed to fetch tts speaker list", slog.Any("err", err)) From e6ac5bdc6e101fb370cee94f3948db000aef68db Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Sat, 22 Aug 2026 00:24:01 +0900 Subject: [PATCH 04/13] =?UTF-8?q?fix:=20TTS=E8=A8=AD=E5=AE=9A=E3=81=AE?= =?UTF-8?q?=E3=82=B0=E3=83=AD=E3=83=BC=E3=83=90=E3=83=AB=E3=83=A2=E3=83=BC?= =?UTF-8?q?=E3=83=89=E5=88=A4=E5=AE=9A=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yuito Akatsuki (Tani Yutaka) --- .../bot/handlers/interaction/messageComponent/tts_voice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go b/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go index 2eb75312..ba62f075 100644 --- a/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go +++ b/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go @@ -61,7 +61,7 @@ func HandleTTSSetVoice(ctx *internal.BotContext) func(data discord.SelectMenuInt userID := e.Member().User.ID modeValue := e.Vars["global"] - if modeValue == "global" { + if modeValue == "true" { setting, err := query.TtsUserPreference.Where(query.TtsUserPreference.UserID.Eq(int64(userID))).First() if err != nil { slog.ErrorContext(e.Ctx, "failed to fetch user tts preference", slog.Any("any", err)) From 5a0bec63e9170051ea97d57278583852a51b0ab6 Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Sat, 22 Aug 2026 00:24:38 +0900 Subject: [PATCH 05/13] =?UTF-8?q?fix:=20=E7=84=A1=E5=8A=B9=E3=81=AA?= =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E7=95=AA=E5=8F=B7=E3=81=8A=E3=82=88?= =?UTF-8?q?=E3=81=B3=E5=BC=95=E6=95=B0=E3=81=AB=E5=AF=BE=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yuito Akatsuki (Tani Yutaka) --- .../interaction/messageComponent/tts_voice.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go b/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go index ba62f075..4b6a1f9c 100644 --- a/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go +++ b/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go @@ -166,12 +166,29 @@ func HandleTTSSetVoicePage(ctx *internal.BotContext) func(data discord.ButtonInt return func(_ discord.ButtonInteractionData, e *handler.ComponentEvent) error { pageIndexString := e.Vars["pageIndex"] pageIndex, err := strconv.Atoi(pageIndexString) + if err != nil { + _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ + Title: "エラー", + Description: "無効なページ番号です。", + Color: ctx.Config.Colors.Error, + Footer: &discord.EmbedFooter{ + Text: fmt.Sprintf("Requested by %s", e.User().Username), + IconURL: e.User().EffectiveAvatarURL(), + }, + Timestamp: func() *time.Time { + t := time.Now() + return &t + }(), + }).WithFlags(discord.MessageFlagEphemeral)) + return err + } + globalModeString := e.Vars["global"] isGlobal, err := strconv.ParseBool(globalModeString) if err != nil { _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ Title: "エラー", - Description: "無効なページ番号です。", + Description: "無効な引数です。", Color: ctx.Config.Colors.Error, Footer: &discord.EmbedFooter{ Text: fmt.Sprintf("Requested by %s", e.User().Username), From 55c78f3c9d705be271fc2960220ec3589ea08088 Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Sat, 22 Aug 2026 00:26:58 +0900 Subject: [PATCH 06/13] =?UTF-8?q?fix:=20=E3=82=AE=E3=83=AB=E3=83=89ID?= =?UTF-8?q?=E3=81=8C=E8=A8=AD=E5=AE=9A=E3=81=95=E3=82=8C=E3=81=A6=E3=81=84?= =?UTF-8?q?=E3=81=AA=E3=81=84=E5=A0=B4=E5=90=88=E3=81=AE=E3=82=A8=E3=83=A9?= =?UTF-8?q?=E3=83=BC=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC=E3=82=B8=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yuito Akatsuki (Tani Yutaka) --- src/internal/bot/ttsutil/voice_picker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/bot/ttsutil/voice_picker.go b/src/internal/bot/ttsutil/voice_picker.go index ee3cc035..c6aac75f 100644 --- a/src/internal/bot/ttsutil/voice_picker.go +++ b/src/internal/bot/ttsutil/voice_picker.go @@ -205,7 +205,7 @@ func GetCurrentSpeakerID(ctx *internal.BotContext, memberID snowflake.ID, isGlob } return setting.SpeakerID, nil } else { - if guildID != nil { + if guildID == nil { return 0, fmt.Errorf("guild id not set") } setting, err := query.TtsMemberPreference.Where(query.TtsMemberPreference.UserID.Eq(int64(memberID))).First() From bf34e7c2b568b222c3b9e6f8569152938fb6d61a Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Sat, 22 Aug 2026 00:28:16 +0900 Subject: [PATCH 07/13] =?UTF-8?q?fix:=20=E3=83=A6=E3=83=BC=E3=82=B6?= =?UTF-8?q?=E3=83=BC=E3=81=8A=E3=82=88=E3=81=B3=E3=83=A1=E3=83=B3=E3=83=90?= =?UTF-8?q?=E3=83=BC=E3=81=AETTS=E8=A8=AD=E5=AE=9A=E5=8F=96=E5=BE=97?= =?UTF-8?q?=E6=99=82=E3=81=AB=E3=83=AC=E3=82=B3=E3=83=BC=E3=83=89=E3=81=8C?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E3=81=97=E3=81=AA=E3=81=84=E5=A0=B4=E5=90=88?= =?UTF-8?q?=E3=81=AE=E3=82=A8=E3=83=A9=E3=83=BC=E3=83=8F=E3=83=B3=E3=83=89?= =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=B0=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yuito Akatsuki (Tani Yutaka) --- .../bot/handlers/interaction/messageComponent/tts_voice.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go b/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go index 4b6a1f9c..89bb9469 100644 --- a/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go +++ b/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go @@ -11,6 +11,7 @@ import ( "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/handler" + "gorm.io/gorm" ) // HandleTTSSetVoice は話者選択のセレクトメニューを処理します @@ -63,7 +64,7 @@ func HandleTTSSetVoice(ctx *internal.BotContext) func(data discord.SelectMenuInt modeValue := e.Vars["global"] if modeValue == "true" { setting, err := query.TtsUserPreference.Where(query.TtsUserPreference.UserID.Eq(int64(userID))).First() - if err != nil { + if err != nil && err != gorm.ErrRecordNotFound { slog.ErrorContext(e.Ctx, "failed to fetch user tts preference", slog.Any("any", err)) _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ Title: "エラー", @@ -103,7 +104,7 @@ func HandleTTSSetVoice(ctx *internal.BotContext) func(data discord.SelectMenuInt } else { { setting, err := query.TtsMemberPreference.Where(query.TtsMemberPreference.UserID.Eq(int64(userID))).First() - if err != nil { + if err != nil && err != gorm.ErrRecordNotFound { slog.ErrorContext(e.Ctx, "failed to fetch member tts preference", slog.Any("any", err)) _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ Title: "エラー", From 8fd519ee2901e8772ea6bb12c1a3e807f9e69dfa Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Sat, 22 Aug 2026 00:33:07 +0900 Subject: [PATCH 08/13] =?UTF-8?q?fix:=20DM=E3=81=A7=E3=81=AE=E3=82=B3?= =?UTF-8?q?=E3=83=9E=E3=83=B3=E3=83=89=E5=AE=9F=E8=A1=8C=E6=99=82=E3=81=AE?= =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=83=8F=E3=83=B3=E3=83=89=E3=83=AA?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yuito Akatsuki (Tani Yutaka) --- .../handlers/interaction/command/general/tts/ttsSet/voice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/bot/handlers/interaction/command/general/tts/ttsSet/voice.go b/src/internal/bot/handlers/interaction/command/general/tts/ttsSet/voice.go index dcb19503..8ed41778 100644 --- a/src/internal/bot/handlers/interaction/command/general/tts/ttsSet/voice.go +++ b/src/internal/bot/handlers/interaction/command/general/tts/ttsSet/voice.go @@ -29,7 +29,7 @@ func Voice(ctx *internal.BotContext) func(data discord.SlashCommandInteractionDa return func(data discord.SlashCommandInteractionData, e *handler.CommandEvent) error { config := ctx.Config - if _, ok := e.Guild(); ok { + if _, ok := e.Guild(); !ok { responseEmbed := discord.Embed{ Title: "エラー", Description: "DMで実行することはできません。", From 9a29c5f3687ab09464933c60cd05ff6904eef56e Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Sat, 22 Aug 2026 10:30:46 +0900 Subject: [PATCH 09/13] =?UTF-8?q?fix:=20TTS=E8=A8=AD=E5=AE=9A=E3=81=AE?= =?UTF-8?q?=E5=8F=96=E5=BE=97=E3=81=AB=E3=82=AE=E3=83=AB=E3=83=89ID?= =?UTF-8?q?=E3=82=92=E8=80=83=E6=85=AE=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yuito Akatsuki (Tani Yutaka) --- .../interaction/messageComponent/tts_voice.go | 76 +++++++++---------- src/internal/bot/ttsutil/voice_picker.go | 2 +- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go b/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go index 89bb9469..ee8045a8 100644 --- a/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go +++ b/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go @@ -102,45 +102,45 @@ func HandleTTSSetVoice(ctx *internal.BotContext) func(data discord.SelectMenuInt return err } } else { - { - setting, err := query.TtsMemberPreference.Where(query.TtsMemberPreference.UserID.Eq(int64(userID))).First() - if err != nil && err != gorm.ErrRecordNotFound { - slog.ErrorContext(e.Ctx, "failed to fetch member tts preference", slog.Any("any", err)) - _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ - Title: "エラー", - Description: "TTS個人設定の取得に失敗しました。", - Color: config.Colors.Error, - Footer: &discord.EmbedFooter{ - Text: fmt.Sprintf("Requested by %s", e.User().Username), - IconURL: e.User().EffectiveAvatarURL(), - }, - Timestamp: func() *time.Time { - t := time.Now() - return &t - }(), - }).WithFlags(discord.MessageFlagEphemeral)) - return err - } + guildID := e.GuildID() + setting, err := query.TtsMemberPreference.Where(query.TtsMemberPreference.UserID.Eq(int64(userID)), query.TtsMemberPreference.GuildID.Eq(int64(*guildID))).First() + if err != nil && err != gorm.ErrRecordNotFound { + slog.ErrorContext(e.Ctx, "failed to fetch member tts preference", slog.Any("any", err)) + _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ + Title: "エラー", + Description: "TTS個人設定の取得に失敗しました。", + Color: config.Colors.Error, + Footer: &discord.EmbedFooter{ + Text: fmt.Sprintf("Requested by %s", e.User().Username), + IconURL: e.User().EffectiveAvatarURL(), + }, + Timestamp: func() *time.Time { + t := time.Now() + return &t + }(), + }).WithFlags(discord.MessageFlagEphemeral)) + return err + } - setting.UserID = int64(userID) - setting.SpeakerID = &speakerID - if err := query.TtsMemberPreference.Save(setting); err != nil { - slog.ErrorContext(e.Ctx, "failed save member tts preference", slog.Any("err", err)) - _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ - Title: "エラー", - Description: "TTS個人設定の更新に失敗しました。", - Color: config.Colors.Error, - Footer: &discord.EmbedFooter{ - Text: fmt.Sprintf("Requested by %s", e.User().Username), - IconURL: e.User().EffectiveAvatarURL(), - }, - Timestamp: func() *time.Time { - t := time.Now() - return &t - }(), - }).WithFlags(discord.MessageFlagEphemeral)) - return err - } + setting.UserID = int64(userID) + setting.GuildID = int64(*guildID) + setting.SpeakerID = &speakerID + if err := query.TtsMemberPreference.Save(setting); err != nil { + slog.ErrorContext(e.Ctx, "failed save member tts preference", slog.Any("err", err)) + _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ + Title: "エラー", + Description: "TTS個人設定の更新に失敗しました。", + Color: config.Colors.Error, + Footer: &discord.EmbedFooter{ + Text: fmt.Sprintf("Requested by %s", e.User().Username), + IconURL: e.User().EffectiveAvatarURL(), + }, + Timestamp: func() *time.Time { + t := time.Now() + return &t + }(), + }).WithFlags(discord.MessageFlagEphemeral)) + return err } } diff --git a/src/internal/bot/ttsutil/voice_picker.go b/src/internal/bot/ttsutil/voice_picker.go index c6aac75f..70506eb4 100644 --- a/src/internal/bot/ttsutil/voice_picker.go +++ b/src/internal/bot/ttsutil/voice_picker.go @@ -208,7 +208,7 @@ func GetCurrentSpeakerID(ctx *internal.BotContext, memberID snowflake.ID, isGlob if guildID == nil { return 0, fmt.Errorf("guild id not set") } - setting, err := query.TtsMemberPreference.Where(query.TtsMemberPreference.UserID.Eq(int64(memberID))).First() + setting, err := query.TtsMemberPreference.Where(query.TtsMemberPreference.UserID.Eq(int64(memberID)), query.TtsMemberPreference.GuildID.Eq(int64(*guildID))).First() if err != nil || setting == nil || setting.SpeakerID == nil { return 0, nil } From 9237f0a865f3317c342b033e209fb87fed5c543e Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Sat, 22 Aug 2026 10:31:22 +0900 Subject: [PATCH 10/13] =?UTF-8?q?fix:=20=E4=B8=8D=E6=AD=A3=E3=81=AA?= =?UTF-8?q?=E8=A9=B1=E8=80=85ID=E9=81=B8=E6=8A=9E=E6=99=82=E3=81=AE?= =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=83=8F=E3=83=B3=E3=83=89=E3=83=AA?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yuito Akatsuki (Tani Yutaka) --- .../interaction/messageComponent/tts_voice.go | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go b/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go index ee8045a8..200fd595 100644 --- a/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go +++ b/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go @@ -39,8 +39,27 @@ func HandleTTSSetVoice(ctx *internal.BotContext) func(data discord.SelectMenuInt speakerIDstring := values[0] speakerIDraw, err := strconv.Atoi(speakerIDstring) + if err != nil { + if err != nil { + slog.ErrorContext(e.Ctx, "failed to parse speaker id", slog.Any("err", err)) + } + _, err := e.Client().Rest.CreateFollowupMessage(e.ApplicationID(), e.Token(), discord.NewMessageCreate().WithEmbeds(discord.Embed{ + Title: "エラー", + Description: "不正な話者IDが選択されました。もう一度お試しください。", + Color: config.Colors.Error, + Footer: &discord.EmbedFooter{ + Text: fmt.Sprintf("Requested by %s", e.User().Username), + IconURL: e.User().EffectiveAvatarURL(), + }, + Timestamp: func() *time.Time { + t := time.Now() + return &t + }(), + }).WithFlags(discord.MessageFlagEphemeral)) + return err + } speakerID := int32(speakerIDraw) - if !ttsutil.IsSpeakerIDValid(ctx, speakerIDstring) || err != nil { + if !ttsutil.IsSpeakerIDValid(ctx, speakerIDstring) { if err != nil { slog.ErrorContext(e.Ctx, "failed to parse speaker id", slog.Any("err", err)) } From 2cb6502169f18e6432a2a17a556d07b34a5da7be Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Sat, 22 Aug 2026 10:37:36 +0900 Subject: [PATCH 11/13] =?UTF-8?q?fix:=20GetCurrentSpeakerID=E3=81=A7?= =?UTF-8?q?=E3=81=AE=E3=83=87=E3=83=BC=E3=82=BF=E3=83=99=E3=83=BC=E3=82=B9?= =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E6=99=82=E3=81=AE=E8=BF=94=E5=8D=B4?= =?UTF-8?q?=E5=80=A4=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yuito Akatsuki (Tani Yutaka) --- src/internal/bot/ttsutil/voice_picker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/bot/ttsutil/voice_picker.go b/src/internal/bot/ttsutil/voice_picker.go index 70506eb4..92fe6729 100644 --- a/src/internal/bot/ttsutil/voice_picker.go +++ b/src/internal/bot/ttsutil/voice_picker.go @@ -210,7 +210,7 @@ func GetCurrentSpeakerID(ctx *internal.BotContext, memberID snowflake.ID, isGlob } setting, err := query.TtsMemberPreference.Where(query.TtsMemberPreference.UserID.Eq(int64(memberID)), query.TtsMemberPreference.GuildID.Eq(int64(*guildID))).First() if err != nil || setting == nil || setting.SpeakerID == nil { - return 0, nil + return 0, err } return *setting.SpeakerID, nil } From c0707e08e991d845f47d758c94cce62a29702a29 Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Sat, 22 Aug 2026 10:38:12 +0900 Subject: [PATCH 12/13] =?UTF-8?q?fix:=20GetCurrentSpeakerID=E3=81=A7?= =?UTF-8?q?=E3=81=AE=E3=82=A8=E3=83=A9=E3=83=BC=E3=83=8F=E3=83=B3=E3=83=89?= =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=B0=E3=82=92=E6=94=B9=E5=96=84=E3=81=97?= =?UTF-8?q?=E3=80=81=E3=83=AC=E3=82=B3=E3=83=BC=E3=83=89=E3=81=8C=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E3=81=97=E3=81=AA=E3=81=84=E5=A0=B4=E5=90=88=E3=81=AE?= =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=82=92=E9=81=A9=E5=88=87=E3=81=AB?= =?UTF-8?q?=E5=87=A6=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yuito Akatsuki (Tani Yutaka) --- src/internal/bot/ttsutil/voice_picker.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/internal/bot/ttsutil/voice_picker.go b/src/internal/bot/ttsutil/voice_picker.go index 92fe6729..9e38fbeb 100644 --- a/src/internal/bot/ttsutil/voice_picker.go +++ b/src/internal/bot/ttsutil/voice_picker.go @@ -12,6 +12,7 @@ import ( "github.com/disgoorg/disgo/discord" "github.com/disgoorg/snowflake/v2" + "gorm.io/gorm" ) const ( @@ -209,7 +210,7 @@ func GetCurrentSpeakerID(ctx *internal.BotContext, memberID snowflake.ID, isGlob return 0, fmt.Errorf("guild id not set") } setting, err := query.TtsMemberPreference.Where(query.TtsMemberPreference.UserID.Eq(int64(memberID)), query.TtsMemberPreference.GuildID.Eq(int64(*guildID))).First() - if err != nil || setting == nil || setting.SpeakerID == nil { + if (err != nil && err != gorm.ErrRecordNotFound) || setting == nil || setting.SpeakerID == nil { return 0, err } return *setting.SpeakerID, nil From 006836cd9ac57aec5fce1c4cd729a06e3b57be37 Mon Sep 17 00:00:00 2001 From: "Yuito Akatsuki (Tani Yutaka)" Date: Sat, 22 Aug 2026 10:40:21 +0900 Subject: [PATCH 13/13] =?UTF-8?q?fix:=20GetCurrentSpeakerID=E3=81=A7?= =?UTF-8?q?=E3=81=AE=E8=A8=AD=E5=AE=9A=E3=81=8C=E5=AD=98=E5=9C=A8=E3=81=97?= =?UTF-8?q?=E3=81=AA=E3=81=84=E5=A0=B4=E5=90=88=E3=81=AE=E8=BF=94=E5=8D=B4?= =?UTF-8?q?=E5=80=A4=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yuito Akatsuki (Tani Yutaka) --- src/internal/bot/ttsutil/voice_picker.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/internal/bot/ttsutil/voice_picker.go b/src/internal/bot/ttsutil/voice_picker.go index 9e38fbeb..7ffc9053 100644 --- a/src/internal/bot/ttsutil/voice_picker.go +++ b/src/internal/bot/ttsutil/voice_picker.go @@ -210,8 +210,10 @@ func GetCurrentSpeakerID(ctx *internal.BotContext, memberID snowflake.ID, isGlob return 0, fmt.Errorf("guild id not set") } setting, err := query.TtsMemberPreference.Where(query.TtsMemberPreference.UserID.Eq(int64(memberID)), query.TtsMemberPreference.GuildID.Eq(int64(*guildID))).First() - if (err != nil && err != gorm.ErrRecordNotFound) || setting == nil || setting.SpeakerID == nil { + if err != nil && err != gorm.ErrRecordNotFound { return 0, err + } else if setting == nil || setting.SpeakerID == nil { + return 0, nil } return *setting.SpeakerID, nil }