From ae7d5810437f15a22eb7636d13578adcacdf8f53 Mon Sep 17 00:00:00 2001 From: Veysinator Date: Wed, 26 May 2021 13:31:04 +0300 Subject: [PATCH 1/4] Added Turkish language and formatted code using Prettier --- AccountSwitcher.plugin.js | 1207 +++++++++++++++++++++++-------------- translations.json | 728 +++++++++++----------- 2 files changed, 1134 insertions(+), 801 deletions(-) diff --git a/AccountSwitcher.plugin.js b/AccountSwitcher.plugin.js index dd5b812..237ec85 100755 --- a/AccountSwitcher.plugin.js +++ b/AccountSwitcher.plugin.js @@ -1,11 +1,11 @@ /** -* @name AccountSwitcher -* @displayName AccountSwitcher -* @source https://github.com/l0c4lh057/AccountSwitcher/blob/master/AccountSwitcher.plugin.js -* @patreon https://www.patreon.com/l0c4lh057 -* @authorId 226677096091484160 -* @invite YzzeuJPpyj -*/ + * @name AccountSwitcher + * @displayName AccountSwitcher + * @source https://github.com/l0c4lh057/AccountSwitcher/blob/master/AccountSwitcher.plugin.js + * @patreon https://www.patreon.com/l0c4lh057 + * @authorId 226677096091484160 + * @invite YzzeuJPpyj + */ /*@cc_on @if (@_jscript) @@ -39,153 +39,246 @@ module.exports = (() => { name: "l0c4lh057", discord_id: "226677096091484160", github_username: "l0c4lh057", - twitter_username: "l0c4lh057" - } + twitter_username: "l0c4lh057", + }, ], version: "1.3.2", - description: "Simply switch between accounts with the ease of pressing a single key.", + description: + "Simply switch between accounts with the ease of pressing a single key.", github: "https://github.com/l0c4lh057/AccountSwitcher", - github_raw: "https://raw.githubusercontent.com/l0c4lh057/AccountSwitcher/master/AccountSwitcher.plugin.js" + github_raw: + "https://raw.githubusercontent.com/l0c4lh057/AccountSwitcher/master/AccountSwitcher.plugin.js", }, changelog: [ { title: "Changed", type: "progress", - items: ["Switched to a newer, even more secure encryption method."] + items: ["Switched to a newer, even more secure encryption method."], }, { title: "Fixed", type: "fixed", - items: ["Showing the keybinds in the settings again. (The toggle for encryption still does not work but that is added by ZeresPluginLibrary which I do not control.)"] - } - ] + items: [ + "Showing the keybinds in the settings again. (The toggle for encryption still does not work but that is added by ZeresPluginLibrary which I do not control.)", + ], + }, + ], }; - + let password = null; const algorithm = "aes-256-cbc"; const IV_LENGTH = 16; const Buffer = require("buffer").Buffer; - - return !global.ZeresPluginLibrary ? class { - constructor(){ this._config = config; } - getName(){ return config.info.name; } - getAuthor(){ return config.info.authors.map(a => a.name).join(", "); } - getDescription(){ return config.info.description; } - getVersion(){ return config.info.version; } - load(){ - BdApi.showConfirmationModal("Library plugin is needed", - [`The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`], { - confirmText: "Download", - cancelText: "Cancel", - onConfirm: () => { - require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (error, response, body) => { - if (error) return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js"); - await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r)); - }); - } - }); - } - start(){} - stop(){} - } : (([Plugin, Api]) => { - const plugin = (Plugin, Api) => { - const { WebpackModules, PluginUtilities, DiscordModules, Settings, Toasts, Modals, DOMTools } = Api; - const { React, ReactDOM, UserStore, UserInfoStore } = DiscordModules; - const AccountManager = WebpackModules.getByProps("loginToken"); - const Markdown = WebpackModules.getByDisplayName("Markdown"); - const unregisterKeybind = WebpackModules.getByProps("inputEventUnregister").inputEventUnregister.bind(WebpackModules.getByProps("inputEventUnregister")); - const registerKeybind = WebpackModules.getByProps("inputEventRegister").inputEventRegister.bind(WebpackModules.getByProps("inputEventUnregister")); - const crypto = require("crypto"); - - if(!BdApi.Plugins.get("BugReportHelper") && !BdApi.getData(config.info.name, "didShowIssueHelperPopup")){ - BdApi.saveData(config.info.name, "didShowIssueHelperPopup", true); - BdApi.showConfirmationModal("Do you want to download a helper plugin?", - [`Do you want to download a helper plugin that makes it easier for you to report issues? That plugin is not needed to anything else to function correctly but nice to have when reporting iissues, shortening the time until the problem gets resolved by asking you for specific information and also including additional information you did not provide.`], - { - confirmText: "Download", - cancelText: "Cancel", - onConfirm: () => { - require("request").get("https://raw.githubusercontent.com/l0c4lh057/BetterDiscordStuff/master/Plugins/BugReportHelper/BugReportHelper.plugin.js", (error, response, body) => { - if (error) return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/l0c4lh057/BetterDiscordStuff/master/Plugins/BugReportHelper/BugReportHelper.plugin.js"); - else require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "BugReportHelper.plugin.js"), body, ()=>{ - window.setTimeout(()=>BdApi.Plugins.enable("BugReportHelper"), 1000); - }); - }); - } - } - ); - } - - const KeyRecorder = class KeyRecorder extends WebpackModules.getByDisplayName("KeyRecorder") { - render() { - const ButtonOptions = WebpackModules.getByProps("ButtonLink"); - const Button = ButtonOptions.default; - const ret = super.render(); - ret.props.children.props.children.props.children.push( - React.createElement( - DiscordModules.FlexChild, - { - style: { margin: 0 } - }, - React.createElement( - Button, - { - className: WebpackModules.getByProps("editIcon", "button").button.split(" ")[1], - size: Button.Sizes.MIN, - color: ButtonOptions.ButtonColors.GREY, - look: ButtonOptions.ButtonLooks.GHOST, - onClick: this.props.onRemove - }, - "Remove" - ) - ) - ); - return ret; + + return !global.ZeresPluginLibrary + ? class { + constructor() { + this._config = config; } - }; - const KeybindModule = class KeybindModule extends DiscordModules.Keybind { - constructor(props) { - super(props); + getName() { + return config.info.name; } - render() { - const ret = super.render(); - ret.type = KeyRecorder; - ret.props.account = this.props.account; - ret.props.onRemove = this.props.onRemove; - return ret; + getAuthor() { + return config.info.authors.map((a) => a.name).join(", "); } - }; - const Keybind = class Keybind extends Settings.SettingField { - constructor(account, onChange, onRemove) { - super(account.name + " (" + account.id + ")", "", onChange, KeybindModule, { - defaultValue: (account.keybind[0] !== -1 && account.keybind.map(a => [0, a])) || [], - onChange: element => value => { - if (!Array.isArray(value)) return; - element.props.value = value; - this.onChange(value.map(a => a[1])); - }, - account, - onRemove - }); + getDescription() { + return config.info.description; } - }; - return class AccountSwitcher extends Plugin { - updateAvatars(){ - this.settings.accounts.forEach(acc => { - const u = UserStore.getUser(acc.id); - if(u) acc.avatar = u.avatarURL; - }) + getVersion() { + return config.info.version; } - - onStart(){ - password = null; - this.loadSettings(); - if(this.settings.salt === undefined){ - this.settings.salt = crypto.randomBytes(32).toString("base64"); + load() { + BdApi.showConfirmationModal( + "Library plugin is needed", + [ + `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, + ], + { + confirmText: "Download", + cancelText: "Cancel", + onConfirm: () => { + require("request").get( + "https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", + async (error, response, body) => { + if (error) + return require("electron").shell.openExternal( + "https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js" + ); + await new Promise((r) => + require("fs").writeFile( + require("path").join( + BdApi.Plugins.folder, + "0PluginLibrary.plugin.js" + ), + body, + r + ) + ); + } + ); + }, + } + ); + } + start() {} + stop() {} + } + : (([Plugin, Api]) => { + const plugin = (Plugin, Api) => { + const { + WebpackModules, + PluginUtilities, + DiscordModules, + Settings, + Toasts, + Modals, + DOMTools, + } = Api; + const { React, ReactDOM, UserStore, UserInfoStore } = DiscordModules; + const AccountManager = WebpackModules.getByProps("loginToken"); + const Markdown = WebpackModules.getByDisplayName("Markdown"); + const unregisterKeybind = WebpackModules.getByProps( + "inputEventUnregister" + ).inputEventUnregister.bind( + WebpackModules.getByProps("inputEventUnregister") + ); + const registerKeybind = WebpackModules.getByProps( + "inputEventRegister" + ).inputEventRegister.bind( + WebpackModules.getByProps("inputEventUnregister") + ); + const crypto = require("crypto"); + + if ( + !BdApi.Plugins.get("BugReportHelper") && + !BdApi.getData(config.info.name, "didShowIssueHelperPopup") + ) { + BdApi.saveData(config.info.name, "didShowIssueHelperPopup", true); + BdApi.showConfirmationModal( + "Do you want to download a helper plugin?", + [ + `Do you want to download a helper plugin that makes it easier for you to report issues? That plugin is not needed to anything else to function correctly but nice to have when reporting iissues, shortening the time until the problem gets resolved by asking you for specific information and also including additional information you did not provide.`, + ], + { + confirmText: "Download", + cancelText: "Cancel", + onConfirm: () => { + require("request").get( + "https://raw.githubusercontent.com/l0c4lh057/BetterDiscordStuff/master/Plugins/BugReportHelper/BugReportHelper.plugin.js", + (error, response, body) => { + if (error) + return require("electron").shell.openExternal( + "https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/l0c4lh057/BetterDiscordStuff/master/Plugins/BugReportHelper/BugReportHelper.plugin.js" + ); + else + require("fs").writeFile( + require("path").join( + BdApi.Plugins.folder, + "BugReportHelper.plugin.js" + ), + body, + () => { + window.setTimeout( + () => BdApi.Plugins.enable("BugReportHelper"), + 1000 + ); + } + ); + } + ); + }, + } + ); } - this.settings.accounts.forEach(acc => this.registerKeybind(acc)); - this.openMenu = this.openMenu.bind(this); - PluginUtilities.addStyle("accountswitcher-style", ` + + const KeyRecorder = class KeyRecorder extends WebpackModules.getByDisplayName( + "KeyRecorder" + ) { + render() { + const ButtonOptions = WebpackModules.getByProps("ButtonLink"); + const Button = ButtonOptions.default; + const ret = super.render(); + ret.props.children.props.children.props.children.push( + React.createElement( + DiscordModules.FlexChild, + { + style: { margin: 0 }, + }, + React.createElement( + Button, + { + className: WebpackModules.getByProps( + "editIcon", + "button" + ).button.split(" ")[1], + size: Button.Sizes.MIN, + color: ButtonOptions.ButtonColors.GREY, + look: ButtonOptions.ButtonLooks.GHOST, + onClick: this.props.onRemove, + }, + "Remove" + ) + ) + ); + return ret; + } + }; + const KeybindModule = class KeybindModule extends DiscordModules.Keybind { + constructor(props) { + super(props); + } + render() { + const ret = super.render(); + ret.type = KeyRecorder; + ret.props.account = this.props.account; + ret.props.onRemove = this.props.onRemove; + return ret; + } + }; + const Keybind = class Keybind extends Settings.SettingField { + constructor(account, onChange, onRemove) { + super( + account.name + " (" + account.id + ")", + "", + onChange, + KeybindModule, + { + defaultValue: + (account.keybind[0] !== -1 && + account.keybind.map((a) => [0, a])) || + [], + onChange: (element) => (value) => { + if (!Array.isArray(value)) return; + element.props.value = value; + this.onChange(value.map((a) => a[1])); + }, + account, + onRemove, + } + ); + } + }; + return class AccountSwitcher extends Plugin { + updateAvatars() { + this.settings.accounts.forEach((acc) => { + const u = UserStore.getUser(acc.id); + if (u) acc.avatar = u.avatarURL; + }); + } + + onStart() { + password = null; + this.loadSettings(); + if (this.settings.salt === undefined) { + this.settings.salt = crypto.randomBytes(32).toString("base64"); + } + this.settings.accounts.forEach((acc) => + this.registerKeybind(acc) + ); + this.openMenu = this.openMenu.bind(this); + PluginUtilities.addStyle( + "accountswitcher-style", + ` .accountswitcher-switchmenu { position: fixed; width: auto; @@ -222,348 +315,532 @@ module.exports = (() => { right: 0; position: absolute; } - `); - document.addEventListener("mouseup", this.openMenu); - this.updateAvatars(); - if(this.settings.encryptionVersion !== 2){ - if(!this.settings.encrypted){ - this.settings.accounts.forEach(acc => { - acc.token = this.encryptUpdated(this.decryptDeprecated(acc.token, acc.id), acc.id); - }); - this.settings.encryptionVersion = 2; - this.saveSettings(); - }else{ - this.requirePassword("The encryption algorithm got updated for better security. Please enter your password to decrypt your tokens and encrypt them again with the new algorithm.").then(()=>{ - this.settings.encTest = this.encryptUpdated("test", password); - this.settings.accounts.forEach(acc => { - acc.token = this.encryptUpdated(this.encryptUpdated(this.decryptDeprecated(this.decryptDeprecated(acc.token, password), acc.id), acc.id), password); - }); - this.settings.encryptionVersion = 2; - this.saveSettings(); - }); - } - } - } - - onStop(){ - this.settings.accounts.forEach(acc => this.unregisterKeybind(acc)); - document.removeEventListener("mouseup", this.openMenu); - PluginUtilities.removeStyle("accountswitcher-style"); - } - - get defaultSettings(){ - return { - accounts: [], - encrypted: false, - encTest: "test", - pluginsToRestart: ["AccountDetailsPlus", "AutoStartRichPresence"], - encryptionVersion: 1 - } - } - - openMenu(e){ - if(e.which != 2) return; - if(!e.target || !e.target.classList) return; - if(!e.target.classList.contains(WebpackModules.getByProps("avatar", "container", "nameTag").avatar.split(" ")[0])) return; - e.preventDefault(); - const menu = document.createElement("div"); - const AccountPanel = account=>React.createElement( - "div", - { - className: "accountswitcher-accountwrapper" - }, - React.createElement("img", { - src: account.avatar, - className: "accountswitcher-menuavatar", - onClick: e=>this.login(account) - }), - React.createElement("div", { - className: "accountswitcher-removeaccount", - onClick: e=>{ - this.unregisterKeybind(account); - this.settings.accounts = this.settings.accounts.filter(acc => acc.id != account.id); - this.saveSettings(); - Toasts.show("Account " + account.name + " removed", {type: Toasts.ToastTypes.success}); - } - }, "⨯") - ); - if(this.settings.accounts.length > 0){ - const offset = DOMTools.offset(e.target); - ReactDOM.render(React.createElement( - "div", - { - className: "accountswitcher-switchmenu", - style: { - bottom: (offset.bottom - offset.top + 27), - left: (offset.left - 5) + ` + ); + document.addEventListener("mouseup", this.openMenu); + this.updateAvatars(); + if (this.settings.encryptionVersion !== 2) { + if (!this.settings.encrypted) { + this.settings.accounts.forEach((acc) => { + acc.token = this.encryptUpdated( + this.decryptDeprecated(acc.token, acc.id), + acc.id + ); + }); + this.settings.encryptionVersion = 2; + this.saveSettings(); + } else { + this.requirePassword( + "The encryption algorithm got updated for better security. Please enter your password to decrypt your tokens and encrypt them again with the new algorithm." + ).then(() => { + this.settings.encTest = this.encryptUpdated( + "test", + password + ); + this.settings.accounts.forEach((acc) => { + acc.token = this.encryptUpdated( + this.encryptUpdated( + this.decryptDeprecated( + this.decryptDeprecated(acc.token, password), + acc.id + ), + acc.id + ), + password + ); + }); + this.settings.encryptionVersion = 2; + this.saveSettings(); + }); } - }, - this.settings.accounts.map(account=>React.createElement(AccountPanel, account)) - ), menu); - document.body.appendChild(menu); - }else{ - Toasts.show("No accounts to display", {type: Toasts.ToastTypes.warning}); - } - const eventHandler = ev=>{ - if(!ev.target || !ev.target.classList) return; - if(!ev.target.classList.contains("accountswitcher-switchmenu") && !ev.target.classList.contains("accountswitcher-removeaccount")){ - menu.remove(); - document.removeEventListener(eventHandler); - } - }; - document.addEventListener("click", eventHandler); - } - - login(account){ - console.log("Logging in as " + account.name); - if(account.id == UserStore.getCurrentUser().id) return Toasts.show("Already using account " + account.name, {type: Toasts.ToastTypes.warning}); - console.log("Logging in as " + account.name); - this.requirePassword().then(r => { - const token = password == null ? account.token : this.decrypt(account.token, password); - AccountManager.loginToken(this.decrypt(token, account.id)); - window.setTimeout(this.updateAvatars, 5000); - this.settings.pluginsToRestart.forEach(pl => { - if(BdApi.Plugins.isEnabled(pl)){ - BdApi.Plugins.disable(pl); - window.setTimeout(()=>BdApi.Plugins.enable(pl), 5000); } - }); - }); - } - - getSettingsPanel(){ - const panel = document.createElement("div"); - panel.className = "form"; - panel.style = "width:100%;" - const accountsField = new Settings.SettingGroup("Accounts", {shown:true}); - const addAccount = account=>{ - if(!account){ - let u = UserStore.getCurrentUser(); - let t = this.encrypt(UserInfoStore.getToken(), u.id); - let acc = { - name: u.tag, - id: u.id, - avatar: u.avatarURL, - keybind: [64, 10+this.settings.accounts.length], - token: this.settings.encrypted ? this.encrypt(t, password) : t - }; - this.settings.accounts.push(acc); - this.saveSettings(); - this.registerKeybind(acc); - return addAccount(acc); } - const kbPanel = new Keybind(account, keybind => { - this.unregisterKeybind(account); - account.keybind = keybind; - this.saveSettings(); - this.registerKeybind(account); - }, ()=>{ - this.unregisterKeybind(account); - this.settings.accounts = this.settings.accounts.filter(acc => acc.id != account.id); - this.saveSettings(); - // TODO: remove account from DOM so you are not required to repopen the settings - Toasts.show("Account " + account.name + " got removed. After reopening the settings it will also be gone from this list.", {type: Toasts.ToastTypes.success}); - }); - accountsField.append(kbPanel); - }; - const addAccountButton = document.createElement("button"); - // TODO: remove hardcoded classes - addAccountButton.className = "button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMedium-1AC_Sl grow-q77ONN"; - addAccountButton.addEventListener("click", ()=>{ - if(this.settings.accounts.some(acc => acc.id == UserStore.getCurrentUser().id)){ - return Toasts.show("You already saved this account", {type: Toasts.ToastTypes.error}); + + onStop() { + this.settings.accounts.forEach((acc) => + this.unregisterKeybind(acc) + ); + document.removeEventListener("mouseup", this.openMenu); + PluginUtilities.removeStyle("accountswitcher-style"); } - this.requirePassword().then(r => { - addAccount(); - }) - }); - addAccountButton.innerText = "Save Account"; - - new Settings.SettingGroup(this.getName(), {shown:true}).appendTo(panel) - .append( - new Settings.Switch("Encrypt tokens", "Encrypting tokens makes sure that nobody will be able to get the tokens without knowing the password.", this.settings.encrypted, checked => { - if(checked === this.settings.encrypted) return; - if(checked){ - const retry = ()=>{ - let pw1 = ""; - let pw2 = ""; - Modals.showModal("Set password", React.createElement("div", {}, - React.createElement("input", { - type: "password", - placeholder: "Password", - onChange: e=>{pw1 = e.target.value;} - }), - React.createElement("input", { - type: "password", - placeholder: "Repeat password", - onChange: e=>{pw2 = e.target.value;} - }) - ), { - onConfirm: ()=>{ - if(pw1 != pw2){ - Toasts.show("Passwords don't match", {type: Toasts.ToastTypes.error}); - return retry(); - } - password = pw1; - this.settings.encrypted = true; - this.settings.encTest = this.encrypt("test", password); - this.settings.accounts.forEach(acc => acc.token = this.encrypt(acc.token, password)); - this.saveSettings(); - } - }); - } - retry(); - }else{ - const retry = ()=>{ - let pw = ""; - Modals.showModal("Disable encryption", React.createElement("div", {}, - React.createElement(Markdown, {}, "Are you sure that you want to disable encryption? To verify please input your current password. You can also choose the 'Forgot Password' option which will remove all saved accounts. To abort just click outside of this popout."), - React.createElement("input", { - type: "password", - placeholder: "Password", - onChange: e=>{pw = e.target.value;} - }) - ), { - onConfirm: ()=>{ - try { - if(this.decrypt(this.settings.encTest, pw) !== "test"){ - Toasts.show("Passwords incorrect", {type: Toasts.ToastTypes.error}); - return retry(); - } - this.settings.encrypted = false; - this.settings.encTest = "test"; - this.settings.accounts.forEach(acc => acc.token = this.decrypt(acc.token, pw)) - password = null; - this.saveSettings(); - }catch(ex){ - Toasts.show("Passwords incorrect", {type: Toasts.ToastTypes.error}); - return retry(); - } - }, - onCancel: ()=>{ - Modals.showConfirmationModal("Are you sure?", "You are about to disable encryption which will remove all your currently saved accounts without an option to recover them. Only use this if you really forgot your password.", { - onConfirm: ()=>{ - this.settings.encTest = "test"; - this.settings.encrypted = false; - this.settings.accounts = []; - password = null; - this.saveSettings(); - } - }); - }, - confirmText: "Disable encryption", - cancelText: "Forgot Password" - }); - }; - retry(); - } - }) + + get defaultSettings() { + return { + accounts: [], + encrypted: false, + encTest: "test", + pluginsToRestart: [ + "AccountDetailsPlus", + "AutoStartRichPresence", + ], + encryptionVersion: 1, + }; + } + + openMenu(e) { + if (e.which != 2) return; + if (!e.target || !e.target.classList) return; + if ( + !e.target.classList.contains( + WebpackModules.getByProps( + "avatar", + "container", + "nameTag" + ).avatar.split(" ")[0] + ) ) - .append(accountsField) - .append(addAccountButton) - .append( - new Settings.Textbox("Plugins to restart", "Put the name of all plugins that should get restarted when you switch accounts in this textbox separated by a comma", this.settings.pluginsToRestart.join(","), val=>{ - this.settings.pluginsToRestart = val.split(",").map(x=>x.trim()).filter(x=>x); - this.saveSettings(); - }) - ); - this.settings.accounts.forEach(acc => addAccount(acc)); - return panel; - } - - // This function does NOT return the password, it just ensures that the correct password is stored in the "password" variable. - // The password should never be exposed so there should be no way to access the password from outside this plugin. - async requirePassword(message){ - if(!this.settings.encrypted || password !== null) return Promise.resolve(); - return new Promise((resolve, reject) => { - const retry = t=>{ - let pw = ""; - Modals.showModal("Password required", React.createElement( - React.Fragment, - !message ? null : message, - !message ? null : React.createElement("br"), + return; + e.preventDefault(); + const menu = document.createElement("div"); + const AccountPanel = (account) => React.createElement( - "input", + "div", { - type: "password", - onChange: e=>{pw = e.target.value;} + className: "accountswitcher-accountwrapper", + }, + React.createElement("img", { + src: account.avatar, + className: "accountswitcher-menuavatar", + onClick: (e) => this.login(account), + }), + React.createElement( + "div", + { + className: "accountswitcher-removeaccount", + onClick: (e) => { + this.unregisterKeybind(account); + this.settings.accounts = this.settings.accounts.filter( + (acc) => acc.id != account.id + ); + this.saveSettings(); + Toasts.show("Account " + account.name + " removed", { + type: Toasts.ToastTypes.success, + }); + }, + }, + "⨯" + ) + ); + if (this.settings.accounts.length > 0) { + const offset = DOMTools.offset(e.target); + ReactDOM.render( + React.createElement( + "div", + { + className: "accountswitcher-switchmenu", + style: { + bottom: offset.bottom - offset.top + 27, + left: offset.left - 5, + }, + }, + this.settings.accounts.map((account) => + React.createElement(AccountPanel, account) + ) + ), + menu + ); + document.body.appendChild(menu); + } else { + Toasts.show("No accounts to display", { + type: Toasts.ToastTypes.warning, + }); + } + const eventHandler = (ev) => { + if (!ev.target || !ev.target.classList) return; + if ( + !ev.target.classList.contains("accountswitcher-switchmenu") && + !ev.target.classList.contains("accountswitcher-removeaccount") + ) { + menu.remove(); + document.removeEventListener(eventHandler); + } + }; + document.addEventListener("click", eventHandler); + } + + login(account) { + console.log("Logging in as " + account.name); + if (account.id == UserStore.getCurrentUser().id) + return Toasts.show("Already using account " + account.name, { + type: Toasts.ToastTypes.warning, + }); + console.log("Logging in as " + account.name); + this.requirePassword().then((r) => { + const token = + password == null + ? account.token + : this.decrypt(account.token, password); + AccountManager.loginToken(this.decrypt(token, account.id)); + window.setTimeout(this.updateAvatars, 5000); + this.settings.pluginsToRestart.forEach((pl) => { + if (BdApi.Plugins.isEnabled(pl)) { + BdApi.Plugins.disable(pl); + window.setTimeout(() => BdApi.Plugins.enable(pl), 5000); } - ) - ), { - onConfirm: ()=>{ - try{ - if(this.decrypt(this.settings.encTest, pw) !== "test"){ - Toasts.show("Wrong password", {type: Toasts.ToastTypes.error}); - return retry(t+1); - } - password = pw; - resolve(); - }catch(ex){ - Toasts.show("Wrong password", {type: Toasts.ToastTypes.error}); - retry(t+1); + }); + }); + } + + getSettingsPanel() { + const panel = document.createElement("div"); + panel.className = "form"; + panel.style = "width:100%;"; + const accountsField = new Settings.SettingGroup("Accounts", { + shown: true, + }); + const addAccount = (account) => { + if (!account) { + let u = UserStore.getCurrentUser(); + let t = this.encrypt(UserInfoStore.getToken(), u.id); + let acc = { + name: u.tag, + id: u.id, + avatar: u.avatarURL, + keybind: [64, 10 + this.settings.accounts.length], + token: this.settings.encrypted + ? this.encrypt(t, password) + : t, + }; + this.settings.accounts.push(acc); + this.saveSettings(); + this.registerKeybind(acc); + return addAccount(acc); + } + const kbPanel = new Keybind( + account, + (keybind) => { + this.unregisterKeybind(account); + account.keybind = keybind; + this.saveSettings(); + this.registerKeybind(account); + }, + () => { + this.unregisterKeybind(account); + this.settings.accounts = this.settings.accounts.filter( + (acc) => acc.id != account.id + ); + this.saveSettings(); + // TODO: remove account from DOM so you are not required to repopen the settings + Toasts.show( + "Account " + + account.name + + " got removed. After reopening the settings it will also be gone from this list.", + { type: Toasts.ToastTypes.success } + ); } + ); + accountsField.append(kbPanel); + }; + const addAccountButton = document.createElement("button"); + // TODO: remove hardcoded classes + addAccountButton.className = + "button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMedium-1AC_Sl grow-q77ONN"; + addAccountButton.addEventListener("click", () => { + if ( + this.settings.accounts.some( + (acc) => acc.id == UserStore.getCurrentUser().id + ) + ) { + return Toasts.show("You already saved this account", { + type: Toasts.ToastTypes.error, + }); } - }) - }; - retry(0); - }); - } - - encryptDeprecated(text, pw){ - const key = crypto.createCipher("aes-128-cbc", pw); - return key.update(text, "utf8", "hex") + key.final("hex"); - } - decryptDeprecated(text, pw){ - const key = crypto.createDecipher("aes-128-cbc", pw); - return key.update(text, "hex", "utf8") + key.final("utf8"); - } - - generateEncryptionKey(pw){ - return crypto.pbkdf2Sync(Buffer.from(pw, "utf8"), this.settings.salt, 100000, 32, "sha512"); - } - encryptUpdated(text, pw){ - const iv = crypto.randomBytes(IV_LENGTH); - const cipher = crypto.createCipheriv(algorithm, this.generateEncryptionKey(pw), iv); - const encrypted = Buffer.concat([cipher.update(text), cipher.final()]); - return iv.toString("base64") + ":" + encrypted.toString("base64"); - } - decryptUpdated(text, pw){ - const parts = text.split(":"); - const iv = Buffer.from(parts.shift(), "base64"); - const encrypted = Buffer.from(parts.join(":"), "base64"); - const decipher = crypto.createDecipheriv(algorithm, this.generateEncryptionKey(pw), iv); - const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]); - return decrypted.toString(); - } - - encrypt(text, pw){ - return this.settings.encryptionVersion === 2 ? this.encryptUpdated(text, pw) : this.encryptDeprecated(text, pw); - } - decrypt(text, pw){ - return this.settings.encryptionVersion === 2 ? this.decryptUpdated(text, pw) : this.decryptDeprecated(text, pw); - } - - registerKeybind(account){ - registerKeybind("119" + account.id, account.keybind.map(a=>[0,a]), pressed => { - this.login(account); - }, {blurred: false, focused: true, keydown: true, keyup: false}); - } - unregisterKeybind(account){ - unregisterKeybind("119" + account.id); - } - - loadSettings(){ - this.settings = PluginUtilities.loadSettings(this.getName(), this.defaultSettings); - if(!Array.isArray(this.settings.accounts)) this.settings.accounts = Object.values(this.settings.accounts); - this.settings.accounts.forEach(acc => { - if(!Array.isArray(acc.keybind)) acc.keybind = Object.values(acc.keybind); - }); - } - saveSettings(){ - PluginUtilities.saveSettings(this.getName(), this.settings); - } - } - }; - return plugin(Plugin, Api); - })(global.ZeresPluginLibrary.buildPlugin(config)); + this.requirePassword().then((r) => { + addAccount(); + }); + }); + addAccountButton.innerText = "Save Account"; + + new Settings.SettingGroup(this.getName(), { shown: true }) + .appendTo(panel) + .append( + new Settings.Switch( + "Encrypt tokens", + "Encrypting tokens makes sure that nobody will be able to get the tokens without knowing the password.", + this.settings.encrypted, + (checked) => { + if (checked === this.settings.encrypted) return; + if (checked) { + const retry = () => { + let pw1 = ""; + let pw2 = ""; + Modals.showModal( + "Set password", + React.createElement( + "div", + {}, + React.createElement("input", { + type: "password", + placeholder: "Password", + onChange: (e) => { + pw1 = e.target.value; + }, + }), + React.createElement("input", { + type: "password", + placeholder: "Repeat password", + onChange: (e) => { + pw2 = e.target.value; + }, + }) + ), + { + onConfirm: () => { + if (pw1 != pw2) { + Toasts.show("Passwords don't match", { + type: Toasts.ToastTypes.error, + }); + return retry(); + } + password = pw1; + this.settings.encrypted = true; + this.settings.encTest = this.encrypt( + "test", + password + ); + this.settings.accounts.forEach( + (acc) => + (acc.token = this.encrypt( + acc.token, + password + )) + ); + this.saveSettings(); + }, + } + ); + }; + retry(); + } else { + const retry = () => { + let pw = ""; + Modals.showModal( + "Disable encryption", + React.createElement( + "div", + {}, + React.createElement( + Markdown, + {}, + "Are you sure that you want to disable encryption? To verify please input your current password. You can also choose the 'Forgot Password' option which will remove all saved accounts. To abort just click outside of this popout." + ), + React.createElement("input", { + type: "password", + placeholder: "Password", + onChange: (e) => { + pw = e.target.value; + }, + }) + ), + { + onConfirm: () => { + try { + if ( + this.decrypt(this.settings.encTest, pw) !== + "test" + ) { + Toasts.show("Passwords incorrect", { + type: Toasts.ToastTypes.error, + }); + return retry(); + } + this.settings.encrypted = false; + this.settings.encTest = "test"; + this.settings.accounts.forEach( + (acc) => + (acc.token = this.decrypt(acc.token, pw)) + ); + password = null; + this.saveSettings(); + } catch (ex) { + Toasts.show("Passwords incorrect", { + type: Toasts.ToastTypes.error, + }); + return retry(); + } + }, + onCancel: () => { + Modals.showConfirmationModal( + "Are you sure?", + "You are about to disable encryption which will remove all your currently saved accounts without an option to recover them. Only use this if you really forgot your password.", + { + onConfirm: () => { + this.settings.encTest = "test"; + this.settings.encrypted = false; + this.settings.accounts = []; + password = null; + this.saveSettings(); + }, + } + ); + }, + confirmText: "Disable encryption", + cancelText: "Forgot Password", + } + ); + }; + retry(); + } + } + ) + ) + .append(accountsField) + .append(addAccountButton) + .append( + new Settings.Textbox( + "Plugins to restart", + "Put the name of all plugins that should get restarted when you switch accounts in this textbox separated by a comma", + this.settings.pluginsToRestart.join(","), + (val) => { + this.settings.pluginsToRestart = val + .split(",") + .map((x) => x.trim()) + .filter((x) => x); + this.saveSettings(); + } + ) + ); + this.settings.accounts.forEach((acc) => addAccount(acc)); + return panel; + } + + // This function does NOT return the password, it just ensures that the correct password is stored in the "password" variable. + // The password should never be exposed so there should be no way to access the password from outside this plugin. + async requirePassword(message) { + if (!this.settings.encrypted || password !== null) + return Promise.resolve(); + return new Promise((resolve, reject) => { + const retry = (t) => { + let pw = ""; + Modals.showModal( + "Password required", + React.createElement( + React.Fragment, + !message ? null : message, + !message ? null : React.createElement("br"), + React.createElement("input", { + type: "password", + onChange: (e) => { + pw = e.target.value; + }, + }) + ), + { + onConfirm: () => { + try { + if ( + this.decrypt(this.settings.encTest, pw) !== "test" + ) { + Toasts.show("Wrong password", { + type: Toasts.ToastTypes.error, + }); + return retry(t + 1); + } + password = pw; + resolve(); + } catch (ex) { + Toasts.show("Wrong password", { + type: Toasts.ToastTypes.error, + }); + retry(t + 1); + } + }, + } + ); + }; + retry(0); + }); + } + + encryptDeprecated(text, pw) { + const key = crypto.createCipher("aes-128-cbc", pw); + return key.update(text, "utf8", "hex") + key.final("hex"); + } + decryptDeprecated(text, pw) { + const key = crypto.createDecipher("aes-128-cbc", pw); + return key.update(text, "hex", "utf8") + key.final("utf8"); + } + + generateEncryptionKey(pw) { + return crypto.pbkdf2Sync( + Buffer.from(pw, "utf8"), + this.settings.salt, + 100000, + 32, + "sha512" + ); + } + encryptUpdated(text, pw) { + const iv = crypto.randomBytes(IV_LENGTH); + const cipher = crypto.createCipheriv( + algorithm, + this.generateEncryptionKey(pw), + iv + ); + const encrypted = Buffer.concat([ + cipher.update(text), + cipher.final(), + ]); + return iv.toString("base64") + ":" + encrypted.toString("base64"); + } + decryptUpdated(text, pw) { + const parts = text.split(":"); + const iv = Buffer.from(parts.shift(), "base64"); + const encrypted = Buffer.from(parts.join(":"), "base64"); + const decipher = crypto.createDecipheriv( + algorithm, + this.generateEncryptionKey(pw), + iv + ); + const decrypted = Buffer.concat([ + decipher.update(encrypted), + decipher.final(), + ]); + return decrypted.toString(); + } + + encrypt(text, pw) { + return this.settings.encryptionVersion === 2 + ? this.encryptUpdated(text, pw) + : this.encryptDeprecated(text, pw); + } + decrypt(text, pw) { + return this.settings.encryptionVersion === 2 + ? this.decryptUpdated(text, pw) + : this.decryptDeprecated(text, pw); + } + + registerKeybind(account) { + registerKeybind( + "119" + account.id, + account.keybind.map((a) => [0, a]), + (pressed) => { + this.login(account); + }, + { blurred: false, focused: true, keydown: true, keyup: false } + ); + } + unregisterKeybind(account) { + unregisterKeybind("119" + account.id); + } + + loadSettings() { + this.settings = PluginUtilities.loadSettings( + this.getName(), + this.defaultSettings + ); + if (!Array.isArray(this.settings.accounts)) + this.settings.accounts = Object.values(this.settings.accounts); + this.settings.accounts.forEach((acc) => { + if (!Array.isArray(acc.keybind)) + acc.keybind = Object.values(acc.keybind); + }); + } + saveSettings() { + PluginUtilities.saveSettings(this.getName(), this.settings); + } + }; + }; + return plugin(Plugin, Api); + })(global.ZeresPluginLibrary.buildPlugin(config)); })(); diff --git a/translations.json b/translations.json index d1a1407..099aa4f 100644 --- a/translations.json +++ b/translations.json @@ -1,338 +1,394 @@ { - "en": { - "plugin": { - "description": "Switch between multiple accounts with AltLeft+1 up to AltLeft+0" - }, - "settings": { - "language": "Language", - "languages": { - "en": {"name":"English","translator":"l0c4lh057"}, - "de": {"name":"German","translator":"l0c4lh057"}, - "fr": {"name":"French","translator":"Dark Mood"}, - "ru": {"name":"Russian","translator":"•MGC•Mr_ChAI#7272"}, - "ja": {"name":"Japanese","translator":"Near"}, - "pl": {"name":"Polish","translator":"Killrog"}, - "auto": {"name":"Detect automatically"} - }, - "warning": "Do NOT share any of your tokens with someone else. Otherwise they can use your account with all actions that don't need a password. This can't be prevented by 2fa.
If you think someone has your token, enable 2fa and change your password. For both actions your account will get a new token. But don't forget to change the token in this settings!

