diff --git a/api.go b/api.go index 4c1ccb2..66b6f0f 100644 --- a/api.go +++ b/api.go @@ -2233,6 +2233,68 @@ func (app *appContext) DiscordServerInvite(gc *gin.Context) { gc.JSON(200, DiscordInviteDTO{invURL, iconURL}) } +// @Summary Generate and send a new PIN to a specified Matrix user. +// @Produce json +// @Success 200 {object} boolResponse +// @Failure 400 {object} boolResponse +// @Failure 401 {object} boolResponse +// @Failure 500 {object} boolResponse +// @Param invCode path string true "invite Code" +// @Param MatrixSendPINDTO body MatrixSendPINDTO true "User's Matrix ID." +// @Router /invite/{invCode}/matrix/user [post] +// @tags Other +func (app *appContext) MatrixSendPIN(gc *gin.Context) { + code := gc.Param("invCode") + if _, ok := app.storage.invites[code]; !ok { + respondBool(401, false, gc) + return + } + var req MatrixSendPINDTO + gc.BindJSON(&req) + if req.UserID == "" { + respondBool(400, false, gc) + return + } + ok := app.matrix.SendStart(req.UserID) + if !ok { + respondBool(500, false, gc) + return + } + respondBool(200, true, gc) +} + +// @Summary Check whether a matrix PIN is valid. Requires invite code. +// @Produce json +// @Success 200 {object} boolResponse +// @Failure 401 {object} boolResponse +// @Param pin path string true "PIN code to check" +// @Param invCode path string true "invite Code" +// @Param userID path string true "Matrix User ID" +// @Router /invite/{invCode}/matrix/verified/{userID}/{pin} [get] +// @tags Other +func (app *appContext) MatrixCheckPIN(gc *gin.Context) { + code := gc.Param("invCode") + if _, ok := app.storage.invites[code]; !ok { + app.debug.Println("Matrix: Invite code was invalid") + respondBool(401, false, gc) + return + } + userID := gc.Param("userID") + pin := gc.Param("pin") + user, ok := app.matrix.tokens[pin] + if !ok { + app.debug.Println("Matrix: PIN not found") + respondBool(200, false, gc) + return + } + if user.User.UserID != userID { + app.debug.Println("Matrix: User ID of PIN didn't match") + respondBool(200, false, gc) + return + } + respondBool(200, true, gc) +} + // @Summary Returns a list of matching users from a Discord guild, given a username (discriminator optional). // @Produce json // @Success 200 {object} DiscordUsersDTO diff --git a/config.go b/config.go index d605ef5..70e1330 100644 --- a/config.go +++ b/config.go @@ -15,6 +15,7 @@ var emailEnabled = false var messagesEnabled = false var telegramEnabled = false var discordEnabled = false +var matrixEnabled = false func (app *appContext) GetPath(sect, key string) (fs.FS, string) { val := app.config.Section(sect).Key(key).MustString("") @@ -43,7 +44,7 @@ func (app *appContext) loadConfig() error { key.SetValue(key.MustString(filepath.Join(app.dataPath, (key.Name() + ".json")))) } } - for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template", "invites", "emails", "user_template", "custom_emails", "users", "telegram_users", "discord_users"} { + for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template", "invites", "emails", "user_template", "custom_emails", "users", "telegram_users", "discord_users", "matrix_users"} { app.config.Section("files").Key(key).SetValue(app.config.Section("files").Key(key).MustString(filepath.Join(app.dataPath, (key + ".json")))) } app.URLBase = strings.TrimSuffix(app.config.Section("ui").Key("url_base").MustString(""), "/") @@ -89,16 +90,18 @@ func (app *appContext) loadConfig() error { messagesEnabled = app.config.Section("messages").Key("enabled").MustBool(false) telegramEnabled = app.config.Section("telegram").Key("enabled").MustBool(false) discordEnabled = app.config.Section("discord").Key("enabled").MustBool(false) + matrixEnabled = app.config.Section("matrix").Key("enabled").MustBool(false) if !messagesEnabled { emailEnabled = false telegramEnabled = false discordEnabled = false + matrixEnabled = false } else if app.config.Section("email").Key("method").MustString("") == "" { emailEnabled = false } else { emailEnabled = true } - if !emailEnabled && !telegramEnabled && !discordEnabled { + if !emailEnabled && !telegramEnabled && !discordEnabled && !matrixEnabled { messagesEnabled = false } diff --git a/config/config-base.json b/config/config-base.json index 36bad28..46d2b18 100644 --- a/config/config-base.json +++ b/config/config-base.json @@ -676,6 +676,71 @@ } } }, + "matrix": { + "order": [], + "meta": { + "name": "Matrix", + "description": "Settings for Matrix invites/signup/notifications" + }, + "settings": { + "enabled": { + "name": "Enabled", + "required": false, + "requires_restart": true, + "type": "bool", + "value": false, + "description": "Enable signup verification through Matrix and the sending of notifications through it.\nSee the jfa-go wiki for setting up a bot." + }, + "required": { + "name": "Require on sign-up", + "required": false, + "required_restart": true, + "depends_true": "enabled", + "type": "bool", + "value": false, + "description": "Require Matrix connection on sign-up." + }, + "homeserver": { + "name": "Home Server URL", + "required": false, + "requires_restart": true, + "depends_true": "enabled", + "type": "text", + "value": "", + "description": "Matrix Home server URL." + }, + "token": { + "name": "Access Token", + "required": false, + "requires_restart": true, + "depends_true": "enabled", + "type": "text", + "value": "", + "description": "Matrix Bot API Token." + }, + "user_id": { + "name": "Bot User ID", + "required": false, + "requires_restart": true, + "depends_true": "enabled", + "type": "text", + "value": "", + "description": "User ID of bot account (Example: @jfa-bot:riot.im)" + }, + "language": { + "name": "Language", + "required": false, + "requires_restart": false, + "depends_true": "enabled", + "type": "select", + "options": [ + ["en-us", "English (US)"] + ], + "value": "en-us", + "description": "Default Matrix message language. Visit weblate if you'd like to translate." + } + } + }, "password_resets": { "order": [], "meta": { @@ -1225,6 +1290,14 @@ "value": "", "description": "Stores telegram user IDs and language preferences." }, + "matrix_users": { + "name": "Matrix users", + "required": false, + "requires_restart": false, + "type": "text", + "value": "", + "description": "Stores matrix user IDs and language preferences." + }, "discord_users": { "name": "Discord users", "required": false, diff --git a/go.mod b/go.mod index 33d4591..7e15dfa 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ require ( github.com/lithammer/shortuuid/v3 v3.0.4 github.com/mailgun/mailgun-go/v4 v4.5.1 github.com/mailru/easyjson v0.7.7 // indirect + github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect diff --git a/go.sum b/go.sum index 42b3910..c20797a 100644 --- a/go.sum +++ b/go.sum @@ -189,6 +189,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4= +github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= diff --git a/html/form-base.html b/html/form-base.html index 855253b..be95c3e 100644 --- a/html/form-base.html +++ b/html/form-base.html @@ -22,6 +22,9 @@ window.discordPIN = "{{ .discordPIN }}"; window.discordInviteLink = {{ .discordInviteLink }}; window.discordServerName = "{{ .discordServerName }}"; + window.matrixEnabled = {{ .matrixEnabled }}; + window.matrixRequired = {{ .matrixRequired }}; + window.matrixUserID = "{{ .matrixUser }}"; {{ end }} diff --git a/html/form.html b/html/form.html index 6b785d0..fecaccd 100644 --- a/html/form.html +++ b/html/form.html @@ -48,6 +48,24 @@ {{ end }} + {{ if .matrixEnabled }} + + {{ end }} @@ -84,7 +102,10 @@ {{ if .discordEnabled }} {{ .strings.linkDiscord }} {{ end }} - {{ if or (.telegramEnabled) (.discordEnabled) }} + {{ if .matrixEnabled }} + {{ .strings.linkMatrix }} + {{ end }} + {{ if or (.telegramEnabled) (or .discordEnabled .matrixEnabled) }}
{{ end }} + {{ if .matrixEnabled }} + + {{ end }}
{{ end }} diff --git a/lang/common/en-us.json b/lang/common/en-us.json index 3e9ef53..5f8ec44 100644 --- a/lang/common/en-us.json +++ b/lang/common/en-us.json @@ -8,6 +8,7 @@ "emailAddress": "Email Address", "name": "Name", "submit": "Submit", + "send": "Send", "success": "Success", "error": "Error", "copy": "Copy", @@ -18,6 +19,7 @@ "contactEmail": "Contact through Email", "contactTelegram": "Contact through Telegram", "linkDiscord": "Link Discord", + "linkMatrix": "Link Matrix", "contactDiscord": "Contact through Discord", "theme": "Theme" } diff --git a/lang/form/en-us.json b/lang/form/en-us.json index 6726c0b..995da7f 100644 --- a/lang/form/en-us.json +++ b/lang/form/en-us.json @@ -19,7 +19,8 @@ "confirmationRequiredMessage": "Please check your email inbox to verify your address.", "yourAccountIsValidUntil": "Your account will be valid until {date}.", "sendPIN": "Send the PIN below to the bot, then come back here to link your account.", - "sendPINDiscord": "Type {command} in {server_channel} on Discord, then send the PIN below via DM to the bot." + "sendPINDiscord": "Type {command} in {server_channel} on Discord, then send the PIN below via DM to the bot.", + "matrixEnterUser": "Enter your User ID, press submit, and a PIN will be sent to you. Enter it here to continue." }, "notifications": { "errorUserExists": "User already exists.", @@ -27,6 +28,7 @@ "errorTelegramVerification": "Telegram verification required.", "errorDiscordVerification": "Discord verification required.", "errorInvalidPIN": "PIN is invalid.", + "errorUnknown": "Unknown error.", "verified": "Account verified." }, "validationStrings": { diff --git a/lang/telegram/en-us.json b/lang/telegram/en-us.json index 642399f..e93aed7 100644 --- a/lang/telegram/en-us.json +++ b/lang/telegram/en-us.json @@ -4,6 +4,7 @@ }, "strings": { "startMessage": "Hi!\nEnter your Jellyfin PIN code here to verify your account.", + "matrixStartMessage": "Hi\nEnter the below PIN in the Jellyfin sign-up page to verify your account.", "invalidPIN": "That PIN was invalid, try again.", "pinSuccess": "Success! You can now return to the sign-up page.", "languageMessage": "Note: See available languages with {command}, and set language with {command} ." diff --git a/main.go b/main.go index fa4749d..b9a1483 100644 --- a/main.go +++ b/main.go @@ -100,6 +100,7 @@ type appContext struct { email *Emailer telegram *TelegramDaemon discord *DiscordDaemon + matrix *MatrixDaemon info, debug, err logger.Logger host string port int @@ -590,6 +591,16 @@ func start(asDaemon, firstCall bool) { defer app.discord.Shutdown() } } + if matrixEnabled { + app.matrix, err = newMatrixDaemon(app) + if err != nil { + app.err.Printf("Failed to initialize Matrix daemon: %v", err) + matrixEnabled = false + } else { + go app.matrix.run() + defer app.matrix.Shutdown() + } + } } else { debugMode = false address = "0.0.0.0:8056" diff --git a/matrix.go b/matrix.go new file mode 100644 index 0000000..1d004b5 --- /dev/null +++ b/matrix.go @@ -0,0 +1,133 @@ +package main + +import ( + "encoding/json" + + "github.com/matrix-org/gomatrix" +) + +type MatrixDaemon struct { + Stopped bool + ShutdownChannel chan string + bot *gomatrix.Client + userID string + tokens map[string]UnverifiedUser // Map of tokens to users + languages map[string]string // Map of roomIDs to language codes + app *appContext +} + +type UnverifiedUser struct { + Verified bool + User *MatrixUser +} + +type MatrixUser struct { + RoomID string + UserID string + Lang string + Contact bool +} + +var matrixFilter = gomatrix.Filter{ + Room: gomatrix.RoomFilter{ + Timeline: gomatrix.FilterPart{ + Types: []string{ + "m.room.message", + "m.room.member", + }, + }, + }, + EventFields: []string{ + "type", + "event_id", + "room_id", + "state_key", + "sender", + "content.body", + "content.membership", + }, +} + +func newMatrixDaemon(app *appContext) (d *MatrixDaemon, err error) { + matrix := app.config.Section("matrix") + homeserver := matrix.Key("homeserver").String() + token := matrix.Key("token").String() + d = &MatrixDaemon{ + ShutdownChannel: make(chan string), + userID: matrix.Key("user_id").String(), + tokens: map[string]UnverifiedUser{}, + languages: map[string]string{}, + app: app, + } + d.bot, err = gomatrix.NewClient(homeserver, d.userID, token) + if err != nil { + return + } + filter, err := json.Marshal(matrixFilter) + if err != nil { + return + } + resp, err := d.bot.CreateFilter(filter) + d.bot.Store.SaveFilterID(d.userID, resp.FilterID) + for _, user := range app.storage.matrix { + if user.Lang != "" { + d.languages[user.RoomID] = user.Lang + } + } + return +} + +func (d *MatrixDaemon) run() { + d.app.info.Println("Starting Matrix bot daemon") + syncer := d.bot.Syncer.(*gomatrix.DefaultSyncer) + syncer.OnEventType("m.room.message", d.handleMessage) + // syncer.OnEventType("m.room.member", d.handleMembership) + if err := d.bot.Sync(); err != nil { + d.app.err.Printf("Matrix sync failed: %v", err) + } +} + +func (d *MatrixDaemon) Shutdown() { + d.bot.StopSync() + d.Stopped = true + close(d.ShutdownChannel) +} + +func (d *MatrixDaemon) handleMessage(event *gomatrix.Event) { return } + +func (d *MatrixDaemon) SendStart(userID string) (ok bool) { + room, err := d.bot.CreateRoom(&gomatrix.ReqCreateRoom{ + Visibility: "private", + Invite: []string{userID}, + Topic: "jfa-go", + }) + if err != nil { + d.app.err.Printf("Failed to create room for user \"%s\": %v", userID, err) + return + } + lang := "en-us" + pin := genAuthToken() + d.tokens[pin] = UnverifiedUser{ + false, + &MatrixUser{ + RoomID: room.RoomID, + UserID: userID, + Lang: lang, + }, + } + _, err = d.bot.SendText( + room.RoomID, + d.app.storage.lang.Telegram[lang].Strings.get("matrixStartMessage")+"\n\n"+pin+"\n\n"+ + d.app.storage.lang.Telegram[lang].Strings.template("languageMessage", tmpl{"command": "!lang"}), + ) + if err != nil { + d.app.err.Printf("Matrix: Failed to send welcome message to \"%s\": %v", userID, err) + return + } + ok = true + return +} + +// User enters ID on sign-up, a PIN is sent to them. They enter it on sign-up. + +// Message the user first, to avoid E2EE by default diff --git a/models.go b/models.go index e14030d..a5b2ae6 100644 --- a/models.go +++ b/models.go @@ -281,3 +281,10 @@ type DiscordInviteDTO struct { InviteURL string `json:"invite"` IconURL string `json:"icon"` } + +type MatrixSendPINDTO struct { + UserID string `json:"user_id"` +} +type MatrixCheckPINDTO struct { + PIN string `json:"pin"` +} diff --git a/router.go b/router.go index a717d49..9b14672 100644 --- a/router.go +++ b/router.go @@ -127,6 +127,10 @@ func (app *appContext) loadRoutes(router *gin.Engine) { router.GET(p+"/invite/:invCode/discord/invite", app.DiscordServerInvite) } } + if matrixEnabled { + router.GET(p+"/invite/:invCode/matrix/verified/:userID/:pin", app.MatrixCheckPIN) + router.POST(p+"/invite/:invCode/matrix/user", app.MatrixSendPIN) + } } if *SWAGGER { app.info.Print(warning("\n\nWARNING: Swagger should not be used on a public instance.\n\n")) diff --git a/storage.go b/storage.go index c32fb75..ae2022a 100644 --- a/storage.go +++ b/storage.go @@ -15,21 +15,22 @@ import ( ) type Storage struct { - timePattern string - invite_path, emails_path, policy_path, configuration_path, displayprefs_path, ombi_path, profiles_path, customEmails_path, users_path, telegram_path, discord_path string - users map[string]time.Time - invites Invites - profiles map[string]Profile - defaultProfile string - 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 - policy mediabrowser.Policy - configuration mediabrowser.Configuration - lang Lang - invitesLock, usersLock sync.Mutex + timePattern string + invite_path, emails_path, policy_path, configuration_path, displayprefs_path, ombi_path, profiles_path, customEmails_path, users_path, telegram_path, discord_path, matrix_path string + users map[string]time.Time + invites Invites + profiles map[string]Profile + defaultProfile string + 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. + matrix map[string]MatrixUser // Map of Jellyfin user IDs to Matrix users. + customEmails customEmails + policy mediabrowser.Policy + configuration mediabrowser.Configuration + lang Lang + invitesLock, usersLock sync.Mutex } type TelegramUser struct { @@ -790,6 +791,14 @@ func (st *Storage) storeDiscordUsers() error { return storeJSON(st.discord_path, st.discord) } +func (st *Storage) loadMatrixUsers() error { + return loadJSON(st.matrix_path, &st.matrix) +} + +func (st *Storage) storeMatrixUsers() error { + return storeJSON(st.matrix_path, st.matrix) +} + func (st *Storage) loadCustomEmails() error { return loadJSON(st.customEmails_path, &st.customEmails) } diff --git a/telegram.go b/telegram.go index 4cca16f..654d05b 100644 --- a/telegram.go +++ b/telegram.go @@ -58,7 +58,7 @@ func genAuthToken() string { rand.Seed(time.Now().UnixNano()) pin := make([]rune, 8) for i := range pin { - if i == 2 || i == 5 { + if (i+1)%3 == 0 { pin[i] = '-' } else { pin[i] = runes[rand.Intn(len(runes))] diff --git a/ts/form.ts b/ts/form.ts index 94d3874..b3b0815 100644 --- a/ts/form.ts +++ b/ts/form.ts @@ -1,6 +1,6 @@ import { Modal } from "./modules/modal.js"; import { notificationBox, whichAnimationEvent } from "./modules/common.js"; -import { _get, _post, toggleLoader, toDateString } from "./modules/common.js"; +import { _get, _post, toggleLoader, addLoader, removeLoader, toDateString } from "./modules/common.js"; import { loadLangSelector } from "./modules/lang.js"; interface formWindow extends Window { @@ -9,6 +9,7 @@ interface formWindow extends Window { successModal: Modal; telegramModal: Modal; discordModal: Modal; + matrixModal: Modal; confirmationModal: Modal code: string; messages: { [key: string]: string }; @@ -20,6 +21,8 @@ interface formWindow extends Window { discordStartCommand: string; discordInviteLink: boolean; discordServerName: string; + matrixRequired: boolean; + matrixUserID: string; userExpiryEnabled: boolean; userExpiryMonths: number; userExpiryDays: number; @@ -150,6 +153,69 @@ if (window.discordEnabled) { }; } +var matrixVerified = false; +var matrixPIN = ""; +if (window.matrixEnabled) { + window.matrixModal = new Modal(document.getElementById("modal-matrix"), window.matrixRequired); + const matrixButton = document.getElementById("link-matrix") as HTMLSpanElement; + matrixButton.onclick = window.matrixModal.show; + const submitButton = document.getElementById("matrix-send") as HTMLSpanElement; + const input = document.getElementById("matrix-userid") as HTMLInputElement; + let userID = ""; + submitButton.onclick = () => { + addLoader(submitButton); + if (userID == "") { + const send = { + user_id: input.value + }; + _post("/invite/" + window.code + "/matrix/user", send, (req: XMLHttpRequest) => { + if (req.readyState == 4) { + removeLoader(submitButton); + userID = input.value; + if (req.status != 200) { + window.notifications.customError("errorUnknown", window.messages["errorUnknown"]); + window.matrixModal.close(); + return; + } + submitButton.classList.add("~positive"); + submitButton.classList.remove("~info"); + setTimeout(() => { + submitButton.classList.add("~info"); + submitButton.classList.remove("~positive"); + }, 2000); + input.placeholder = "PIN"; + input.value = ""; + } + }); + } else { + _get("/invite/" + window.code + "/matrix/verified/" + userID + "/" + input.value, null, (req: XMLHttpRequest) => { + if (req.readyState == 4) { + removeLoader(submitButton) + const valid = req.response["success"] as boolean; + if (valid) { + window.matrixModal.close(); + window.notifications.customPositive("successVerified", "", window.messages["verified"]); + matrixVerified = true; + matrixPIN = input.value; + matrixButton.classList.add("unfocused"); + document.getElementById("contact-via").classList.remove("unfocused"); + const radio = document.getElementById("contact-via-discord") as HTMLInputElement; + radio.checked = true; + } else { + window.notifications.customError("errorInvalidPIN", window.messages["errorInvalidPIN"]); + submitButton.classList.add("~critical"); + submitButton.classList.remove("~info"); + setTimeout(() => { + submitButton.classList.add("~info"); + submitButton.classList.remove("~critical"); + }, 800); + } + } + },); + } + }; +} + if (window.confirmation) { window.confirmationModal = new Modal(document.getElementById("modal-confirmation"), true); } @@ -229,6 +295,8 @@ interface sendDTO { telegram_contact?: boolean; discord_pin?: string; discord_contact?: boolean; + matrix_pin?: string; + matrix_contact?: boolean; } const create = (event: SubmitEvent) => { @@ -254,6 +322,13 @@ const create = (event: SubmitEvent) => { send.discord_contact = true; } } + if (matrixVerified) { + send.matrix_pin = matrixPIN; + const radio = document.getElementById("contact-via-matrix") as HTMLInputElement; + if (radio.checked) { + send.matrix_contact = true; + } + } _post("/newUser", send, (req: XMLHttpRequest) => { if (req.readyState == 4) { let vals = req.response as respDTO; diff --git a/ts/modules/common.ts b/ts/modules/common.ts index f761a4c..05b6bcb 100644 --- a/ts/modules/common.ts +++ b/ts/modules/common.ts @@ -105,7 +105,11 @@ export class notificationBox implements NotificationBox { private _error = (message: string): HTMLElement => { const noti = document.createElement('aside'); noti.classList.add("aside", "~critical", "!normal", "mt-half", "notification-error"); - noti.innerHTML = `${window.lang.strings("error")}: ${message}`; + let error = ""; + if (window.lang) { + error = window.lang.strings("error") + ":" + } + noti.innerHTML = `${error} ${message}`; const closeButton = document.createElement('span') as HTMLSpanElement; closeButton.classList.add("button", "~critical", "!low", "ml-1"); closeButton.innerHTML = ``; diff --git a/ts/typings/d.ts b/ts/typings/d.ts index eb5be55..785db4d 100644 --- a/ts/typings/d.ts +++ b/ts/typings/d.ts @@ -22,6 +22,7 @@ declare interface Window { emailEnabled: boolean; telegramEnabled: boolean; discordEnabled: boolean; + matrixEnabled: boolean; ombiEnabled: boolean; usernameEnabled: boolean; token: string; diff --git a/views.go b/views.go index 6d8e306..f463fd9 100644 --- a/views.go +++ b/views.go @@ -287,6 +287,7 @@ func (app *appContext) InviteProxy(gc *gin.Context) { "langName": lang, "telegramEnabled": telegramEnabled, "discordEnabled": discordEnabled, + "matrixEnabled": matrixEnabled, } if telegramEnabled { data["telegramPIN"] = app.telegram.NewAuthToken() @@ -294,6 +295,10 @@ func (app *appContext) InviteProxy(gc *gin.Context) { data["telegramURL"] = app.telegram.link data["telegramRequired"] = app.config.Section("telegram").Key("required").MustBool(false) } + if matrixEnabled { + data["matrixRequired"] = app.config.Section("matrix").Key("required").MustBool(false) + data["matrixUser"] = app.matrix.userID + } if discordEnabled { data["discordPIN"] = app.discord.NewAuthToken() data["discordUsername"] = app.discord.username