mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 09:00:10 +00:00
Matrix: Setup bot, add PIN verification
PIN is verified but not used currently. Works a little different than the others, you input your matrix user ID and then the PIN is sent to you. The bot doesn't support E2EE, so the bot being the first one to message ensures the chat is unencrypted.
This commit is contained in:
parent
fb6256d1ed
commit
e97b90d4d7
62
api.go
62
api.go
@ -2233,6 +2233,68 @@ func (app *appContext) DiscordServerInvite(gc *gin.Context) {
|
|||||||
gc.JSON(200, DiscordInviteDTO{invURL, iconURL})
|
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).
|
// @Summary Returns a list of matching users from a Discord guild, given a username (discriminator optional).
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} DiscordUsersDTO
|
// @Success 200 {object} DiscordUsersDTO
|
||||||
|
@ -15,6 +15,7 @@ var emailEnabled = false
|
|||||||
var messagesEnabled = false
|
var messagesEnabled = false
|
||||||
var telegramEnabled = false
|
var telegramEnabled = false
|
||||||
var discordEnabled = false
|
var discordEnabled = false
|
||||||
|
var matrixEnabled = false
|
||||||
|
|
||||||
func (app *appContext) GetPath(sect, key string) (fs.FS, string) {
|
func (app *appContext) GetPath(sect, key string) (fs.FS, string) {
|
||||||
val := app.config.Section(sect).Key(key).MustString("")
|
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"))))
|
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.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(""), "/")
|
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)
|
messagesEnabled = app.config.Section("messages").Key("enabled").MustBool(false)
|
||||||
telegramEnabled = app.config.Section("telegram").Key("enabled").MustBool(false)
|
telegramEnabled = app.config.Section("telegram").Key("enabled").MustBool(false)
|
||||||
discordEnabled = app.config.Section("discord").Key("enabled").MustBool(false)
|
discordEnabled = app.config.Section("discord").Key("enabled").MustBool(false)
|
||||||
|
matrixEnabled = app.config.Section("matrix").Key("enabled").MustBool(false)
|
||||||
if !messagesEnabled {
|
if !messagesEnabled {
|
||||||
emailEnabled = false
|
emailEnabled = false
|
||||||
telegramEnabled = false
|
telegramEnabled = false
|
||||||
discordEnabled = false
|
discordEnabled = false
|
||||||
|
matrixEnabled = false
|
||||||
} else if app.config.Section("email").Key("method").MustString("") == "" {
|
} else if app.config.Section("email").Key("method").MustString("") == "" {
|
||||||
emailEnabled = false
|
emailEnabled = false
|
||||||
} else {
|
} else {
|
||||||
emailEnabled = true
|
emailEnabled = true
|
||||||
}
|
}
|
||||||
if !emailEnabled && !telegramEnabled && !discordEnabled {
|
if !emailEnabled && !telegramEnabled && !discordEnabled && !matrixEnabled {
|
||||||
messagesEnabled = false
|
messagesEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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": {
|
"password_resets": {
|
||||||
"order": [],
|
"order": [],
|
||||||
"meta": {
|
"meta": {
|
||||||
@ -1225,6 +1290,14 @@
|
|||||||
"value": "",
|
"value": "",
|
||||||
"description": "Stores telegram user IDs and language preferences."
|
"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": {
|
"discord_users": {
|
||||||
"name": "Discord users",
|
"name": "Discord users",
|
||||||
"required": false,
|
"required": false,
|
||||||
|
1
go.mod
1
go.mod
@ -38,6 +38,7 @@ require (
|
|||||||
github.com/lithammer/shortuuid/v3 v3.0.4
|
github.com/lithammer/shortuuid/v3 v3.0.4
|
||||||
github.com/mailgun/mailgun-go/v4 v4.5.1
|
github.com/mailgun/mailgun-go/v4 v4.5.1
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
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/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
||||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||||
|
2
go.sum
2
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.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
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 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
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=
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
window.discordPIN = "{{ .discordPIN }}";
|
window.discordPIN = "{{ .discordPIN }}";
|
||||||
window.discordInviteLink = {{ .discordInviteLink }};
|
window.discordInviteLink = {{ .discordInviteLink }};
|
||||||
window.discordServerName = "{{ .discordServerName }}";
|
window.discordServerName = "{{ .discordServerName }}";
|
||||||
|
window.matrixEnabled = {{ .matrixEnabled }};
|
||||||
|
window.matrixRequired = {{ .matrixRequired }};
|
||||||
|
window.matrixUserID = "{{ .matrixUser }}";
|
||||||
</script>
|
</script>
|
||||||
<script src="js/form.js" type="module"></script>
|
<script src="js/form.js" type="module"></script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -48,6 +48,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ if .matrixEnabled }}
|
||||||
|
<div id="modal-matrix" class="modal">
|
||||||
|
<div class="modal-content card">
|
||||||
|
<span class="heading mb-1">{{ .strings.linkMatrix }}</span>
|
||||||
|
<p class="content mb-1"> {{ .strings.matrixEnterUser }}</p>
|
||||||
|
<input type="text" class="input ~neutral !high" placeholder="@user:riot.im" id="matrix-userid">
|
||||||
|
<div class="subheading link-center mt-1">
|
||||||
|
<span class="shield ~info mr-1">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="ri-chat-3-line"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
{{ .matrixUser }}
|
||||||
|
</div>
|
||||||
|
<span class="button ~info !normal full-width center mt-1" id="matrix-send">{{ .strings.submit }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
<span class="dropdown" tabindex="0" id="lang-dropdown">
|
<span class="dropdown" tabindex="0" id="lang-dropdown">
|
||||||
<span class="button ~urge dropdown-button">
|
<span class="button ~urge dropdown-button">
|
||||||
<i class="ri-global-line"></i>
|
<i class="ri-global-line"></i>
|
||||||
@ -84,7 +102,10 @@
|
|||||||
{{ if .discordEnabled }}
|
{{ if .discordEnabled }}
|
||||||
<span class="button ~info !normal full-width center mb-1" id="link-discord">{{ .strings.linkDiscord }}</span>
|
<span class="button ~info !normal full-width center mb-1" id="link-discord">{{ .strings.linkDiscord }}</span>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ if or (.telegramEnabled) (.discordEnabled) }}
|
{{ if .matrixEnabled }}
|
||||||
|
<span class="button ~info !normal full-width center mb-1" id="link-matrix">{{ .strings.linkMatrix }}</span>
|
||||||
|
{{ end }}
|
||||||
|
{{ if or (.telegramEnabled) (or .discordEnabled .matrixEnabled) }}
|
||||||
<div id="contact-via" class="unfocused">
|
<div id="contact-via" class="unfocused">
|
||||||
<label class="row switch pb-1">
|
<label class="row switch pb-1">
|
||||||
<input type="radio" name="contact-via" value="email"><span>Contact through Email</span>
|
<input type="radio" name="contact-via" value="email"><span>Contact through Email</span>
|
||||||
@ -99,6 +120,11 @@
|
|||||||
<input type="radio" name="contact-via" value="discord" id="contact-via-discord"><span>Contact through Discord</span>
|
<input type="radio" name="contact-via" value="discord" id="contact-via-discord"><span>Contact through Discord</span>
|
||||||
</label>
|
</label>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ if .matrixEnabled }}
|
||||||
|
<label class="row switch pb-1">
|
||||||
|
<input type="radio" name="contact-via" value="matrix" id="contact-via-matrix"><span>Contact through Matrix</span>
|
||||||
|
</label>
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<label class="label supra" for="create-password">{{ .strings.password }}</label>
|
<label class="label supra" for="create-password">{{ .strings.password }}</label>
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"emailAddress": "Email Address",
|
"emailAddress": "Email Address",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
|
"send": "Send",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
@ -18,6 +19,7 @@
|
|||||||
"contactEmail": "Contact through Email",
|
"contactEmail": "Contact through Email",
|
||||||
"contactTelegram": "Contact through Telegram",
|
"contactTelegram": "Contact through Telegram",
|
||||||
"linkDiscord": "Link Discord",
|
"linkDiscord": "Link Discord",
|
||||||
|
"linkMatrix": "Link Matrix",
|
||||||
"contactDiscord": "Contact through Discord",
|
"contactDiscord": "Contact through Discord",
|
||||||
"theme": "Theme"
|
"theme": "Theme"
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"confirmationRequiredMessage": "Please check your email inbox to verify your address.",
|
"confirmationRequiredMessage": "Please check your email inbox to verify your address.",
|
||||||
"yourAccountIsValidUntil": "Your account will be valid until {date}.",
|
"yourAccountIsValidUntil": "Your account will be valid until {date}.",
|
||||||
"sendPIN": "Send the PIN below to the bot, then come back here to link your account.",
|
"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": {
|
"notifications": {
|
||||||
"errorUserExists": "User already exists.",
|
"errorUserExists": "User already exists.",
|
||||||
@ -27,6 +28,7 @@
|
|||||||
"errorTelegramVerification": "Telegram verification required.",
|
"errorTelegramVerification": "Telegram verification required.",
|
||||||
"errorDiscordVerification": "Discord verification required.",
|
"errorDiscordVerification": "Discord verification required.",
|
||||||
"errorInvalidPIN": "PIN is invalid.",
|
"errorInvalidPIN": "PIN is invalid.",
|
||||||
|
"errorUnknown": "Unknown error.",
|
||||||
"verified": "Account verified."
|
"verified": "Account verified."
|
||||||
},
|
},
|
||||||
"validationStrings": {
|
"validationStrings": {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
},
|
},
|
||||||
"strings": {
|
"strings": {
|
||||||
"startMessage": "Hi!\nEnter your Jellyfin PIN code here to verify your account.",
|
"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.",
|
"invalidPIN": "That PIN was invalid, try again.",
|
||||||
"pinSuccess": "Success! You can now return to the sign-up page.",
|
"pinSuccess": "Success! You can now return to the sign-up page.",
|
||||||
"languageMessage": "Note: See available languages with {command}, and set language with {command} <language code>."
|
"languageMessage": "Note: See available languages with {command}, and set language with {command} <language code>."
|
||||||
|
11
main.go
11
main.go
@ -100,6 +100,7 @@ type appContext struct {
|
|||||||
email *Emailer
|
email *Emailer
|
||||||
telegram *TelegramDaemon
|
telegram *TelegramDaemon
|
||||||
discord *DiscordDaemon
|
discord *DiscordDaemon
|
||||||
|
matrix *MatrixDaemon
|
||||||
info, debug, err logger.Logger
|
info, debug, err logger.Logger
|
||||||
host string
|
host string
|
||||||
port int
|
port int
|
||||||
@ -590,6 +591,16 @@ func start(asDaemon, firstCall bool) {
|
|||||||
defer app.discord.Shutdown()
|
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 {
|
} else {
|
||||||
debugMode = false
|
debugMode = false
|
||||||
address = "0.0.0.0:8056"
|
address = "0.0.0.0:8056"
|
||||||
|
133
matrix.go
Normal file
133
matrix.go
Normal file
@ -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
|
@ -281,3 +281,10 @@ type DiscordInviteDTO struct {
|
|||||||
InviteURL string `json:"invite"`
|
InviteURL string `json:"invite"`
|
||||||
IconURL string `json:"icon"`
|
IconURL string `json:"icon"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MatrixSendPINDTO struct {
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
}
|
||||||
|
type MatrixCheckPINDTO struct {
|
||||||
|
PIN string `json:"pin"`
|
||||||
|
}
|
||||||
|
@ -127,6 +127,10 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
|||||||
router.GET(p+"/invite/:invCode/discord/invite", app.DiscordServerInvite)
|
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 {
|
if *SWAGGER {
|
||||||
app.info.Print(warning("\n\nWARNING: Swagger should not be used on a public instance.\n\n"))
|
app.info.Print(warning("\n\nWARNING: Swagger should not be used on a public instance.\n\n"))
|
||||||
|
39
storage.go
39
storage.go
@ -15,21 +15,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Storage struct {
|
type Storage struct {
|
||||||
timePattern string
|
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
|
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
|
users map[string]time.Time
|
||||||
invites Invites
|
invites Invites
|
||||||
profiles map[string]Profile
|
profiles map[string]Profile
|
||||||
defaultProfile string
|
defaultProfile string
|
||||||
displayprefs, ombi_template map[string]interface{}
|
displayprefs, ombi_template map[string]interface{}
|
||||||
emails map[string]EmailAddress
|
emails map[string]EmailAddress
|
||||||
telegram map[string]TelegramUser // Map of Jellyfin User IDs to telegram users.
|
telegram map[string]TelegramUser // Map of Jellyfin User IDs to telegram users.
|
||||||
discord map[string]DiscordUser // Map of Jellyfin user IDs to discord users.
|
discord map[string]DiscordUser // Map of Jellyfin user IDs to discord users.
|
||||||
customEmails customEmails
|
matrix map[string]MatrixUser // Map of Jellyfin user IDs to Matrix users.
|
||||||
policy mediabrowser.Policy
|
customEmails customEmails
|
||||||
configuration mediabrowser.Configuration
|
policy mediabrowser.Policy
|
||||||
lang Lang
|
configuration mediabrowser.Configuration
|
||||||
invitesLock, usersLock sync.Mutex
|
lang Lang
|
||||||
|
invitesLock, usersLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type TelegramUser struct {
|
type TelegramUser struct {
|
||||||
@ -790,6 +791,14 @@ func (st *Storage) storeDiscordUsers() error {
|
|||||||
return storeJSON(st.discord_path, st.discord)
|
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 {
|
func (st *Storage) loadCustomEmails() error {
|
||||||
return loadJSON(st.customEmails_path, &st.customEmails)
|
return loadJSON(st.customEmails_path, &st.customEmails)
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ func genAuthToken() string {
|
|||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
pin := make([]rune, 8)
|
pin := make([]rune, 8)
|
||||||
for i := range pin {
|
for i := range pin {
|
||||||
if i == 2 || i == 5 {
|
if (i+1)%3 == 0 {
|
||||||
pin[i] = '-'
|
pin[i] = '-'
|
||||||
} else {
|
} else {
|
||||||
pin[i] = runes[rand.Intn(len(runes))]
|
pin[i] = runes[rand.Intn(len(runes))]
|
||||||
|
77
ts/form.ts
77
ts/form.ts
@ -1,6 +1,6 @@
|
|||||||
import { Modal } from "./modules/modal.js";
|
import { Modal } from "./modules/modal.js";
|
||||||
import { notificationBox, whichAnimationEvent } from "./modules/common.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";
|
import { loadLangSelector } from "./modules/lang.js";
|
||||||
|
|
||||||
interface formWindow extends Window {
|
interface formWindow extends Window {
|
||||||
@ -9,6 +9,7 @@ interface formWindow extends Window {
|
|||||||
successModal: Modal;
|
successModal: Modal;
|
||||||
telegramModal: Modal;
|
telegramModal: Modal;
|
||||||
discordModal: Modal;
|
discordModal: Modal;
|
||||||
|
matrixModal: Modal;
|
||||||
confirmationModal: Modal
|
confirmationModal: Modal
|
||||||
code: string;
|
code: string;
|
||||||
messages: { [key: string]: string };
|
messages: { [key: string]: string };
|
||||||
@ -20,6 +21,8 @@ interface formWindow extends Window {
|
|||||||
discordStartCommand: string;
|
discordStartCommand: string;
|
||||||
discordInviteLink: boolean;
|
discordInviteLink: boolean;
|
||||||
discordServerName: string;
|
discordServerName: string;
|
||||||
|
matrixRequired: boolean;
|
||||||
|
matrixUserID: string;
|
||||||
userExpiryEnabled: boolean;
|
userExpiryEnabled: boolean;
|
||||||
userExpiryMonths: number;
|
userExpiryMonths: number;
|
||||||
userExpiryDays: 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) {
|
if (window.confirmation) {
|
||||||
window.confirmationModal = new Modal(document.getElementById("modal-confirmation"), true);
|
window.confirmationModal = new Modal(document.getElementById("modal-confirmation"), true);
|
||||||
}
|
}
|
||||||
@ -229,6 +295,8 @@ interface sendDTO {
|
|||||||
telegram_contact?: boolean;
|
telegram_contact?: boolean;
|
||||||
discord_pin?: string;
|
discord_pin?: string;
|
||||||
discord_contact?: boolean;
|
discord_contact?: boolean;
|
||||||
|
matrix_pin?: string;
|
||||||
|
matrix_contact?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const create = (event: SubmitEvent) => {
|
const create = (event: SubmitEvent) => {
|
||||||
@ -254,6 +322,13 @@ const create = (event: SubmitEvent) => {
|
|||||||
send.discord_contact = true;
|
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) => {
|
_post("/newUser", send, (req: XMLHttpRequest) => {
|
||||||
if (req.readyState == 4) {
|
if (req.readyState == 4) {
|
||||||
let vals = req.response as respDTO;
|
let vals = req.response as respDTO;
|
||||||
|
@ -105,7 +105,11 @@ export class notificationBox implements NotificationBox {
|
|||||||
private _error = (message: string): HTMLElement => {
|
private _error = (message: string): HTMLElement => {
|
||||||
const noti = document.createElement('aside');
|
const noti = document.createElement('aside');
|
||||||
noti.classList.add("aside", "~critical", "!normal", "mt-half", "notification-error");
|
noti.classList.add("aside", "~critical", "!normal", "mt-half", "notification-error");
|
||||||
noti.innerHTML = `<strong>${window.lang.strings("error")}:</strong> ${message}`;
|
let error = "";
|
||||||
|
if (window.lang) {
|
||||||
|
error = window.lang.strings("error") + ":"
|
||||||
|
}
|
||||||
|
noti.innerHTML = `<strong>${error}</strong> ${message}`;
|
||||||
const closeButton = document.createElement('span') as HTMLSpanElement;
|
const closeButton = document.createElement('span') as HTMLSpanElement;
|
||||||
closeButton.classList.add("button", "~critical", "!low", "ml-1");
|
closeButton.classList.add("button", "~critical", "!low", "ml-1");
|
||||||
closeButton.innerHTML = `<i class="icon ri-close-line"></i>`;
|
closeButton.innerHTML = `<i class="icon ri-close-line"></i>`;
|
||||||
|
@ -22,6 +22,7 @@ declare interface Window {
|
|||||||
emailEnabled: boolean;
|
emailEnabled: boolean;
|
||||||
telegramEnabled: boolean;
|
telegramEnabled: boolean;
|
||||||
discordEnabled: boolean;
|
discordEnabled: boolean;
|
||||||
|
matrixEnabled: boolean;
|
||||||
ombiEnabled: boolean;
|
ombiEnabled: boolean;
|
||||||
usernameEnabled: boolean;
|
usernameEnabled: boolean;
|
||||||
token: string;
|
token: string;
|
||||||
|
5
views.go
5
views.go
@ -287,6 +287,7 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
|
|||||||
"langName": lang,
|
"langName": lang,
|
||||||
"telegramEnabled": telegramEnabled,
|
"telegramEnabled": telegramEnabled,
|
||||||
"discordEnabled": discordEnabled,
|
"discordEnabled": discordEnabled,
|
||||||
|
"matrixEnabled": matrixEnabled,
|
||||||
}
|
}
|
||||||
if telegramEnabled {
|
if telegramEnabled {
|
||||||
data["telegramPIN"] = app.telegram.NewAuthToken()
|
data["telegramPIN"] = app.telegram.NewAuthToken()
|
||||||
@ -294,6 +295,10 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
|
|||||||
data["telegramURL"] = app.telegram.link
|
data["telegramURL"] = app.telegram.link
|
||||||
data["telegramRequired"] = app.config.Section("telegram").Key("required").MustBool(false)
|
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 {
|
if discordEnabled {
|
||||||
data["discordPIN"] = app.discord.NewAuthToken()
|
data["discordPIN"] = app.discord.NewAuthToken()
|
||||||
data["discordUsername"] = app.discord.username
|
data["discordUsername"] = app.discord.username
|
||||||
|
Loading…
Reference in New Issue
Block a user