PLEASE SET A PASSWORD BY ENABLING ENCRYPTION! If you don't do this, all your tokens will be saved in clear text. Every plugin and every program on your computer can access the file and all your tokens could get public at once. If you activate encryption all tokens will be encrypted with your password as key. You will need to enter your password every time you open the settings and every time you want to change your account.", - "encryption": "Encrypt tokens", - "account": "Account {0}", - "password": { - "set": "Set password", - "setDescription": "Please set the password you want to use for this plugin here. If you forget it all your tokens can't be restored.
", - "remove": "Remove password", - "removeDescription": "Are you sure you want to remove the password? This will save your tokens in clear text!
If you really want to risk this click the OKAY button otherwise click outside of this popup." - }, - "accountNamePlaceholder": "Account name", - "accountTokenPlaceholder": "Account token", - "copyToken": "Copy token of current account", - "copiedToken": "Token copied", - "fetchTranslations": "Fetch translations", - "fetchedTranslations": "Translations fetched!", - "support": "Get Support", - "passwordRequired": { - "title": "Password required", - "description": "" - }, - "saveAccount": "Save Account", - "removeAccount": "Remove Account", - "showChangelog": "Show changelog" - }, - "couldNotDecrypt": "Could not decrypt token {0}.", - "alreadyUsingAccount": "You are already using this account", - "invalidToken": "This token is invalid", - "passwordRequired": { - "title": "Password required", - "description": "To change the account you need to type in the password you once set.
If you can't remember it you can disable token encryption in the settings but then all your tokens are gone.
" - }, - "removeAccount": { - "title": "Removing account", - "description": "Do you really want to remove that account? If you accept this you can't get the account information back again.", - "tooltip": "Remove Account" - }, - "noAccountSet": { - "title": "No account set", - "description": "Token {0} is not set. Do you want to go to the login form?" - } - }, - "de": { - "plugin": { - "description": "Wechsel zwischen mehreren Accounts, indem du AltLeft+1 bis AltLeft+0 drückst" - }, - "settings": { - "language": "Sprache", - "languages": { - "en": {"name":"Englisch","translator":"l0c4lh057"}, - "de": {"name":"Deutsch","translator":"l0c4lh057"}, - "fr": {"name":"Französisch","translator":"Dark Mood"}, - "ru": {"name":"Russian","translator":"•MGC•Mr_ChAI#7272"}, - "ja": {"name":"Japanisch","translator":"Near"}, - "pl": {"name":"Polish","translator":"Killrog"}, - "auto": {"name":"Automatisch erkennen"} - }, - "warning": "Teil deine Account-Tokens NIEMALS mit jemand anderem! Dadurch könnten diese deinen Account mit allen Funktionen, die keine Bestätigung per Passwort benötigen, nutzen. Das kann auch nicht mit 2FA verhindert werden.
Wenn du denkst, dass jemand deinen Token hat, aktiviere 2FA und änder dein Passwort. Für beide Aktionen sollte dein Account einen neuen Token bekommen. Vergiss aber nicht, den Token in den Einstellungen dieses Plugins zu ändern.

