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..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 @@ -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, + }, + }, } } @@ -21,8 +29,27 @@ 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)) responseEmbed := discord.Embed{ Title: "エラー", Description: "話者情報の取得に失敗しました。", @@ -59,10 +86,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..200fd595 100644 --- a/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go +++ b/src/internal/bot/handlers/interaction/messageComponent/tts_voice.go @@ -2,26 +2,92 @@ 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" + "gorm.io/gorm" ) // 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")) + 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 + } + + 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) { + 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 + } + userID := e.Member().User.ID + + modeValue := e.Vars["global"] + if modeValue == "true" { + setting, err := query.TtsUserPreference.Where(query.TtsUserPreference.UserID.Eq(int64(userID))).First() + 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: "エラー", - Description: "話者が選択されていません。もう一度お試しください。", + Description: "TTS個人設定の取得に失敗しました。", Color: config.Colors.Error, Footer: &discord.EmbedFooter{ Text: fmt.Sprintf("Requested by %s", e.User().Username), @@ -35,11 +101,13 @@ func HandleTTSSetVoice(ctx *internal.BotContext) func(data discord.SelectMenuInt return err } - speakerID := values[0] - if !ttsutil.IsSpeakerIDValid(ctx, speakerID) { + 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: "不正な話者IDが選択されました。もう一度お試しください。", + Description: "TTS個人設定の更新に失敗しました。", Color: config.Colors.Error, Footer: &discord.EmbedFooter{ Text: fmt.Sprintf("Requested by %s", e.User().Username), @@ -52,13 +120,14 @@ func HandleTTSSetVoice(ctx *internal.BotContext) func(data discord.SelectMenuInt }).WithFlags(discord.MessageFlagEphemeral)) return err } - memberID := e.Member().User.ID.String() - - memberRepo := repository.NewMemberRepository(ctx.DB) - if err := memberRepo.Create(memberID); err != nil { + } else { + 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: "メンバー情報の作成に失敗しました。", + Description: "TTS個人設定の取得に失敗しました。", Color: config.Colors.Error, Footer: &discord.EmbedFooter{ Text: fmt.Sprintf("Requested by %s", e.User().Username), @@ -72,12 +141,14 @@ 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.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個人設定の取得に失敗しました。", + Description: "TTS個人設定の更新に失敗しました。", Color: config.Colors.Error, Footer: &discord.EmbedFooter{ Text: fmt.Sprintf("Requested by %s", e.User().Username), @@ -90,53 +161,36 @@ 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 { - _, 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 - } - } else { - setting.SpeakerID = speakerID - if err := repo.Update(setting); err != nil { - _, 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 - } - } + 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 + } +} - label := ttsutil.ResolveSpeakerLabel(ctx, speakerID) - _, err = e.UpdateInteractionResponse(discord.NewMessageUpdate().WithEmbeds(discord.Embed{ - Title: "話者設定を更新しました", - Description: "選択した話者: " + label, - Color: config.Colors.Success, +// HandleTTSSetVoicePage は話者選択のページ送りを処理します +func HandleTTSSetVoicePage(ctx *internal.BotContext) func(data discord.ButtonInteractionData, e *handler.ComponentEvent) error { + 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(), @@ -145,22 +199,16 @@ func HandleTTSSetVoice(ctx *internal.BotContext) func(data discord.SelectMenuInt t := time.Now() return &t }(), - })) + }).WithFlags(discord.MessageFlagEphemeral)) return err - */ - return nil - } -} + } -// HandleTTSSetVoicePage は話者選択のページ送りを処理します -func HandleTTSSetVoicePage(ctx *internal.BotContext) func(data discord.ButtonInteractionData, e *handler.ComponentEvent) error { - 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: "エラー", - Description: "無効なページ番号です。", + Description: "無効な引数です。", Color: ctx.Config.Colors.Error, Footer: &discord.EmbedFooter{ Text: fmt.Sprintf("Requested by %s", e.User().Username), @@ -210,23 +258,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..7ffc9053 100644 --- a/src/internal/bot/ttsutil/voice_picker.go +++ b/src/internal/bot/ttsutil/voice_picker.go @@ -3,12 +3,16 @@ 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" + "gorm.io/gorm" ) const ( @@ -123,10 +127,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 +139,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.Sprintf("%s/%s", VoiceSelectCustomID, globalModeText), "話者を選択してください", ).SetOptions(pages[pageIndex].Options...), }, @@ -149,8 +175,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 +195,28 @@ 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)), query.TtsMemberPreference.GuildID.Eq(int64(*guildID))).First() + if err != nil && err != gorm.ErrRecordNotFound { + return 0, err + } else if setting == nil || setting.SpeakerID == nil { + return 0, nil + } + return *setting.SpeakerID, nil + } } func ResolveSpeakerLabel(ctx *internal.BotContext, speakerID string) string {