From 41ddf73e4f68aa49e6bebc7acf7e137b597b6e10 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Mon, 14 Jul 2025 13:58:03 +0100 Subject: [PATCH] invites: emails -> messages, log when sendTo attempted when disabled A user's lengthy debugging resulted in them figuring out "Invite emails" being disabled stopped the "/inv" command from sending invites on discord, which makes sense except the confusing setting name (now renamed "Messages" in the UI), and the fact that no error was reported. This setting being disabled is now logged to the console when it's attempted through the admin page or discord. For #378. --- api-invites.go | 71 +++++++++++++++------------- config/config-base.yaml | 6 +-- discord.go | 97 +++++++++++++++++++------------------- logmessages/logmessages.go | 1 + 4 files changed, 90 insertions(+), 85 deletions(-) diff --git a/api-invites.go b/api-invites.go index 66e97fb..51e2236 100644 --- a/api-invites.go +++ b/api-invites.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "strconv" "strings" @@ -195,43 +196,47 @@ func (app *appContext) GenerateInvite(gc *gin.Context) { invite.UserMinutes = req.UserMinutes } invite.ValidTill = validTill - if req.SendTo != "" && app.config.Section("invite_emails").Key("enabled").MustBool(false) { - addressValid := false - discord := "" - if discordEnabled && (!strings.Contains(req.SendTo, "@") || strings.HasPrefix(req.SendTo, "@")) { - users := app.discord.GetUsers(req.SendTo) - if len(users) == 0 { - invite.SendTo = fmt.Sprintf(lm.FailedSendToTooltipNoUser, req.SendTo) - } else if len(users) > 1 { - invite.SendTo = fmt.Sprintf(lm.FailedSendToTooltipMultiUser, req.SendTo) - } else { - invite.SendTo = req.SendTo - addressValid = true - discord = users[0].User.ID - } - } else if emailEnabled { - addressValid = true - invite.SendTo = req.SendTo - } - if addressValid { - msg, err := app.email.constructInvite(invite.Code, invite, app, false) - if err != nil { - // Slight misuse of the template - invite.SendTo = fmt.Sprintf(lm.FailedConstructInviteMessage, req.SendTo, err) - - app.err.Printf(lm.FailedConstructInviteMessage, invite.Code, err) - } else { - var err error - if discord != "" { - err = app.discord.SendDM(msg, discord) + if req.SendTo != "" { + if !(app.config.Section("invite_emails").Key("enabled").MustBool(false)) { + app.err.Printf(lm.FailedSendInviteMessage, invite.Code, req.SendTo, errors.New(lm.InviteMessagesDisabled)) + } else { + addressValid := false + discord := "" + if discordEnabled && (!strings.Contains(req.SendTo, "@") || strings.HasPrefix(req.SendTo, "@")) { + users := app.discord.GetUsers(req.SendTo) + if len(users) == 0 { + invite.SendTo = fmt.Sprintf(lm.FailedSendToTooltipNoUser, req.SendTo) + } else if len(users) > 1 { + invite.SendTo = fmt.Sprintf(lm.FailedSendToTooltipMultiUser, req.SendTo) } else { - err = app.email.send(msg, req.SendTo) + invite.SendTo = req.SendTo + addressValid = true + discord = users[0].User.ID } + } else if emailEnabled { + addressValid = true + invite.SendTo = req.SendTo + } + if addressValid { + msg, err := app.email.constructInvite(invite.Code, invite, app, false) if err != nil { - invite.SendTo = fmt.Sprintf(lm.FailedSendInviteMessage, invite.Code, req.SendTo, err) - app.err.Println(invite.SendTo) + // Slight misuse of the template + invite.SendTo = fmt.Sprintf(lm.FailedConstructInviteMessage, req.SendTo, err) + + app.err.Printf(lm.FailedConstructInviteMessage, invite.Code, err) } else { - app.info.Printf(lm.SentInviteMessage, invite.Code, req.SendTo) + var err error + if discord != "" { + err = app.discord.SendDM(msg, discord) + } else { + err = app.email.send(msg, req.SendTo) + } + if err != nil { + invite.SendTo = fmt.Sprintf(lm.FailedSendInviteMessage, invite.Code, req.SendTo, err) + app.err.Println(invite.SendTo) + } else { + app.info.Printf(lm.SentInviteMessage, invite.Code, req.SendTo) + } } } } diff --git a/config/config-base.yaml b/config/config-base.yaml index d7f7f1d..2084ec6 100644 --- a/config/config-base.yaml +++ b/config/config-base.yaml @@ -1138,7 +1138,7 @@ sections: type: note depends_true: link_reset required: false - description: Set the "External jfa-go URL" in General so that links to jfa-go can be made. + description: Set the "External jfa-go URL" value in General so that links to jfa-go can be made. - setting: language name: Default reset link language requires_restart: true @@ -1167,9 +1167,9 @@ sections: description: Subject of password reset emails. - section: invite_emails meta: - name: Invite emails + name: Invite Messages description: Settings for sending invites directly to users. - depends_true: email|method + depends_true: messages|enabled settings: - setting: enabled name: Enabled diff --git a/discord.go b/discord.go index df34929..af0d52e 100644 --- a/discord.go +++ b/discord.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "net/http" "strings" @@ -605,6 +606,21 @@ func (d *DiscordDaemon) cmdInvite(s *dg.Session, i *dg.InteractionCreate, lang s requester := d.MustGetUser(channel.ID, i.Interaction.Member.User.ID, i.Interaction.Member.User.Discriminator, i.Interaction.Member.User.Username) d.users[i.Interaction.Member.User.ID] = requester recipient := i.ApplicationCommandData().Options[0].UserValue(s) + + // We don't reveal much in the message response itself so we can re-use this easily. + sendResponse := func(langKey string) { + err := s.InteractionRespond(i.Interaction, &dg.InteractionResponse{ + Type: dg.InteractionResponseChannelMessageWithSource, + Data: &dg.InteractionResponseData{ + Content: d.app.storage.lang.Telegram[lang].Strings.get(langKey), + Flags: 64, // Ephemeral + }, + }) + if err != nil { + d.app.err.Printf(lm.FailedReply, lm.Discord, requester.ID, err) + } + } + // d.app.debug.Println(invuser) //label := i.ApplicationCommandData().Options[2].StringValue() //profile := i.ApplicationCommandData().Options[3].StringValue() @@ -615,13 +631,7 @@ func (d *DiscordDaemon) cmdInvite(s *dg.Session, i *dg.InteractionCreate, lang s // We want the same criteria for running this command as accessing the admin page (i.e. an "admin" of some sort) if !(d.app.canAccessAdminPageByID(requester.JellyfinID)) { d.app.err.Printf(lm.FailedGenerateInvite, fmt.Sprintf(lm.NonAdminUser, requester.JellyfinID)) - s.InteractionRespond(i.Interaction, &dg.InteractionResponse{ - Type: dg.InteractionResponseChannelMessageWithSource, - Data: &dg.InteractionResponseData{ - Content: d.app.storage.lang.Telegram[lang].Strings.get("noPermission"), - Flags: 64, // Ephemeral - }, - }) + sendResponse("noPermission") return } @@ -663,54 +673,43 @@ func (d *DiscordDaemon) cmdInvite(s *dg.Session, i *dg.InteractionCreate, lang s } } - if recipient != nil && d.app.config.Section("invite_emails").Key("enabled").MustBool(false) { - invname, err := d.bot.GuildMember(d.guildID, recipient.ID) + if recipient != nil { + err = nil + + var invname *dg.Member = nil + invname, err = d.bot.GuildMember(d.guildID, recipient.ID) invite.SendTo = invname.User.Username - msg, err := d.app.email.constructInvite(invite.Code, invite, d.app, false) - if err != nil { - invite.SendTo = fmt.Sprintf(lm.FailedConstructInviteMessage, invite.Code, err) - d.app.err.Println(invite.SendTo) - err := s.InteractionRespond(i.Interaction, &dg.InteractionResponse{ - Type: dg.InteractionResponseChannelMessageWithSource, - Data: &dg.InteractionResponseData{ - Content: d.app.storage.lang.Telegram[lang].Strings.get("sentInviteFailure"), - Flags: 64, // Ephemeral - }, - }) + + if err == nil && !(d.app.config.Section("invite_emails").Key("enabled").MustBool(false)) { + err = errors.New(lm.InviteMessagesDisabled) + } + + var msg *Message + if err == nil { + msg, err = d.app.email.constructInvite(invite.Code, invite, d.app, false) if err != nil { - d.app.err.Printf(lm.FailedReply, lm.Discord, requester.ID, err) - } - } else { - var err error - err = d.app.discord.SendDM(msg, recipient.ID) - if err != nil { - invite.SendTo = fmt.Sprintf(lm.FailedSendInviteMessage, invite.Code, RenderDiscordUsername(recipient), err) + // Print extra message, ideally we'd just print this, or get rid of it though. + invite.SendTo = fmt.Sprintf(lm.FailedConstructInviteMessage, invite.Code, err) d.app.err.Println(invite.SendTo) - err := s.InteractionRespond(i.Interaction, &dg.InteractionResponse{ - Type: dg.InteractionResponseChannelMessageWithSource, - Data: &dg.InteractionResponseData{ - Content: d.app.storage.lang.Telegram[lang].Strings.get("sentInviteFailure"), - Flags: 64, // Ephemeral - }, - }) - if err != nil { - d.app.err.Printf(lm.FailedReply, lm.Discord, requester.ID, err) - } - } else { - d.app.info.Printf(lm.SentInviteMessage, invite.Code, RenderDiscordUsername(recipient)) - err := s.InteractionRespond(i.Interaction, &dg.InteractionResponse{ - Type: dg.InteractionResponseChannelMessageWithSource, - Data: &dg.InteractionResponseData{ - Content: d.app.storage.lang.Telegram[lang].Strings.get("sentInvite"), - Flags: 64, // Ephemeral - }, - }) - if err != nil { - d.app.err.Printf(lm.FailedReply, lm.Discord, requester.ID, err) - } } } + + if err == nil { + err = d.app.discord.SendDM(msg, recipient.ID) + } + + if err == nil { + d.app.info.Printf(lm.SentInviteMessage, invite.Code, RenderDiscordUsername(recipient)) + sendResponse("sentInvite") + } + + if err != nil { + invite.SendTo = fmt.Sprintf(lm.FailedSendInviteMessage, invite.Code, RenderDiscordUsername(recipient), err) + d.app.err.Println(invite.SendTo) + sendResponse("sentInviteFailure") + } } + //if profile != "" { d.app.storage.SetInvitesKey(invite.Code, invite) } diff --git a/logmessages/logmessages.go b/logmessages/logmessages.go index 5e4c333..160dabe 100644 --- a/logmessages/logmessages.go +++ b/logmessages/logmessages.go @@ -355,6 +355,7 @@ const ( FailedConstructInviteMessage = "Failed to construct invite message for \"%s\": %v" FailedSendInviteMessage = "Failed to send invite message for \"%s\" to \"%s\": %v" SentInviteMessage = "Sent invite message for \"%s\" to \"%s\"" + InviteMessagesDisabled = "invite messages are disabled, check settings" FailedConstructConfirmationEmail = "Failed to construct confirmation email for \"%s\": %v" FailedSendConfirmationEmail = "Failed to send confirmation email for \"%s\" to \"%s\": %v"