BITTE SETZ EIN PASSWORT, INDEM DU VERSCHLÜSSELUNG AKTIVIERST! Wenn du das nicht tust, werden alle Tokens in Klartext gespeichert. Jedes Plugin und jedes Programm auf deinem Computer kann auf die Datei und damit auf alle Tokens zugreifen, wodurch diese alle mit einem Mal öffentlich geraten können. Wenn du Verschlüsselung aktivierst, werden alle Tokens mit dem eingegebenen Passwort verschlüsselt. Jedes Mal, wenn auf einen Token zugegriffen wird (Einstellungen öffnen/Account wechseln), musst du dieses Passwort wieder eingeben.", - "encryption": "Token verschlüsseln", - "account": "Account {0}", - "password": { - "set": "Set password", - "setDescription": "Setz hier das Passwort, das du in diesem Plugin verwenden willst. Wenn du das Passwort vergisst, kannst du die Tokens nicht mehr wiederherstellen.
", - "remove": "Remove password", - "removeDescription": "Willst du das Passwort wirklich entfernen? Dadurch werden alle Token in Klartext gespeichert!
Wenn du das Passwort wirklich entfernen willst, klick auf den OKAY-Knopf, sonst außerhalb dieses Popups." - }, - "accountNamePlaceholder": "Account-Name", - "accountTokenPlaceholder": "Account-Token", - "copyToken": "Token des aktuellen Accounts kopieren", - "copiedToken": "Token kopiert", - "fetchTranslations": "Fetch translations", - "fetchedTranslations": "Translations fetched!", - "support": "Support", - "passwordRequired": { - "title": "Passwort benötigt", - "description": "" - }, - "saveAccount": "Account speichern", - "removeAccount": "Account entfernen", - "showChangelog": "Changelog anzeigen" - }, - "couldNotDecrypt": "Token {0} konnte nicht entschlüsselt werden.", - "alreadyUsingAccount": "Du benutzt diesen Account bereits", - "invalidToken": "Der Token ist ungültig", - "passwordRequired": { - "title": "Passwort benötigt", - "description": "Um deinen Account zu wechseln muss du das Passwort eingeben, dass du gesetzt hast.
Wenn du dein Passwort vergessen hast, kannst du die Verschlüsselung in den Einstellungen deaktivieren, aber dann sind all deine Tokens nicht mehr zugreifbar.
" - }, - "removeAccount": { - "title": "Account entfernen", - "description": "Willst du den Account wirklich entfernen? Dann kannst du diesen nicht mehr nutzen, die Daten gehen verloren.", - "tooltip": "Account entfernen" - }, - "noAccountSet": { - "title": "Kein Account gesetzt", - "description": "Token {0} wurde nicht gesetzt. Möchtest du zum Login geleitet werden?" - } - }, - "fr": { - "plugin": { - "description": "Passez d'un compte à l'autre avec AltLeft+1 , AltLeft+2 , etc..." - }, - "settings": { - "language": "Langage", - "languages": { - "en": {"name":"English","translator":"l0c4lh057"}, - "de": {"name":"German","translator":"l0c4lh057"}, - "fr": {"name":"French","translator":"Dark Mood"}, - "ru": {"name":"Russian","translator":"•MGC•Mr_ChAI#7272"}, - "ja": {"name":"Japonais","translator":"Near"}, - "pl": {"name":"Polish","translator":"Killrog"}, - "auto": {"name":"Détection automatique"} - }, - "warning": "Ne PAS partager vos tokens avec quelqu'un d'autre. Sinon, ils peuvent utiliser votre compte/vos tokens avec toutes les actions qui n'ont pas besoin d'un mot de passe. Ceci ne peut être évité par l'a2f.
Si vous pensez que quelqu'un a votre token, activez l'a2f et changez votre mot de passe. Pour les deux actions, votre compte recevra un nouveau jeton. Mais n'oubliez pas de changer le token dans les paramètres suivants!

