Compare commits

...

10 Commits

Author SHA1 Message Date
Violet Scheen e575b20cef
Merge 2fb2f3ee74 into 77f6b1042e 2023-10-11 11:39:05 +01:00
Harvey Tindall 2fb2f3ee74
discord: send error message when inv construction fails 2023-10-11 11:38:55 +01:00
Harvey Tindall 7813c8c68b
discord: Use GenerateInviteCode in /inv 2023-10-11 11:35:08 +01:00
Harvey Tindall e528f7c348
Merge latest changes 2023-10-11 11:33:51 +01:00
Harvey Tindall 77f6b1042e
invites: move code gen to function
code to generate an invite code w/ a non-integer first character was
reused a bunch, so it's now function GenerateInviteCode().
2023-10-11 11:30:28 +01:00
Harvey Tindall 7db94dcebf
Merge /inv command additions
Merge branch 'main' of github.com:VioletLeporid/jfa-go
2023-10-11 11:25:00 +01:00
Harvey Tindall 62923d5e45
discord: register available profiles for /inv
profiles are registered as options for /inv as startup. Note in
description added to restart jfa-go to reload them.
2023-10-10 15:15:25 +01:00
Harvey Tindall 10a32ad1ae
discord: re-add optional args 2023-10-10 14:52:54 +01:00
Harvey Tindall e52e21a54b
discord: fix up /inv basic functionality
sending now succeeds, and a reponse of "Invite sent." is given to the
requester. Also some formatting changes.
2023-10-10 13:45:29 +01:00
Harvey Tindall 7c861e5763
lang: fix the usual mistakes
someone directly translating "English (US)", and lowercasing lang files.
2023-10-10 10:36:57 +01:00
19 changed files with 152 additions and 120 deletions

View File

