mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 09:00:10 +00:00
telegram: Fix UI and store useful Telegram info
Creation now works, and language preferences made before signup are kept. telegram file storage now uses the Jellyfin ID as a key, which makes much more sense. Also added radios to select preferred notification method (email/telegram) as well, which the admin will soon be able to change also.
This commit is contained in:
parent
326c2cf70a
commit
72bf280e2d
76
api.go
76
api.go
@ -330,15 +330,44 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
||||
success = false
|
||||
return
|
||||
}
|
||||
telegramTokenIndex := -1
|
||||
if app.config.Section("telegram").Key("enabled").MustBool(false) {
|
||||
if req.TelegramPIN == "" {
|
||||
if app.config.Section("telegram").Key("required").MustBool(false) {
|
||||
f = func(gc *gin.Context) {
|
||||
app.debug.Printf("%s: New user failed: Telegram verification not completed", req.Code)
|
||||
respond(401, "errorTelegramVerification", gc)
|
||||
}
|
||||
success = false
|
||||
return
|
||||
}
|
||||
} else {
|
||||
for i, v := range app.telegram.verifiedTokens {
|
||||
if v.Token == req.TelegramPIN {
|
||||
telegramTokenIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if telegramTokenIndex == -1 {
|
||||
f = func(gc *gin.Context) {
|
||||
app.debug.Printf("%s: New user failed: Telegram PIN was invalid", req.Code)
|
||||
respond(401, "errorInvalidPIN", gc)
|
||||
}
|
||||
success = false
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if emailEnabled && app.config.Section("email_confirmation").Key("enabled").MustBool(false) && !confirmed {
|
||||
claims := jwt.MapClaims{
|
||||
"valid": true,
|
||||
"invite": req.Code,
|
||||
"email": req.Email,
|
||||
"username": req.Username,
|
||||
"password": req.Password,
|
||||
"exp": strconv.FormatInt(time.Now().Add(time.Hour*12).Unix(), 10),
|
||||
"type": "confirmation",
|
||||
"valid": true,
|
||||
"invite": req.Code,
|
||||
"email": req.Email,
|
||||
"username": req.Username,
|
||||
"password": req.Password,
|
||||
"telegramPIN": req.TelegramPIN,
|
||||
"exp": strconv.FormatInt(time.Now().Add(time.Hour*12).Unix(), 10),
|
||||
"type": "confirmation",
|
||||
}
|
||||
tk := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
key, err := tk.SignedString([]byte(os.Getenv("JFA_SECRET")))
|
||||
@ -450,6 +479,27 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
||||
app.err.Printf("Failed to store user duration: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if app.config.Section("telegram").Key("enabled").MustBool(false) && telegramTokenIndex != -1 {
|
||||
tgToken := app.telegram.verifiedTokens[telegramTokenIndex]
|
||||
tgUser := TelegramUser{
|
||||
ChatID: tgToken.ChatID,
|
||||
Username: tgToken.Username,
|
||||
Contact: req.TelegramContact,
|
||||
}
|
||||
if lang, ok := app.telegram.languages[tgToken.ChatID]; ok {
|
||||
tgUser.Lang = lang
|
||||
}
|
||||
app.storage.telegram[user.ID] = tgUser
|
||||
err := app.storage.storeTelegramUsers()
|
||||
if err != nil {
|
||||
app.err.Printf("Failed to store Telegram users: %v", err)
|
||||
} else {
|
||||
app.telegram.verifiedTokens[len(app.telegram.verifiedTokens)-1], app.telegram.verifiedTokens[telegramTokenIndex] = app.telegram.verifiedTokens[telegramTokenIndex], app.telegram.verifiedTokens[len(app.telegram.verifiedTokens)-1]
|
||||
app.telegram.verifiedTokens = app.telegram.verifiedTokens[:len(app.telegram.verifiedTokens)-1]
|
||||
}
|
||||
}
|
||||
|
||||
if emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "" {
|
||||
app.debug.Printf("%s: Sending welcome email to %s", req.Username, req.Email)
|
||||
msg, err := app.email.constructWelcome(req.Username, expiry, app, false)
|
||||
@ -1891,16 +1941,16 @@ func (app *appContext) TelegramVerified(gc *gin.Context) {
|
||||
pin := gc.Param("pin")
|
||||
tokenIndex := -1
|
||||
for i, v := range app.telegram.verifiedTokens {
|
||||
if v == pin {
|
||||
if v.Token == pin {
|
||||
tokenIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if tokenIndex != -1 {
|
||||
length := len(app.telegram.verifiedTokens)
|
||||
app.telegram.verifiedTokens[length-1], app.telegram.verifiedTokens[tokenIndex] = app.telegram.verifiedTokens[tokenIndex], app.telegram.verifiedTokens[length-1]
|
||||
app.telegram.verifiedTokens = app.telegram.verifiedTokens[:length-1]
|
||||
}
|
||||
// if tokenIndex != -1 {
|
||||
// length := len(app.telegram.verifiedTokens)
|
||||
// app.telegram.verifiedTokens[length-1], app.telegram.verifiedTokens[tokenIndex] = app.telegram.verifiedTokens[tokenIndex], app.telegram.verifiedTokens[length-1]
|
||||
// app.telegram.verifiedTokens = app.telegram.verifiedTokens[:length-1]
|
||||
// }
|
||||
respondBool(200, tokenIndex != -1, gc)
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
||||
</span>
|
||||
@{{ .telegramUsername }}
|
||||
</a>
|
||||
<span class="button ~info !normal full-width center mt-1" id="telegram-waiting">.</span>
|
||||
<span class="button ~info !normal full-width center mt-1" id="telegram-waiting">{{ .strings.success }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
@ -68,7 +68,15 @@
|
||||
<label class="label supra" for="create-email">{{ .strings.emailAddress }}</label>
|
||||
<input type="email" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.emailAddress }}" id="create-email" aria-label="{{ .strings.emailAddress }}" value="{{ .email }}">
|
||||
{{ if .telegramEnabled }}
|
||||
<span class="button ~info !normal full-width center" id="link-telegram">{{ .strings.linkTelegram }}</span>
|
||||
<span class="button ~info !normal full-width center mb-1" id="link-telegram">{{ .strings.linkTelegram }}</span>
|
||||
<div id="contact-via" class="unfocused">
|
||||
<label class="row switch pb-1">
|
||||
<input type="radio" name="contact-via" value="email"><span>Contact through Email</span>
|
||||
</label>
|
||||
<label class="row switch pb-1">
|
||||
<input type="radio" name="contact-via" value="telegram" id="contact-via-telegram"><span>Contact through Telegram</span>
|
||||
</label>
|
||||
</div>
|
||||
{{ end }}
|
||||
<label class="label supra" for="create-password">{{ .strings.password }}</label>
|
||||
<input type="password" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.password }}" id="create-password" aria-label="{{ .strings.password }}">
|
||||
|
@ -19,11 +19,15 @@
|
||||
"confirmationRequiredMessage": "Please check your email inbox to verify your address.",
|
||||
"yourAccountIsValidUntil": "Your account will be valid until {date}.",
|
||||
"linkTelegram": "Link Telegram",
|
||||
"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.",
|
||||
"contactEmail": "Contact through Email",
|
||||
"contactTelegram": "Contact through Telegram"
|
||||
},
|
||||
"notifications": {
|
||||
"errorUserExists": "User already exists.",
|
||||
"errorInvalidCode": "Invalid invite code.",
|
||||
"errorTelegramVerification": "Telegram verification required.",
|
||||
"errorInvalidPIN": "Telegram PIN is invalid.",
|
||||
"telegramVerified": "Telegram account verified."
|
||||
},
|
||||
"validationStrings": {
|
||||
|
10
models.go
10
models.go
@ -11,10 +11,12 @@ type boolResponse struct {
|
||||
}
|
||||
|
||||
type newUserDTO struct {
|
||||
Username string `json:"username" example:"jeff" binding:"required"` // User's username
|
||||
Password string `json:"password" example:"guest" binding:"required"` // User's password
|
||||
Email string `json:"email" example:"jeff@jellyf.in"` // User's email address
|
||||
Code string `json:"code" example:"abc0933jncjkcjj"` // Invite code (required on /newUser)
|
||||
Username string `json:"username" example:"jeff" binding:"required"` // User's username
|
||||
Password string `json:"password" example:"guest" binding:"required"` // User's password
|
||||
Email string `json:"email" example:"jeff@jellyf.in"` // User's email address
|
||||
Code string `json:"code" example:"abc0933jncjkcjj"` // Invite code (required on /newUser)
|
||||
TelegramPIN string `json:"telegram_pin" example:"A1-B2-3C"` // Telegram verification PIN (if used)
|
||||
TelegramContact bool `json:"telegram_contact"` // Whether or not to use telegram for notifications/pwrs
|
||||
}
|
||||
|
||||
type newUserResponse struct {
|
||||
|
@ -22,7 +22,7 @@ type Storage struct {
|
||||
profiles map[string]Profile
|
||||
defaultProfile string
|
||||
emails, displayprefs, ombi_template map[string]interface{}
|
||||
telegram map[int64]TelegramUser
|
||||
telegram map[string]TelegramUser // Map of Jellyfin User IDs to telegram users.
|
||||
customEmails customEmails
|
||||
policy mediabrowser.Policy
|
||||
configuration mediabrowser.Configuration
|
||||
@ -34,6 +34,7 @@ type TelegramUser struct {
|
||||
ChatID int64
|
||||
Username string
|
||||
Lang string
|
||||
Contact bool // Whether to contact through telegram or not
|
||||
}
|
||||
|
||||
type customEmails struct {
|
||||
|
69
telegram.go
69
telegram.go
@ -10,13 +10,20 @@ import (
|
||||
tg "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||
)
|
||||
|
||||
type VerifiedToken struct {
|
||||
Token string
|
||||
ChatID int64
|
||||
Username string
|
||||
}
|
||||
|
||||
type TelegramDaemon struct {
|
||||
Stopped bool
|
||||
ShutdownChannel chan string
|
||||
bot *tg.BotAPI
|
||||
username string
|
||||
tokens []string
|
||||
verifiedTokens []string
|
||||
verifiedTokens []VerifiedToken
|
||||
languages map[int64]string // Store of languages for chatIDs. Added to on first interaction, and loaded from app.storage.telegram on start.
|
||||
link string
|
||||
app *appContext
|
||||
}
|
||||
@ -30,16 +37,23 @@ func newTelegramDaemon(app *appContext) (*TelegramDaemon, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &TelegramDaemon{
|
||||
td := &TelegramDaemon{
|
||||
Stopped: false,
|
||||
ShutdownChannel: make(chan string),
|
||||
bot: bot,
|
||||
username: bot.Self.UserName,
|
||||
tokens: []string{},
|
||||
verifiedTokens: []string{},
|
||||
verifiedTokens: []VerifiedToken{},
|
||||
languages: map[int64]string{},
|
||||
link: "https://t.me/" + bot.Self.UserName,
|
||||
app: app,
|
||||
}, nil
|
||||
}
|
||||
for _, user := range app.storage.telegram {
|
||||
if user.Lang != "" {
|
||||
td.languages[user.ChatID] = user.Lang
|
||||
}
|
||||
}
|
||||
return td, nil
|
||||
}
|
||||
|
||||
var runes = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
@ -80,28 +94,21 @@ func (t *TelegramDaemon) run() {
|
||||
continue
|
||||
}
|
||||
lang := t.app.storage.lang.chosenTelegramLang
|
||||
user, ok := t.app.storage.telegram[upd.Message.Chat.ID]
|
||||
storedLang, ok := t.languages[upd.Message.Chat.ID]
|
||||
if !ok {
|
||||
user := TelegramUser{
|
||||
Username: upd.Message.Chat.UserName,
|
||||
ChatID: upd.Message.Chat.ID,
|
||||
Lang: "",
|
||||
}
|
||||
t.app.storage.telegram[upd.Message.Chat.ID] = user
|
||||
err := t.app.storage.storeTelegramUsers()
|
||||
if err != nil {
|
||||
t.app.err.Printf("Failed to store Telegram users: %v", err)
|
||||
}
|
||||
}
|
||||
if user.Lang != "" {
|
||||
lang = user.Lang
|
||||
} else {
|
||||
found := false
|
||||
for code := range t.app.storage.lang.Telegram {
|
||||
if code[:2] == upd.Message.From.LanguageCode {
|
||||
lang = code
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
t.languages[upd.Message.Chat.ID] = lang
|
||||
}
|
||||
} else {
|
||||
lang = storedLang
|
||||
}
|
||||
switch msg := sects[0]; msg {
|
||||
case "/start":
|
||||
@ -125,11 +132,17 @@ func (t *TelegramDaemon) run() {
|
||||
continue
|
||||
}
|
||||
if _, ok := t.app.storage.lang.Telegram[sects[1]]; ok {
|
||||
user.Lang = sects[1]
|
||||
t.app.storage.telegram[upd.Message.Chat.ID] = user
|
||||
err := t.app.storage.storeTelegramUsers()
|
||||
if err != nil {
|
||||
t.app.err.Printf("Failed to store Telegram users: %v", err)
|
||||
t.languages[upd.Message.Chat.ID] = sects[1]
|
||||
for jfID, user := range t.app.storage.telegram {
|
||||
if user.ChatID == upd.Message.Chat.ID {
|
||||
user.Lang = sects[1]
|
||||
t.app.storage.telegram[jfID] = user
|
||||
err := t.app.storage.storeTelegramUsers()
|
||||
if err != nil {
|
||||
t.app.err.Printf("Failed to store Telegram users: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
@ -148,11 +161,15 @@ func (t *TelegramDaemon) run() {
|
||||
}
|
||||
continue
|
||||
}
|
||||
err := t.QuoteReply(&upd, t.app.storage.lang.Telegram[lang].Strings.get("success"))
|
||||
err := t.QuoteReply(&upd, t.app.storage.lang.Telegram[lang].Strings.get("pinSuccess"))
|
||||
if err != nil {
|
||||
t.app.err.Printf("Telegram: Failed to send message to \"%s\": %v", upd.Message.From.UserName, err)
|
||||
}
|
||||
t.verifiedTokens = append(t.verifiedTokens, upd.Message.Text)
|
||||
t.verifiedTokens = append(t.verifiedTokens, VerifiedToken{
|
||||
Token: upd.Message.Text,
|
||||
ChatID: upd.Message.Chat.ID,
|
||||
Username: upd.Message.Chat.UserName,
|
||||
})
|
||||
t.tokens[len(t.tokens)-1], t.tokens[tokenIndex] = t.tokens[tokenIndex], t.tokens[len(t.tokens)-1]
|
||||
t.tokens = t.tokens[:len(t.tokens)-1]
|
||||
}
|
||||
|
34
ts/form.ts
34
ts/form.ts
@ -1,5 +1,5 @@
|
||||
import { Modal } from "./modules/modal.js";
|
||||
import { notificationBox } from "./modules/common.js";
|
||||
import { notificationBox, whichAnimationEvent } from "./modules/common.js";
|
||||
import { _get, _post, toggleLoader, toDateString } from "./modules/common.js";
|
||||
import { loadLangSelector } from "./modules/lang.js";
|
||||
|
||||
@ -41,26 +41,39 @@ loadLangSelector("form");
|
||||
|
||||
window.notifications = new notificationBox(document.getElementById("notification-box") as HTMLDivElement);
|
||||
|
||||
window.animationEvent = whichAnimationEvent();
|
||||
|
||||
window.successModal = new Modal(document.getElementById("modal-success"), true);
|
||||
|
||||
var telegramVerified = false;
|
||||
if (window.telegramEnabled) {
|
||||
window.telegramModal = new Modal(document.getElementById("modal-telegram"), window.telegramRequired);
|
||||
(document.getElementById("link-telegram") as HTMLSpanElement).onclick = () => {
|
||||
const telegramButton = document.getElementById("link-telegram") as HTMLSpanElement;
|
||||
telegramButton.onclick = () => {
|
||||
const waiting = document.getElementById("telegram-waiting") as HTMLSpanElement;
|
||||
toggleLoader(waiting);
|
||||
window.telegramModal.show();
|
||||
let modalClosed = false;
|
||||
window.telegramModal.onclose = () => { modalClosed = true; }
|
||||
const checkVerified = () => _get("/invite/" + window.code + "/telegram/verified/" + window.telegramPIN, null, (req: XMLHttpRequest) => {
|
||||
if (req.readyState == 4) {
|
||||
if (req.status == 401) {
|
||||
window.telegramModal.close();
|
||||
window.notifications.customError("invalidCodeError", window.lang.notif("errorInvalidCode"));
|
||||
window.notifications.customError("invalidCodeError", window.messages["errorInvalidCode"]);
|
||||
return;
|
||||
} else if (req.status == 200) {
|
||||
if (req.response["success"] as boolean) {
|
||||
telegramVerified = true;
|
||||
toggleLoader(waiting);
|
||||
window.telegramModal.close()
|
||||
window.notifications.customSuccess("accountVerified", window.lang.notif("telegramVerified"))
|
||||
} else {
|
||||
waiting.classList.add("~positive");
|
||||
waiting.classList.remove("~info");
|
||||
window.notifications.customPositive("telegramVerified", "", window.messages["telegramVerified"]);
|
||||
setTimeout(window.telegramModal.close, 2000);
|
||||
telegramButton.classList.add("unfocused");
|
||||
document.getElementById("contact-via").classList.remove("unfocused");
|
||||
const radio = document.getElementById("contact-via-telegram") as HTMLInputElement;
|
||||
radio.checked = true;
|
||||
} else if (!modalClosed) {
|
||||
setTimeout(checkVerified, 1500);
|
||||
}
|
||||
}
|
||||
@ -145,6 +158,8 @@ interface sendDTO {
|
||||
email: string;
|
||||
username: string;
|
||||
password: string;
|
||||
telegram_pin?: string;
|
||||
telegram_contact?: boolean;
|
||||
}
|
||||
|
||||
const create = (event: SubmitEvent) => {
|
||||
@ -156,6 +171,13 @@ const create = (event: SubmitEvent) => {
|
||||
email: emailField.value,
|
||||
password: passwordField.value
|
||||
};
|
||||
if (telegramVerified) {
|
||||
send.telegram_pin = window.telegramPIN;
|
||||
const radio = document.getElementById("contact-via-telegram") as HTMLInputElement;
|
||||
if (radio.checked) {
|
||||
send.telegram_contact = true;
|
||||
}
|
||||
}
|
||||
_post("/newUser", send, (req: XMLHttpRequest) => {
|
||||
if (req.readyState == 4) {
|
||||
let vals = req.response as respDTO;
|
||||
|
Loading…
Reference in New Issue
Block a user