VEUILLEZ DÉFINIR UN MOT DE PASSE POUR ACTIVER LE CRYPTAGE! Si vous ne le faites pas, tous vos tokens seront sauvegardés en texte clair. Chaque plugin et chaque programme sur votre ordinateur peut accéder au fichier et tous vos jetons peuvent être rendus publics en même temps. Si vous activez le cryptage, tous les jetons seront cryptés avec votre mot de passe comme clé. Vous devrez entrer votre mot de passe chaque fois que vous ouvrirez les paramètres et chaque fois que vous voudrez changer votre compte.", - "encryption": "Crypter les tokens", - "account": "Comptes: {0}", - "password": { - "set": "Définir un mot de passe", - "setDescription": "Veuillez définir ici le mot de passe que vous souhaitez utiliser pour ce plugin. Si vous l'oubliez, tous vos jetons ne peuvent pas être restaurés et seront donc supprimés...
", - "remove": "Supprimer le mot de passe", - "removeDescription": "Êtes-vous sûr de vouloir supprimer le mot de passe ? Ceci sauvegardera vos tokens en texte clair!
ISi vous voulez vraiment prendre ce risque, cliquez sur le bouton OKAY/OK sinon cliquez à l'extérieur de ce popup." - }, - "accountNamePlaceholder": "Nom de compte", - "accountTokenPlaceholder": "Token de compte", - "copyToken": "Copier le token du compte actuel", - "copiedToken": "Token copié", - "fetchTranslations": "Fetch translations", - "fetchedTranslations": "Translations fetched!", - "support": "Avoir le support", - "passwordRequired": { - "title": "Mot de passe requis", - "description": "" - }, - "saveAccount": "Save Account", - "removeAccount": "Remove Account", - "showChangelog": "Show changelog" - }, - "couldNotDecrypt": "Impossible de décrypter les jetons {0}.", - "alreadyUsingAccount": "Vous utilisez déjà ce compte", - "invalidToken": "Le token est invalide", - "passwordRequired": { - "title": "Mot de passe requis", - "description": "Pour changer de compte, vous devez saisir le mot de passe que vous avez défini une fois.
Si vous ne vous en souvenez pas, vous pouvez désactiver le cryptage des tokens dans les paramètres, mais tous vos tokens auront disparus.
" - }, - "removeAccount": { - "title": "Removing account", - "description": "Do you really want to remove that account? If you accept this you can't get the account information back again.", - "tooltip": "Remove Account" - }, - "noAccountSet": { - "title": "No account set", - "description": "Token {0} is not set. Do you want to go to the login form?" - } - }, - "ru": { - "plugin": { - "description": "Переключайтесь между аккаунтами с помощью сочетаний клавиш от AltLeft+1 до AltLeft+0" - }, - "settings": { - "language": "Язык", - "languages": { - "en": {"name":"Английский","translator":"l0c4lh057"}, - "de": {"name":"Немецкий","translator":"l0c4lh057"}, - "fr": {"name":"Французский","translator":"Dark Mood"}, - "ru": {"name":"Русский","translator":"•MGC•Mr_ChAI#7272"}, - "ja": {"name":"японский","translator":"Near"}, - "pl": {"name":"Polish","translator":"Killrog"}, - "auto": {"name":"Автоопределение"} - }, - "warning": "НЕ передавайте никому свой токен! Иначе он(а) получит полный доступ к вашему аккаунту. Это не может быть предотвращено с помощью 2FA.
Если вы считаете, что у кого-то есть ваш токен, включите/выключите 2FA или поменяйте пароль - каждое из этих действий меняет пароль. Но не забудьте поменять его в настройках!

ПОЖАЛУЙСТА, УСТАНОВИТЕ ПАРОЛЬ, ВКЛЮЧИВ ШИФРОВАНИЕ! Если этого не сделать, токены будут храниться в виде обычного текста, и любой плагин/программа сможет их прочесть, в результате все ваши токены могут быть переданы кому-то другому. Если вы включите шифрование, все токены будут зашифрованы с паролем в качестве ключа. Вам придется вводить пароль каждый раз, когда вы открываете настройки плагина или меняете аккаунт.", - "encryption": "Шифрование токенов", - "account": "Аккаунт {0}", - "password": { - "set": "Установите пароль", - "setDescription": "Пожалуйста, установите пароль. Если вы его забудете, вы не сможете восстановить токены.
", - "remove": "Удаление пароля", - "removeDescription": "Вы уверены, что хотите убрать пароль? Ваши токены будут храниться в виде обычного текста!
Если вы все же решили рискнуть, нажмите кнопку OKAY, в противном случае кликните снаружи этого окошка." - }, - "accountNamePlaceholder": "Имя аккаунта", - "accountTokenPlaceholder": "Токен аккаунта", - "copyToken": "Скопировать токен текущего аккаунта", - "copiedToken": "Токен скопирован", - "fetchTranslations": "Обновить переводы", - "fetchedTranslations": "Переводы обновлены!", - "support": "Помощь", - "passwordRequired": { - "title": "Необходим пароль", - "description": "" - }, - "saveAccount": "Save Account", - "removeAccount": "Remove Account", - "showChangelog": "Показывать список изменений" - }, - "couldNotDecrypt": "Не удалось расшифровать токен аккаунта {0}.", - "alreadyUsingAccount": "Вы уже используете этот аккаунт", - "invalidToken": "Неправильный токен", - "passwordRequired": { - "title": "Необходим пароль", - "description": "Чтобы сменить аккаунт, нужен установленный вами пароль.
Если вы его забыли, вы можете отключить шифрование, но тогда все токены пропадут.
" - }, - "removeAccount": { - "title": "Удаление аккаунта", - "description": "Вы точно хотите удалить аккаунт? Если вы подтвердите, вы не сможете вернуть информацию об аккаунте.", - "tooltip": "Удалить аккаунт" - }, - "noAccountSet": { - "title": "Аккаунт не задан", - "description": "Токен {0} не задан. Вы хотите переключиться на страницу входа?" - } - }, - "ja": { - "plugin": { - "description": "複数のアカウントを左Alt + 1などで切り替えられるようにします" - }, - "settings": { - "language": "言語", - "languages": { - "en": {"name":"英語","translator":"l0c4lh057"}, - "de": {"name":"ドイツ語","translator":"l0c4lh057"}, - "fr": {"name":"フランス語","translator":"Dark Mood"}, - "ru": {"name":"ロシア語","translator":"•MGC•Mr_ChAI#7272"}, - "ja": {"name":"日本語","translator":"にあ"}, - "pl": {"name":"ポーランド語","translator":"Killrog"}, - "auto": {"name":"使用してる言語に合わせます"} - }, - "warning": "絶対に他人とトークン(Discord内で使われる認証用の文字列)を共有しないでください。共有し悪用された場合他人がパスワードを用いなくてもDiscordのアカウントの乗っ取りができます。そしてそれは二段階認証などの影響を受けません。
もしも誰かにトークンを盗まれたと思ったときはパスワードの変更か二段階認証の有効可・無効化などでトークンが変更できます。ただしこの操作をした場合トークンはもちろん更新されるため保存し直す必要があります。

暗号化を有効にしてパスワードを設定してください。これを行わないとこのプラグインで保存したトークンはすべてそのまま保存されます。コンピューター上のプログラムやDiscordに適応しているプラグインなどはこのトークンを保存したファイルにアクセス、それらを公開することが可能であることを留意してください。暗号化を有効にするとパスワードをキーとした暗号化がなされます。また、暗号化をした場合設定やアカウントの切り替えの際にパスワードが必要になります。(再起動とかした時の最初だけ!)", - "encryption": "トークンを暗号化する", - "account": "アカウント {0}", - "password": { - "set": "パスワードを設定", - "setDescription": "このプラグインを使用する際はパスワードを設定してください。もしもパスワードを忘れた場合、トークンを復号化することはできません。
", - "remove": "パスワードを削除する", - "removeDescription": "パスワードを削除しますか?すべてのトークンは暗号化されてない状態で保存されるようになります。
このリスクが理解できてなお暗号化を解除する場合は Okay と書かれたボタンを押してください
暗号化を解除したくない場合はこのポップアップの外を押してください(一回だけでいいよ!)" - }, - "accountNamePlaceholder": "名前", - "accountTokenPlaceholder": "トークン", - "copyToken": "今のアカウントのトークンをクリップボードにコピーする", - "copiedToken": "トークンをクリップボードにコピーしました", - "fetchTranslations": "言語を適用", - "fetchedTranslations": "言語が指定した言語にしっかり適応されました!", - "support": "サポート(日本語のサポートは無いから気をつけてね)", - "passwordRequired": { - "title": "パスワードを入力してください", - "description": "" - }, - "saveAccount": "保存", - "removeAccount": "削除", - "showChangelog": "変更履歴を表示" - }, - "couldNotDecrypt": "トークンの復号化に失敗しました。 {0}.", - "alreadyUsingAccount": "あなたは既にこのアカウントに切り替えてます", - "invalidToken": "このトークンは無効化されています。", - "passwordRequired": { - "title": "パスワードを入力してください", - "description": "アカウントを切り替えるには一度設定したパスワードが必要になります。
もしもあなたがパスワードを忘れたのなら暗号化されたトークンを復号化することはできません。
" - }, - "removeAccount": { - "title": "アカウントを削除する", - "description": "このアカウントを削除したいですか? 削除した場合切り替えることができなくなります", - "tooltip": "アカウントを削除する" - }, - "noAccountSet": { - "title": "ここにはアカウントが保存されていません", - "description": "スロット {0} にはトークンが保存されていません。ログインしますか?" - } - }, - "pl": { - "plugin": { - "description": "Przechodź między kontami używając klawiszy od AltLeft+1 do AltLeft+0" - }, - "settings": { - "language": "Język", - "languages": { - "en": {"name":"Angielski","translator":"l0c4lh057"}, - "de": {"name":"Niemiecki","translator":"l0c4lh057"}, - "fr": {"name":"Francuski","translator":"Dark Mood"}, - "ru": {"name":"Rosyjski","translator":"•MGC•Mr_ChAI#7272"}, - "ja": {"name":"Japoński","translator":"Near"}, - "pl": {"name":"Polski","translator":"Killrog"}, - "auto": {"name":"Wykryj automatycznie"} - }, - "warning": "Nie udostępniaj swoich tokenów innym osobom. W przeciwnym razie uzyskają oni pełny dostęp do twojego konta. Żadne 2FA ani logowanie SMS tutaj nie pomoże!
Jeżeli uważasz, że ktoś ma twój token, zmień hasło (token także się zmieni). Po tym nie zapomnij zmienić nowego tokenu w ustawieniach
PROSZE USTAW HASŁO PO PRZEZ WŁĄCZENIE SZYFROWANIA! Jeśli tego nie zrobisz, wszystkie twoje tokeny zostaną zapisane w postaci zwykłego tektu. Każda wtyczka i program na twoim komputerze będzie mogła go odczytać i ukraść. Jeżeli tokeny zostaną zaszyfrowane przy logowaniu lub zmianie konta będziesz musiał podać hasło.", - "encryption": "Szyfruj tokeny", - "account": "Konta {0}", - "password": { - "set": "Ustaw hasło", - "setDescription": "Ustaw hasło do szyfrowania tokenów. Nie ma możliwości przypomnienia hasła. Jeżeli zapomnisz hasło, nie odzyskasz tokenów.
", - "remove": "Usuń hasło", - "removeDescription": "Jesteś pewien że chcesz usunąć hasło? Twoje tokeny zostaną zapisane zwykłym tekstem!
Jeżeli jesteś pewien kliknij OK, w przeciwnym wypadku kliknij obok okienka." - }, - "accountNamePlaceholder": "Nazwa konta", - "accountTokenPlaceholder": "Token", - "copyToken": "Skopiuj token aktualnego konta", - "copiedToken": "Token skopiowany", - "fetchTranslations": "Pobierz tłumaczenia", - "fetchedTranslations": "Tłumaczenia pobrane!", - "support": "Pomoc", - "passwordRequired": { - "title": "Hasło jest wymagane", - "description": "" - }, - "saveAccount": "Zapisz Konto", - "removeAccount": "Usuń Konto", - "showChangelog": "Pokaż dziennik zmian" - }, - "couldNotDecrypt": "Nie można odszyfrować tokenu {0}.", - "alreadyUsingAccount": "Już korzystasz z tego konta", - "invalidToken": "Ten token jest nieprawidłowy", - "passwordRequired": { - "title": "Hasło jest wymagane", - "description": "Aby zmienić konto musisz podać hasło do odszyfrowania tokenów.
Jeżeli nie pamiętasz go możesz wyłączyć szyfrowanie w ustawieniach, ale wtedy wszystkie zapisane tokeny zostaną usunięte.
" - }, - "removeAccount": { - "title": "Usuwanie konta", - "description": "Jesteś pewien że chcesz usunąć to konto?", - "tooltip": "Usuń Konto" - }, - "noAccountSet": { - "title": "Konto nie istnieje", - "description": "Token {0} nie został ustawiony. Chcesz przejść do strony logowania?" - } - } + "en": { + "plugin": { + "description": "Switch between multiple accounts with AltLeft+1 up to AltLeft+0" + }, + "settings": { + "language": "Language", + "languages": { + "en": { "name": "English", "translator": "l0c4lh057" }, + "de": { "name": "German", "translator": "l0c4lh057" }, + "fr": { "name": "French", "translator": "Dark Mood" }, + "ru": { "name": "Russian", "translator": "•MGC•Mr_ChAI#7272" }, + "ja": { "name": "Japanese", "translator": "Near" }, + "pl": { "name": "Polish", "translator": "Killrog" }, + "auto": { "name": "Detect automatically" } + }, + "warning": "Do NOT share any of your tokens with someone else. Otherwise they can use your account with all actions that don't need a password. This can't be prevented by 2fa.
If you think someone has your token, enable 2fa and change your password. For both actions your account will get a new token. But don't forget to change the token in this settings!

