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.
This commit is contained in:
Harvey Tindall
2025-07-14 13:58:03 +01:00
parent 4f02c44e39
commit 41ddf73e4f
4 changed files with 90 additions and 85 deletions
+38 -33
View File
@@ -1,6 +1,7 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
@@ -195,43 +196,47 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
invite.UserMinutes = req.UserMinutes invite.UserMinutes = req.UserMinutes
} }
invite.ValidTill = validTill invite.ValidTill = validTill
if req.SendTo != "" && app.config.Section("invite_emails").Key("enabled").MustBool(false) { if req.SendTo != "" {
addressValid := false if !(app.config.Section("invite_emails").Key("enabled").MustBool(false)) {
discord := "" app.err.Printf(lm.FailedSendInviteMessage, invite.Code, req.SendTo, errors.New(lm.InviteMessagesDisabled))
if discordEnabled && (!strings.Contains(req.SendTo, "@") || strings.HasPrefix(req.SendTo, "@")) { } else {
users := app.discord.GetUsers(req.SendTo) addressValid := false
if len(users) == 0 { discord := ""
invite.SendTo = fmt.Sprintf(lm.FailedSendToTooltipNoUser, req.SendTo) if discordEnabled && (!strings.Contains(req.SendTo, "@") || strings.HasPrefix(req.SendTo, "@")) {
} else if len(users) > 1 { users := app.discord.GetUsers(req.SendTo)
invite.SendTo = fmt.Sprintf(lm.FailedSendToTooltipMultiUser, req.SendTo) if len(users) == 0 {
} else { invite.SendTo = fmt.Sprintf(lm.FailedSendToTooltipNoUser, req.SendTo)
invite.SendTo = req.SendTo } else if len(users) > 1 {
addressValid = true invite.SendTo = fmt.Sprintf(lm.FailedSendToTooltipMultiUser, req.SendTo)
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)
} else { } 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 { if err != nil {
invite.SendTo = fmt.Sprintf(lm.FailedSendInviteMessage, invite.Code, req.SendTo, err) // Slight misuse of the template
app.err.Println(invite.SendTo) invite.SendTo = fmt.Sprintf(lm.FailedConstructInviteMessage, req.SendTo, err)
app.err.Printf(lm.FailedConstructInviteMessage, invite.Code, err)
} else { } 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)
}
} }
} }
} }
+3 -3
View File
@@ -1138,7 +1138,7 @@ sections:
type: note type: note
depends_true: link_reset depends_true: link_reset
required: false 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 - setting: language
name: Default reset link language name: Default reset link language
requires_restart: true requires_restart: true
@@ -1167,9 +1167,9 @@ sections:
description: Subject of password reset emails. description: Subject of password reset emails.
- section: invite_emails - section: invite_emails
meta: meta:
name: Invite emails name: Invite Messages
description: Settings for sending invites directly to users. description: Settings for sending invites directly to users.
depends_true: email|method depends_true: messages|enabled
settings: settings:
- setting: enabled - setting: enabled
name: Enabled name: Enabled
+48 -49
View File
@@ -1,6 +1,7 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
"strings" "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) 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 d.users[i.Interaction.Member.User.ID] = requester
recipient := i.ApplicationCommandData().Options[0].UserValue(s) 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) // d.app.debug.Println(invuser)
//label := i.ApplicationCommandData().Options[2].StringValue() //label := i.ApplicationCommandData().Options[2].StringValue()
//profile := i.ApplicationCommandData().Options[3].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) // 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)) { if !(d.app.canAccessAdminPageByID(requester.JellyfinID)) {
d.app.err.Printf(lm.FailedGenerateInvite, fmt.Sprintf(lm.NonAdminUser, requester.JellyfinID)) d.app.err.Printf(lm.FailedGenerateInvite, fmt.Sprintf(lm.NonAdminUser, requester.JellyfinID))
s.InteractionRespond(i.Interaction, &dg.InteractionResponse{ sendResponse("noPermission")
Type: dg.InteractionResponseChannelMessageWithSource,
Data: &dg.InteractionResponseData{
Content: d.app.storage.lang.Telegram[lang].Strings.get("noPermission"),
Flags: 64, // Ephemeral
},
})
return 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) { if recipient != nil {
invname, err := d.bot.GuildMember(d.guildID, recipient.ID) err = nil
var invname *dg.Member = nil
invname, err = d.bot.GuildMember(d.guildID, recipient.ID)
invite.SendTo = invname.User.Username invite.SendTo = invname.User.Username
msg, err := d.app.email.constructInvite(invite.Code, invite, d.app, false)
if err != nil { if err == nil && !(d.app.config.Section("invite_emails").Key("enabled").MustBool(false)) {
invite.SendTo = fmt.Sprintf(lm.FailedConstructInviteMessage, invite.Code, err) err = errors.New(lm.InviteMessagesDisabled)
d.app.err.Println(invite.SendTo) }
err := s.InteractionRespond(i.Interaction, &dg.InteractionResponse{
Type: dg.InteractionResponseChannelMessageWithSource, var msg *Message
Data: &dg.InteractionResponseData{ if err == nil {
Content: d.app.storage.lang.Telegram[lang].Strings.get("sentInviteFailure"), msg, err = d.app.email.constructInvite(invite.Code, invite, d.app, false)
Flags: 64, // Ephemeral
},
})
if err != nil { if err != nil {
d.app.err.Printf(lm.FailedReply, lm.Discord, requester.ID, 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)
} 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)
d.app.err.Println(invite.SendTo) 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 != "" { //if profile != "" {
d.app.storage.SetInvitesKey(invite.Code, invite) d.app.storage.SetInvitesKey(invite.Code, invite)
} }
+1
View File
@@ -355,6 +355,7 @@ const (
FailedConstructInviteMessage = "Failed to construct invite message for \"%s\": %v" FailedConstructInviteMessage = "Failed to construct invite message for \"%s\": %v"
FailedSendInviteMessage = "Failed to send invite message for \"%s\" to \"%s\": %v" FailedSendInviteMessage = "Failed to send invite message for \"%s\" to \"%s\": %v"
SentInviteMessage = "Sent invite message for \"%s\" to \"%s\"" SentInviteMessage = "Sent invite message for \"%s\" to \"%s\""
InviteMessagesDisabled = "invite messages are disabled, check settings"
FailedConstructConfirmationEmail = "Failed to construct confirmation email for \"%s\": %v" FailedConstructConfirmationEmail = "Failed to construct confirmation email for \"%s\": %v"
FailedSendConfirmationEmail = "Failed to send confirmation email for \"%s\" to \"%s\": %v" FailedSendConfirmationEmail = "Failed to send confirmation email for \"%s\" to \"%s\": %v"