@ -17,6 +17,18 @@ const (
CAPTCHA_VALIDITY = 20 * 60 // Seconds
)
// GenerateInviteCode generates an invite code in the correct format.
func GenerateInviteCode() string {
// make sure code doesn't begin with number
inviteCode := shortuuid.New()
_, err := strconv.Atoi(string(inviteCode[0]))
for err == nil {
inviteCode = shortuuid.New()
_, err = strconv.Atoi(string(inviteCode[0]))
}
return inviteCode
}
func (app *appContext) checkInvites() {
currentTime := time.Now()
for _, data := range app.storage.GetInvites() {
@ -150,14 +162,8 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
currentTime := time.Now()
validTill := currentTime.AddDate(0, req.Months, req.Days)
validTill = validTill.Add(time.Hour*time.Duration(req.Hours) + time.Minute*time.Duration(req.Minutes))
// make sure code doesn't begin with number
inviteCode := shortuuid.New()
_, err := strconv.Atoi(string(inviteCode[0]))
for err == nil {
inviteCode = shortuuid.New()
_, err = strconv.Atoi(string(inviteCode[0]))
}
var invite Invite
invite.Code = GenerateInviteCode()
if req.Label != "" {
invite.Label = req.Label
}
@ -185,7 +191,7 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
if req.SendTo != "" && app.config.Section("invite_emails").Key("enabled").MustBool(false) {
addressValid := false
discord := ""
app.debug.Printf("%s: Sending invite message", inviteCode)
app.debug.Printf("%s: Sending invite message", invite.Code)
if discordEnabled && !strings.Contains(req.SendTo, "@") {
users := app.discord.GetUsers(req.SendTo)
if len(users) == 0 {
@ -202,10 +208,10 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
invite.SendTo = req.SendTo
}
if addressValid {
msg, err := app.email.constructInvite(inviteCode, invite, app, false)
msg, err := app.email.constructInvite(invite.Code, invite, app, false)
if err != nil {
invite.SendTo = fmt.Sprintf("Failed to send to %s", req.SendTo)
app.err.Printf("%s: Failed to construct invite message: %v", inviteCode, err)
app.err.Printf("%s: Failed to construct invite message: %v", invite.Code, err)
} else {
var err error
if discord != "" {
@ -215,9 +221,9 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
}
if err != nil {
invite.SendTo = fmt.Sprintf("Failed to send to %s", req.SendTo)
app.err.Printf("%s: %s: %v", inviteCode, invite.SendTo, err)
app.err.Printf("%s: %s: %v", invite.Code, invite.SendTo, err)
} else {
app.info.Printf("%s: Sent invite email to \"%s\"", inviteCode, req.SendTo)
app.info.Printf("%s: Sent invite email to \"%s\"", invite.Code, req.SendTo)
}
}
}
@ -229,7 +235,7 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
invite.Profile = "Default"
}
}
app.storage.SetInvitesKey(inviteCode, invite)
app.storage.SetInvitesKey(invite.Code, invite)
respondBool(200, true, gc)
}

View File

@ -1,11 +1,9 @@
package main
import (
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/lithammer/shortuuid/v3"
"github.com/timshannon/badgerhold/v4"
)
@ -151,13 +149,7 @@ func (app *appContext) EnableReferralForProfile(gc *gin.Context) {
}
// Generate new code for referral template
inv.Code = shortuuid.New()
// make sure code doesn't begin with number
_, err := strconv.Atoi(string(inv.Code[0]))
for err == nil {
inv.Code = shortuuid.New()
_, err = strconv.Atoi(string(inv.Code[0]))
}
inv.Code = GenerateInviteCode()
inv.Created = time.Now()
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
inv.IsReferral = true

View File

@ -3,13 +3,11 @@ package main
import (
"net/http"
"os"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
"github.com/lithammer/shortuuid/v3"
"github.com/timshannon/badgerhold/v4"
)
@ -673,13 +671,7 @@ func (app *appContext) GetMyReferral(gc *gin.Context) {
respondBool(400, false, gc)
return
}
inv.Code = shortuuid.New()
// make sure code doesn't begin with number
_, err := strconv.Atoi(string(inv.Code[0]))
for err == nil {
inv.Code = shortuuid.New()
_, err = strconv.Atoi(string(inv.Code[0]))
}
inv.Code = GenerateInviteCode()
inv.Created = time.Now()
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
inv.IsReferral = true
@ -689,13 +681,7 @@ func (app *appContext) GetMyReferral(gc *gin.Context) {
// 3. We found an invite for us, but it's expired.
// We delete it from storage, and put it back with a fresh code and expiry.
app.storage.DeleteInvitesKey(inv.Code)
inv.Code = shortuuid.New()
// make sure code doesn't begin with number
_, err := strconv.Atoi(string(inv.Code[0]))
for err == nil {
inv.Code = shortuuid.New()
_, err = strconv.Atoi(string(inv.Code[0]))
}
inv.Code = GenerateInviteCode()
inv.Created = time.Now()
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
app.storage.SetInvitesKey(inv.Code, inv)

View File

@ -3,14 +3,12 @@ package main
import (
"fmt"
"os"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
"github.com/hrfee/mediabrowser"
"github.com/lithammer/shortuuid/v3"
"github.com/timshannon/badgerhold/v4"
)
@ -707,13 +705,7 @@ func (app *appContext) EnableReferralForUsers(gc *gin.Context) {
// 2. Generate referral invite.
inv := baseInv
inv.Code = shortuuid.New()
// make sure code doesn't begin with number
_, err := strconv.Atoi(string(inv.Code[0]))
for err == nil {
inv.Code = shortuuid.New()
_, err = strconv.Atoi(string(inv.Code[0]))
}
inv.Code = GenerateInviteCode()
inv.Created = time.Now()
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
inv.IsReferral = true

View File

@ -3,11 +3,9 @@ package main
import (
"fmt"
"strings"
"strconv"
"time"
dg "github.com/bwmarrin/discordgo"
"github.com/lithammer/shortuuid/v3"
"github.com/timshannon/badgerhold/v4"
)
@ -349,34 +347,41 @@ func (d *DiscordDaemon) registerCommands() {
{
Type: dg.ApplicationCommandOptionUser,
Name: "user",
Description: "User to Invite",
Description: "User to Invite.",
Required: true,
}, // running with just one option for now to mesh with what we've got, also may have the syntax wrong here
/*{
},
{
Type: dg.ApplicationCommandOptionInteger,
Name: "expire after",
Description: "Time in minutes before expiration",
Name: "expiry",
Description: "Time in minutes before expiration.",
Required: false,
},
/* Label should be automatically set to something like "Discord invite for @username"
{
Type: dg.ApplicationCommandOptionString,
Name: "label",
Description: "Label to apply to the user created with this invite",
Description: "Label given to this invite (shown on the Admin page)",
Required: false,
}, */
{
Type: dg.ApplicationCommandOptionString,
Name: "user_label",
Description: "Label given to users created with this invite.",
Required: false,
},
{
Type: dg.ApplicationCommandOptionString,
Name: "profile",
Description: "Profile to apply to the user created with this invite",
Description: "Profile to apply. Restart jfa-go if there's any missing.",
Required: false,
}, */
},
},
},
}
commands[1].Options[0].Choices = make([]*dg.ApplicationCommandOptionChoice, len(d.app.storage.lang.Telegram))
i := 0
for code := range d.app.storage.lang.Telegram {
d.app.debug.Printf("Registering choice \"%s\":\"%s\"\n", d.app.storage.lang.Telegram[code].Meta.Name, code)
d.app.debug.Printf("Discord: registering lang choice \"%s\":\"%s\"\n", d.app.storage.lang.Telegram[code].Meta.Name, code)
commands[1].Options[0].Choices[i] = &dg.ApplicationCommandOptionChoice{
Name: d.app.storage.lang.Telegram[code].Meta.Name,
Value: code,
@ -384,6 +389,17 @@ func (d *DiscordDaemon) registerCommands() {
i++
}
// FIXME: Maybe make this reload when profiles change
profiles := d.app.storage.GetProfiles()
commands[3].Options[3].Choices = make([]*dg.ApplicationCommandOptionChoice, len(profiles))
for i, profile := range profiles {
d.app.debug.Printf("Discord: registering profile choice \"%s\"", profile.Name)
commands[3].Options[3].Choices[i] = &dg.ApplicationCommandOptionChoice{
Name: profile.Name,
Value: profile.Name,
}
}
// d.deregisterCommands()
d.commandIDs = make([]string, len(commands))
@ -409,7 +425,7 @@ func (d *DiscordDaemon) deregisterCommands() {
return
}
for _, cmd := range existingCommands {
if err := d.bot.ApplicationCommandDelete(d.bot.State.User.ID, "", cmd.ID); err != nil {
if err := d.bot.ApplicationCommandDelete(d.bot.State.User.ID, d.guildID, cmd.ID); err != nil {
d.app.err.Printf("Failed to deregister command: %v", err)
}
}
@ -539,84 +555,122 @@ func (d *DiscordDaemon) cmdLang(s *dg.Session, i *dg.InteractionCreate, lang str
func (d *DiscordDaemon) cmdInvite(s *dg.Session, i *dg.InteractionCreate, lang string) {
channel, err := s.UserChannelCreate(i.Interaction.Member.User.ID)
requestor := 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] = requestor
invuser := fmt.Sprintf("%v", i.ApplicationCommandData().Options[0].Value)
d.app.debug.Println(invuser)
if err != nil {
d.app.err.Printf("Discord: Failed to create private channel with \"%s\": %v", i.Interaction.Member.User.Username, err)
return
}
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)
// d.app.debug.Println(invuser)
//label := i.ApplicationCommandData().Options[2].StringValue()
//profile := i.ApplicationCommandData().Options[3].StringValue()
//mins, err := strconv.Atoi(i.ApplicationCommandData().Options[1].StringValue())
expmin := 30
//if mins > 0 {
// expmin = mins
//}
// Check whether requestor is linked to the admin account
requestoremail, ok := d.app.storage.GetEmailsKey(requestor.JellyfinID)
requesterEmail, ok := d.app.storage.GetEmailsKey(requester.JellyfinID)
if !ok {
d.app.err.Printf("Failed to verify admin")
}
if !requestoremail.Admin {
if !requesterEmail.Admin {
d.app.err.Printf("User is not admin")
//add response message
return
}
// variation of app.GenerateInvite, some parts commented to potentially add back in later with the other command options
//d.app.debug.Println("Generating new invite with options: %s: %i: %s: %s", invuser, expmin, profile, label)
currentTime := time.Now()
validTill := currentTime.Add(time.Minute*time.Duration(expmin))
// make sure code doesn't begin with number
inviteCode := shortuuid.New()
_, err = strconv.Atoi(string(inviteCode[0]))
for err == nil {
inviteCode = shortuuid.New()
_, err = strconv.Atoi(string(inviteCode[0]))
var expiryMinutes int64 = 30
userLabel := ""
profileName := ""
for i, opt := range i.ApplicationCommandData().Options {
if i == 0 {
continue
}
switch opt.Name {
case "expiry":
expiryMinutes = opt.IntValue()
case "user_label":
userLabel = opt.StringValue()
case "profile":
profileName = opt.StringValue()
}
}
var invite Invite
//if label != "" {
// invite.Label = label
//}
invite.Created = currentTime
invite.RemainingUses = 1
invite.UserExpiry = false
/*if invite.UserExpiry {
invite.UserMonths = req.UserMonths
invite.UserDays = req.UserDays
invite.UserHours = req.UserHours
invite.UserMinutes = req.UserMinutes
}*/
invite.ValidTill = validTill
if invuser != "" && d.app.config.Section("invite_emails").Key("enabled").MustBool(false) {
d.app.debug.Printf("%s: Sending invite message", inviteCode)
invname, err := d.bot.GuildMember(d.guildID, invuser)
currentTime := time.Now()
validTill := currentTime.Add(time.Minute * time.Duration(expiryMinutes))
invite := Invite{
Code: GenerateInviteCode(),
Created: currentTime,
RemainingUses: 1,
UserExpiry: false,
ValidTill: validTill,
UserLabel: userLabel,
Profile: "Default",
Label: fmt.Sprintf("Discord: %s", RenderDiscordUsername(recipient)),
}
if profileName != "" {
if _, ok := d.app.storage.GetProfileKey(profileName); ok {
invite.Profile = profileName
}
}
if recipient != nil && d.app.config.Section("invite_emails").Key("enabled").MustBool(false) {
d.app.debug.Printf("%s: Sending invite message", invite.Code)
invname, err := d.bot.GuildMember(d.guildID, recipient.ID)
invite.SendTo = invname.User.Username
msg, err := d.app.email.constructInvite(inviteCode, invite, d.app, false)
msg, err := d.app.email.constructInvite(invite.Code, invite, d.app, false)
if err != nil {
invite.SendTo = fmt.Sprintf("Failed to send to %s", invuser)
d.app.err.Printf("%s: Failed to construct invite message: %v", inviteCode, err)
//add response message
invite.SendTo = fmt.Sprintf("Failed to send to %s", RenderDiscordUsername(recipient))
d.app.err.Printf("%s: Failed to construct invite message: %v", invite.Code, err)
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("Discord: Failed to send message to \"%s\": %v", RenderDiscordUsername(requester), err)
}
} else {
var err error
err = d.app.discord.SendDM(msg, invuser)
err = d.app.discord.SendDM(msg, recipient.ID)
if err != nil {
invite.SendTo = fmt.Sprintf("Failed to send to %s", invuser)
d.app.err.Printf("%s: %s: %v", inviteCode, invite.SendTo, err)
//add response message
invite.SendTo = fmt.Sprintf("Failed to send to %s", RenderDiscordUsername(recipient))
d.app.err.Printf("%s: %s: %v", invite.Code, invite.SendTo, err)
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("Discord: Failed to send message to \"%s\": %v", RenderDiscordUsername(requester), err)
}
} else {
d.app.info.Printf("%s: Sent invite email to \"%s\"", inviteCode, invuser)
//add response message
d.app.info.Printf("%s: Sent invite email to \"%s\"", 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("Discord: Failed to send message to \"%s\": %v", RenderDiscordUsername(requester), err)
}
}
}
}
//if profile != "" {
// if _, ok := d.app.storage.GetProfileKey(profile); ok {
// invite.Profile = profile
// } else {
// invite.Profile = "Default"
// }
//}
d.app.storage.SetInvitesKey(inviteCode, invite)
d.app.storage.SetInvitesKey(invite.Code, invite)
}
func (d *DiscordDaemon) messageHandler(s *dg.Session, m *dg.MessageCreate) {
if m.GuildID != "" && d.channelName != "" {
if d.channelID == "" {

View File

@ -1,6 +1,6 @@
{
"meta": {
"name": "Angol (US)"
"name": "Magyar (HU)"
},
"strings": {
"invites": "Meghívások",

View File

@ -1,6 +1,6 @@
{
"meta": {
"name": "Angol (US)"
"name": "Magyar (HU)"
},
"strings": {
"login": "Belépés",

View File

@ -1,6 +1,6 @@
{
"meta": {
"name": "Angol (US)"
"name": "Magyar (HU)"
},
"strings": {
"ifItWasNotYou": "",

View File

@ -1,6 +1,6 @@
{
"meta": {
"name": "Angol (US)"
"name": "Magyar (HU)"
},
"strings": {
"pageTitle": "Jellyfin fiók létrehozása",

View File

@ -1,6 +1,6 @@
{
"meta": {
"name": "Angol (US)"
"name": "Magyar (HU)"
},
"strings": {
"passwordReset": "Jelszó visszaállítás",

View File

@ -1,6 +1,6 @@
{
"meta": {
"name": "Angol (US)"
"name": "Magyar (HU)"
},
"strings": {
"pageTitle": "Telepítés - jfa-go",

View File

@ -11,6 +11,8 @@
"languageMessage": "Note: See available languages with {command}, and set language with {command} <language code>.",
"languageMessageDiscord": "Note: set your language with /lang <language name>.",
"languageSet": "Language set to {language}.",
"discordDMs": "Please check your DMs for a response."
"discordDMs": "Please check your DMs for a response.",
"sentInvite": "Sent invite.",
"sentInviteFailure": "Failed to send invite, check logs."
}
}
}

View File

@ -1,6 +1,6 @@
{
"meta": {
"name": "Angol (US)"
"name": "Magyar (HU)"
},
"strings": {
"startMessage": "Helló!\nAdd meg a Jellyfin PIN kódodat itt, hogy megerősítsd a fiókodat.",