PLEASE SET A PASSWORD BY ENABLING ENCRYPTION! If you don't do this, all your tokens will be saved in clear text. Every plugin and every program on your computer can access the file and all your tokens could get public at once. If you activate encryption all tokens will be encrypted with your password as key. You will need to enter your password every time you open the settings and every time you want to change your account.", + "encryption": "Encrypt tokens", + "account": "Account {0}", + "password": { + "set": "Set password", + "setDescription": "Please set the password you want to use for this plugin here. If you forget it all your tokens can't be restored.
", + "remove": "Remove password", + "removeDescription": "Are you sure you want to remove the password? This will save your tokens in clear text!
If you really want to risk this click the OKAY button otherwise click outside of this popup." + }, + "accountNamePlaceholder": "Account name", + "accountTokenPlaceholder": "Account token", + "copyToken": "Copy token of current account", + "copiedToken": "Token copied", + "fetchTranslations": "Fetch translations", + "fetchedTranslations": "Translations fetched!", + "support": "Get Support", + "passwordRequired": { + "title": "Password required", + "description": "" + }, + "saveAccount": "Save Account", + "removeAccount": "Remove Account", + "showChangelog": "Show changelog" + }, + "couldNotDecrypt": "Could not decrypt token {0}.", + "alreadyUsingAccount": "You are already using this account", + "invalidToken": "This token is invalid", + "passwordRequired": { + "title": "Password required", + "description": "To change the account you need to type in the password you once set.
If you can't remember it you can disable token encryption in the settings but then all your tokens are gone.
" + }, + "removeAccount": { + "title": "Removing account", + "description": "Do you really want to remove that account? If you accept this you can't get the account information back again.", + "tooltip": "Remove Account" + }, + "noAccountSet": { + "title": "No account set", + "description": "Token {0} is not set. Do you want to go to the login form?" + } + }, + "de": { + "plugin": { + "description": "Wechsel zwischen mehreren Accounts, indem du AltLeft+1 bis AltLeft+0 drückst" + }, + "settings": { + "language": "Sprache", + "languages": { + "en": { "name": "Englisch", "translator": "l0c4lh057" }, + "de": { "name": "Deutsch", "translator": "l0c4lh057" }, + "fr": { "name": "Französisch", "translator": "Dark Mood" }, + "ru": { "name": "Russian", "translator": "•MGC•Mr_ChAI#7272" }, + "ja": { "name": "Japanisch", "translator": "Near" }, + "pl": { "name": "Polish", "translator": "Killrog" }, + "auto": { "name": "Automatisch erkennen" } + }, + "warning": "Teil deine Account-Tokens NIEMALS mit jemand anderem! Dadurch könnten diese deinen Account mit allen Funktionen, die keine Bestätigung per Passwort benötigen, nutzen. Das kann auch nicht mit 2FA verhindert werden.
Wenn du denkst, dass jemand deinen Token hat, aktiviere 2FA und änder dein Passwort. Für beide Aktionen sollte dein Account einen neuen Token bekommen. Vergiss aber nicht, den Token in den Einstellungen dieses Plugins zu ändern.

BITTE SETZ EIN PASSWORT, INDEM DU VERSCHLÜSSELUNG AKTIVIERST! Wenn du das nicht tust, werden alle Tokens in Klartext gespeichert. Jedes Plugin und jedes Programm auf deinem Computer kann auf die Datei und damit auf alle Tokens zugreifen, wodurch diese alle mit einem Mal öffentlich geraten können. Wenn du Verschlüsselung aktivierst, werden alle Tokens mit dem eingegebenen Passwort verschlüsselt. Jedes Mal, wenn auf einen Token zugegriffen wird (Einstellungen öffnen/Account wechseln), musst du dieses Passwort wieder eingeben.", + "encryption": "Token verschlüsseln", + "account": "Account {0}", + "password": { + "set": "Set password", + "setDescription": "Setz hier das Passwort, das du in diesem Plugin verwenden willst. Wenn du das Passwort vergisst, kannst du die Tokens nicht mehr wiederherstellen.
", + "remove": "Remove password", + "removeDescription": "Willst du das Passwort wirklich entfernen? Dadurch werden alle Token in Klartext gespeichert!
Wenn du das Passwort wirklich entfernen willst, klick auf den OKAY-Knopf, sonst außerhalb dieses Popups." + }, + "accountNamePlaceholder": "Account-Name", + "accountTokenPlaceholder": "Account-Token", + "copyToken": "Token des aktuellen Accounts kopieren", + "copiedToken": "Token kopiert", + "fetchTranslations": "Fetch translations", + "fetchedTranslations": "Translations fetched!", + "support": "Support", + "passwordRequired": { + "title": "Passwort benötigt", + "description": "" + }, + "saveAccount": "Account speichern", + "removeAccount": "Account entfernen", + "showChangelog": "Changelog anzeigen" + }, + "couldNotDecrypt": "Token {0} konnte nicht entschlüsselt werden.", + "alreadyUsingAccount": "Du benutzt diesen Account bereits", + "invalidToken": "Der Token ist ungültig", + "passwordRequired": { + "title": "Passwort benötigt", + "description": "Um deinen Account zu wechseln muss du das Passwort eingeben, dass du gesetzt hast.
Wenn du dein Passwort vergessen hast, kannst du die Verschlüsselung in den Einstellungen deaktivieren, aber dann sind all deine Tokens nicht mehr zugreifbar.
" + }, + "removeAccount": { + "title": "Account entfernen", + "description": "Willst du den Account wirklich entfernen? Dann kannst du diesen nicht mehr nutzen, die Daten gehen verloren.", + "tooltip": "Account entfernen" + }, + "noAccountSet": { + "title": "Kein Account gesetzt", + "description": "Token {0} wurde nicht gesetzt. Möchtest du zum Login geleitet werden?" + } + }, + "fr": { + "plugin": { + "description": "Passez d'un compte à l'autre avec AltLeft+1 , AltLeft+2 , etc..." + }, + "settings": { + "language": "Langage", + "languages": { + "en": { "name": "English", "translator": "l0c4lh057" }, + "de": { "name": "German", "translator": "l0c4lh057" }, + "fr": { "name": "French", "translator": "Dark Mood" }, + "ru": { "name": "Russian", "translator": "•MGC•Mr_ChAI#7272" }, + "ja": { "name": "Japonais", "translator": "Near" }, + "pl": { "name": "Polish", "translator": "Killrog" }, + "auto": { "name": "Détection automatique" } + }, + "warning": "Ne PAS partager vos tokens avec quelqu'un d'autre. Sinon, ils peuvent utiliser votre compte/vos tokens avec toutes les actions qui n'ont pas besoin d'un mot de passe. Ceci ne peut être évité par l'a2f.
Si vous pensez que quelqu'un a votre token, activez l'a2f et changez votre mot de passe. Pour les deux actions, votre compte recevra un nouveau jeton. Mais n'oubliez pas de changer le token dans les paramètres suivants!

VEUILLEZ DÉFINIR UN MOT DE PASSE POUR ACTIVER LE CRYPTAGE! Si vous ne le faites pas, tous vos tokens seront sauvegardés en texte clair. Chaque plugin et chaque programme sur votre ordinateur peut accéder au fichier et tous vos jetons peuvent être rendus publics en même temps. Si vous activez le cryptage, tous les jetons seront cryptés avec votre mot de passe comme clé. Vous devrez entrer votre mot de passe chaque fois que vous ouvrirez les paramètres et chaque fois que vous voudrez changer votre compte.", + "encryption": "Crypter les tokens", + "account": "Comptes: {0}", + "password": { + "set": "Définir un mot de passe", + "setDescription": "Veuillez définir ici le mot de passe que vous souhaitez utiliser pour ce plugin. Si vous l'oubliez, tous vos jetons ne peuvent pas être restaurés et seront donc supprimés...
", + "remove": "Supprimer le mot de passe", + "removeDescription": "Êtes-vous sûr de vouloir supprimer le mot de passe ? Ceci sauvegardera vos tokens en texte clair!
ISi vous voulez vraiment prendre ce risque, cliquez sur le bouton OKAY/OK sinon cliquez à l'extérieur de ce popup." + }, + "accountNamePlaceholder": "Nom de compte", + "accountTokenPlaceholder": "Token de compte", + "copyToken": "Copier le token du compte actuel", + "copiedToken": "Token copié", + "fetchTranslations": "Fetch translations", + "fetchedTranslations": "Translations fetched!", + "support": "Avoir le support", + "passwordRequired": { + "title": "Mot de passe requis", + "description": "" + }, + "saveAccount": "Save Account", + "removeAccount": "Remove Account", + "showChangelog": "Show changelog" + }, + "couldNotDecrypt": "Impossible de décrypter les jetons {0}.", + "alreadyUsingAccount": "Vous utilisez déjà ce compte", + "invalidToken": "Le token est invalide", + "passwordRequired": { + "title": "Mot de passe requis", + "description": "Pour changer de compte, vous devez saisir le mot de passe que vous avez défini une fois.
Si vous ne vous en souvenez pas, vous pouvez désactiver le cryptage des tokens dans les paramètres, mais tous vos tokens auront disparus.
" + }, + "removeAccount": { + "title": "Removing account", + "description": "Do you really want to remove that account? If you accept this you can't get the account information back again.", + "tooltip": "Remove Account" + }, + "noAccountSet": { + "title": "No account set", + "description": "Token {0} is not set. Do you want to go to the login form?" + } + }, + "ru": { + "plugin": { + "description": "Переключайтесь между аккаунтами с помощью сочетаний клавиш от AltLeft+1 до AltLeft+0" + }, + "settings": { + "language": "Язык", + "languages": { + "en": { "name": "Английский", "translator": "l0c4lh057" }, + "de": { "name": "Немецкий", "translator": "l0c4lh057" }, + "fr": { "name": "Французский", "translator": "Dark Mood" }, + "ru": { "name": "Русский", "translator": "•MGC•Mr_ChAI#7272" }, + "ja": { "name": "японский", "translator": "Near" }, + "pl": { "name": "Polish", "translator": "Killrog" }, + "auto": { "name": "Автоопределение" } + }, + "warning": "НЕ передавайте никому свой токен! Иначе он(а) получит полный доступ к вашему аккаунту. Это не может быть предотвращено с помощью 2FA.
Если вы считаете, что у кого-то есть ваш токен, включите/выключите 2FA или поменяйте пароль - каждое из этих действий меняет пароль. Но не забудьте поменять его в настройках!

ПОЖАЛУЙСТА, УСТАНОВИТЕ ПАРОЛЬ, ВКЛЮЧИВ ШИФРОВАНИЕ! Если этого не сделать, токены будут храниться в виде обычного текста, и любой плагин/программа сможет их прочесть, в результате все ваши токены могут быть переданы кому-то другому. Если вы включите шифрование, все токены будут зашифрованы с паролем в качестве ключа. Вам придется вводить пароль каждый раз, когда вы открываете настройки плагина или меняете аккаунт.", + "encryption": "Шифрование токенов", + "account": "Аккаунт {0}", + "password": { + "set": "Установите пароль", + "setDescription": "Пожалуйста, установите пароль. Если вы его забудете, вы не сможете восстановить токены.
", + "remove": "Удаление пароля", + "removeDescription": "Вы уверены, что хотите убрать пароль? Ваши токены будут храниться в виде обычного текста!
Если вы все же решили рискнуть, нажмите кнопку OKAY, в противном случае кликните снаружи этого окошка." + }, + "accountNamePlaceholder": "Имя аккаунта", + "accountTokenPlaceholder": "Токен аккаунта", + "copyToken": "Скопировать токен текущего аккаунта", + "copiedToken": "Токен скопирован", + "fetchTranslations": "Обновить переводы", + "fetchedTranslations": "Переводы обновлены!", + "support": "Помощь", + "passwordRequired": { + "title": "Необходим пароль", + "description": "" + }, + "saveAccount": "Save Account", + "removeAccount": "Remove Account", + "showChangelog": "Показывать список изменений" + }, + "couldNotDecrypt": "Не удалось расшифровать токен аккаунта {0}.", + "alreadyUsingAccount": "Вы уже используете этот аккаунт", + "invalidToken": "Неправильный токен", + "passwordRequired": { + "title": "Необходим пароль", + "description": "Чтобы сменить аккаунт, нужен установленный вами пароль.
Если вы его забыли, вы можете отключить шифрование, но тогда все токены пропадут.
" + }, + "removeAccount": { + "title": "Удаление аккаунта", + "description": "Вы точно хотите удалить аккаунт? Если вы подтвердите, вы не сможете вернуть информацию об аккаунте.", + "tooltip": "Удалить аккаунт" + }, + "noAccountSet": { + "title": "Аккаунт не задан", + "description": "Токен {0} не задан. Вы хотите переключиться на страницу входа?" + } + }, + "ja": { + "plugin": { + "description": "複数のアカウントを左Alt + 1などで切り替えられるようにします" + }, + "settings": { + "language": "言語", + "languages": { + "en": { "name": "英語", "translator": "l0c4lh057" }, + "de": { "name": "ドイツ語", "translator": "l0c4lh057" }, + "fr": { "name": "フランス語", "translator": "Dark Mood" }, + "ru": { "name": "ロシア語", "translator": "•MGC•Mr_ChAI#7272" }, + "ja": { "name": "日本語", "translator": "にあ" }, + "pl": { "name": "ポーランド語", "translator": "Killrog" }, + "auto": { "name": "使用してる言語に合わせます" } + }, + "warning": "絶対に他人とトークン(Discord内で使われる認証用の文字列)を共有しないでください。共有し悪用された場合他人がパスワードを用いなくてもDiscordのアカウントの乗っ取りができます。そしてそれは二段階認証などの影響を受けません。
もしも誰かにトークンを盗まれたと思ったときはパスワードの変更か二段階認証の有効可・無効化などでトークンが変更できます。ただしこの操作をした場合トークンはもちろん更新されるため保存し直す必要があります。

