diff --git a/api.go b/api.go
index ee09b09..4a00fbb 100644
--- a/api.go
+++ b/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)
}
diff --git a/html/form.html b/html/form.html
index 7f14330..df3ee14 100644
--- a/html/form.html
+++ b/html/form.html
@@ -33,7 +33,7 @@
@{{ .telegramUsername }}
- .
+ {{ .strings.success }}
{{ end }}
@@ -68,7 +68,15 @@
{{ if .telegramEnabled }}
- {{ .strings.linkTelegram }}
+ {{ .strings.linkTelegram }}
+
+
+
+
{{ end }}
diff --git a/lang/form/en-us.json b/lang/form/en-us.json
index c6411e6..36b1089 100644
--- a/lang/form/en-us.json
+++ b/lang/form/en-us.json
@@ -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": {
diff --git a/models.go b/models.go
index 0dd9be3..28a2241 100644
--- a/models.go
+++ b/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 {
diff --git a/storage.go b/storage.go
index 93dcc1a..0fe7333 100644
--- a/storage.go
+++ b/storage.go
@@ -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 {
diff --git a/telegram.go b/telegram.go
index 5d06e93..f8655f0 100644
--- a/telegram.go
+++ b/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]
}
diff --git a/ts/form.ts b/ts/form.ts
index c6a1adb..5e3a019 100644
--- a/ts/form.ts
+++ b/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;