diff --git a/api.go b/api.go index 31d4816..141c951 100644 --- a/api.go +++ b/api.go @@ -217,7 +217,7 @@ func (app *appContext) getOmbiUser(jfID string) (map[string]interface{}, int, er username := jfUser.Name email := "" if e, ok := app.storage.emails[jfID]; ok { - email = e.(string) + email = e.Addr } for _, ombiUser := range ombiUsers { ombiAddr := "" @@ -283,7 +283,7 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) { } app.jf.CacheExpiry = time.Now() if emailEnabled { - app.storage.emails[id] = req.Email + app.storage.emails[id] = EmailAddress{Addr: req.Email, Contact: true} app.storage.storeEmails() } if app.config.Section("ombi").Key("enabled").MustBool(false) { @@ -478,7 +478,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc } // if app.config.Section("password_resets").Key("enabled").MustBool(false) { if req.Email != "" { - app.storage.emails[id] = req.Email + app.storage.emails[id] = EmailAddress{Addr: req.Email, Contact: true} app.storage.storeEmails() } if app.config.Section("ombi").Key("enabled").MustBool(false) { @@ -908,8 +908,8 @@ func (app *appContext) GetInvites(gc *gin.Context) { var address string if app.config.Section("ui").Key("jellyfin_login").MustBool(false) { app.storage.loadEmails() - if addr := app.storage.emails[gc.GetString("jfId")]; addr != nil { - address = addr.(string) + if addr, ok := app.storage.emails[gc.GetString("jfId")]; ok && addr.Addr != "" { + address = addr.Addr } } else { address = app.config.Section("ui").Key("email").String() @@ -1108,14 +1108,14 @@ func (app *appContext) SetNotify(gc *gin.Context) { } var address string if app.config.Section("ui").Key("jellyfin_login").MustBool(false) { - var ok bool - address, ok = app.storage.emails[gc.GetString("jfId")].(string) + addr, ok := app.storage.emails[gc.GetString("jfId")] if !ok { app.err.Printf("%s: Couldn't find email address. Make sure it's set", code) app.debug.Printf("%s: User ID \"%s\"", code, gc.GetString("jfId")) respond(500, "Missing user email", gc) return } + address = addr.Addr } else { address = app.config.Section("ui").Key("email").String() } @@ -1202,7 +1202,7 @@ func (app *appContext) GetUsers(gc *gin.Context) { user.LastActive = jfUser.LastActivityDate.Unix() } if email, ok := app.storage.emails[jfUser.ID]; ok { - user.Email = email.(string) + user.Email = email.Addr user.NotifyThroughEmail = user.Email != "" } expiry, ok := app.storage.users[jfUser.ID] @@ -1293,7 +1293,11 @@ func (app *appContext) ModifyEmails(gc *gin.Context) { for _, jfUser := range users { id := jfUser.ID if address, ok := req[id]; ok { - app.storage.emails[id] = address + contact := true + if oldAddr, ok := app.storage.emails[id]; ok { + contact = oldAddr.Contact + } + app.storage.emails[id] = EmailAddress{Addr: address, Contact: contact} if ombiEnabled { ombiUser, code, err := app.getOmbiUser(id) if code == 200 && err == nil { @@ -2052,7 +2056,7 @@ func (app *appContext) TelegramAddUser(gc *gin.Context) { // @Security Bearer // @tags Other func (app *appContext) SetContactMethods(gc *gin.Context) { - var req telegramNotifyDTO + var req SetContactMethodsDTO gc.BindJSON(&req) if req.ID == "" { respondBool(400, false, gc) @@ -2068,9 +2072,9 @@ func (app *appContext) SetContactMethods(gc *gin.Context) { } msg := "" if !req.Telegram { - msg = "not" + msg = " not" } - app.debug.Printf("Telegram: User \"%s\" will %s be notified through Telegram.", tgUser.Username, msg) + app.debug.Printf("Telegram: User \"%s\" will%s be notified through Telegram.", tgUser.Username, msg) } if dcUser, ok := app.storage.discord[req.ID]; ok { dcUser.Contact = req.Discord @@ -2082,9 +2086,23 @@ func (app *appContext) SetContactMethods(gc *gin.Context) { } msg := "" if !req.Discord { - msg = "not" + msg = " not" } - app.debug.Printf("Discord: User \"%s\" will %s be notified through Discord.", dcUser.Username, msg) + app.debug.Printf("Discord: User \"%s\" will%s be notified through Discord.", dcUser.Username, msg) + } + if email, ok := app.storage.emails[req.ID]; ok { + email.Contact = req.Email + app.storage.emails[req.ID] = email + if err := app.storage.storeEmails(); err != nil { + respondBool(500, false, gc) + app.err.Printf("Failed to store emails: %v", err) + return + } + msg := "" + if !req.Email { + msg = " not" + } + app.debug.Printf("\"%s\" will%s be notified via Email.", email.Addr, msg) } respondBool(200, true, gc) } diff --git a/config.go b/config.go index 9e18bba..d605ef5 100644 --- a/config.go +++ b/config.go @@ -171,3 +171,28 @@ func (app *appContext) migrateEmailConfig() { } app.loadConfig() } + +func (app *appContext) migrateEmailStorage() error { + var emails map[string]interface{} + err := loadJSON(app.storage.emails_path, &emails) + if err != nil { + return err + } + newEmails := map[string]EmailAddress{} + for jfID, addr := range emails { + newEmails[jfID] = EmailAddress{ + Addr: addr.(string), + Contact: true, + } + } + err = storeJSON(app.storage.emails_path+".bak", emails) + if err != nil { + return err + } + err = storeJSON(app.storage.emails_path, newEmails) + if err != nil { + return err + } + app.info.Println("Migrated to new email format. A backup has also been made.") + return nil +} diff --git a/email.go b/email.go index 5f752fd..8d4399b 100644 --- a/email.go +++ b/email.go @@ -817,8 +817,8 @@ func (app *appContext) sendByID(email *Message, ID ...string) error { return err } } - if address, ok := app.storage.emails[id]; ok { - err = app.email.send(email, address.(string)) + if address, ok := app.storage.emails[id]; ok && address.Contact && emailEnabled { + err = app.email.send(email, address.Addr) if err != nil { return err } @@ -838,7 +838,7 @@ func (app *appContext) getAddressOrName(jfID string) string { return "@" + tgChat.Username } if addr, ok := app.storage.emails[jfID]; ok { - return addr.(string) + return addr.Addr } return "" } diff --git a/main.go b/main.go index a16d561..b2b99cc 100644 --- a/main.go +++ b/main.go @@ -16,7 +16,6 @@ import ( "os/signal" "path/filepath" "runtime" - "strconv" "strings" "time" @@ -321,6 +320,10 @@ func start(asDaemon, firstCall bool) { app.storage.emails_path = app.config.Section("files").Key("emails").String() if err := app.storage.loadEmails(); err != nil { app.err.Printf("Failed to load Emails: %v", err) + err := app.migrateEmailStorage() + if err != nil { + app.err.Printf("Failed to migrate Email storage: %v", err) + } } app.storage.policy_path = app.config.Section("files").Key("user_template").String() if err := app.storage.loadPolicy(); err != nil { @@ -434,76 +437,76 @@ func start(asDaemon, firstCall bool) { app.err.Fatalf("Failed to authenticate with Jellyfin @ %s (%d): %v", server, status, err) } app.info.Printf("Authenticated with %s", server) - /* A couple of unstable Jellyfin 10.7.0 releases decided to hyphenate user IDs. - This checks if the version is equal or higher. */ - checkVersion := func(version string) int { - numberStrings := strings.Split(version, ".") - n := 0 - for _, s := range numberStrings { - num, err := strconv.Atoi(s) - if err == nil { - n += num - } - } - return n - } - if serverType == mediabrowser.JellyfinServer && checkVersion(app.jf.ServerInfo.Version) >= checkVersion("10.7.0") { - // Get users to check if server uses hyphenated userIDs - app.jf.GetUsers(false) + // /* A couple of unstable Jellyfin 10.7.0 releases decided to hyphenate user IDs. + // This checks if the version is equal or higher. */ + // checkVersion := func(version string) int { + // numberStrings := strings.Split(version, ".") + // n := 0 + // for _, s := range numberStrings { + // num, err := strconv.Atoi(s) + // if err == nil { + // n += num + // } + // } + // return n + // } + // if serverType == mediabrowser.JellyfinServer && checkVersion(app.jf.ServerInfo.Version) >= checkVersion("10.7.0") { + // // Get users to check if server uses hyphenated userIDs + // app.jf.GetUsers(false) - noHyphens := true - for id := range app.storage.emails { - if strings.Contains(id, "-") { - noHyphens = false - break - } - } - if noHyphens == app.jf.Hyphens { - var newEmails map[string]interface{} - var newUsers map[string]time.Time - var status, status2 int - var err, err2 error - if app.jf.Hyphens { - app.info.Println(info("Your build of Jellyfin appears to hypenate user IDs. Your emails.json/users.json file will be modified to match.")) - time.Sleep(time.Second * time.Duration(3)) - newEmails, status, err = app.hyphenateEmailStorage(app.storage.emails) - newUsers, status2, err2 = app.hyphenateUserStorage(app.storage.users) - } else { - app.info.Println(info("Your emails.json/users.json file uses hyphens, but the Jellyfin server no longer does. It will be modified.")) - time.Sleep(time.Second * time.Duration(3)) - newEmails, status, err = app.deHyphenateEmailStorage(app.storage.emails) - newUsers, status2, err2 = app.deHyphenateUserStorage(app.storage.users) - } - if status != 200 || err != nil { - app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err) - app.err.Fatalf("Couldn't upgrade emails.json") - } - if status2 != 200 || err2 != nil { - app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err) - app.err.Fatalf("Couldn't upgrade users.json") - } - emailBakFile := app.storage.emails_path + ".bak" - usersBakFile := app.storage.users_path + ".bak" - err = storeJSON(emailBakFile, app.storage.emails) - err2 = storeJSON(usersBakFile, app.storage.users) - if err != nil { - app.err.Fatalf("couldn't store emails.json backup: %v", err) - } - if err2 != nil { - app.err.Fatalf("couldn't store users.json backup: %v", err) - } - app.storage.emails = newEmails - app.storage.users = newUsers - err = app.storage.storeEmails() - err2 = app.storage.storeUsers() - if err != nil { - app.err.Fatalf("couldn't store emails.json: %v", err) - } - if err2 != nil { - app.err.Fatalf("couldn't store users.json: %v", err) - } - } - } + // noHyphens := true + // for id := range app.storage.emails { + // if strings.Contains(id, "-") { + // noHyphens = false + // break + // } + // } + // if noHyphens == app.jf.Hyphens { + // var newEmails map[string]interface{} + // var newUsers map[string]time.Time + // var status, status2 int + // var err, err2 error + // if app.jf.Hyphens { + // app.info.Println(info("Your build of Jellyfin appears to hypenate user IDs. Your emails.json/users.json file will be modified to match.")) + // time.Sleep(time.Second * time.Duration(3)) + // newEmails, status, err = app.hyphenateEmailStorage(app.storage.emails) + // newUsers, status2, err2 = app.hyphenateUserStorage(app.storage.users) + // } else { + // app.info.Println(info("Your emails.json/users.json file uses hyphens, but the Jellyfin server no longer does. It will be modified.")) + // time.Sleep(time.Second * time.Duration(3)) + // newEmails, status, err = app.deHyphenateEmailStorage(app.storage.emails) + // newUsers, status2, err2 = app.deHyphenateUserStorage(app.storage.users) + // } + // if status != 200 || err != nil { + // app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err) + // app.err.Fatalf("Couldn't upgrade emails.json") + // } + // if status2 != 200 || err2 != nil { + // app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err) + // app.err.Fatalf("Couldn't upgrade users.json") + // } + // emailBakFile := app.storage.emails_path + ".bak" + // usersBakFile := app.storage.users_path + ".bak" + // err = storeJSON(emailBakFile, app.storage.emails) + // err2 = storeJSON(usersBakFile, app.storage.users) + // if err != nil { + // app.err.Fatalf("couldn't store emails.json backup: %v", err) + // } + // if err2 != nil { + // app.err.Fatalf("couldn't store users.json backup: %v", err) + // } + // app.storage.emails = newEmails + // app.storage.users = newUsers + // err = app.storage.storeEmails() + // err2 = app.storage.storeUsers() + // if err != nil { + // app.err.Fatalf("couldn't store emails.json: %v", err) + // } + // if err2 != nil { + // app.err.Fatalf("couldn't store users.json: %v", err) + // } + // } + // } // Auth (manual user/pass or jellyfin) app.jellyfinLogin = true diff --git a/models.go b/models.go index c79cab4..d018738 100644 --- a/models.go +++ b/models.go @@ -255,7 +255,7 @@ type telegramSetDTO struct { ID string `json:"id"` // Jellyfin ID of user. } -type telegramNotifyDTO struct { +type SetContactMethodsDTO struct { ID string `json:"id"` Email bool `json:"email"` Discord bool `json:"discord"` diff --git a/router.go b/router.go index 4bad4d8..8dcf793 100644 --- a/router.go +++ b/router.go @@ -161,10 +161,12 @@ func (app *appContext) loadRoutes(router *gin.Engine) { api.GET(p+"/config", app.GetConfig) api.POST(p+"/config", app.ModifyConfig) api.POST(p+"/restart", app.restart) - if telegramEnabled { + if telegramEnabled || discordEnabled { api.GET(p+"/telegram/pin", app.TelegramGetPin) api.GET(p+"/telegram/verified/:pin", app.TelegramVerified) api.POST(p+"/users/telegram", app.TelegramAddUser) + } + if discordEnabled || telegramEnabled { api.POST(p+"/users/contact", app.SetContactMethods) } if app.config.Section("ombi").Key("enabled").MustBool(false) { diff --git a/storage.go b/storage.go index b0f0ee8..a190961 100644 --- a/storage.go +++ b/storage.go @@ -21,7 +21,8 @@ type Storage struct { invites Invites profiles map[string]Profile defaultProfile string - emails, displayprefs, ombi_template map[string]interface{} + displayprefs, ombi_template map[string]interface{} + emails map[string]EmailAddress telegram map[string]TelegramUser // Map of Jellyfin User IDs to telegram users. discord map[string]DiscordUser // Map of Jellyfin user IDs to discord users. customEmails customEmails @@ -47,6 +48,11 @@ type DiscordUser struct { Contact bool } +type EmailAddress struct { + Addr string + Contact bool +} + type customEmails struct { UserCreated customEmail `json:"userCreated"` InviteExpiry customEmail `json:"inviteExpiry"` @@ -902,85 +908,85 @@ func storeJSON(path string, obj interface{}) error { return err } -// One build of JF 10.7.0 hyphenated user IDs while another one later didn't. These functions will hyphenate/de-hyphenate email storage. - -func hyphenate(userID string) string { - if userID[8] == '-' { - return userID - } - return userID[:8] + "-" + userID[8:12] + "-" + userID[12:16] + "-" + userID[16:20] + "-" + userID[20:] -} - -func (app *appContext) deHyphenateStorage(old map[string]interface{}) (map[string]interface{}, int, error) { - jfUsers, status, err := app.jf.GetUsers(false) - if status != 200 || err != nil { - return nil, status, err - } - newEmails := map[string]interface{}{} - for _, user := range jfUsers { - unHyphenated := user.ID - hyphenated := hyphenate(unHyphenated) - val, ok := old[hyphenated] - if ok { - newEmails[unHyphenated] = val - } - } - return newEmails, status, err -} - -func (app *appContext) hyphenateStorage(old map[string]interface{}) (map[string]interface{}, int, error) { - jfUsers, status, err := app.jf.GetUsers(false) - if status != 200 || err != nil { - return nil, status, err - } - newEmails := map[string]interface{}{} - for _, user := range jfUsers { - unstripped := user.ID - stripped := strings.ReplaceAll(unstripped, "-", "") - val, ok := old[stripped] - if ok { - newEmails[unstripped] = val - } - } - return newEmails, status, err -} - -func (app *appContext) hyphenateEmailStorage(old map[string]interface{}) (map[string]interface{}, int, error) { - return app.hyphenateStorage(old) -} - -func (app *appContext) deHyphenateEmailStorage(old map[string]interface{}) (map[string]interface{}, int, error) { - return app.deHyphenateStorage(old) -} - -func (app *appContext) hyphenateUserStorage(old map[string]time.Time) (map[string]time.Time, int, error) { - asInterface := map[string]interface{}{} - for k, v := range old { - asInterface[k] = v - } - fixed, status, err := app.hyphenateStorage(asInterface) - if err != nil { - return nil, status, err - } - out := map[string]time.Time{} - for k, v := range fixed { - out[k] = v.(time.Time) - } - return out, status, err -} - -func (app *appContext) deHyphenateUserStorage(old map[string]time.Time) (map[string]time.Time, int, error) { - asInterface := map[string]interface{}{} - for k, v := range old { - asInterface[k] = v - } - fixed, status, err := app.deHyphenateStorage(asInterface) - if err != nil { - return nil, status, err - } - out := map[string]time.Time{} - for k, v := range fixed { - out[k] = v.(time.Time) - } - return out, status, err -} +// // One build of JF 10.7.0 hyphenated user IDs while another one later didn't. These functions will hyphenate/de-hyphenate email storage. +// +// func hyphenate(userID string) string { +// if userID[8] == '-' { +// return userID +// } +// return userID[:8] + "-" + userID[8:12] + "-" + userID[12:16] + "-" + userID[16:20] + "-" + userID[20:] +// } +// +// func (app *appContext) deHyphenateStorage(old map[string]interface{}) (map[string]interface{}, int, error) { +// jfUsers, status, err := app.jf.GetUsers(false) +// if status != 200 || err != nil { +// return nil, status, err +// } +// newEmails := map[string]interface{}{} +// for _, user := range jfUsers { +// unHyphenated := user.ID +// hyphenated := hyphenate(unHyphenated) +// val, ok := old[hyphenated] +// if ok { +// newEmails[unHyphenated] = val +// } +// } +// return newEmails, status, err +// } +// +// func (app *appContext) hyphenateStorage(old map[string]interface{}) (map[string]interface{}, int, error) { +// jfUsers, status, err := app.jf.GetUsers(false) +// if status != 200 || err != nil { +// return nil, status, err +// } +// newEmails := map[string]interface{}{} +// for _, user := range jfUsers { +// unstripped := user.ID +// stripped := strings.ReplaceAll(unstripped, "-", "") +// val, ok := old[stripped] +// if ok { +// newEmails[unstripped] = val +// } +// } +// return newEmails, status, err +// } +// +// func (app *appContext) hyphenateEmailStorage(old map[string]interface{}) (map[string]interface{}, int, error) { +// return app.hyphenateStorage(old) +// } +// +// func (app *appContext) deHyphenateEmailStorage(old map[string]interface{}) (map[string]interface{}, int, error) { +// return app.deHyphenateStorage(old) +// } +// +// func (app *appContext) hyphenateUserStorage(old map[string]time.Time) (map[string]time.Time, int, error) { +// asInterface := map[string]interface{}{} +// for k, v := range old { +// asInterface[k] = v +// } +// fixed, status, err := app.hyphenateStorage(asInterface) +// if err != nil { +// return nil, status, err +// } +// out := map[string]time.Time{} +// for k, v := range fixed { +// out[k] = v.(time.Time) +// } +// return out, status, err +// } +// +// func (app *appContext) deHyphenateUserStorage(old map[string]time.Time) (map[string]time.Time, int, error) { +// asInterface := map[string]interface{}{} +// for k, v := range old { +// asInterface[k] = v +// } +// fixed, status, err := app.deHyphenateStorage(asInterface) +// if err != nil { +// return nil, status, err +// } +// out := map[string]time.Time{} +// for k, v := range fixed { +// out[k] = v.(time.Time) +// } +// return out, status, err +// } diff --git a/ts/modules/accounts.ts b/ts/modules/accounts.ts index 21f19ef..9b53504 100644 --- a/ts/modules/accounts.ts +++ b/ts/modules/accounts.ts @@ -228,18 +228,18 @@ class user implements User {
${window.lang.strings("contactThrough")} `; if (window.telegramEnabled && this._telegramUsername != "") { innerHTML += ` `;