暗号化を有効にしてパスワードを設定してください。これを行わないとこのプラグインで保存したトークンはすべてそのまま保存されます。コンピューター上のプログラムやDiscordに適応しているプラグインなどはこのトークンを保存したファイルにアクセス、それらを公開することが可能であることを留意してください。暗号化を有効にするとパスワードをキーとした暗号化がなされます。また、暗号化をした場合設定やアカウントの切り替えの際にパスワードが必要になります。(再起動とかした時の最初だけ!)", + "encryption": "トークンを暗号化する", + "account": "アカウント {0}", + "password": { + "set": "パスワードを設定", + "setDescription": "このプラグインを使用する際はパスワードを設定してください。もしもパスワードを忘れた場合、トークンを復号化することはできません。
", + "remove": "パスワードを削除する", + "removeDescription": "パスワードを削除しますか?すべてのトークンは暗号化されてない状態で保存されるようになります。
このリスクが理解できてなお暗号化を解除する場合は Okay と書かれたボタンを押してください
暗号化を解除したくない場合はこのポップアップの外を押してください(一回だけでいいよ!)" + }, + "accountNamePlaceholder": "名前", + "accountTokenPlaceholder": "トークン", + "copyToken": "今のアカウントのトークンをクリップボードにコピーする", + "copiedToken": "トークンをクリップボードにコピーしました", + "fetchTranslations": "言語を適用", + "fetchedTranslations": "言語が指定した言語にしっかり適応されました!", + "support": "サポート(日本語のサポートは無いから気をつけてね)", + "passwordRequired": { + "title": "パスワードを入力してください", + "description": "" + }, + "saveAccount": "保存", + "removeAccount": "削除", + "showChangelog": "変更履歴を表示" + }, + "couldNotDecrypt": "トークンの復号化に失敗しました。 {0}.", + "alreadyUsingAccount": "あなたは既にこのアカウントに切り替えてます", + "invalidToken": "このトークンは無効化されています。", + "passwordRequired": { + "title": "パスワードを入力してください", + "description": "アカウントを切り替えるには一度設定したパスワードが必要になります。
もしもあなたがパスワードを忘れたのなら暗号化されたトークンを復号化することはできません。
" + }, + "removeAccount": { + "title": "アカウントを削除する", + "description": "このアカウントを削除したいですか? 削除した場合切り替えることができなくなります", + "tooltip": "アカウントを削除する" + }, + "noAccountSet": { + "title": "ここにはアカウントが保存されていません", + "description": "スロット {0} にはトークンが保存されていません。ログインしますか?" + } + }, + "pl": { + "plugin": { + "description": "Przechodź między kontami używając klawiszy od AltLeft+1 do AltLeft+0" + }, + "settings": { + "language": "Język", + "languages": { + "en": { "name": "Angielski", "translator": "l0c4lh057" }, + "de": { "name": "Niemiecki", "translator": "l0c4lh057" }, + "fr": { "name": "Francuski", "translator": "Dark Mood" }, + "ru": { "name": "Rosyjski", "translator": "•MGC•Mr_ChAI#7272" }, + "ja": { "name": "Japoński", "translator": "Near" }, + "pl": { "name": "Polski", "translator": "Killrog" }, + "auto": { "name": "Wykryj automatycznie" } + }, + "warning": "Nie udostępniaj swoich tokenów innym osobom. W przeciwnym razie uzyskają oni pełny dostęp do twojego konta. Żadne 2FA ani logowanie SMS tutaj nie pomoże!
Jeżeli uważasz, że ktoś ma twój token, zmień hasło (token także się zmieni). Po tym nie zapomnij zmienić nowego tokenu w ustawieniach
PROSZE USTAW HASŁO PO PRZEZ WŁĄCZENIE SZYFROWANIA! Jeśli tego nie zrobisz, wszystkie twoje tokeny zostaną zapisane w postaci zwykłego tektu. Każda wtyczka i program na twoim komputerze będzie mogła go odczytać i ukraść. Jeżeli tokeny zostaną zaszyfrowane przy logowaniu lub zmianie konta będziesz musiał podać hasło.", + "encryption": "Szyfruj tokeny", + "account": "Konta {0}", + "password": { + "set": "Ustaw hasło", + "setDescription": "Ustaw hasło do szyfrowania tokenów. Nie ma możliwości przypomnienia hasła. Jeżeli zapomnisz hasło, nie odzyskasz tokenów.
", + "remove": "Usuń hasło", + "removeDescription": "Jesteś pewien że chcesz usunąć hasło? Twoje tokeny zostaną zapisane zwykłym tekstem!
Jeżeli jesteś pewien kliknij OK, w przeciwnym wypadku kliknij obok okienka." + }, + "accountNamePlaceholder": "Nazwa konta", + "accountTokenPlaceholder": "Token", + "copyToken": "Skopiuj token aktualnego konta", + "copiedToken": "Token skopiowany", + "fetchTranslations": "Pobierz tłumaczenia", + "fetchedTranslations": "Tłumaczenia pobrane!", + "support": "Pomoc", + "passwordRequired": { + "title": "Hasło jest wymagane", + "description": "" + }, + "saveAccount": "Zapisz Konto", + "removeAccount": "Usuń Konto", + "showChangelog": "Pokaż dziennik zmian" + }, + "couldNotDecrypt": "Nie można odszyfrować tokenu {0}.", + "alreadyUsingAccount": "Już korzystasz z tego konta", + "invalidToken": "Ten token jest nieprawidłowy", + "passwordRequired": { + "title": "Hasło jest wymagane", + "description": "Aby zmienić konto musisz podać hasło do odszyfrowania tokenów.
Jeżeli nie pamiętasz go możesz wyłączyć szyfrowanie w ustawieniach, ale wtedy wszystkie zapisane tokeny zostaną usunięte.
" + }, + "removeAccount": { + "title": "Usuwanie konta", + "description": "Jesteś pewien że chcesz usunąć to konto?", + "tooltip": "Usuń Konto" + }, + "noAccountSet": { + "title": "Konto nie istnieje", + "description": "Token {0} nie został ustawiony. Chcesz przejść do strony logowania?" + } + }, + "tr": { + "plugin": { + "description": "AltLeft+1 den AltLeft+0 tuşuna kadar hesaplar arasında geçiş yap" + }, + "settings": { + "language": "Dil", + "languages": { + "en": { "name": "İngilizce", "translator": "l0c4lh057" }, + "de": { "name": "Almanca", "translator": "l0c4lh057" }, + "fr": { "name": "Fransızca", "translator": "Dark Mood" }, + "ru": { "name": "Rusça", "translator": "•MGC•Mr_ChAI#7272" }, + "ja": { "name": "Japonca", "translator": "Near" }, + "pl": { "name": "Lehçe", "translator": "Killrog" }, + "auto": { "name": "Otomatik algıla" } + }, + "warning": "SAKIN hiçbir token'inizi başkalarıyla paylaşmayınız. Aksi taktirde parola gerektirmeyen her eyleme erişebilirler. Bu 2ad tarafından engellenemez.
Eğer birisinin token'inize sahip olduğunu düşünüyorsanız, 2ad'yi etkinleştirin ve parolanızı değiştirin. İki eylem için de hesabınız yeni bir token alacaktır. Ama sakın token'inizi ayarlardan değiştirmeyi unutmayın!

