diff --git a/.goreleaser.yml b/.goreleaser.yml
index 88c2404..b15cb59 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -26,11 +26,11 @@ before:
- cp -r ts tempts
- scripts/dark-variant.sh tempts
- scripts/dark-variant.sh tempts/modules
- - npx esbuild --bundle tempts/admin.ts --outfile=./data/web/js/admin.js --minify
- - npx esbuild --bundle tempts/pwr.ts --outfile=./data/web/js/pwr.js --minify
- - npx esbuild --bundle tempts/form.ts --outfile=./data/web/js/form.js --minify
- - npx esbuild --bundle tempts/setup.ts --outfile=./data/web/js/setup.js --minify
- - npx esbuild --bundle tempts/crash.ts --outfile=./data/crash.js --minify
+ - npx esbuild --target=es6 --format=esm --bundle tempts/admin.ts --outfile=./data/web/js/admin.js --minify
+ - npx esbuild --target=es6 --format=esm --bundle tempts/pwr.ts --outfile=./data/web/js/pwr.js --minify
+ - npx esbuild --target=es6 --format=esm --bundle tempts/form.ts --outfile=./data/web/js/form.js --minify
+ - npx esbuild --target=es6 --format=esm --bundle tempts/setup.ts --outfile=./data/web/js/setup.js --minify
+ - npx esbuild --target=es6 --format=esm --bundle tempts/crash.ts --outfile=./data/crash.js --minify
- rm -r tempts
- npx esbuild --bundle css/base.css --outfile=./data/web/css/bundle.css --external:remixicon.css --minify
- cp html/crash.html data/
diff --git a/api-userpage.go b/api-userpage.go
index c771b73..8c3f288 100644
--- a/api-userpage.go
+++ b/api-userpage.go
@@ -329,8 +329,58 @@ func (app *appContext) MyDiscordVerifiedInvite(gc *gin.Context) {
}
}
dc := app.storage.discord
+ existingUser, ok := app.storage.discord[gc.GetString("jfId")]
+ if ok {
+ dcUser.Lang = existingUser.Lang
+ dcUser.Contact = existingUser.Contact
+ }
dc[gc.GetString("jfId")] = dcUser
app.storage.discord = dc
app.storage.storeDiscordUsers()
respondBool(200, true, gc)
}
+
+// @Summary Returns true/false on whether or not your telegram PIN was verified, and assigns the telegram user to you.
+// @Produce json
+// @Success 200 {object} boolResponse
+// @Failure 401 {object} boolResponse
+// @Param pin path string true "PIN code to check"
+// @Router /my/telegram/verified/{pin} [get]
+// @tags User Page
+func (app *appContext) MyTelegramVerifiedInvite(gc *gin.Context) {
+ pin := gc.Param("pin")
+ tokenIndex := -1
+ for i, v := range app.telegram.verifiedTokens {
+ if v.Token == pin {
+ tokenIndex = i
+ break
+ }
+ }
+ if tokenIndex == -1 {
+ respondBool(200, false, gc)
+ return
+ }
+ if app.config.Section("telegram").Key("require_unique").MustBool(false) {
+ for _, u := range app.storage.telegram {
+ if app.telegram.verifiedTokens[tokenIndex].Username == u.Username {
+ respondBool(400, false, gc)
+ return
+ }
+ }
+ }
+ tgUser := TelegramUser{
+ ChatID: app.telegram.verifiedTokens[tokenIndex].ChatID,
+ Username: app.telegram.verifiedTokens[tokenIndex].Username,
+ Contact: true,
+ }
+
+ tg := app.storage.telegram
+ existingUser, ok := app.storage.telegram[gc.GetString("jfId")]
+ if ok {
+ tgUser.Lang = existingUser.Lang
+ tgUser.Contact = existingUser.Contact
+ }
+ tg[gc.GetString("jfId")] = tgUser
+ app.storage.storeTelegramUsers()
+ respondBool(200, true, gc)
+}
diff --git a/html/account-linking.html b/html/account-linking.html
index 0fcaf66..e38f013 100644
--- a/html/account-linking.html
+++ b/html/account-linking.html
@@ -5,7 +5,10 @@
{{ .discordSendPINMessage }}
{{ .strings.success }}
@@ -16,7 +19,7 @@
{{ .strings.linkTelegram }}
{{ .strings.sendPIN }}
-
{{ .telegramPIN }}
+
diff --git a/html/user.html b/html/user.html
index 5135c2e..609f7c0 100644
--- a/html/user.html
+++ b/html/user.html
@@ -10,6 +10,8 @@
window.language = "{{ .langName }}";
window.telegramEnabled = {{ .telegramEnabled }};
window.telegramRequired = {{ .telegramRequired }};
+ window.telegramUsername = {{ .telegramUsername }};
+ window.telegramURL = {{ .telegramURL }};
window.emailEnabled = {{ .emailEnabled }};
window.emailRequired = {{ .emailRequired }};
window.discordEnabled = {{ .discordEnabled }};
diff --git a/router.go b/router.go
index 80bec1a..b1b5b95 100644
--- a/router.go
+++ b/router.go
@@ -234,6 +234,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
user.GET(p+"/discord/invite", app.MyDiscordServerInvite)
user.GET(p+"/pin/:service", app.GetMyPIN)
user.GET(p+"/discord/verified/:pin", app.MyDiscordVerifiedInvite)
+ user.GET(p+"/telegram/verified/:pin", app.MyTelegramVerifiedInvite)
}
}
}
diff --git a/ts/form.ts b/ts/form.ts
index 01b8d87..59f4de2 100644
--- a/ts/form.ts
+++ b/ts/form.ts
@@ -3,7 +3,7 @@ import { notificationBox, whichAnimationEvent } from "./modules/common.js";
import { _get, _post, toggleLoader, addLoader, removeLoader, toDateString } from "./modules/common.js";
import { loadLangSelector } from "./modules/lang.js";
import { initValidator } from "./modules/validator.js";
-import { Discord, DiscordConfiguration } from "./modules/account-linking.js";
+import { Discord, Telegram, ServiceConfiguration } from "./modules/account-linking.js";
interface formWindow extends Window {
invalidPassword: string;
@@ -50,46 +50,31 @@ var telegramVerified = false;
if (window.telegramEnabled) {
window.telegramModal = new Modal(document.getElementById("modal-telegram"), window.telegramRequired);
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;
- toggleLoader(waiting);
+
+ const telegramConf: ServiceConfiguration = {
+ modal: window.telegramModal as Modal,
+ pin: window.telegramPIN,
+ pinURL: "",
+ verifiedURL: "/invite/" + window.code + "/telegram/verified/",
+ invalidCodeError: window.messages["errorInvalidCode"],
+ accountLinkedError: window.messages["errorAccountLinked"],
+ successError: window.messages["verified"],
+ successFunc: (modalClosed: boolean) => {
+ if (modalClosed) return;
+ telegramVerified = true;
+ telegramButton.classList.add("unfocused");
+ document.getElementById("contact-via").classList.remove("unfocused");
+ document.getElementById("contact-via-email").parentElement.classList.remove("unfocused");
+ const radio = document.getElementById("contact-via-telegram") as HTMLInputElement;
+ radio.parentElement.classList.remove("unfocused");
+ radio.checked = true;
+ validatorFunc();
}
- 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.messages["errorInvalidCode"]);
- return;
- } else if (req.status == 400) {
- window.telegramModal.close();
- window.notifications.customError("accountLinkedError", window.messages["errorAccountLinked"]);
- } else if (req.status == 200) {
- if (req.response["success"] as boolean) {
- telegramVerified = true;
- waiting.classList.add("~positive");
- waiting.classList.remove("~info");
- window.notifications.customPositive("telegramVerified", "", window.messages["verified"]);
- setTimeout(window.telegramModal.close, 2000);
- telegramButton.classList.add("unfocused");
- document.getElementById("contact-via").classList.remove("unfocused");
- document.getElementById("contact-via-email").parentElement.classList.remove("unfocused");
- const radio = document.getElementById("contact-via-telegram") as HTMLInputElement;
- radio.parentElement.classList.remove("unfocused");
- radio.checked = true;
- validatorFunc();
- } else if (!modalClosed) {
- setTimeout(checkVerified, 1500);
- }
- }
- }
- });
- checkVerified();
};
+
+ const telegram = new Telegram(telegramConf);
+
+ telegramButton.onclick = () => { telegram.onclick(); };
}
var discordVerified = false;
@@ -97,7 +82,7 @@ if (window.discordEnabled) {
window.discordModal = new Modal(document.getElementById("modal-discord"), window.discordRequired);
const discordButton = document.getElementById("link-discord") as HTMLSpanElement;
- const discordConf: DiscordConfiguration = {
+ const discordConf: ServiceConfiguration = {
modal: window.discordModal as Modal,
pin: window.discordPIN,
inviteURL: window.discordInviteLink ? ("/invite/" + window.code + "/discord/invite") : "",
@@ -107,21 +92,21 @@ if (window.discordEnabled) {
accountLinkedError: window.messages["errorAccountLinked"],
successError: window.messages["verified"],
successFunc: (modalClosed: boolean) => {
- if (!modalClosed) {
- discordButton.classList.add("unfocused");
- document.getElementById("contact-via").classList.remove("unfocused");
- document.getElementById("contact-via-email").parentElement.classList.remove("unfocused");
- const radio = document.getElementById("contact-via-discord") as HTMLInputElement;
- radio.parentElement.classList.remove("unfocused")
- radio.checked = true;
- validatorFunc();
- }
+ if (modalClosed) return;
+ discordVerified = true;
+ discordButton.classList.add("unfocused");
+ document.getElementById("contact-via").classList.remove("unfocused");
+ document.getElementById("contact-via-email").parentElement.classList.remove("unfocused");
+ const radio = document.getElementById("contact-via-discord") as HTMLInputElement;
+ radio.parentElement.classList.remove("unfocused")
+ radio.checked = true;
+ validatorFunc();
}
};
const discord = new Discord(discordConf);
- discordButton.onclick = discord.onclick;
+ discordButton.onclick = () => { discord.onclick(); };
}
var matrixVerified = false;
diff --git a/ts/modules/account-linking.ts b/ts/modules/account-linking.ts
index 5902732..61bed1e 100644
--- a/ts/modules/account-linking.ts
+++ b/ts/modules/account-linking.ts
@@ -35,10 +35,10 @@ interface formWindow extends Window {
declare var window: formWindow;
-export interface DiscordConfiguration {
+export interface ServiceConfiguration {
modal: Modal;
pin: string;
- inviteURL: string;
+ inviteURL?: string;
pinURL: string;
verifiedURL: string;
invalidCodeError: string;
@@ -52,16 +52,17 @@ export interface DiscordInvite {
icon: string;
}
-export class Discord {
- private _conf: DiscordConfiguration;
- private _pinAcquired = false;
- private _modalClosed = false;
- private _waiting = document.getElementById("discord-waiting") as HTMLSpanElement;
- private _verified = false;
+export class ServiceLinker {
+ protected _conf: ServiceConfiguration;
+ protected _pinAcquired = false;
+ protected _modalClosed = false;
+ protected _waiting: HTMLSpanElement;
+ protected _verified = false;
+ protected _name: string;
get verified(): boolean { return this._verified; }
- constructor(conf: DiscordConfiguration) {
+ constructor(conf: ServiceConfiguration) {
this._conf = conf;
this._conf.modal.onclose = () => {
this._modalClosed = true;
@@ -69,24 +70,7 @@ export class Discord {
};
}
- private _getInviteURL = () => _get(this._conf.inviteURL, null, (req: XMLHttpRequest) => {
- if (req.readyState != 4) return;
- const inv = req.response as DiscordInvite;
- const link = document.getElementById("discord-invite") as HTMLAnchorElement;
- link.href = inv.invite;
- link.target = "_blank";
- let innerHTML = `${window.lang.strings("joinTheServer")}`;
- if (inv.icon != "") {
- innerHTML += `${window.discordServerName}`;
- } else {
- innerHTML += `
- ${window.discordServerName}
- `;
- }
- link.innerHTML = innerHTML;
- });
-
- private _checkVerified = () => {
+ protected _checkVerified = () => {
if (this._modalClosed) return;
if (!this._pinAcquired) {
setTimeout(this._checkVerified, 1500);
@@ -105,7 +89,7 @@ export class Discord {
this._verified = true;
this._waiting.classList.add("~positive");
this._waiting.classList.remove("~info");
- window.notifications.customPositive("discordVerified", "", this._conf.successError);
+ window.notifications.customPositive(this._name + "Verified", "", this._conf.successError);
if (this._conf.successFunc) {
this._conf.successFunc(false);
}
@@ -123,18 +107,14 @@ export class Discord {
});
};
- onclick = () => {
- if (this._conf.inviteURL != "") {
- this._getInviteURL();
- }
-
+ onclick() {
toggleLoader(this._waiting);
this._pinAcquired = false;
if (this._conf.pin) {
this._pinAcquired = true;
this._conf.modal.modal.querySelector(".pin").textContent = this._conf.pin;
- } else {
+ } else if (this._conf.pinURL) {
_get(this._conf.pinURL, null, (req: XMLHttpRequest) => {
if (req.readyState == 4 && req.status == 200) {
this._conf.pin = req.response["pin"];
@@ -150,3 +130,45 @@ export class Discord {
this._checkVerified();
}
}
+
+export class Discord extends ServiceLinker {
+
+ constructor(conf: ServiceConfiguration) {
+ super(conf);
+ this._name = "discord";
+ this._waiting = document.getElementById("discord-waiting") as HTMLSpanElement;
+ }
+
+ private _getInviteURL = () => _get(this._conf.inviteURL, null, (req: XMLHttpRequest) => {
+ if (req.readyState != 4) return;
+ const inv = req.response as DiscordInvite;
+ const link = document.getElementById("discord-invite") as HTMLSpanElement;
+ (link.parentElement as HTMLAnchorElement).href = inv.invite;
+ (link.parentElement as HTMLAnchorElement).target = "_blank";
+ let innerHTML = ``;
+ if (inv.icon != "") {
+ innerHTML += `${window.discordServerName}`;
+ } else {
+ innerHTML += `
+ ${window.discordServerName}
+ `;
+ }
+ link.innerHTML = innerHTML;
+ });
+
+ onclick() {
+ if (this._conf.inviteURL != "") {
+ this._getInviteURL();
+ }
+
+ super.onclick();
+ }
+}
+
+export class Telegram extends ServiceLinker {
+ constructor(conf: ServiceConfiguration) {
+ super(conf);
+ this._name = "telegram";
+ this._waiting = document.getElementById("telegram-waiting") as HTMLSpanElement;
+ }
+};
diff --git a/ts/tsconfig.json b/ts/tsconfig.json
index 4bdf020..939f873 100644
--- a/ts/tsconfig.json
+++ b/ts/tsconfig.json
@@ -1,10 +1,10 @@
{
"compilerOptions": {
"outDir": "../js",
- "target": "es6",
+ "target": "es2017",
"lib": ["dom", "es2017"],
"typeRoots": ["./typings", "../node_modules/@types"],
- "moduleResolution": "node",
+ "moduleResolution": "nodenext",
"esModuleInterop": true
}
}
diff --git a/ts/user.ts b/ts/user.ts
index 340f7e5..bf621c1 100644
--- a/ts/user.ts
+++ b/ts/user.ts
@@ -3,7 +3,7 @@ import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
import { Modal } from "./modules/modal.js";
import { _get, _post, notificationBox, whichAnimationEvent, toDateString, toggleLoader } from "./modules/common.js";
import { Login } from "./modules/login.js";
-import { Discord, DiscordConfiguration } from "./modules/account-linking.js";
+import { Discord, Telegram, ServiceConfiguration } from "./modules/account-linking.js";
interface userWindow extends Window {
jellyfinID: string;
@@ -285,7 +285,7 @@ const addEditEmail = (add: boolean): void => {
window.modals.email.show();
}
-const discordConf: DiscordConfiguration = {
+const discordConf: ServiceConfiguration = {
modal: window.modals.discord as Modal,
pin: "",
inviteURL: window.discordInviteLink ? "/my/discord/invite" : "",
@@ -301,6 +301,21 @@ const discordConf: DiscordConfiguration = {
let discord = new Discord(discordConf);
+const telegramConf: ServiceConfiguration = {
+ modal: window.modals.telegram as Modal,
+ pin: "",
+ pinURL: "/my/pin/telegram",
+ verifiedURL: "/my/telegram/verified/",
+ invalidCodeError: window.lang.notif("errorInvalidCode"),
+ accountLinkedError: window.lang.notif("errorAccountLinked"),
+ successError: window.lang.notif("verified"),
+ successFunc: (modalClosed: boolean) => {
+ if (modalClosed) window.location.reload();
+ }
+};
+
+let telegram = new Telegram(telegramConf);
+
document.addEventListener("details-reload", () => {
_get("/my/details", null, (req: XMLHttpRequest) => {
if (req.readyState == 4) {
@@ -325,10 +340,13 @@ document.addEventListener("details-reload", () => {
contactMethodList.clear();
+ // Note the weird format of the functions for discord/telegram:
+ // "this" was being redefined within the onclick() method, so
+ // they had to be wrapped in an anonymous function.
const contactMethods: { name: string, icon: string, f: (add: boolean) => void }[] = [
{name: "email", icon: ``, f: addEditEmail},
- {name: "discord", icon: ``, f: discord.onclick},
- {name: "telegram", icon: ``, f: null},
+ {name: "discord", icon: ``, f: (add: boolean) => { discord.onclick(); }},
+ {name: "telegram", icon: ``, f: (add: boolean) => { telegram.onclick() }},
{name: "matrix", icon: `[m]`, f: null}
];
diff --git a/views.go b/views.go
index ebb855b..055ca88 100644
--- a/views.go
+++ b/views.go
@@ -185,7 +185,7 @@ func (app *appContext) MyUserPage(gc *gin.Context) {
"langName": lang,
}
if telegramEnabled {
- data["telegramUser"] = app.telegram.username
+ data["telegramUsername"] = app.telegram.username
data["telegramURL"] = app.telegram.link
data["telegramRequired"] = app.config.Section("telegram").Key("required").MustBool(false)
}