LÜTFEN ŞİFRELEMEYİ AÇARAK PAROLA OLUŞTURUN! Eğer bunu yapmazsanız, tüm token'leriniz okunabilir şekilde kaydedilecektir. Bilgisayarınızdaki her eklenti ve her program dosyaya erişebilir ve tüm token'leriniz tek bir seferde herkese açık hale getirilebilir. Eğer şifrelemeyi etkinleştirirseniz tüm token'leriniz anahtar olarak parolanızla şifrelenecektir. Ayarları her açtığınızda ve hesabınızı her değiştirmek istediğinizde parolanızı girmeniz gerekecektir.", + "encryption": "Token'leri şifrele", + "account": "Hesap {0}", + "password": { + "set": "Parola ayarla", + "setDescription": "Bu eklentide kullanmak üzere parolanızı ayarlayın. Eğer unutursanız hiçbir token'leriniz geri yüklenemez.
", + "remove": "Parolayı kaldır", + "removeDescription": "Parolayı kaldırmak istediğinize emin misiniz? Bu tüm token'lerinizi okunabilir şekilde kaydedecektir!
Eğer bunu gerçekten riske atmak istiyorsanız TAMAM tuşuna basın, eğer istemiyorsanız bu açılır pencerenin dışına tıklayın." + }, + "accountNamePlaceholder": "Hesap adı", + "accountTokenPlaceholder": "Hesap token'i", + "copyToken": "Mevcut hesabın token'ini kopyala", + "copiedToken": "Token kopyalandı", + "fetchTranslations": "Çevirileri yenile", + "fetchedTranslations": "Çeviriler yenilendi!", + "support": "Yardım Al", + "passwordRequired": { + "title": "Parola gerekli", + "description": "" + }, + "saveAccount": "Hesabı Kaydet", + "removeAccount": "Hesabı Kaldır", + "showChangelog": "Değişim kaydını görüntüle" + }, + "couldNotDecrypt": "Token'in şifresi çözülemedi {0}.", + "alreadyUsingAccount": "Zaten bu hesabı kullanıyorsunuz", + "invalidToken": "Bu token geçersiz", + "passwordRequired": { + "title": "Parola gerekli", + "description": "Hesabınızı değiştirmek için önceden ayarladığınız parolayı girmeniz gerekmektedir.
Eğer hatırlamıyorsanız ayarlardan token şifrelemesini kapatabilirsiniz ama tüm token'leriniz gider.
" + }, + "removeAccount": { + "title": "Hesap kaldırılıyor", + "description": "Bu hesabı kaldırmak istediğinize emin misiniz? Eğer kabul ederseniz hesap bilgilerini geri alamazsınız.", + "tooltip": "Hesabı Kaldır" + }, + "noAccountSet": { + "title": "Hiçbir hesap ayarlanmadı", + "description": "Token {0} ayarlanmadı. Giriş ekranına gitmek ister misiniz?" + } + } } From c5688568107b6abd002eb43e5c2161c3e0a2d58a Mon Sep 17 00:00:00 2001 From: Veysel <71281955+Veysinator@users.noreply.github.com> Date: Fri, 11 Jun 2021 15:34:31 +0300 Subject: [PATCH 2/4] Create .deepsource.toml --- .deepsource.toml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .deepsource.toml diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 0000000..f59843c --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,5 @@ +version = 1 + +[[analyzers]] +name = "javascript" +enabled = true From d8887024c945c71f3f6d707766f947810b1634c9 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Fri, 11 Jun 2021 12:38:37 +0000 Subject: [PATCH 3/4] Replace `==`/`!=` with `===`/`!==` --- AccountSwitcher.plugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AccountSwitcher.plugin.js b/AccountSwitcher.plugin.js index 237ec85..1bb0e25 100755 --- a/AccountSwitcher.plugin.js +++ b/AccountSwitcher.plugin.js @@ -467,7 +467,7 @@ module.exports = (() => { console.log("Logging in as " + account.name); this.requirePassword().then((r) => { const token = - password == null + password === null ? account.token : this.decrypt(account.token, password); AccountManager.loginToken(this.decrypt(token, account.id)); From 8c461076b34633dabb7dcc23b972c8d823d85cd1 Mon Sep 17 00:00:00 2001 From: Veysel <71281955+Veysinator@users.noreply.github.com> Date: Sun, 13 Jun 2021 22:50:07 +0300 Subject: [PATCH 4/4] Update AccountSwitcher.plugin.js --- AccountSwitcher.plugin.js | 812 +------------------------------------- 1 file changed, 2 insertions(+), 810 deletions(-) diff --git a/AccountSwitcher.plugin.js b/AccountSwitcher.plugin.js index 1bb0e25..a684618 100755 --- a/AccountSwitcher.plugin.js +++ b/AccountSwitcher.plugin.js @@ -1,284 +1,4 @@ -/** - * @name AccountSwitcher - * @displayName AccountSwitcher - * @source https://github.com/l0c4lh057/AccountSwitcher/blob/master/AccountSwitcher.plugin.js - * @patreon https://www.patreon.com/l0c4lh057 - * @authorId 226677096091484160 - * @invite YzzeuJPpyj - */ -/*@cc_on -@if (@_jscript) - - // Offer to self-install for clueless users that try to run this directly. - var shell = WScript.CreateObject("WScript.Shell"); - var fs = new ActiveXObject("Scripting.FileSystemObject"); - var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\BetterDiscord\plugins"); - var pathSelf = WScript.ScriptFullName; - // Put the user at ease by addressing them in the first person - shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30); - if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) { - shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40); - } else if (!fs.FolderExists(pathPlugins)) { - shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10); - } else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) { - fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true); - // Show the user where to put plugins in the future - shell.Exec("explorer " + pathPlugins); - shell.Popup("I'm installed!", 0, "Successfully installed", 0x40); - } - WScript.Quit(); - -@else@*/ - -module.exports = (() => { - const config = { - info: { - name: "AccountSwitcher", - authors: [ - { - name: "l0c4lh057", - discord_id: "226677096091484160", - github_username: "l0c4lh057", - twitter_username: "l0c4lh057", - }, - ], - version: "1.3.2", - description: - "Simply switch between accounts with the ease of pressing a single key.", - github: "https://github.com/l0c4lh057/AccountSwitcher", - github_raw: - "https://raw.githubusercontent.com/l0c4lh057/AccountSwitcher/master/AccountSwitcher.plugin.js", - }, - changelog: [ - { - title: "Changed", - type: "progress", - items: ["Switched to a newer, even more secure encryption method."], - }, - { - title: "Fixed", - type: "fixed", - items: [ - "Showing the keybinds in the settings again. (The toggle for encryption still does not work but that is added by ZeresPluginLibrary which I do not control.)", - ], - }, - ], - }; - - let password = null; - const algorithm = "aes-256-cbc"; - const IV_LENGTH = 16; - const Buffer = require("buffer").Buffer; - - return !global.ZeresPluginLibrary - ? class { - constructor() { - this._config = config; - } - getName() { - return config.info.name; - } - getAuthor() { - return config.info.authors.map((a) => a.name).join(", "); - } - getDescription() { - return config.info.description; - } - getVersion() { - return config.info.version; - } - load() { - BdApi.showConfirmationModal( - "Library plugin is needed", - [ - `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, - ], - { - confirmText: "Download", - cancelText: "Cancel", - onConfirm: () => { - require("request").get( - "https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", - async (error, response, body) => { - if (error) - return require("electron").shell.openExternal( - "https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js" - ); - await new Promise((r) => - require("fs").writeFile( - require("path").join( - BdApi.Plugins.folder, - "0PluginLibrary.plugin.js" - ), - body, - r - ) - ); - } - ); - }, - } - ); - } - start() {} - stop() {} - } - : (([Plugin, Api]) => { - const plugin = (Plugin, Api) => { - const { - WebpackModules, - PluginUtilities, - DiscordModules, - Settings, - Toasts, - Modals, - DOMTools, - } = Api; - const { React, ReactDOM, UserStore, UserInfoStore } = DiscordModules; - const AccountManager = WebpackModules.getByProps("loginToken"); - const Markdown = WebpackModules.getByDisplayName("Markdown"); - const unregisterKeybind = WebpackModules.getByProps( - "inputEventUnregister" - ).inputEventUnregister.bind( - WebpackModules.getByProps("inputEventUnregister") - ); - const registerKeybind = WebpackModules.getByProps( - "inputEventRegister" - ).inputEventRegister.bind( - WebpackModules.getByProps("inputEventUnregister") - ); - const crypto = require("crypto"); - - if ( - !BdApi.Plugins.get("BugReportHelper") && - !BdApi.getData(config.info.name, "didShowIssueHelperPopup") - ) { - BdApi.saveData(config.info.name, "didShowIssueHelperPopup", true); - BdApi.showConfirmationModal( - "Do you want to download a helper plugin?", - [ - `Do you want to download a helper plugin that makes it easier for you to report issues? That plugin is not needed to anything else to function correctly but nice to have when reporting iissues, shortening the time until the problem gets resolved by asking you for specific information and also including additional information you did not provide.`, - ], - { - confirmText: "Download", - cancelText: "Cancel", - onConfirm: () => { - require("request").get( - "https://raw.githubusercontent.com/l0c4lh057/BetterDiscordStuff/master/Plugins/BugReportHelper/BugReportHelper.plugin.js", - (error, response, body) => { - if (error) - return require("electron").shell.openExternal( - "https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/l0c4lh057/BetterDiscordStuff/master/Plugins/BugReportHelper/BugReportHelper.plugin.js" - ); - else - require("fs").writeFile( - require("path").join( - BdApi.Plugins.folder, - "BugReportHelper.plugin.js" - ), - body, - () => { - window.setTimeout( - () => BdApi.Plugins.enable("BugReportHelper"), - 1000 - ); - } - ); - } - ); - }, - } - ); - } - - const KeyRecorder = class KeyRecorder extends WebpackModules.getByDisplayName( - "KeyRecorder" - ) { - render() { - const ButtonOptions = WebpackModules.getByProps("ButtonLink"); - const Button = ButtonOptions.default; - const ret = super.render(); - ret.props.children.props.children.props.children.push( - React.createElement( - DiscordModules.FlexChild, - { - style: { margin: 0 }, - }, - React.createElement( - Button, - { - className: WebpackModules.getByProps( - "editIcon", - "button" - ).button.split(" ")[1], - size: Button.Sizes.MIN, - color: ButtonOptions.ButtonColors.GREY, - look: ButtonOptions.ButtonLooks.GHOST, - onClick: this.props.onRemove, - }, - "Remove" - ) - ) - ); - return ret; - } - }; - const KeybindModule = class KeybindModule extends DiscordModules.Keybind { - constructor(props) { - super(props); - } - render() { - const ret = super.render(); - ret.type = KeyRecorder; - ret.props.account = this.props.account; - ret.props.onRemove = this.props.onRemove; - return ret; - } - }; - const Keybind = class Keybind extends Settings.SettingField { - constructor(account, onChange, onRemove) { - super( - account.name + " (" + account.id + ")", - "", - onChange, - KeybindModule, - { - defaultValue: - (account.keybind[0] !== -1 && - account.keybind.map((a) => [0, a])) || - [], - onChange: (element) => (value) => { - if (!Array.isArray(value)) return; - element.props.value = value; - this.onChange(value.map((a) => a[1])); - }, - account, - onRemove, - } - ); - } - }; - return class AccountSwitcher extends Plugin { - updateAvatars() { - this.settings.accounts.forEach((acc) => { - const u = UserStore.getUser(acc.id); - if (u) acc.avatar = u.avatarURL; - }); - } - - onStart() { - password = null; - this.loadSettings(); - if (this.settings.salt === undefined) { - this.settings.salt = crypto.randomBytes(32).toString("base64"); - } - this.settings.accounts.forEach((acc) => - this.registerKeybind(acc) - ); - this.openMenu = this.openMenu.bind(this); - PluginUtilities.addStyle( - "accountswitcher-style", - ` +module.exports=(()=>{const a={info:{name:"AccountSwitcher",authors:[{name:"l0c4lh057",discord_id:"226677096091484160",github_username:"l0c4lh057",twitter_username:"l0c4lh057"}],version:"1.3.2",description:"Simply switch between accounts with the ease of pressing a single key.",github:"https://github.com/l0c4lh057/AccountSwitcher",github_raw:"https://raw.githubusercontent.com/l0c4lh057/AccountSwitcher/master/AccountSwitcher.plugin.js"},changelog:[{title:"Changed",type:"progress",items:["Switched to a newer, even more secure encryption method."]},{title:"Fixed",type:"fixed",items:["Showing the keybinds in the settings again. (The toggle for encryption still does not work but that is added by ZeresPluginLibrary which I do not control.)"]}]};let b=null;const c="aes-256-cbc",d=require("buffer").Buffer;return global.ZeresPluginLibrary?(([e,f])=>{return((e,f)=>{const{WebpackModules:g,PluginUtilities:h,DiscordModules:i,Settings:j,Toasts:k,Modals:l,DOMTools:m}=f,{React:n,ReactDOM:o,UserStore:p,UserInfoStore:q}=i,r=g.getByProps("loginToken"),s=g.getByDisplayName("Markdown"),t=g.getByProps("inputEventUnregister").inputEventUnregister.bind(g.getByProps("inputEventUnregister")),u=g.getByProps("inputEventRegister").inputEventRegister.bind(g.getByProps("inputEventUnregister")),v=require("crypto");BdApi.Plugins.get("BugReportHelper")||BdApi.getData(a.info.name,"didShowIssueHelperPopup")||(BdApi.saveData(a.info.name,"didShowIssueHelperPopup",!0),BdApi.showConfirmationModal("Do you want to download a helper plugin?",[`Do you want to download a helper plugin that makes it easier for you to report issues? That plugin is not needed to anything else to function correctly but nice to have when reporting iissues, shortening the time until the problem gets resolved by asking you for specific information and also including additional information you did not provide.`],{confirmText:"Download",cancelText:"Cancel",onConfirm:()=>{require("request").get("https://raw.githubusercontent.com/l0c4lh057/BetterDiscordStuff/master/Plugins/BugReportHelper/BugReportHelper.plugin.js",(a,b,c)=>a?require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/l0c4lh057/BetterDiscordStuff/master/Plugins/BugReportHelper/BugReportHelper.plugin.js"):void require("fs").writeFile(require("path").join(BdApi.Plugins.folder,"BugReportHelper.plugin.js"),c,()=>{window.setTimeout(()=>BdApi.Plugins.enable("BugReportHelper"),1e3)}))}}));const w=class extends g.getByDisplayName("KeyRecorder"){render(){const a=g.getByProps("ButtonLink"),b=a.default,c=super.render();return c.props.children.props.children.props.children.push(n.createElement(i.FlexChild,{style:{margin:0}},n.createElement(b,{className:g.getByProps("editIcon","button").button.split(" ")[1],size:b.Sizes.MIN,color:a.ButtonColors.GREY,look:a.ButtonLooks.GHOST,onClick:this.props.onRemove},"Remove"))),c}},x=class extends i.Keybind{constructor(a){super(a)}render(){const a=super.render();return a.type=w,a.props.account=this.props.account,a.props.onRemove=this.props.onRemove,a}},y=class extends j.SettingField{constructor(a,b,c){super(a.name+" ("+a.id+")","",b,x,{defaultValue:-1!==a.keybind[0]&&a.keybind.map(b=>[0,b])||[],onChange:a=>b=>{Array.isArray(b)&&(a.props.value=b,this.onChange(b.map(b=>b[1])))},account:a,onRemove:c})}};return class extends e{updateAvatars(){this.settings.accounts.forEach(a=>{const b=p.getUser(a.id);b&&(a.avatar=b.avatarURL)})}onStart(){b=null,this.loadSettings(),this.settings.salt===void 0&&(this.settings.salt=v.randomBytes(32).toString("base64")),this.settings.accounts.forEach(a=>this.registerKeybind(a)),this.openMenu=this.openMenu.bind(this),h.addStyle("accountswitcher-style",` .accountswitcher-switchmenu { position: fixed; width: auto; @@ -315,532 +35,4 @@ module.exports = (() => { right: 0; position: absolute; } - ` - ); - document.addEventListener("mouseup", this.openMenu); - this.updateAvatars(); - if (this.settings.encryptionVersion !== 2) { - if (!this.settings.encrypted) { - this.settings.accounts.forEach((acc) => { - acc.token = this.encryptUpdated( - this.decryptDeprecated(acc.token, acc.id), - acc.id - ); - }); - this.settings.encryptionVersion = 2; - this.saveSettings(); - } else { - this.requirePassword( - "The encryption algorithm got updated for better security. Please enter your password to decrypt your tokens and encrypt them again with the new algorithm." - ).then(() => { - this.settings.encTest = this.encryptUpdated( - "test", - password - ); - this.settings.accounts.forEach((acc) => { - acc.token = this.encryptUpdated( - this.encryptUpdated( - this.decryptDeprecated( - this.decryptDeprecated(acc.token, password), - acc.id - ), - acc.id - ), - password - ); - }); - this.settings.encryptionVersion = 2; - this.saveSettings(); - }); - } - } - } - - onStop() { - this.settings.accounts.forEach((acc) => - this.unregisterKeybind(acc) - ); - document.removeEventListener("mouseup", this.openMenu); - PluginUtilities.removeStyle("accountswitcher-style"); - } - - get defaultSettings() { - return { - accounts: [], - encrypted: false, - encTest: "test", - pluginsToRestart: [ - "AccountDetailsPlus", - "AutoStartRichPresence", - ], - encryptionVersion: 1, - }; - } - - openMenu(e) { - if (e.which != 2) return; - if (!e.target || !e.target.classList) return; - if ( - !e.target.classList.contains( - WebpackModules.getByProps( - "avatar", - "container", - "nameTag" - ).avatar.split(" ")[0] - ) - ) - return; - e.preventDefault(); - const menu = document.createElement("div"); - const AccountPanel = (account) => - React.createElement( - "div", - { - className: "accountswitcher-accountwrapper", - }, - React.createElement("img", { - src: account.avatar, - className: "accountswitcher-menuavatar", - onClick: (e) => this.login(account), - }), - React.createElement( - "div", - { - className: "accountswitcher-removeaccount", - onClick: (e) => { - this.unregisterKeybind(account); - this.settings.accounts = this.settings.accounts.filter( - (acc) => acc.id != account.id - ); - this.saveSettings(); - Toasts.show("Account " + account.name + " removed", { - type: Toasts.ToastTypes.success, - }); - }, - }, - "⨯" - ) - ); - if (this.settings.accounts.length > 0) { - const offset = DOMTools.offset(e.target); - ReactDOM.render( - React.createElement( - "div", - { - className: "accountswitcher-switchmenu", - style: { - bottom: offset.bottom - offset.top + 27, - left: offset.left - 5, - }, - }, - this.settings.accounts.map((account) => - React.createElement(AccountPanel, account) - ) - ), - menu - ); - document.body.appendChild(menu); - } else { - Toasts.show("No accounts to display", { - type: Toasts.ToastTypes.warning, - }); - } - const eventHandler = (ev) => { - if (!ev.target || !ev.target.classList) return; - if ( - !ev.target.classList.contains("accountswitcher-switchmenu") && - !ev.target.classList.contains("accountswitcher-removeaccount") - ) { - menu.remove(); - document.removeEventListener(eventHandler); - } - }; - document.addEventListener("click", eventHandler); - } - - login(account) { - console.log("Logging in as " + account.name); - if (account.id == UserStore.getCurrentUser().id) - return Toasts.show("Already using account " + account.name, { - type: Toasts.ToastTypes.warning, - }); - console.log("Logging in as " + account.name); - this.requirePassword().then((r) => { - const token = - password === null - ? account.token - : this.decrypt(account.token, password); - AccountManager.loginToken(this.decrypt(token, account.id)); - window.setTimeout(this.updateAvatars, 5000); - this.settings.pluginsToRestart.forEach((pl) => { - if (BdApi.Plugins.isEnabled(pl)) { - BdApi.Plugins.disable(pl); - window.setTimeout(() => BdApi.Plugins.enable(pl), 5000); - } - }); - }); - } - - getSettingsPanel() { - const panel = document.createElement("div"); - panel.className = "form"; - panel.style = "width:100%;"; - const accountsField = new Settings.SettingGroup("Accounts", { - shown: true, - }); - const addAccount = (account) => { - if (!account) { - let u = UserStore.getCurrentUser(); - let t = this.encrypt(UserInfoStore.getToken(), u.id); - let acc = { - name: u.tag, - id: u.id, - avatar: u.avatarURL, - keybind: [64, 10 + this.settings.accounts.length], - token: this.settings.encrypted - ? this.encrypt(t, password) - : t, - }; - this.settings.accounts.push(acc); - this.saveSettings(); - this.registerKeybind(acc); - return addAccount(acc); - } - const kbPanel = new Keybind( - account, - (keybind) => { - this.unregisterKeybind(account); - account.keybind = keybind; - this.saveSettings(); - this.registerKeybind(account); - }, - () => { - this.unregisterKeybind(account); - this.settings.accounts = this.settings.accounts.filter( - (acc) => acc.id != account.id - ); - this.saveSettings(); - // TODO: remove account from DOM so you are not required to repopen the settings - Toasts.show( - "Account " + - account.name + - " got removed. After reopening the settings it will also be gone from this list.", - { type: Toasts.ToastTypes.success } - ); - } - ); - accountsField.append(kbPanel); - }; - const addAccountButton = document.createElement("button"); - // TODO: remove hardcoded classes - addAccountButton.className = - "button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMedium-1AC_Sl grow-q77ONN"; - addAccountButton.addEventListener("click", () => { - if ( - this.settings.accounts.some( - (acc) => acc.id == UserStore.getCurrentUser().id - ) - ) { - return Toasts.show("You already saved this account", { - type: Toasts.ToastTypes.error, - }); - } - this.requirePassword().then((r) => { - addAccount(); - }); - }); - addAccountButton.innerText = "Save Account"; - - new Settings.SettingGroup(this.getName(), { shown: true }) - .appendTo(panel) - .append( - new Settings.Switch( - "Encrypt tokens", - "Encrypting tokens makes sure that nobody will be able to get the tokens without knowing the password.", - this.settings.encrypted, - (checked) => { - if (checked === this.settings.encrypted) return; - if (checked) { - const retry = () => { - let pw1 = ""; - let pw2 = ""; - Modals.showModal( - "Set password", - React.createElement( - "div", - {}, - React.createElement("input", { - type: "password", - placeholder: "Password", - onChange: (e) => { - pw1 = e.target.value; - }, - }), - React.createElement("input", { - type: "password", - placeholder: "Repeat password", - onChange: (e) => { - pw2 = e.target.value; - }, - }) - ), - { - onConfirm: () => { - if (pw1 != pw2) { - Toasts.show("Passwords don't match", { - type: Toasts.ToastTypes.error, - }); - return retry(); - } - password = pw1; - this.settings.encrypted = true; - this.settings.encTest = this.encrypt( - "test", - password - ); - this.settings.accounts.forEach( - (acc) => - (acc.token = this.encrypt( - acc.token, - password - )) - ); - this.saveSettings(); - }, - } - ); - }; - retry(); - } else { - const retry = () => { - let pw = ""; - Modals.showModal( - "Disable encryption", - React.createElement( - "div", - {}, - React.createElement( - Markdown, - {}, - "Are you sure that you want to disable encryption? To verify please input your current password. You can also choose the 'Forgot Password' option which will remove all saved accounts. To abort just click outside of this popout." - ), - React.createElement("input", { - type: "password", - placeholder: "Password", - onChange: (e) => { - pw = e.target.value; - }, - }) - ), - { - onConfirm: () => { - try { - if ( - this.decrypt(this.settings.encTest, pw) !== - "test" - ) { - Toasts.show("Passwords incorrect", { - type: Toasts.ToastTypes.error, - }); - return retry(); - } - this.settings.encrypted = false; - this.settings.encTest = "test"; - this.settings.accounts.forEach( - (acc) => - (acc.token = this.decrypt(acc.token, pw)) - ); - password = null; - this.saveSettings(); - } catch (ex) { - Toasts.show("Passwords incorrect", { - type: Toasts.ToastTypes.error, - }); - return retry(); - } - }, - onCancel: () => { - Modals.showConfirmationModal( - "Are you sure?", - "You are about to disable encryption which will remove all your currently saved accounts without an option to recover them. Only use this if you really forgot your password.", - { - onConfirm: () => { - this.settings.encTest = "test"; - this.settings.encrypted = false; - this.settings.accounts = []; - password = null; - this.saveSettings(); - }, - } - ); - }, - confirmText: "Disable encryption", - cancelText: "Forgot Password", - } - ); - }; - retry(); - } - } - ) - ) - .append(accountsField) - .append(addAccountButton) - .append( - new Settings.Textbox( - "Plugins to restart", - "Put the name of all plugins that should get restarted when you switch accounts in this textbox separated by a comma", - this.settings.pluginsToRestart.join(","), - (val) => { - this.settings.pluginsToRestart = val - .split(",") - .map((x) => x.trim()) - .filter((x) => x); - this.saveSettings(); - } - ) - ); - this.settings.accounts.forEach((acc) => addAccount(acc)); - return panel; - } - - // This function does NOT return the password, it just ensures that the correct password is stored in the "password" variable. - // The password should never be exposed so there should be no way to access the password from outside this plugin. - async requirePassword(message) { - if (!this.settings.encrypted || password !== null) - return Promise.resolve(); - return new Promise((resolve, reject) => { - const retry = (t) => { - let pw = ""; - Modals.showModal( - "Password required", - React.createElement( - React.Fragment, - !message ? null : message, - !message ? null : React.createElement("br"), - React.createElement("input", { - type: "password", - onChange: (e) => { - pw = e.target.value; - }, - }) - ), - { - onConfirm: () => { - try { - if ( - this.decrypt(this.settings.encTest, pw) !== "test" - ) { - Toasts.show("Wrong password", { - type: Toasts.ToastTypes.error, - }); - return retry(t + 1); - } - password = pw; - resolve(); - } catch (ex) { - Toasts.show("Wrong password", { - type: Toasts.ToastTypes.error, - }); - retry(t + 1); - } - }, - } - ); - }; - retry(0); - }); - } - - encryptDeprecated(text, pw) { - const key = crypto.createCipher("aes-128-cbc", pw); - return key.update(text, "utf8", "hex") + key.final("hex"); - } - decryptDeprecated(text, pw) { - const key = crypto.createDecipher("aes-128-cbc", pw); - return key.update(text, "hex", "utf8") + key.final("utf8"); - } - - generateEncryptionKey(pw) { - return crypto.pbkdf2Sync( - Buffer.from(pw, "utf8"), - this.settings.salt, - 100000, - 32, - "sha512" - ); - } - encryptUpdated(text, pw) { - const iv = crypto.randomBytes(IV_LENGTH); - const cipher = crypto.createCipheriv( - algorithm, - this.generateEncryptionKey(pw), - iv - ); - const encrypted = Buffer.concat([ - cipher.update(text), - cipher.final(), - ]); - return iv.toString("base64") + ":" + encrypted.toString("base64"); - } - decryptUpdated(text, pw) { - const parts = text.split(":"); - const iv = Buffer.from(parts.shift(), "base64"); - const encrypted = Buffer.from(parts.join(":"), "base64"); - const decipher = crypto.createDecipheriv( - algorithm, - this.generateEncryptionKey(pw), - iv - ); - const decrypted = Buffer.concat([ - decipher.update(encrypted), - decipher.final(), - ]); - return decrypted.toString(); - } - - encrypt(text, pw) { - return this.settings.encryptionVersion === 2 - ? this.encryptUpdated(text, pw) - : this.encryptDeprecated(text, pw); - } - decrypt(text, pw) { - return this.settings.encryptionVersion === 2 - ? this.decryptUpdated(text, pw) - : this.decryptDeprecated(text, pw); - } - - registerKeybind(account) { - registerKeybind( - "119" + account.id, - account.keybind.map((a) => [0, a]), - (pressed) => { - this.login(account); - }, - { blurred: false, focused: true, keydown: true, keyup: false } - ); - } - unregisterKeybind(account) { - unregisterKeybind("119" + account.id); - } - - loadSettings() { - this.settings = PluginUtilities.loadSettings( - this.getName(), - this.defaultSettings - ); - if (!Array.isArray(this.settings.accounts)) - this.settings.accounts = Object.values(this.settings.accounts); - this.settings.accounts.forEach((acc) => { - if (!Array.isArray(acc.keybind)) - acc.keybind = Object.values(acc.keybind); - }); - } - saveSettings() { - PluginUtilities.saveSettings(this.getName(), this.settings); - } - }; - }; - return plugin(Plugin, Api); - })(global.ZeresPluginLibrary.buildPlugin(config)); -})(); + `),document.addEventListener("mouseup",this.openMenu),this.updateAvatars(),2!==this.settings.encryptionVersion&&(this.settings.encrypted?this.requirePassword("The encryption algorithm got updated for better security. Please enter your password to decrypt your tokens and encrypt them again with the new algorithm.").then(()=>{this.settings.encTest=this.encryptUpdated("test",b),this.settings.accounts.forEach(a=>{a.token=this.encryptUpdated(this.encryptUpdated(this.decryptDeprecated(this.decryptDeprecated(a.token,b),a.id),a.id),b)}),this.settings.encryptionVersion=2,this.saveSettings()}):(this.settings.accounts.forEach(a=>{a.token=this.encryptUpdated(this.decryptDeprecated(a.token,a.id),a.id)}),this.settings.encryptionVersion=2,this.saveSettings()))}onStop(){this.settings.accounts.forEach(a=>this.unregisterKeybind(a)),document.removeEventListener("mouseup",this.openMenu),h.removeStyle("accountswitcher-style")}get defaultSettings(){return{accounts:[],encrypted:!1,encTest:"test",pluginsToRestart:["AccountDetailsPlus","AutoStartRichPresence"],encryptionVersion:1}}openMenu(a){if(2!=a.which)return;if(!a.target||!a.target.classList)return;if(!a.target.classList.contains(g.getByProps("avatar","container","nameTag").avatar.split(" ")[0]))return;a.preventDefault();const b=document.createElement("div"),c=a=>n.createElement("div",{className:"accountswitcher-accountwrapper"},n.createElement("img",{src:a.avatar,className:"accountswitcher-menuavatar",onClick:()=>this.login(a)}),n.createElement("div",{className:"accountswitcher-removeaccount",onClick:()=>{this.unregisterKeybind(a),this.settings.accounts=this.settings.accounts.filter(b=>b.id!=a.id),this.saveSettings(),k.show("Account "+a.name+" removed",{type:k.ToastTypes.success})}},"\u2A2F"));if(0n.createElement(c,a))),b),document.body.appendChild(b)}else k.show("No accounts to display",{type:k.ToastTypes.warning});const d=a=>{a.target&&a.target.classList&&(a.target.classList.contains("accountswitcher-switchmenu")||a.target.classList.contains("accountswitcher-removeaccount")||(b.remove(),document.removeEventListener(d)))};document.addEventListener("click",d)}login(a){return console.log("Logging in as "+a.name),a.id==p.getCurrentUser().id?k.show("Already using account "+a.name,{type:k.ToastTypes.warning}):void(console.log("Logging in as "+a.name),this.requirePassword().then(()=>{const c=null===b?a.token:this.decrypt(a.token,b);r.loginToken(this.decrypt(c,a.id)),window.setTimeout(this.updateAvatars,5e3),this.settings.pluginsToRestart.forEach(a=>{BdApi.Plugins.isEnabled(a)&&(BdApi.Plugins.disable(a),window.setTimeout(()=>BdApi.Plugins.enable(a),5e3))})}))}getSettingsPanel(){const a=document.createElement("div");a.className="form",a.style="width:100%;";const c=new j.SettingGroup("Accounts",{shown:!0}),d=a=>{if(!a){let a=p.getCurrentUser(),c=this.encrypt(q.getToken(),a.id),e={name:a.tag,id:a.id,avatar:a.avatarURL,keybind:[64,10+this.settings.accounts.length],token:this.settings.encrypted?this.encrypt(c,b):c};return this.settings.accounts.push(e),this.saveSettings(),this.registerKeybind(e),d(e)}const e=new y(a,b=>{this.unregisterKeybind(a),a.keybind=b,this.saveSettings(),this.registerKeybind(a)},()=>{this.unregisterKeybind(a),this.settings.accounts=this.settings.accounts.filter(b=>b.id!=a.id),this.saveSettings(),k.show("Account "+a.name+" got removed. After reopening the settings it will also be gone from this list.",{type:k.ToastTypes.success})});c.append(e)},e=document.createElement("button");return e.className="button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMedium-1AC_Sl grow-q77ONN",e.addEventListener("click",()=>this.settings.accounts.some(a=>a.id==p.getCurrentUser().id)?k.show("You already saved this account",{type:k.ToastTypes.error}):void this.requirePassword().then(()=>{d()})),e.innerText="Save Account",new j.SettingGroup(this.getName(),{shown:!0}).appendTo(a).append(new j.Switch("Encrypt tokens","Encrypting tokens makes sure that nobody will be able to get the tokens without knowing the password.",this.settings.encrypted,a=>{if(a!==this.settings.encrypted)if(a){const a=()=>{let c="",d="";l.showModal("Set password",n.createElement("div",{},n.createElement("input",{type:"password",placeholder:"Password",onChange:a=>{c=a.target.value}}),n.createElement("input",{type:"password",placeholder:"Repeat password",onChange:a=>{d=a.target.value}})),{onConfirm:()=>c==d?void(b=c,this.settings.encrypted=!0,this.settings.encTest=this.encrypt("test",b),this.settings.accounts.forEach(a=>a.token=this.encrypt(a.token,b)),this.saveSettings()):(k.show("Passwords don't match",{type:k.ToastTypes.error}),a())})};a()}else{const a=()=>{let c="";l.showModal("Disable encryption",n.createElement("div",{},n.createElement(s,{},"Are you sure that you want to disable encryption? To verify please input your current password. You can also choose the 'Forgot Password' option which will remove all saved accounts. To abort just click outside of this popout."),n.createElement("input",{type:"password",placeholder:"Password",onChange:a=>{c=a.target.value}})),{onConfirm:()=>{try{if("test"!==this.decrypt(this.settings.encTest,c))return k.show("Passwords incorrect",{type:k.ToastTypes.error}),a();this.settings.encrypted=!1,this.settings.encTest="test",this.settings.accounts.forEach(a=>a.token=this.decrypt(a.token,c)),b=null,this.saveSettings()}catch(b){return k.show("Passwords incorrect",{type:k.ToastTypes.error}),a()}},onCancel:()=>{l.showConfirmationModal("Are you sure?","You are about to disable encryption which will remove all your currently saved accounts without an option to recover them. Only use this if you really forgot your password.",{onConfirm:()=>{this.settings.encTest="test",this.settings.encrypted=!1,this.settings.accounts=[],b=null,this.saveSettings()}})},confirmText:"Disable encryption",cancelText:"Forgot Password"})};a()}})).append(c).append(e).append(new j.Textbox("Plugins to restart","Put the name of all plugins that should get restarted when you switch accounts in this textbox separated by a comma",this.settings.pluginsToRestart.join(","),a=>{this.settings.pluginsToRestart=a.split(",").map(a=>a.trim()).filter(a=>a),this.saveSettings()})),this.settings.accounts.forEach(a=>d(a)),a}async requirePassword(a){return this.settings.encrypted&&null===b?new Promise(c=>{const d=e=>{let f="";l.showModal("Password required",n.createElement(n.Fragment,a?a:null,a?n.createElement("br"):null,n.createElement("input",{type:"password",onChange:a=>{f=a.target.value}})),{onConfirm:()=>{try{if("test"!==this.decrypt(this.settings.encTest,f))return k.show("Wrong password",{type:k.ToastTypes.error}),d(e+1);b=f,c()}catch(a){k.show("Wrong password",{type:k.ToastTypes.error}),d(e+1)}}})};d(0)}):Promise.resolve()}encryptDeprecated(a,b){const c=v.createCipher("aes-128-cbc",b);return c.update(a,"utf8","hex")+c.final("hex")}decryptDeprecated(a,b){const c=v.createDecipher("aes-128-cbc",b);return c.update(a,"hex","utf8")+c.final("utf8")}generateEncryptionKey(a){return v.pbkdf2Sync(d.from(a,"utf8"),this.settings.salt,1e5,32,"sha512")}encryptUpdated(a,b){const e=v.randomBytes(16),f=v.createCipheriv(c,this.generateEncryptionKey(b),e),g=d.concat([f.update(a),f.final()]);return e.toString("base64")+":"+g.toString("base64")}decryptUpdated(a,b){const e=a.split(":"),f=d.from(e.shift(),"base64"),g=d.from(e.join(":"),"base64"),h=v.createDecipheriv(c,this.generateEncryptionKey(b),f),i=d.concat([h.update(g),h.final()]);return i.toString()}encrypt(a,b){return 2===this.settings.encryptionVersion?this.encryptUpdated(a,b):this.encryptDeprecated(a,b)}decrypt(a,b){return 2===this.settings.encryptionVersion?this.decryptUpdated(a,b):this.decryptDeprecated(a,b)}registerKeybind(a){u("119"+a.id,a.keybind.map(b=>[0,b]),()=>{this.login(a)},{blurred:!1,focused:!0,keydown:!0,keyup:!1})}unregisterKeybind(a){t("119"+a.id)}loadSettings(){this.settings=h.loadSettings(this.getName(),this.defaultSettings),Array.isArray(this.settings.accounts)||(this.settings.accounts=Object.values(this.settings.accounts)),this.settings.accounts.forEach(a=>{Array.isArray(a.keybind)||(a.keybind=Object.values(a.keybind))})}saveSettings(){h.saveSettings(this.getName(),this.settings)}}})(e,f)})(global.ZeresPluginLibrary.buildPlugin(a)):class{constructor(){this._config=a}getName(){return a.info.name}getAuthor(){return a.info.authors.map(b=>b.name).join(", ")}getDescription(){return a.info.description}getVersion(){return a.info.version}load(){BdApi.showConfirmationModal("Library plugin is needed",[`The library plugin needed for ${a.info.name} is missing. Please click Download Now to install it.`],{confirmText:"Download",cancelText:"Cancel",onConfirm:()=>{require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js",async(a,b,c)=>a?require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js"):void(await new Promise(a=>require("fs").writeFile(require("path").join(BdApi.Plugins.folder,"0PluginLibrary.plugin.js"),c,a))))}})}start(){}stop(){}}})();