[
  {
    "path": "Assets/AC/Preview/README.md",
    "content": "![Preview](e0.gif)\n![Settings Preview #1](se0.png)\n![Settings Preview #2](se1.png)\n"
  },
  {
    "path": "Assets/AIC/Preview/README.md",
    "content": "![Example #1](e0.png)\n![Example #2](e1.png)\n![Example #3](e2.png)\n![Example #4](e3.png)\n![Example #5](e4.png)\n"
  },
  {
    "path": "Assets/BDEA/Preview/README.md",
    "content": "![Preview](e0.png)\n![Settings Preview](se0.png)\n"
  },
  {
    "path": "Assets/BES/Preview/README.md",
    "content": "![Example #1](e0.png)\n![Example #2](e1.png)\n"
  },
  {
    "path": "Assets/BPECC/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n"
  },
  {
    "path": "Assets/CJS/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n"
  },
  {
    "path": "Assets/DST/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n![Settings Preview](se0.png)\n"
  },
  {
    "path": "Assets/FMC/Preview/README.md",
    "content": "![Video Preview #1](ve0.mp4)\n![Video Preview #2](ve1.mp4)\n![Preview #1](e0.png)\n![Preview #2](e1.png)\n"
  },
  {
    "path": "Assets/GC/Preview/README.md",
    "content": "![Preview #1](e0.png)\n"
  },
  {
    "path": "Assets/GFRA/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n![Preview #3](e2.png)\n"
  },
  {
    "path": "Assets/IB/Preview/README.md",
    "content": "![Video Preview](ve0.mp4)\n"
  },
  {
    "path": "Assets/MA/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n![Preview #3](e2.png)\n"
  },
  {
    "path": "Assets/ML/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n![Settings Preview #1](se0.png)\n![Settings Preview #2](se1.png)\n"
  },
  {
    "path": "Assets/OLID/Preview/README.md",
    "content": "![Video Preview](ve0.mp4)\n"
  },
  {
    "path": "Assets/PCC/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n![Preview #3](e2.png)\n"
  },
  {
    "path": "Assets/PPT/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n![Preview #3](e2.png)\n"
  },
  {
    "path": "Assets/RA/Preview/README.md",
    "content": "![Video Preview](ve0.mp4)\n![Settings Preview](se0.png)\n"
  },
  {
    "path": "Assets/SB/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n![Preview #3](e2.png)\n![Preview #4](e3.png)\n"
  },
  {
    "path": "Assets/SBDE/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n"
  },
  {
    "path": "Assets/SLD/Preview/README.md",
    "content": "![Video Preview](ve0.mp4)\n"
  },
  {
    "path": "Assets/ST/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n![Preview #3](e2.png)\n![Video Preview](ve0.mp4)\n![Settings Preview #1](e3.png)\n![Settings Preview #1](e4.png)\n"
  },
  {
    "path": "Assets/TB/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n![Video Preview #1](ve0.mp4)\n![Video Preview #2](ve1.mp4)\n"
  },
  {
    "path": "Assets/UCB/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n![Settings Preview](se0.png)\n"
  },
  {
    "path": "Assets/VCN/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n![Preview #3](e2.png)\n![Settings Preview](e3.png)\n"
  },
  {
    "path": "Assets/VGR/Preview/README.md",
    "content": "![Preview #1](e0.png)\n![Preview #2](e1.png)\n"
  },
  {
    "path": "AutoCorrect.plugin.js",
    "content": "/**\r\n * @name AutoCorrect\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www.paypal.me/israelboone\r\n * @website https://metalloriff.github.io/toms-discord-stuff/\r\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/AutoCorrect.plugin.js\r\n */\r\n\r\n/* BUGS AND TODO\r\n\tImplement DB's SpellChecker.\r\n*/\r\n\r\nmodule.exports = (() =>\r\n{\r\n\tconst config =\r\n\t{\r\n\t\tinfo:\r\n\t\t{\r\n\t\t\tname: \"AutoCorrect\",\r\n\t\t\tauthors:\r\n\t\t\t[\r\n\t\t\t\t{\r\n\t\t\t\t\tname: \"Metalloriff\",\r\n\t\t\t\t\tdiscord_id: \"264163473179672576\",\r\n\t\t\t\t\tgithub_username: \"metalloriff\",\r\n\t\t\t\t\ttwitter_username: \"Metalloriff\"\r\n\t\t\t\t}\r\n\t\t\t],\r\n\t\t\tversion: \"2.0.1\",\r\n\t\t\tdescription: \"Gives you the options to automatically replace all mis-spelled words with Discord's first correction, automatically punctuate and/or capitalize your sentences, and set up override aliases for typing.\",\r\n\t\t\tgithub: \"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/AutoCorrect.plugin.js\",\r\n\t\t\tgithub_raw: \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/AutoCorrect.plugin.js\"\r\n\t\t},\r\n\t\tchangelog:\r\n\t\t[\r\n\t\t\t{\r\n\t\t\t\ttitle: \"2.0 Rewrite\",\r\n\t\t\t\ttype: \"fixed\",\r\n\t\t\t\titems: [\r\n\t\t\t\t\t\"AutoCorrect has been completely rewritten from the ground up, new bugs that I missed during development are likely to occur. If you experience any bugs, please report them to me.\",\r\n\t\t\t\t\t\"Fixed a settings save/load issue.\",\r\n\t\t\t\t\t\"Fixed spelling errors not auto replacing, I did big dumb.\"\r\n\t\t\t\t]\r\n\t\t\t}\r\n\t\t]\r\n\t};\r\n\r\n\treturn !global.ZeresPluginLibrary ? class\r\n\t{\r\n\t\tconstructor() { this._config = config; }\r\n\r\n\t\tgetName = () => config.info.name;\r\n\t\tgetAuthor = () => config.info.description;\r\n\t\tgetVersion = () => config.info.version;\r\n\r\n\t\tload()\r\n\t\t{\r\n\t\t\tBdApi.showConfirmationModal(\"Library Missing\", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {\r\n\t\t\t\tconfirmText: \"Download Now\",\r\n\t\t\t\tcancelText: \"Cancel\",\r\n\t\t\t\tonConfirm: () =>\r\n\t\t\t\t{\r\n\t\t\t\t\trequire(\"request\").get(\"https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js\", async (err, res, body) =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (err) return require(\"electron\").shell.openExternal(\"https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js\");\r\n\t\t\t\t\t\tawait new Promise(r => require(\"fs\").writeFile(require(\"path\").join(BdApi.Plugins.folder, \"0PluginLibrary.plugin.js\"), body, r));\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tstart() { }\r\n\t\tstop() { }\r\n\t} : (([Plugin, Api]) => {\r\n\r\n\t\tconst plugin = (Plugin, Api) =>\r\n\t\t{\r\n\t\t\tconst { WebpackModules, DiscordModules, ReactComponents, Patcher, PluginUtilities, Settings, Utilities } = Api;\r\n\t\t\tconst { React } = DiscordModules;\r\n\r\n\t\t\tconst process = require(\"process\");\r\n\t\t\tconst SlateModule = WebpackModules.find(m => m.deserialize && !m.add);\r\n\t\t\tconst SpellChecker = WebpackModules.getByProps(\"isMisspelled\");\r\n\t\t\tconst ContextMenu = WebpackModules.getByProps(\"MenuItem\");\r\n\t\t\tconst Button = WebpackModules.find(m => m.Link && m.Link.displayName == \"ButtonLink\");\r\n\r\n\t\t\tconst getSelectiontext = WebpackModules.getByProps(\"getSelectionText\").getSelectionText;\r\n\t\t\tconst formatToDict = s =>\r\n\t\t\t{\r\n\t\t\t\tif (s == null || s.toLowerCase == null)\r\n\t\t\t\t\treturn \"\";\r\n\t\t\t\tconst match = s.toLowerCase().match(/([a-z]|[0-9])/gi);\r\n\t\t\t\treturn match == null ? \"\" : match.join(\"\");\r\n\t\t\t};\r\n\r\n\t\t\tlet lastRender = new Date();\r\n\t\t\tlet lastError;\r\n\t\t\tlet lastLength;\r\n\t\t\tlet lastTextAreaEvent;\r\n\r\n\t\t\treturn class AutoCorrect extends Plugin \r\n\t\t\t{\r\n\t\t\t\tconstructor()\r\n\t\t\t\t{\r\n\t\t\t\t\tsuper();\r\n\r\n\t\t\t\t\tthis.defaultSettings =\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tautoReplace: true,\r\n\t\t\t\t\t\tpunctuate: true,\r\n\t\t\t\t\t\tcapitalize: true,\r\n\t\t\t\t\t\tbsUndo: true,\r\n\t\t\t\t\t\toverrides:\r\n\t\t\t\t\t\t[\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tkey: \"idk\",\r\n\t\t\t\t\t\t\t\tvalue: \"I don't know\"\r\n\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tkey: \"discord\",\r\n\t\t\t\t\t\t\t\tvalue: \"shitcord\"\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t],\r\n\t\t\t\t\t\tdictionary: []\r\n\t\t\t\t\t};\r\n\t\t\t\t}\r\n\r\n\t\t\t\tasync showChangelog(footer)\r\n\t\t\t\t{\r\n\t\t\t\t\ttry { footer = (await WebpackModules.getByProps(\"getUser\", \"acceptAgreements\").getUser(\"264163473179672576\")).tag + \" | https://discord.gg/yNqzuJa\"; }\r\n\t\t\t\t\tfinally { super.showChangelog(footer); }\r\n\t\t\t\t}\r\n\r\n\t\t\t\tloadSettings = () => this.settings = PluginUtilities.loadSettings(config.info.name, this.defaultSettings);\r\n\t\t\t\tsaveSettings = () => PluginUtilities.saveSettings(config.info.name, this.settings);\r\n\r\n\t\t\t\tcreateSettingsSwitch(title, desc, setting)\r\n\t\t\t\t{\r\n\t\t\t\t\treturn new Settings.Switch(title, desc, this.settings[setting], value =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tthis.settings[setting] = value;\r\n\t\t\t\t\t\tthis.saveSettings();\r\n\t\t\t\t\t}).getElement();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcreateSettingsOverrideItem(index)\r\n\t\t\t\t{\r\n\t\t\t\t\tconst item = new Settings.SettingGroup(\"Override Item #\" + (parseInt(index) + 1)).append(\r\n\t\t\t\t\t\tnew Settings.Textbox(\"Key\", null, this.settings.overrides[index].key, key =>\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tthis.settings.overrides[index].key = key;\r\n\t\t\t\t\t\t\tthis.saveSettings();\r\n\t\t\t\t\t\t}).getElement(),\r\n\t\t\t\t\t\tnew Settings.Textbox(\"Value\", null, this.settings.overrides[index].value, value =>\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tthis.settings.overrides[index].value = value;\r\n\t\t\t\t\t\t\tthis.saveSettings();\r\n\t\t\t\t\t\t}).getElement(),\r\n\t\t\t\t\t\tnew Settings.SettingField(null, null, null, Button,\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tchildren: \"Remove Override\",\r\n\t\t\t\t\t\t\tonClick: () =>\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tthis.settings.overrides.splice(index);\r\n\t\t\t\t\t\t\t\tthis.saveSettings();\r\n\t\t\t\t\t\t\t\tdocument.getElementById(\"ac-oi-\" + index).remove();\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}).getElement()\r\n\t\t\t\t\t).getElement();\r\n\r\n\t\t\t\t\titem.id = \"ac-oi-\" + index;\r\n\r\n\t\t\t\t\treturn item;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tgetSettingsPanel()\r\n\t\t\t\t{\r\n\t\t\t\t\tthis.loadSettings();\r\n\r\n\t\t\t\t\tlet overrideItems = [];\r\n\t\t\t\t\tfor (let i in this.settings.overrides)\r\n\t\t\t\t\t\toverrideItems.push(this.createSettingsOverrideItem(i));\r\n\r\n\t\t\t\t\treturn new Settings.SettingPanel(null,\r\n\t\t\t\t\t\tthis.createSettingsSwitch(\"Auto Replace Spelling Errors\", \"Automatically replaces any spelling mistakes you make with the first available correction, if any.\", \"autoReplace\"),\r\n\t\t\t\t\t\tthis.createSettingsSwitch(\"Auto Punctuate\", \"Automatically punctuates your messages when you send them.\", \"punctuate\"),\r\n\t\t\t\t\t\tthis.createSettingsSwitch(\"Auto Capitalize\", \"Automatically capitlizes the first words after punctuations and at the start of your messages.\", \"capitalize\"),\r\n\t\t\t\t\t\tthis.createSettingsSwitch(\"Backspace Undo Correction\", \"Hitting backspace after a correction will undo it to the errored word.\", \"bsUndo\"),\r\n\t\t\t\t\t\tnew Settings.SettingGroup(\"Overrides\").append(\r\n\t\t\t\t\t\t\t...overrideItems,\r\n\t\t\t\t\t\t\tnew Settings.SettingField(null, null, null, Button,\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tchildren: \"Add Override\",\r\n\t\t\t\t\t\t\t\tonClick: () =>\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tthis.settings.overrides.push(\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tkey: \"ex\",\r\n\t\t\t\t\t\t\t\t\t\tvalue: \"Example.\"\t\r\n\t\t\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\t\t\tthis.saveSettings();\r\n\r\n\t\t\t\t\t\t\t\t\tconst parent = document.querySelector(\"div.plugin-settings div.plugin-inputs\");\r\n\t\t\t\t\t\t\t\t\tparent.insertBefore(this.createSettingsOverrideItem(this.settings.overrides.length - 1), parent.lastChild);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}).getElement()\r\n\t\t\t\t\t\t).getElement()\r\n\t\t\t\t\t).getElement();\r\n\t\t\t\t}\r\n\t\r\n\t\t\t\tasync onStart()\r\n\t\t\t\t{\r\n\t\t\t\t\tthis.loadSettings();\r\n\r\n\t\t\t\t\tconst TextArea = await ReactComponents.getComponentByName(\"ChannelEditorContainer\", \"*\");\r\n\t\t\t\t\tconst SlateTextAreaContextMenu = WebpackModules.find(m => m.default && m.default.displayName == \"SlateTextAreaContextMenu\");\r\n\r\n\t\t\t\t\tSpellChecker.setLearnedWords(new Set(this.settings.dictionary));\r\n\t\t\t\t\tPatcher.after(SpellChecker, \"setLearnedWords\", (_, [set]) =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfor (let word of set)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (this.settings.dictionary.indexOf(formatToDict(word)) == -1)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tthis.learnWord(formatToDict(word));\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\tPatcher.after(SlateTextAreaContextMenu, \"default\", (_, [], re) =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (getSelectiontext().length == 0)\r\n\t\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\t\t\tlet index = -1;\r\n\t\t\t\t\t\tfor (let i = 0; i < this.settings.dictionary.length; i++)\r\n\t\t\t\t\t\t\tif (this.settings.dictionary[i] == formatToDict(getSelectiontext()))\r\n\t\t\t\t\t\t\t\tindex = i;\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\tif (index == -1)\r\n\t\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\t\t\tconst spellCheckGroup = Utilities.getNestedProp(re, \"props.children.1.props.children.props.children\");\r\n\t\t\t\t\t\tif (spellCheckGroup != null)\r\n\t\t\t\t\t\t\tspellCheckGroup.unshift(\r\n\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\tContextMenu.MenuItem,\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tlabel: \"Remove from Dictionary\",\r\n\t\t\t\t\t\t\t\t\t\tid: \"ac-removefromdict\",\r\n\t\t\t\t\t\t\t\t\t\taction: () => this.forgetWord(index)\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tconsole.warn(\"AutoCorrect: SpellCheckGroup nested prop could not be found!\");\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\tPatcher.instead(DiscordModules.MessageActions, \"sendMessage\", (_, args, sendMessage) =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tthis.tryCorrect(args[1].content + \"  \", true).then(correction =>\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\targs[1].content = correction;\r\n\t\t\t\t\t\t\tsendMessage(...args);\r\n\r\n\t\t\t\t\t\t\tlastLength = -1;\r\n\r\n\t\t\t\t\t\t\tprocess.nextTick(() => this.setText(null, \"\"));\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\tPatcher.after(TextArea.component.prototype, \"render\", e =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconst now = new Date();\r\n\r\n\t\t\t\t\t\tif (now - lastRender > 10)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tthis.tryCorrect(e.props.textValue, false).then(correction =>\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tif (e.props.textValue != correction)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tthis.setText(e, correction);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tlastRender = now;\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\r\n\t\t\t\tasync tryCorrect(textValue, sending)\r\n\t\t\t\t{\r\n\t\t\t\t\tconst words = textValue.split(\" \");\r\n\t\t\t\t\tlet lastWordIndex = words.length - 2;\r\n\r\n\t\t\t\t\tfor (let i = words.length - 1; i > -1; i--)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (words[i].trim().length > 0)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tlastWordIndex = i;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (words.join(\" \").length < lastLength)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (this.settings.bsUndo)\r\n\t\t\t\t\t\t\twords[lastWordIndex] = lastError;\r\n\t\t\t\t\t\tlastLength = words.join(\" \").length;\r\n\r\n\t\t\t\t\t\treturn words.join(\" \");\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (this.settings.autoReplace && (sending || textValue.endsWith(\" \")) && words[lastWordIndex] != lastError)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tlet hasOverride = false;\r\n\t\t\t\t\t\tfor (let o of this.settings.overrides)\r\n\t\t\t\t\t\t\tif (o.value.toLowerCase() == words[lastWordIndex].toLowerCase())\r\n\t\t\t\t\t\t\t\thasOverride = true;\r\n\r\n\t\t\t\t\t\tconst isMisspelled = !hasOverride && this.settings.dictionary.indexOf(formatToDict(words[lastWordIndex])) == -1 && await SpellChecker.isMisspelled(words[lastWordIndex]);\r\n\r\n\t\t\t\t\t\tif (isMisspelled)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tconst corrections = await SpellChecker.getCorrections(words[lastWordIndex]);\r\n\r\n\t\t\t\t\t\t\tif (corrections && corrections.length > 0)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\twords[lastWordIndex] = corrections[0];\r\n\t\t\t\t\t\t\t\tlastError = words[lastWordIndex];\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tawait Promise.resolve();\r\n\r\n\t\t\t\t\tif (words[lastWordIndex] != null)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfor (let override of this.settings.overrides)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (override.key.toLowerCase() == words[lastWordIndex].toLowerCase()\r\n\t\t\t\t\t\t\t\t&& override.value.toLowerCase() != words[lastWordIndex].toLowerCase() && words[lastWordIndex] != lastError)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tlastError = words[lastWordIndex];\r\n\t\t\t\t\t\t\t\twords[lastWordIndex] = override.value;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tlet wasPunctuated = false;\r\n\r\n\t\t\t\t\tif (this.settings.capitalize && (sending || textValue.endsWith(\" \")))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (words[lastWordIndex] == \"i\")\r\n\t\t\t\t\t\t\twords[lastWordIndex] = \"I\";\r\n\t\t\t\t\t\tif (words[0].charAt(0) != words[0].charAt(0).toUpperCase() && !words[0].trim().startsWith(\"http\"))\r\n\t\t\t\t\t\t\twords[0] = words[0].charAt(0).toUpperCase() + words[0].slice(1);\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tfor (let i = 0; i < words.length; i++)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (wasPunctuated && words[i].trim().length > 1 && !words[i].trim().startsWith(\"http\"))\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\twords[i] = words[i].charAt(0).toUpperCase() + words[i].slice(1);\r\n\t\t\t\t\t\t\t\twasPunctuated = false;\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tif (words[i].trim().match(/[.!?\\\\-]$/g))\r\n\t\t\t\t\t\t\t\twasPunctuated = true;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (this.settings.punctuate && (sending || textValue.endsWith(\"  \")))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfor (let i = words.length - 1; i > -1; i--)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (words[i].trim().match(/[.!?\\\\-]$/g))\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\ti = -1;\r\n\t\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tif (words[i].trim().endsWith(\">\")\r\n\t\t\t\t\t\t\t\t|| words[i].trim().match(/(\\u00a9|\\u00ae|[\\u2000-\\u3300]|\\ud83c[\\ud000-\\udfff]|\\ud83d[\\ud000-\\udfff]|\\ud83e[\\ud000-\\udfff])/g) || words[i].trim().startsWith(\"http\"))\r\n\t\t\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\tif (i > -1 && words[i].trim().length > 0)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\twords[i] += \".\";\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\treturn words.join(\" \");\r\n\t\t\t\t}\r\n\r\n\t\t\t\tsetText(_e, text)\r\n\t\t\t\t{\r\n\t\t\t\t\tconst e = _e == null ? lastTextAreaEvent : _e;\r\n\r\n\t\t\t\t\tif (e && e.ref.current)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\te.ref.current.setValue(SlateModule.deserialize(text));\r\n\t\t\t\t\t\te.focus();\r\n\r\n\t\t\t\t\t\te.ref.current.editorRef.moveFocusToEndOfText();\r\n\t\t\t\t\t\te.ref.current.editorRef.moveToFocus();\r\n\r\n\t\t\t\t\t\tlastTextAreaEvent = e;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tlearnWord(word)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (word.length == 0)\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\t\tthis.settings.dictionary.push(formatToDict(word));\r\n\t\t\t\t\tthis.saveSettings();\r\n\r\n\t\t\t\t\tSpellChecker.setLearnedWords(new Set(this.settings.dictionary));\r\n\t\t\t\t}\r\n\r\n\t\t\t\tforgetWord(index)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (index != -1)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tthis.settings.dictionary.splice(index);\r\n\t\t\t\t\t\tthis.saveSettings();\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tSpellChecker.setLearnedWords(new Set(this.settings.dictionary));\r\n\t\t\t\t}\r\n\t\r\n\t\t\t\tonStop()\r\n\t\t\t\t{\r\n\t\t\t\t\tPatcher.unpatchAll();\r\n\t\t\t\t}\r\n\t\t\t};\r\n\t\t}\r\n\r\n\t\treturn plugin(Plugin, Api);\r\n\t})(global.ZeresPluginLibrary.buildPlugin(config));\r\n})();\r\n"
  },
  {
    "path": "AutoRefreshSettingsPanel.plugin.js",
    "content": "//META{\"name\":\"AutoRefreshSettingsPanel\"}*//\r\n\r\nclass AutoRefreshSettingsPanel {\r\n\t\r\n    getName() { return \"AutoRefreshSettingsPanel\"; }\r\n    getDescription() { return \"Automatically refreshes the BetterDiscord plugin settings panel when clicking into Discord, making developing settings menus 1,000x faster if you have a lot of plugins.\"; }\r\n    getVersion() { return \"0.0.1\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\r\n    load() {}\r\n\r\n    start() {\r\n\r\n        this.refreshPluginSettings = () => {\r\n\t\t\tlet panel = document.getElementsByClassName(\"plugin-settings\")[0];\r\n\t\t\tif(panel) panel.innerHTML = BdApi.getPlugin(panel.id.substring(panel.id.lastIndexOf(\"-\") + 1, panel.id.length)).getSettingsPanel();\r\n\t\t};\r\n\r\n        window.addEventListener(\"focus\", this.refreshPluginSettings);\r\n\r\n\t}\r\n\t\r\n    stop() {\r\n        window.removeEventListener(\"focus\", this.refreshPluginSettings);\r\n\t}\r\n\t\r\n}"
  },
  {
    "path": "AvatarIconViewer.plugin.js",
    "content": "/**\r\n * @name AvatarIconViewer\r\n * @invite yNqzuJa\r\n * @authorLink https://discord.com/users/264163473179672576\r\n * @donate https://www.paypal.me/israelboone\r\n * @website https://kinzoku.one/\r\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/AvatarIconViewer.plugin.js\r\n */\r\n\r\nmodule.exports = (() => {\r\n\tconst config =\r\n\t{\r\n\t\tinfo:\r\n\t\t{\r\n\t\t\tname: \"AvatarIconViewer\",\r\n\t\t\tauthors:\r\n\t\t\t\t[\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tname: \"Metalloriff\",\r\n\t\t\t\t\t\tdiscord_id: \"264163473179672576\",\r\n\t\t\t\t\t\tgithub_username: \"metalloriff\",\r\n\t\t\t\t\t\ttwitter_username: \"Metalloriff\"\r\n\t\t\t\t\t}\r\n\t\t\t\t],\r\n\t\t\tversion: \"2.0.2\",\r\n\t\t\tdescription: \"Allows you to view server icons, user avatars, and emotes in fullscreen via the context menu, or copy the link to them.\",\r\n\t\t\tgithub: \"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/AvatarIconViewer.plugin.js\",\r\n\t\t\tgithub_raw: \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/AvatarIconViewer.plugin.js\"\r\n\t\t}\r\n\t};\r\n\r\n\treturn !global.ZeresPluginLibrary ? class {\r\n\t\tconstructor() { this._config = config; }\r\n\r\n\t\tgetName = () => config.info.name;\r\n\t\tgetAuthor = () => config.info.description;\r\n\t\tgetVersion = () => config.info.version;\r\n\r\n\t\tload() {\r\n\t\t\tBdApi.showConfirmationModal(\"Library Missing\", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {\r\n\t\t\t\tconfirmText: \"Download Now\",\r\n\t\t\t\tcancelText: \"Cancel\",\r\n\t\t\t\tonConfirm: () => {\r\n\t\t\t\t\trequire(\"request\").get(\"https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js\", async (err, res, body) => {\r\n\t\t\t\t\t\tif (err) return require(\"electron\").shell.openExternal(\"https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js\");\r\n\t\t\t\t\t\tawait new Promise(r => require(\"fs\").writeFile(require(\"path\").join(BdApi.Plugins.folder, \"0PluginLibrary.plugin.js\"), body, r));\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tstart() { }\r\n\t\tstop() { }\r\n\t} : (([Plugin, Api]) => {\r\n\r\n\t\tconst plugin = (Plugin, Api) => {\r\n\t\t\tconst { DiscordModules, WebpackModules, Patcher } = Api;\r\n\t\t\tconst { React } = DiscordModules\r\n\t\t\tconst ModalStack = WebpackModules.getByProps(\"openModal\", \"hasModalOpen\");\r\n\t\t\tconst { ModalRoot } = WebpackModules.getByProps(\"ModalRoot\");\r\n\t\t\tconst { ModalSize } = WebpackModules.getByProps(\"ModalSize\");\r\n\r\n\t\t\tconst ImageModal = WebpackModules.getByDisplayName(\"ImageModal\");\r\n\t\t\tconst ContextMenu = WebpackModules.getByProps(\"MenuItem\");\r\n\t\t\tconst MaskedLink = WebpackModules.getByDisplayName(\"MaskedLink\");\r\n\r\n\t\t\tconst getChannelIconURL = WebpackModules.getByProps(\"getChannelIconURL\").getChannelIconURL;\r\n\t\t\tconst copyToClipboard = require(\"electron\").clipboard.writeText;\r\n\r\n\t\t\tconst formatURL = url =>\r\n\t\t\t\turl == null || url.length == 0\r\n\t\t\t\t\t? null\r\n\t\t\t\t\t: (url.includes(\"/a_\")\r\n\t\t\t\t\t\t? url.replace(\".webp\", \".gif\").replace(\".png\", \".gif\")\r\n\t\t\t\t\t\t: url).split(\"?\")[0] + \"?size=2048\";\r\n\r\n\t\t\treturn class AvatarIconViewer extends Plugin {\r\n\t\t\t\tconstructor() {\r\n\t\t\t\t\tsuper();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tasync onStart() {\r\n\t\t\t\t\t// I have no idea why, nor do I have the energy to figure out why,\r\n\t\t\t\t\t// but shitcord does not load the context menu modules until the context menu is opened.\r\n\r\n\t\t\t\t\t// Therefore, this shitty workaround was made, which requires the user to\r\n\t\t\t\t\t// open the context menu once before the plugin will work.\r\n\r\n\t\t\t\t\t// UPDATE: It's less spaghetti, but still spaghetti. I'm sorry.\r\n\r\n\t\t\t\t\tthis.patched = [];\r\n\t\t\t\t\tdocument.getElementById(\"app-mount\").addEventListener(\"contextmenu\", this.contextMenuListener = e => {\r\n\t\t\t\t\t\tconst modules = WebpackModules.findAll(\r\n\t\t\t\t\t\t\tm => m.default && m.default.displayName && (\r\n\t\t\t\t\t\t\t\tm.default.displayName.endsWith(\"UserContextMenu\") || ~[\r\n\t\t\t\t\t\t\t\t\t\"GroupDMContextMenu\",\r\n\t\t\t\t\t\t\t\t\t\"GuildContextMenu\",\r\n\t\t\t\t\t\t\t\t\t\"MessageContextMenu\"\r\n\t\t\t\t\t\t\t\t].indexOf(m.default.displayName)\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t);\r\n\r\n\t\t\t\t\t\tfor (const m of modules) {\r\n\t\t\t\t\t\t\tif (~this.patched.indexOf(m.default.displayName)) {\r\n\t\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tswitch (m.default.displayName) {\r\n\t\t\t\t\t\t\t\tdefault: {\r\n\t\t\t\t\t\t\t\t\tPatcher.after(m, \"default\", (_, [props], re) => {\r\n\t\t\t\t\t\t\t\t\t\tconst type = props.user ? \"Avatar\" : props.channel && props.channel.type == 3 ? \"Icon\" : null;\r\n\t\t\t\t\t\t\t\t\t\tconst url = formatURL(type == \"Avatar\" ? props.user.getAvatarURL() : type == \"Icon\" ? getChannelIconURL(props.channel) : null);\r\n\r\n\t\t\t\t\t\t\t\t\t\tif (type && url)\r\n\t\t\t\t\t\t\t\t\t\t\tre.props.children.props.children.push(this.createContext(url, type));\r\n\t\t\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\t\t} break;\r\n\r\n\t\t\t\t\t\t\t\tcase \"GuildContextMenu\": {\r\n\t\t\t\t\t\t\t\t\tPatcher.after(m, \"default\", (_, [props], re) => {\r\n\t\t\t\t\t\t\t\t\t\tconst url = formatURL(props.guild.getIconURL());\r\n\r\n\t\t\t\t\t\t\t\t\t\tif (url)\r\n\t\t\t\t\t\t\t\t\t\t\tre.props.children.push(this.createContext(url, \"Icon\"));\r\n\t\t\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\t\t} break;\r\n\r\n\t\t\t\t\t\t\t\tcase \"MessageContextMenu\": {\r\n\t\t\t\t\t\t\t\t\tPatcher.after(m, \"default\", (_, [props], re) => {\r\n\t\t\t\t\t\t\t\t\t\tif (props.target && props.target.src) {\r\n\t\t\t\t\t\t\t\t\t\t\tre.props.children.push(this.createContext(props.target.src, \"Emoji\"));\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\t\t} break;\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tthis.patched.push(m.default.displayName);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcreateContext(url, type) {\r\n\t\t\t\t\tconst ClassModule = WebpackModules.getByProps(\"modal\", \"image\");\r\n\r\n\t\t\t\t\treturn React.createElement(ContextMenu.MenuGroup,\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tchildren:\r\n\t\t\t\t\t\t\t\t[\r\n\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\tContextMenu.MenuItem,\r\n\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\tlabel: \"View \" + type,\r\n\t\t\t\t\t\t\t\t\t\t\tid: \"aiv-view\",\r\n\t\t\t\t\t\t\t\t\t\t\taction: () =>\r\n\t\t\t\t\t\t\t\t\t\t\t\tModalStack.openModal(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tprops => (\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tModalRoot,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName: ClassModule.modal,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t...props,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsize: ModalSize.DYNAMIC\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tImageModal,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsrc: url,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplaceholder: url,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\toriginal: url,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: 2048,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: 2048,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClickUntrusted: e => e.openHref(),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trenderLinkComponent: props => React.createElement(MaskedLink, props)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t),\r\n\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\tContextMenu.MenuItem,\r\n\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\tlabel: \"Copy \" + type + \" Link\",\r\n\t\t\t\t\t\t\t\t\t\t\tid: \"aiv-copy\",\r\n\t\t\t\t\t\t\t\t\t\t\taction: () => copyToClipboard(url)\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t]\r\n\t\t\t\t\t\t});\r\n\t\t\t\t}\r\n\r\n\t\t\t\tonStop() {\r\n\t\t\t\t\tPatcher.unpatchAll();\r\n\t\t\t\t\tdocument.getElementById(\"app-mount\").removeEventListener(\"contextmenu\", this.contextMenuListener);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\treturn plugin(Plugin, Api);\r\n\t})(global.ZeresPluginLibrary.buildPlugin(config));\r\n})();\r\n"
  },
  {
    "path": "BDEmoteAutocomplete.plugin.js",
    "content": "//META{\"name\":\"BDEmoteAutocomplete\",\"website\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/README.md\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/BDEmoteAutocomplete.plugin.js\"}*//\r\n\r\nclass BDEmoteAutocomplete {\r\n\t\r\n    getName() { return \"BDEmoteAutocomplete\"; }\r\n    getDescription() { return \"Adds an auto-complete menu for BetterDiscord emotes.\"; }\r\n    getVersion() { return \"1.0.4\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\tgetChanges() {\r\n\t\treturn {\r\n\t\t\t\r\n\t\t};\r\n\t}\r\n\r\n    load() {}\r\n\r\n    start() {\r\n\r\n        let libLoadedEvent = () => {\r\n            try{ this.onLibLoaded(); }\r\n            catch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); }\r\n        };\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif(lib == undefined) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.setAttribute(\"id\", \"NeatoBurritoLibrary\");\r\n\t\t\tlib.setAttribute(\"type\", \"text/javascript\");\r\n\t\t\tlib.setAttribute(\"src\", \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\");\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n        if(typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n        else lib.addEventListener(\"load\", libLoadedEvent);\r\n\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\r\n\t\tsetTimeout(() => {\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createToggleSwitch(\"Case-sensitive\", this.settings.caseSensitive, () => {\r\n                this.settings.caseSensitive = !this.settings.caseSensitive;\r\n                this.saveSettings();\r\n            }), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createToggleGroup(\"bdea-enabled-channels\", \"Enabled emote channels\", [\r\n                { title : \"TwitchGlobal\", value : \"TwitchGlobal\", setValue : this.settings.enabledChannels.includes(\"TwitchGlobal\") },\r\n                { title : \"TwitchSubscriber\", value : \"TwitchSubscriber\", setValue : this.settings.enabledChannels.includes(\"TwitchSubscriber\") },\r\n                { title : \"BTTV\", value : \"BTTV\", setValue : this.settings.enabledChannels.includes(\"BTTV\") },\r\n                { title : \"FrankerFaceZ\", value : \"FrankerFaceZ\", setValue : this.settings.enabledChannels.includes(\"FrankerFaceZ\") },\r\n                { title : \"BTTV2\", value : \"BTTV2\", setValue : this.settings.enabledChannels.includes(\"BTTV2\") }\r\n            ], choice => {\r\n                if(this.settings.enabledChannels.includes(choice.value)) this.settings.enabledChannels.splice(this.settings.enabledChannels.indexOf(choice.value, 1));\r\n                else this.settings.enabledChannels.push(choice.value);\r\n                this.saveSettings();\r\n                this.getEmotes();\r\n            }), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createTextField(\"Prefix to display autocomplete\", \"text\", this.settings.prefix, e => {\r\n                this.settings.prefix = e.target.value;\r\n                this.saveSettings();\r\n            }, { tooltip : \"If you set this, it will be required before an emote to display the auto-complete menu.\" }), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createTextField(\"Auto-complete emote size\", \"number\", this.settings.size, e => {\r\n                this.settings.size = e.target.value;\r\n                this.saveSettings();\r\n            }, { tooltip : \"The size in pixels to display the auto-complete emotes.\" }), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createTextField(\"Auto-complete results limit\", \"number\", this.settings.resultsCap, e => {\r\n                this.settings.resultsCap = e.target.value;\r\n                this.saveSettings();\r\n            }, { tooltip : \"Maximum amount of results to display. The higher this is, the slower larger results will be.\" }), this.getName());\r\n            \r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createTextField(\"Auto-complete display delay (ms)\", \"number\", this.settings.autocompleteDelay, e => {\r\n                this.settings.autocompleteDelay = e.target.value;\r\n                this.saveSettings();\r\n            }, { tooltip : \"Delay in millseconds to display the auto-complete menu after pressing a key.\" }), this.getName());\r\n\r\n            NeatoLib.Settings.pushChangelogElements(this);\r\n\r\n\t\t}, 0);\r\n\r\n\t\treturn NeatoLib.Settings.Elements.pluginNameLabel(this.getName());\r\n\t\t\r\n\t}\r\n\r\n\tsaveSettings() {\r\n\t\tNeatoLib.Settings.save(this);\t\r\n\t}\r\n    \r\n    updateSelected() {\r\n\r\n        let items = document.getElementById(\"bdea-autocomplete-list\").getElementsByClassName(\"selector-2IcQBU selectable-3dP3y-\");\r\n\r\n        for(let i = 0; i < items.length; i++) {\r\n            if(i == this.selectedIDX) items[i].classList.add(\"selectorSelected-1_M1WV\");\r\n            else items[i].classList.remove(\"selectorSelected-1_M1WV\");\r\n        }\r\n\r\n    }\r\n\r\n\tonLibLoaded() {\r\n        \r\n        NeatoLib.Updates.check(this);\r\n\t\t\r\n\t\tthis.settings = NeatoLib.Settings.load(this, {\r\n            displayUpdateNotes : true,\r\n            enabledChannels : [\"TwitchGlobal\", \"TwitchSubscriber\", \"BTTV\", \"FrankerFaceZ\", \"BTTV2\"],\r\n            prefix : \"\",\r\n            size : 16,\r\n            caseSensitive : true,\r\n            resultsCap : 15,\r\n            autocompleteDelay : 750\r\n        });\r\n\t\t\r\n\t\t//if(this.settings.displayUpdateNotes) NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());\r\n\r\n        this.selectedIDX = 0;\r\n        this.results = [];\r\n\r\n        this.onGlobalKey = e => {\r\n            if(e.key == \"Tab\") {\r\n                let list = document.getElementById(\"bdea-autocomplete-list\");\r\n                if(list) list.getElementsByClassName(\"autocompleteRowVertical-q1K4ky autocompleteRow-2OthDa\")[this.selectedIDX].click();\r\n            }\r\n        };\r\n\r\n        this.onChatInput = (e, fromTimeout) => {\r\n\r\n            if(this.inputTimeout) clearTimeout(this.inputTimeout);\r\n\r\n            if(e.key.includes(\"Arrow\")) {\r\n                if(e.key.includes(\"Up\") && this.selectedIDX > 0) this.selectedIDX--;\r\n                else if(e.key.includes(\"Down\") && this.selectedIDX < this.results.length - 1) this.selectedIDX++;\r\n                else return;\r\n                this.updateSelected();\r\n                return;\r\n            }\r\n\r\n            let chatbox = e.target, autocomplete = document.getElementById(\"bdea-autocomplete\"), words = chatbox.value.split(\" \"), lastWord = words[words.length - 1];\r\n\r\n            if(!lastWord || lastWord.length < 4 || (this.settings.prefix && !lastWord.startsWith(this.settings.prefix))) {\r\n                if(autocomplete) autocomplete.outerHTML = \"\";\r\n                return;\r\n            }\r\n\r\n            if(this.settings.autocompleteDelay && !fromTimeout) {\r\n                this.inputTimeout = setTimeout(() => {\r\n                    this.onChatInput(e, true);\r\n                }, this.settings.autocompleteDelay);\r\n                return;\r\n            }\r\n\r\n            if(this.settings.prefix) lastWord = lastWord.substring(this.settings.prefix.length, lastWord.length);\r\n\r\n            let emotes = [];\r\n\r\n            let lim = 0;\r\n            for(let i = 0; i < this.emotes.length; i++) {\r\n\r\n                if(lim >= this.settings.resultsCap) break;\r\n                \r\n                if((this.settings.caseSensitive && !this.emotes[i].name.startsWith(lastWord)) || (!this.settings.caseSensitive && !this.emotes[i].name.toLowerCase().startsWith(lastWord.toLowerCase()))) continue;\r\n\r\n                emotes.push(this.emotes[i]);\r\n\r\n                lim++;\r\n\r\n            }\r\n\r\n            this.results = emotes;\r\n\r\n            if(emotes.length == 0) {\r\n                if(autocomplete) autocomplete.outerHTML = \"\";\r\n                return;\r\n            }\r\n\r\n            if(!autocomplete) {\r\n\r\n                chatbox.parentElement.insertAdjacentHTML(\"beforeend\", `\r\n                    <div id=\"bdea-autocomplete\" class=\"autocomplete-1vrmpx autocomplete-i9yVHs\">\r\n                        <div class=\"autocompleteInner-zh20B_\">\r\n                            <div class=\"autocompleteRowVertical-q1K4ky autocompleteRow-2OthDa\">\r\n                                <div class=\"selector-2IcQBU\">\r\n                                    <div class=\"contentTitle-2tG_sM small-29zrCQ size12-3R0845 height16-2Lv3qA weightSemiBold-NJexzi\">Emotes matching <strong>${lastWord}</strong></div>\r\n                                </div>\r\n                            </div>\r\n                            <div id=\"bdea-autocomplete-list\">\r\n\r\n                            </div>\r\n                        </div>\r\n                    </div>\r\n                `);\r\n\r\n                autocomplete = document.getElementById(\"bdea-autocomplete\");\r\n\r\n            }\r\n\r\n            this.selectedIDX = 0;\r\n\r\n            let list = document.getElementById(\"bdea-autocomplete-list\");\r\n\r\n            list.innerHTML = \"\";\r\n\r\n            for(let i = 0; i < emotes.length; i++) {\r\n\r\n                list.insertAdjacentHTML(\"beforeend\", `\r\n                    <div class=\"autocompleteRowVertical-q1K4ky autocompleteRow-2OthDa\">\r\n                        <div class=\"selector-2IcQBU selectable-3dP3y-\">\r\n                            <div class=\"flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2NDFzi alignCenter-1dQNNs noWrap-3jynv6 content-Qb0rXO\" style=\"flex: 1 1 auto;\"><img style=\"width:${this.settings.size}px;height:${this.settings.size}px;\" src=\"${emotes[i].url}\">\r\n                                <div class=\"marginLeft8-1YseBe\">${emotes[i].name}</div>\r\n                                <div style=\"margin-left:auto;opacity:0.5;\">${emotes[i].channel}</div>\r\n                            </div>\r\n                        </div>\r\n                    </div>\r\n                `);\r\n\r\n                let items = document.getElementsByClassName(\"autocompleteRowVertical-q1K4ky autocompleteRow-2OthDa\");\r\n\r\n                items[items.length - 1].addEventListener(\"click\", e => {\r\n                    words[words.length - 1] = emotes[this.selectedIDX].name + \" \";\r\n                    NeatoLib.Chatbox.setText(words.join(\" \"));\r\n                    autocomplete.outerHTML = \"\";\r\n                });\r\n\r\n                items[items.length - 1].addEventListener(\"mouseover\", e => {\r\n                    this.selectedIDX = i;\r\n                    this.updateSelected();\r\n                });\r\n\r\n            }\r\n\r\n            this.updateSelected();\r\n\r\n        };\r\n\r\n        this.getEmotes();\r\n\r\n        this.initialized = true;\r\n\r\n        document.addEventListener(\"keydown\", this.onGlobalKey);\r\n        \r\n        NeatoLib.Events.onPluginLoaded(this);\r\n\r\n\t\tthis.switch();\r\n\r\n\t\tthis.switchEvent = () => this.switch();\r\n\r\n        NeatoLib.Events.attach(\"switch\", this.switchEvent);\r\n\r\n\t}\r\n\r\n    switch() {\r\n\r\n        if(this.initialized != true) return;\r\n\r\n        if(this.emotes.length == 0) this.getEmotes();\r\n\r\n        let chatbox = NeatoLib.Chatbox.get();\r\n\r\n        if(chatbox) chatbox.addEventListener(\"keyup\", this.onChatInput);\r\n\r\n    }\r\n\r\n    getEmotes() {\r\n\r\n        let emoteChannels = this.settings.enabledChannels, pushed = {};\r\n\r\n        this.emotes = [];\r\n\r\n        let blacklisted = {};\r\n\r\n        for(let i = 0; i < window.bemotes.length; i++) blacklisted[window.bemotes[i]] = true;\r\n\r\n\t\tfor(let ec of emoteChannels) {\r\n\t\t\tlet emoteChannel = window.bdEmotes[ec];\r\n\t\t\tfor(let emote in emoteChannel) {\r\n\t\t\t\tif(emote.length > 2 && !pushed[emote] && !blacklisted[emote]) {\r\n                    this.emotes.push({ name : emote, url : emoteChannel[emote], channel : ec });\r\n                    pushed[emote] = true;\r\n                }\r\n\t\t\t}\r\n        }\r\n\r\n        this.emotes.sort((a, b) => a.name.length - b.name.length);\r\n\r\n    }\r\n\t\r\n    stop() {\r\n\r\n        let chatbox = NeatoLib.Chatbox.get();\r\n\r\n        if(chatbox) chatbox.removeEventListener(\"keyup\", this.onChatInput);\r\n\r\n        document.removeEventListener(\"keydown\", this.onGlobalKey);\r\n\r\n        if(this.inputTimeout) clearTimeout(this.inputTimeout);\r\n\r\n        NeatoLib.Events.detach(\"switch\", this.switchEvent);\r\n\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "BetterEmoteSizes.plugin.js",
    "content": "//META{\"name\":\"BetterEmoteSizes\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/BetterEmoteSizes.plugin.js\"}*//\r\n\r\nclass BetterEmoteSizes {\r\n\r\n\tgetName() { return \"Emote Zoom\"; }\r\n\tgetDescription() { return \"Increases the size of emojis, emotes, and reactions upon hovering over them and allows you to change their default sizes.\"; }\r\n\tgetVersion() { return \"2.4.17\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\r\n\tget settingFields() {\r\n\t\treturn {\r\n\t\t\talterSmall: { label: \"Affect small emojis\", type: \"bool\" },\r\n\t\t\tsmallSize: { label: \"Default small emoji size (px)\", type: \"number\" },\r\n\t\t\talterLarge: { label: \"Affect large emojis\", type: \"bool\" },\r\n\t\t\tlargeSize: { label: \"Default large emoji size (px)\", type: \"number\" },\r\n\t\t\talterBD: { label: \"Affect small BetterDiscord emotes\", type: \"bool\" },\r\n\t\t\tbdSize: { label: \"Default small BetterDiscord emote size (px)\", type: \"number\" },\r\n\t\t\talterLargeBD: { label: \"Affect large BetterDiscord emotes\", type: \"bool\" },\r\n\t\t\tlargeBdSize: { label: \"Default large BetterDiscord emote size (px)\", type: \"number\" },\r\n\t\t\talterReactions: { label: \"Affect reactions\", type: \"bool\" },\r\n\t\t\treactionSize: { label: \"Default reaction size (px)\", type: \"number\" },\r\n\t\t\thoverSize: { label: \"Emoji and BetterDiscord emote hover size multiplier\", type: \"number\" },\r\n\t\t\treactionHoverSize: { label: \"Reaction hover size multiplier\", type: \"number\" },\r\n\t\t\ttransitionSpeed: { label: \"Transition speed (seconds)\", type: \"number\" },\r\n\t\t\tdelayAmount: { label: \"Delay amount (seconds)\", type: \"number\" },\r\n\t\t\tequal: { label: \"Small and large emote zoom to equal\", type: \"bool\" }\r\n\t\t};\r\n\t}\r\n\r\n\tget defaultSettings() {\r\n\t\treturn {\r\n\t\t\tdisplayUpdateNotes: true,\r\n\t\t\talterSmall: true,\r\n\t\t\tsmallSize: 22,\r\n\t\t\talterLarge: true,\r\n\t\t\tlargeSize: 32,\r\n\t\t\talterBD: true,\r\n\t\t\tbdSize: 28,\r\n\t\t\talterLargeBD: true,\r\n\t\t\tlargeBdSize: 32,\r\n\t\t\talterReactions: true,\r\n\t\t\treactionSize: 16,\r\n\t\t\thoverSize: 3,\r\n\t\t\ttransitionSpeed: 0.5,\r\n\t\t\tdelayAmount: 0,\r\n\t\t\treactionHoverSize: 2,\r\n\t\t\tequal: false\r\n\t\t};\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\t\treturn NeatoLib.Settings.createPanel(this);\r\n\t}\r\n\r\n\tsaveSettings() {\r\n\t\tNeatoLib.Settings.save(this);\r\n\t\tthis.update();\r\n\t}\r\n\r\n\tload() {}\r\n\r\n\tstart() {\r\n\t\tconst libLoadedEvent = () => {\r\n\t\t\ttry{ this.onLibLoaded(); }\r\n\t\t\tcatch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n\t\t};\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif (!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n\t\tif (typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\t}\r\n\r\n\tonLibLoaded() {\r\n\t\tthis.settings = NeatoLib.Settings.load(this);\r\n\r\n\t\tNeatoLib.Updates.check(this, \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/BetterEmoteSizes.plugin.js\");\r\n\r\n\t\tif (!NeatoLib.hasRequiredLibVersion(this, \"0.7.19\")) return;\r\n\r\n\t\tthis.update();\r\n\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\t}\r\n\r\n\tupdate() {\r\n\t\tconst markup = NeatoLib.getClass(\"markup\"), markupRtl = NeatoLib.getClass(\"markupRtl\"), messageGroup = NeatoLib.getClass(\"cozyMessage\"), message = NeatoLib.getClass(\"message\"), reaction = NeatoLib.getClass(\"reaction\"), \t\treactionMe = NeatoLib.getClass(\"reactionMe\");\r\n\r\n\t\tif (this.style) this.style.destroy();\r\n\t\tthis.style = NeatoLib.injectCSS(`.${messageGroup} { overflow: visible; }`);\r\n\r\n\t\tif (this.settings.alterSmall) {\r\n\t\t\tthis.style.append(`\r\n\t\t\t\t#app-mount .${markup} > .emoji:not(.jumboable),\r\n\t\t\t\t#app-mount .${markupRtl} > .emoji:not(.jumboable) {\r\n\t\t\t\t\theight: ${this.settings.smallSize}px;\r\n\t\t\t\t\twidth: auto;\r\n\t\t\t\t\ttransform: scale(1);\r\n\t\t\t\t\ttransition: transform ${this.settings.transitionSpeed}s;\r\n\t\t\t\t\ttransition-delay: 0s;\r\n\t\t\t\t}\r\n\t\t\t\t#app-mount .${markup} > .emoji:not(.jumboable):hover,\r\n\t\t\t\t#app-mount .${markupRtl} > .emoji:not(.jumboable):hover {\r\n\t\t\t\t\ttransform: scale(${this.settings.equal ? ((this.settings.largeSize / this.settings.smallSize) * this.settings.hoverSize) : this.settings.hoverSize});\r\n\t\t\t\t\tposition: relative;\r\n\t\t\t\t\tz-index: 1;\r\n\t\t\t\t\ttransition-delay: ${this.settings.delayAmount}s;\r\n\t\t\t\t}\r\n\t\t\t\t#app-mount .${messageGroup}:last-child .${message}:nth-last-child(2) .${markup} .emoji:not(.jumboable):hover,\r\n\t\t\t\t#app-mount .${messageGroup}:last-child .${message}:nth-last-child(2) .${markupRtl} .emoji:not(.jumboable):hover {\r\n\t\t\t\t\ttransform: scale(${this.settings.equal ? ((this.settings.largeSize / this.settings.smallSize) * this.settings.hoverSize) : this.settings.hoverSize}) translateY(-35%);\r\n\t\t\t\t}\r\n\t\t\t`);\r\n\t\t}\r\n\r\n\t\tif (this.settings.alterLarge) {\r\n\t\t\tthis.style.append(`\r\n\t\t\t\t#app-mount .${markup} > .emoji.jumboable,\r\n\t\t\t\t#app-mount .${markupRtl} > .emoji.jumboable {\r\n\t\t\t\t\theight: ${this.settings.largeSize}px;\r\n\t\t\t\t\twidth: auto;\r\n\t\t\t\t\ttransform: scale(1);\r\n\t\t\t\t\ttransition: transform ${this.settings.transitionSpeed}s;\r\n\t\t\t\t\ttransition-delay: 0s;\r\n\t\t\t\t}\r\n\t\t\t\t#app-mount .${markup} > .emoji.jumboable:hover,\r\n\t\t\t\t#app-mount .${markupRtl} > .emoji.jumboable:hover {\r\n\t\t\t\t\ttransform: scale(${this.settings.hoverSize});\r\n\t\t\t\t\tposition: relative;\r\n\t\t\t\t\tz-index: 1;\r\n\t\t\t\t\ttransition-delay: ${this.settings.delayAmount}s;\r\n\t\t\t\t}\r\n\t\t\t\t#app-mount .${messageGroup}:last-child .${message}:nth-last-child(2) .${markup} .emoji.jumboable:hover,\r\n\t\t\t\t#app-mount .${messageGroup}:last-child .${message}:nth-last-child(2) .${markupRtl} .emoji.jumboable:hover {\r\n\t\t\t\t\ttransform: scale(${this.settings.hoverSize}) translateY(-35%);\r\n\t\t\t\t}\r\n\t\t\t`);\r\n\t\t}\r\n\r\n\t\tif (this.settings.alterBD) {\r\n\t\t\tthis.style.append(`\r\n\t\t\t\t#app-mount .emote:not(.jumboable) {\r\n\t\t\t\t\theight: ${this.settings.bdSize}px;\r\n\t\t\t\t\twidth: auto;\r\n\t\t\t\t\tmax-height: ${this.settings.bdSize}px !important;\r\n\t\t\t\t\ttransform: scale(1);\r\n\t\t\t\t\ttransition: transform ${this.settings.transitionSpeed}s;\r\n\t\t\t\t\ttransition-delay: 0s;\r\n\t\t\t\t}\r\n\t\t\t\t#app-mount .emote:not(.emoteshake):not(.emoteshake2):not(.emoteshake3):not(.jumboable):hover {\r\n\t\t\t\t\ttransform: scale(${this.settings.hoverSize});\r\n\t\t\t\t\tposition: relative;\r\n\t\t\t\t\tz-index: 1;\r\n\t\t\t\t\ttransition-delay: ${this.settings.delayAmount}s;\r\n\t\t\t\t}\r\n\t\t\t\t#app-mount .${messageGroup}:last-child .${message}:nth-last-child(2) .emote:not(.emoteshake):not(.emoteshake2):not(.emoteshake3):not(.jumboable):hover {\r\n\t\t\t\t\ttransform: scale(${this.settings.hoverSize}) translateY(-35%);\r\n\t\t\t\t}\r\n\t\t\t`);\r\n\t\t}\r\n\r\n\t\tif (this.settings.alterLargeBD) {\r\n\t\t\tthis.style.append(`\r\n\t\t\t\t#app-mount .emote.jumboable {\r\n\t\t\t\t\theight: ${this.settings.largeBdSize}px;\r\n\t\t\t\t\twidth: auto;\r\n\t\t\t\t\tmax-height: ${this.settings.largeBdSize}px !important;\r\n\t\t\t\t\ttransform: scale(1);\r\n\t\t\t\t\ttransition: transform ${this.settings.transitionSpeed}s;\r\n\t\t\t\t\ttransition-delay: 0s;\r\n\t\t\t\t}\r\n\t\t\t\t#app-mount .emote.jumboable:not(.emoteshake):not(.emoteshake2):not(.emoteshake3):hover {\r\n\t\t\t\t\ttransform: scale(${this.settings.hoverSize});\r\n\t\t\t\t\tposition: relative;\r\n\t\t\t\t\tz-index: 1;\r\n\t\t\t\t\ttransition-delay: ${this.settings.delayAmount}s;\r\n\t\t\t\t}\r\n\t\t\t\t#app-mount .${messageGroup}:last-child .${message}:nth-last-child(2) .emote.jumboable:not(.emoteshake2):not(.emoteshake3):hover {\r\n\t\t\t\t\ttransform: scale(${this.settings.hoverSize}) translateY(-35%);\r\n\t\t\t\t}\r\n\t\t\t`);\r\n\t\t}\r\n\r\n\t\tif (this.settings.alterReactions) {\r\n\t\t\tthis.style.append(`\r\n\t\t\t\t#app-mount .${reaction} .emoji, .${reaction}.${reactionMe} .emoji {\r\n\t\t\t\t\theight: ${this.settings.reactionSize}px;\r\n\t\t\t\t\twidth: auto;\r\n\t\t\t\t}\r\n\t\t\t\t#app-mount .${reaction} {\r\n\t\t\t\t\ttransition: transform ${this.settings.transitionSpeed}s;\r\n\t\t\t\t\ttransition-delay: 0s;\r\n\t\t\t\t}\r\n\t\t\t\t#app-mount .${reaction}:hover {\r\n\t\t\t\t\ttransform: scale(${this.settings.reactionHoverSize}) !important;\r\n\t\t\t\t\tz-index: 1000;\r\n\t\t\t\t\ttransition-delay: ${this.settings.delayAmount}s;\r\n\t\t\t\t}\r\n\t\t\t`);\r\n\t\t}\r\n\t}\r\n\r\n\tstop() {\r\n\t\tif (this.style) this.style.destroy();\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "Bruh.plugin.js",
    "content": "/**\r\n * @name Bruh\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www.paypal.me/israelboone\r\n * @website https://metalloriff.github.io/toms-discord-stuff/\r\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/Bruh.plugin.js\r\n */\r\n\r\nmodule.exports = (() => {\r\n\tconst config =\r\n\t{\r\n\t\tinfo: {\r\n\t\t\tname: \"Bruh\",\r\n\t\t\tauthors: [{\r\n\t\t\t\tname: \"Metalloriff\",\r\n\t\t\t\tdiscord_id: \"264163473179672576\",\r\n\t\t\t\tgithub_username: \"metalloriff\",\r\n\t\t\t\ttwitter_username: \"Metalloriff\"\r\n\t\t\t}],\r\n\t\t\tversion: \"0.0.2\",\r\n\t\t\tdescription: \"bruh\",\r\n\t\t\tgithub: \"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/Bruh.plugin.js\",\r\n\t\t\tgithub_raw: \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/Bruh.plugin.js\"\r\n\t\t},\r\n\t\tdefaultConfig: [{\r\n\t\t\tid: \"general\",\r\n\t\t\tname: \"general settings\",\r\n\t\t\ttype: \"category\",\r\n\t\t\tcollapsible: true,\r\n\t\t\tshown: false,\r\n\t\t\tsettings: [{\r\n\t\t\t\tid: \"onlyCur\",\r\n\t\t\t\tname: \"Current channel only\",\r\n\t\t\t\tnote: \"When this is enabled, the bruh sound effect will only play when a bruh is found in the selected channel.\",\r\n\t\t\t\ttype: \"switch\",\r\n\t\t\t\tvalue: true\r\n\t\t\t}, {\r\n\t\t\t\tid: \"delay\",\r\n\t\t\t\tname: \"Delay between each bruh (ms)\",\r\n\t\t\t\tnote: \"The amount of milliseconds to wait between each bruh when multiple bruhs are found within the same message.\",\r\n\t\t\t\ttype: \"slider\",\r\n\t\t\t\tvalue: 200,\r\n\t\t\t\tmin: 10,\r\n\t\t\t\tmax: 1000,\r\n\t\t\t\trenderValue: v => Math.round(v) + \"ms\"\r\n\t\t\t}]\r\n\t\t}]\r\n\t};\r\n\r\n\treturn !global.ZeresPluginLibrary ? class {\r\n\t\tconstructor() { this._config = config; }\r\n\r\n\t\tgetName = () => config.info.name;\r\n\t\tgetAuthor = () => config.info.description;\r\n\t\tgetVersion = () => config.info.version;\r\n\r\n\t\tload() {\r\n\t\t\tBdApi.showConfirmationModal(\"Library Missing\", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {\r\n\t\t\t\tconfirmText: \"Download Now\",\r\n\t\t\t\tcancelText: \"Cancel\",\r\n\t\t\t\tonConfirm: () => {\r\n\t\t\t\t\trequire(\"request\").get(\"https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js\", async (err, res, body) => {\r\n\t\t\t\t\t\tif (err) return require(\"electron\").shell.openExternal(\"https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js\");\r\n\t\t\t\t\t\tawait new Promise(r => require(\"fs\").writeFile(require(\"path\").join(BdApi.Plugins.folder, \"0PluginLibrary.plugin.js\"), body, r));\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tstart() { }\r\n\t\tstop() { }\r\n\t} : (([Plugin, Api]) => {\r\n\r\n\t\tconst plugin = (Plugin, Api) => { try {\r\n\t\t\tconst {\r\n\t\t\t\tDiscordModules: { Dispatcher, SelectedChannelStore }\r\n\t\t\t} = Api;\r\n\r\n\t\t\tconst audio = new Audio();\r\n\r\n\t\t\treturn class Bruh extends Plugin {\r\n\t\t\t\tconstructor() {\r\n\t\t\t\t\tsuper();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tgetSettingsPanel() {\r\n\t\t\t\t\treturn this.buildSettingsPanel().getElement();\r\n\t\t\t\t}\r\n\t\r\n\t\t\t\tonStart() {\r\n\t\t\t\t\tDispatcher.subscribe(\"MESSAGE_CREATE\", this.messageEvent);\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tmessageEvent = async ({ channelId, message, optimistic }) => {\r\n\t\t\t\t\tif (this.settings.general.onlyCur && channelId != SelectedChannelStore.getChannelId())\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\t\tif (!optimistic) {\r\n\t\t\t\t\t\tconst count = (message.content.match(/bruh/gmi) || []).length;\r\n\t\t\t\t\r\n\t\t\t\t\t\tfor (let i = 0; i < count; i++) {\r\n\t\t\t\t\t\t\tthis.playBruh();\r\n\r\n\t\t\t\t\t\t\tawait new Promise(r => setTimeout(r, this.settings.general.delay));\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t};\r\n\t\t\t\t\r\n\t\t\t\tplayBruh() {\r\n\t\t\t\t\taudio.src = \"https://www.myinstants.com/media/sounds/movie_1.mp3\";\r\n\t\t\t\t\taudio.play();\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tonStop() {\r\n\t\t\t\t\tDispatcher.unsubscribe(\"MESSAGE_CREATE\", this.messageEvent);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} catch (e) { console.error(e); }};\r\n\r\n\t\treturn plugin(Plugin, Api);\r\n\t})(global.ZeresPluginLibrary.buildPlugin(config));\r\n})();\r\n"
  },
  {
    "path": "CustomJs.plugin.js",
    "content": "//META{\"name\":\"CustomJs\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/CustomJs.plugin.js\"}*//\r\n\r\nclass CustomJs {\r\n\t\r\n    getName() { return \"CustomJs\"; }\r\n    getDescription() { return \"Allows you to specify a custom JavaScript file similar to custom CSS.\"; }\r\n    getVersion() { return \"0.0.1\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\tgetChanges() {\r\n\t\treturn {\r\n\t\t\t\r\n\t\t};\r\n\t}\r\n\r\n    load() {}\r\n\r\n    start() {\r\n\r\n        let libLoadedEvent = () => {\r\n            try{ this.onLibLoaded(); }\r\n            catch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n        };\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif(!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n        if(typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\r\n\t\tconst fs = require(\"fs\");\r\n\r\n\t\tsetTimeout(() => {\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField(\"Custom JS file path\", this.settings.filepath, e => {\r\n\t\t\t\tif(e.target.value && !fs.existsSync(e.target.value)) return NeatoLib.showToast(\"File does not exist\", \"error\");\r\n\t\t\t\tthis.settings.filepath = e.target.value;\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t}), this.getName());\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createButton(\"Browse\", () => {\r\n\t\t\t\tNeatoLib.browseForFile(filepath => {\r\n\t\t\t\t\tif(!filepath) return NeatoLib.showToast(\"No file selected\", \"error\");\r\n\t\t\t\t\tthis.settings.filepath = filepath;\r\n\t\t\t\t\tthis.saveSettings();\r\n\t\t\t\t})\r\n\t\t\t}), this.getName());\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createButton(\"Reload\", () => this.reloadJs(), \"float:right\"), this.getName());\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.DOM.createElement({ innerHTML : `\r\n\t\t\t\tExample #1\r\n\t\t\t\t<pre><div style=\"background:#202225;display:block;padding:10px;margin-top:10px;border-radius:5px\">\r\nreturn new class {\r\n\r\n\tstart() {\r\n\t\tconsole.log(\"start\");\r\n\t\tdocument.addEventListener(\"click\", this.clickEvent = e => this.onClick(e));\r\n\t}\r\n\r\n\tonClick(e) {\r\n\t\tconsole.log(\"clicked on\", e.target);\r\n\t}\r\n\r\n\tstop() {\r\n\t\tdocument.removeEventListener(\"click\", this.clickEvent);\r\n\t\tconsole.log(\"stop\");\r\n\t}\r\n\r\n\tonSwitch() {\r\n\t\tconsole.log(NeatoLib.getSelectedTextChannel());\t\r\n\t}\r\n\t\r\n}\r\n\t\t\t\t</div></pre>\r\n\t\t\t`, style : \"color:white\" }, { type : \"p\" }), this.getName());\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.DOM.createElement({ innerHTML : `\r\n\t\t\t\tExample #2\r\n\t\t\t\t<pre><div style=\"background:#202225;display:block;padding:10px;margin-top:10px;border-radius:5px\">\r\nreturn {\r\n\r\n\tclickEvent : function(e) {\r\n\t\tconsole.log(\"clicked on\", e.target);\r\n\t},\r\n\r\n\tstart : function() {\r\n\t\tconsole.log(\"start\");\r\n\t\tdocument.addEventListener(\"click\", this.clickEvent);\r\n\t},\r\n\r\n\tstop : function() {\r\n\t\tdocument.removeEventListener(\"click\", this.clickEvent);\r\n\t\tconsole.log(\"stop\");\r\n\t},\r\n\r\n\tonSwitch() {\r\n\t\tconsole.log(NeatoLib.getSelectedTextChannel());\r\n\t}\r\n\r\n}\r\n\t\t\t\t</div></pre>\r\n\t\t\t`, style : \"color:white\" }, { type : \"p\" }), this.getName());\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.DOM.createElement({ innerHTML : `\r\n\t\t\t\tEvents and variables\r\n\t\t\t\t<pre><div style=\"background:#202225;display:block;padding:10px;margin-top:10px;border-radius:5px\">\r\nstart() - Called when the script is loaded. Use this for initialization.\r\n\r\nstop() - Called before the script is stopped. Use this for de-initialization.\r\n\r\nonSwitch() - Called when switching servers and channels.\r\n\r\nloader - This plugin object. Example usage: loader.reloadJs()\r\n\t\t\t\t</div></pre>\r\n\t\t\t`, style : \"color:white\" }, { type : \"p\" }), this.getName());\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createHint(\"Note: My library, 'NeatoLib', is included. You can use this for various things in your script. As of now, there are no docs for it, but you can enter 'NeatoLib' into the console (Ctrl/Cmd + Shift + I) to view all of its functions.\"), this.getName());\r\n\t\t\t\r\n\t\t\tNeatoLib.Settings.pushChangelogElements(this);\r\n\r\n\t\t}, 0);\r\n\r\n\t\treturn NeatoLib.Settings.Elements.pluginNameLabel(this.getName());\r\n\t\t\r\n\t}\r\n\r\n\tsaveSettings() {\r\n\t\tthis.rewatch();\r\n\t\tNeatoLib.Settings.save(this);\r\n\t}\r\n\r\n\tonLibLoaded() {\r\n\t\t\r\n\t\tthis.settings = NeatoLib.Settings.load(this, {\r\n\t\t\tdisplayUpdateNotes : true,\r\n\t\t\tfilepath : \"\"\r\n\t\t});\r\n\t\t\r\n\t\tNeatoLib.Updates.check(this);\r\n\t\t\r\n\t\t//if(this.settings.displayUpdateNotes) NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());\r\n\t\t\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\r\n\t\tthis.reloadJs();\r\n\t\tthis.rewatch();\r\n\r\n\t\tNeatoLib.Events.attach(\"switch\", this.switchEvent = () => {\r\n\t\t\tif(this.script && typeof this.script.onSwitch == \"function\") {\r\n\t\t\t\ttry { this.script.onSwitch(); }\r\n\t\t\t\tcatch(err) {\r\n\t\t\t\t\tconsole.error(err);\r\n\t\t\t\t\tNeatoLib.showToast(\"[Custom JS]: error in onSwitch(), check the console for more details.\");\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t}\r\n\r\n\trewatch() {\r\n\r\n\t\tconst fs = require(\"fs\");\r\n\r\n\t\tif(this.watcher) this.watcher.close();\r\n\r\n\t\tif(this.settings.filepath) this.watcher = fs.watch(this.settings.filepath, () => {\r\n\t\t\tconst current = fs.readFileSync(this.settings.filepath, \"utf-8\");\r\n\t\t\tif(!current) return;\r\n\t\t\tif(this.last != current) this.reloadJs();\r\n\t\t});\r\n\r\n\t}\r\n\r\n\treloadJs() {\r\n\r\n\t\tif(this.script && typeof this.script.stop == \"function\") this.script.stop();\r\n\r\n\t\tthis.script = null;\r\n\r\n\t\tconst fs = require(\"fs\");\r\n\r\n\t\tif(!this.settings.filepath) return;\r\n\t\telse if(!fs.existsSync(this.settings.filepath)) return NeatoLib.showToast(\"[Custom JS]: file not found\", \"error\");\r\n\r\n\t\ttry {\r\n\r\n\t\t\tconst js = this.script = eval(`(function(){${this.last = fs.readFileSync(this.settings.filepath, \"utf-8\")}})();`);\r\n\t\t\tjs.loader = this;\r\n\r\n\t\t\ttry { if(typeof js.start == \"function\") js.start(); }\r\n\t\t\tcatch(err) {\r\n\t\t\t\tconsole.error(err);\r\n\t\t\t\tNeatoLib.showToast(\"[Custom JS]: error in start(), check the console for more details.\");\r\n\t\t\t}\r\n\r\n\t\t} catch(err) {\r\n\t\t\tconsole.error(err);\r\n\t\t\tNeatoLib.showToast(\"[Custom JS]: error evaluating JS, \" + err);\r\n\t\t}\r\n\r\n\t}\r\n\t\r\n    stop() {\r\n\r\n\t\tif(this.script && typeof this.script.stop == \"function\") {\r\n\t\t\ttry { this.script.stop(); }\r\n\t\t\tcatch(err) {\r\n\t\t\t\tconsole.error(err);\r\n\t\t\t\tNeatoLib.showToast(\"[Custom JS]: error in stop(), check the console for more details.\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif(this.watcher) this.watcher.close();\r\n\r\n\t\tNeatoLib.Events.detach(\"switch\", this.switchEvent);\r\n\r\n\t}\r\n\t\r\n}"
  },
  {
    "path": "CustomizableAvatarDPI.plugin.js",
    "content": "//META{\"name\":\"CustomizableAvatarDPI\",\"website\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/README.md\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/CustomizableAvatarDPI.plugin.js\"}*//\r\n\r\nclass CustomizableAvatarDPI {\r\n\t\r\n\tgetName() { return \"Customizable Avatar DPI\"; }\r\n\tgetDescription() { return \"Allows you to change the DPI of user avatars, to reduce bluriness with themes that increase the size of them.\"; }\r\n\tgetVersion() { return \"1.0.6\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\tgetChanges() {\r\n\t\treturn {\r\n\t\t\t\"1.0.6\":\r\n\t\t\t`\r\n\t\t\t\tJust temporary fix until Metalloriff hopefully rewrites this plugin aroung christmas time\r\n\t\t\t`\r\n\t\t}\r\n\t}\r\n\r\n\tload() {}\r\n\r\n\tstart() {\r\n\r\n\t\tlet libLoadedEvent = () => {\r\n\t\t\ttry{ this.onLibLoaded(); }\r\n\t\t\tcatch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n\t\t};\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif(!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n\t\tif(typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\r\n\t}\r\n\r\n\tsaveSettings() {\r\n\t\tNeatoLib.Settings.save(this);\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\r\n\t\tsetTimeout(() => {\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField(\"Popout avatar size\", this.settings.popoutAvatarSize, e => {\r\n\t\t\t\tthis.settings.popoutAvatarSize = e.target.value;\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t}), this.getName(), { tooltip : \"User popouts\" });\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField(\"Other avatar size\", this.settings.otherAvatarSize, e => {\r\n\t\t\t\tthis.settings.otherAvatarSize = e.target.value;\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t}), this.getName(), { tooltip : \"Everything else\" });\r\n\r\n\t\t\tNeatoLib.Settings.pushChangelogElements(this);\r\n\r\n\t\t}, 0);\r\n\r\n\t\treturn NeatoLib.Settings.Elements.pluginNameLabel(this.getName());\r\n\t\t\r\n\t}\r\n\t\r\n\tonLibLoaded(){\r\n\r\n\t\tNeatoLib.Updates.check(this);\r\n\r\n\t\tthis.settings = NeatoLib.Settings.load(this, {\r\n\t\t\tpopoutAvatarSize : 1024,\r\n\t\t\totherAvatarSize : 256,\r\n\t\t\tdisplayUpdateNotes : true\r\n\t\t});\r\n\t\t\r\n\t\tthis.appObserver = new MutationObserver(m => {\r\n\r\n\t\t\tfor(let i = 0; i < m.length; i++) {\r\n\r\n\t\t\t\tif(!m[i].addedNodes.length) continue;\r\n\r\n\t\t\t\tfor(let a = 0; a < m[i].addedNodes.length; a++) {\r\n\r\n\t\t\t\t\tlet added = m[i].addedNodes[a];\r\n\r\n\t\t\t\t\tif(!(added instanceof Element)) continue;\r\n\t\t\t\t\t\r\n\t\t\t\t\tlet imgs = added.getElementsByClassName(NeatoLib.getClass([\"avatar\", \"mask\", \"pointer\", \"status\"], \"avatar\"));\r\n\t\t\t\t\tfor(let img of imgs)\r\n\t\t\t\t\t\tif(img) img.src = img.src.split(\"?size=\")[0] + \"?size=\" + this.settings.otherAvatarSize;\r\n\r\n\t\t\t\t\tlet popouts = added.classList.contains(NeatoLib.getClass([\"body\", \"footer\", \"popout\", \"title\"], \"popout\")) ? [added] : added.getElementsByClassName(NeatoLib.getClass([\"body\", \"footer\", \"popout\", \"title\"], \"popout\"));\r\n\t\t\t\t\tfor(let popout of popouts){\r\n\t\t\t\t\t\tlet imgs = popout.getElementsByClassName(NeatoLib.getClass([\"avatar\", \"mask\", \"pointer\", \"status\"], \"avatar\"));\r\n\t\t\t\t\t\tfor(let img of imgs)\r\n\t\t\t\t\t\t\tif(img) img.src = img.src.split(\"?size=\")[0] + \"?size=\" + this.settings.popoutAvatarSize;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t});\r\n\r\n\t\tthis.appObserver.observe(document.getElementById(\"app-mount\"), { childList : true, subtree : true });\r\n\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\t\t\r\n\t\tNeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());\r\n\t\t\r\n\t}\r\n\t\r\n\tstop() {\r\n\t\tthis.appObserver.disconnect();\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "DetailedServerTooltips.plugin.js",
    "content": "//META{\"name\":\"DetailedServerTooltips\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/DetailedServerTooltips.plugin.js\"}*//\r\n\r\nclass DetailedServerTooltips {\r\n\r\n\tgetName() { return \"DetailedServerTooltips\"; }\r\n\tgetDescription() { return \"Displays a more detailed tooltip for servers similar to user popouts. Contains a larger image, owner's tag, date, time and days ago created, date, time and days ago joined, member count, channel count, role count, region, and whether or not the server is partnered.\"; }\r\n\tgetVersion() { return \"0.3.14\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\tgetChanges() {\r\n\t\treturn {\r\n\t\t\t\"0.1.1\": `\r\n\t\t\t\tAdded a creation date field.\r\n\t\t\t`,\r\n\t\t\t\"0.2.3\": `\r\n\t\t\t\tFixed tooltip color not changing with some themes.\r\n\t\t\t\tFixed the tooltip arrow being offsetted wrong when the tooltip was prevented from going off-screen.\r\n\t\t\t\tTooltip guild icons are now full res.\r\n\t\t\t`,\r\n\t\t\t\"0.3.4\": `\r\n\t\t\t\tFixed update notes.\r\n\t\t\t\tFixed incompatibility with Zerebos' DoNotTrack plugin. (If you still have issues with tooltips sticking with it, please let me know. I barely tested it.)\r\n\t\t\t\tAdded a minimal mode setting.\r\n\t\t\t`,\r\n\t\t\t\"0.3.5\": `\r\n\t\t\t\tFixed tooltip getting stuck with ServerFolders\r\n\t\t\t`,\r\n\t\t\t\"0.3.6\": `\r\n\t\t\t\tFixed tooltips getting stuck when switching from dm to a server.\r\n\t\t\t`,\r\n\t\t\t\"0.3.8\": `\r\n\t\t\t\tFixed tooltips not showing for servers inside of folders with DevilBro's ServerFolders plugin.\r\n\t\t\t`\r\n\t\t};\r\n\t}\r\n\r\n\tload() {}\r\n\r\n\tstart() {\r\n\t\tconst libLoadedEvent = () => {\r\n\t\t\ttry{\r\n\t\t\t\tif(window.pluginCookie[\"DoNotTrack\"] == true) setTimeout(() => this.onLibLoaded(), 2000);\r\n\t\t\t\telse this.onLibLoaded();\r\n\t\t\t}\r\n\t\t\tcatch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n\t\t};\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif (!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n\t\tif (typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\t}\r\n\r\n\tget settingFields() {\r\n\t\treturn {\r\n\t\t\ttooltipColor: { label: \"Tooltip color\", type: \"color\" },\r\n\t\t\tdisplayDelay: { label: \"Tooltip display delay\", description: \"(ms)\", type: \"int\" },\r\n\t\t\tpreview: { type: \"custom\", html: `\r\n\t\t\t\t\t<div class=\"tooltip tooltip-right dst-tooltip\" style=\"position: relative; margin-top: 20px;\">\r\n\t\t\t\t\t\t\tKappa Stretch Server\r\n\t\t\t\t\t\t\t<div class=\"dst-tooltip-icon\" style=\"background-image: url(https://cdn.discordapp.com/attachments/392905457486004224/457784406313271296/KappaStretch.png);\"></div>\r\n\t\t\t\t\t\t\t<div id=\"dst-tooltip-owner-label\" class=\"dst-tooltip-label\">Owner: KappaStretch#0000</div>\r\n\t\t\t\t\t\t\t<div class=\"dst-tooltip-label\">Joined at: ${new Date().toLocaleDateString()}, ${new Date().toLocaleTimeString()} (0 days ago)</div>\r\n\t\t\t\t\t\t\t<div id=\"dst-tooltip-member-count-label\" class=\"dst-tooltip-label\">400 members</div>\r\n\t\t\t\t\t\t\t<div class=\"dst-tooltip-label\">15 channels</div>\r\n\t\t\t\t\t\t\t<div class=\"dst-tooltip-label\">10 roles</div>\r\n\t\t\t\t\t\t\t<div class=\"dst-tooltip-label\">Region: us-central</div>\r\n\t\t\t\t\t\t\t<div style=\"font-weight: bolder;\" class=\"dst-tooltip-label\"><div class=\"profileBadgePartner-SjK6L2 profileBadge-2BqF-Z\" style=\"display: inline-block;\"></div>PARTNERED SERVER</div>\r\n\t\t\t\t\t</div>\r\n\t\t\t` },\r\n\t\t\tminimalMode: { label: \"Minimal mode\", type: \"bool\" },\r\n\t\t\tminimalPreview: { type: \"custom\", html: `\r\n\t\t\t\t\t<div class=\"tooltip tooltip-right dst-tooltip dst-min\" style=\"position: relative; margin-top: 20px;\">\r\n\t\t\t\t\t\t\tKappa Stretch Server\r\n\t\t\t\t\t\t\t<div class=\"dst-tooltip-icon\" style=\"background-image: url(https://cdn.discordapp.com/attachments/392905457486004224/457784406313271296/KappaStretch.png);\"></div>\r\n\t\t\t\t\t\t\t<div id=\"dst-tooltip-owner-label\" class=\"dst-tooltip-label\">Owner: KappaStretch#0000</div>\r\n\t\t\t\t\t\t\t<div class=\"dst-tooltip-label\">Joined at: ${new Date().toLocaleDateString()}, ${new Date().toLocaleTimeString()} (0 days ago)</div>\r\n\t\t\t\t\t\t\t<div id=\"dst-tooltip-member-count-label\" class=\"dst-tooltip-label\">400 members</div>\r\n\t\t\t\t\t\t\t<div class=\"dst-tooltip-label\">15 channels</div>\r\n\t\t\t\t\t\t\t<div class=\"dst-tooltip-label\">10 roles</div>\r\n\t\t\t\t\t\t\t<div class=\"dst-tooltip-label\">Region: us-central</div>\r\n\t\t\t\t\t\t\t<div style=\"font-weight: bolder;\" class=\"dst-tooltip-label\"><div class=\"profileBadgePartner-SjK6L2 profileBadge-2BqF-Z\" style=\"display: inline-block;\"></div>PARTNERED SERVER</div>\r\n\t\t\t\t\t</div>\r\n\t\t\t` }\r\n\t\t};\r\n\t}\r\n\r\n\tget defaultSettings() {\r\n\t\treturn {\r\n\t\t\tdisplayUpdateNotes: true,\r\n\t\t\ttooltipColor: \"#7289da\",\r\n\t\t\tdisplayDelay: 500,\r\n\t\t\tminimalMode: false\r\n\t\t};\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\t\treturn NeatoLib.Settings.createPanel(this);\r\n\t}\r\n\r\n\tsaveSettings() {\r\n\t\tthis.applyCSS();\r\n\t\tNeatoLib.Settings.save(this);\r\n\t}\r\n\r\n\tapplyCSS() {\r\n\t\tif (this.style) this.style.destroy();\r\n\r\n\t\tthis.style = NeatoLib.injectCSS(`\r\n\t\t\t.dst-tooltip {\r\n\t\t\t\t\twidth: 225px;\r\n\t\t\t\t\tmax-width: 225px;\r\n\t\t\t\t\ttext-align: center;\r\n\t\t\t\t\tbackground-color: ${this.settings.tooltipColor} !important;\r\n\t\t\t\t\tcolor: white;\r\n\t\t\t}\r\n\r\n\t\t\t.dst-tooltip:after {\r\n\t\t\t\t\tborder-right-color: ${this.settings.tooltipColor} !important;\r\n\t\t\t\t\ttop: 25px !important;\r\n\t\t\t}\r\n\r\n\t\t\t.dst-tooltip-icon {\r\n\t\t\t\t\twidth: 200px;\r\n\t\t\t\t\theight: 200px;\r\n\t\t\t\t\tbackground-size: cover;\r\n\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t\tmargin-top: 5px;\r\n\t\t\t\t\tflex: 1;\r\n\t\t\t}\r\n\r\n\t\t\t.dst-tooltip-label {\r\n\t\t\t\t\tcolor: white;\r\n\t\t\t\t\tmargin-top: 10px;\r\n\t\t\t\t\tfont-size: 15px;\r\n\t\t\t}\r\n\r\n\t\t\t.dst-min .dst-tooltip-icon{display:none}\r\n\t\t\t.dst-min .dst-tooltip-label{font-size:13px}\r\n\t\t\t.dst-tooltip.dst-min{max-width:200px}\r\n\t`);\r\n\t}\r\n\r\n\tonLibLoaded() {\r\n\t\tif (!NeatoLib.hasRequiredLibVersion(this, \"0.8.20\")) return;\r\n\r\n\t\tNeatoLib.Settings.load(this);\r\n\r\n\t\tNeatoLib.Updates.check(this);\r\n\r\n\t\tif (this.settings.displayUpdateNotes) NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());\r\n\r\n\t\tthis.guildModule = NeatoLib.Modules.get(\"getGuild\");\r\n\t\tthis.userModule = NeatoLib.Modules.get(\"getUser\");\r\n\t\tthis.memberModule = NeatoLib.Modules.get(\"getMembers\");\r\n\t\tthis.channelModule = NeatoLib.Modules.get(\"getChannel\");\r\n\t\tthis.memberCountModule = NeatoLib.Modules.get(\"getMemberCount\");\r\n\r\n\t\tthis.localUser = NeatoLib.getLocalUser();\r\n\r\n\t\tthis.owners = {};\r\n\r\n\t\tthis.applyCSS();\r\n\r\n\t\tlet tooltip, timeout;\r\n\r\n\t\tthis.dragGuild = () => {\r\n\t\t\tthis.mouseLeaveGuild();\r\n\r\n\t\t\tlet tooltips = document.getElementsByClassName(\"dst-tooltip\");\r\n\r\n\t\t\tfor (let i = 0; i < tooltips.length; i++) {\r\n\t\t\t\tif (tooltips[i].updateLoop) clearInterval(tooltips[i].updateLoop);\r\n\t\t\t\ttooltips[i].remove();\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tthis.mouseEnterGuild = e => {\r\n\t\t\ttimeout = setTimeout(() => {\r\n\t\t\t\ttooltip = this.tooltip(((e.target.parentElement.href || e.target.href).match(/\\d+/) || [])[0], e.target);\r\n\t\t\t\tif (!tooltip) return;\r\n\t\t\t\tlet tt = document.getElementsByClassName(NeatoLib.getClass(\"tooltip\"))[0];\r\n\t\t\t\ttt.appendChild(tooltip);\r\n\t\t\t\ttt.getElementsByClassName(NeatoLib.getClass(\"tooltip\", \"tooltipPointer\"))[0].style.borderTopColor = this.settings.tooltipColor;\r\n\t\t\t\tlet bottomPos = parseFloat(tooltip.style.top) + tooltip.offsetHeight;\r\n\t\t\t\tif (bottomPos > window.innerHeight) {\r\n\t\t\t\t\ttooltip.style.top = (parseFloat(tooltip.style.top) - (bottomPos - window.innerHeight)) + \"px\";\r\n\t\t\t\t\ttooltip.insertAdjacentHTML(\"afterbegin\", `<style>.dst-tooltip:after{top:calc(25px + ${parseFloat(tooltip.style.top) - (parseFloat(tooltip.style.top) - (bottomPos - window.innerHeight))}px) !important}</style>`);\r\n\t\t\t\t}\r\n\t\t\t\tvar tooltipObserver = new MutationObserver((mutations) => {\r\n\t\t\t\t\tmutations.forEach((mutation) => {\r\n\t\t\t\t\t\tvar nodes = Array.from(mutation.removedNodes);\r\n\t\t\t\t\t\tvar ownMatch = nodes.indexOf(tooltip) > -1;\r\n\t\t\t\t\t\tvar directMatch = nodes.indexOf(e.target) > -1;\r\n\t\t\t\t\t\tvar parentMatch = nodes.some(parent => parent.contains(e.target));\r\n\t\t\t\t\t\tif (ownMatch || directMatch || parentMatch) {\r\n\t\t\t\t\t\t\ttooltipObserver.disconnect();\r\n\t\t\t\t\t\t\ttooltip.remove();\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t});\r\n\t\t\t\t});\r\n\t\t\t}, this.settings.displayDelay);\r\n\t\t};\r\n\r\n\t\tthis.mouseLeaveGuild = () => {\r\n\t\t\tclearTimeout(timeout);\r\n\t\t\tif (tooltip) tooltip.remove();\r\n\t\t};\r\n\r\n\t\tthis.switchEvent = () => this.applyToGuilds();\r\n\r\n\t\tthis.guildObserver = new MutationObserver(this.switchEvent);\r\n\t\tthis.guildObserver.observe(document.getElementsByClassName(NeatoLib.getClass(\"unreadMentionsBar\", \"scroller\"))[0], { childList: true, subtree: true });\r\n\r\n\t\tNeatoLib.Events.attach(\"switch\", this.switchEvent);\r\n\r\n\t\tthis.applyToGuilds();\r\n\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\t}\r\n\r\n\tapplyToGuilds(detach) {\r\n\t\tconst guilds = document.getElementsByClassName(NeatoLib.getClass(\"acronym\", \"wrapper\"));\r\n\r\n\t\tfor (let i = 0; i < guilds.length; i++) {\r\n\t\t\tlet reactEvents = NeatoLib.ReactData.getEvents(guilds[i]);\r\n\r\n\t\t\tguilds[i].parentElement.removeEventListener(\"dragstart\", this.dragGuild);\r\n\t\t\tguilds[i].parentElement.removeEventListener(\"dragend\", this.dragGuild);\r\n\t\t\tguilds[i].removeEventListener(\"mouseenter\", this.mouseEnterGuild);\r\n\t\t\tguilds[i].removeEventListener(\"mouseleave\", this.mouseLeaveGuild);\r\n\r\n\t\t\tif (detach) continue;\r\n\r\n\t\t\tguilds[i].parentElement.addEventListener(\"dragstart\", this.dragGuild);\r\n\t\t\tguilds[i].parentElement.addEventListener(\"dragend\", this.dragGuild);\r\n\t\t\tguilds[i].addEventListener(\"mouseenter\", this.mouseEnterGuild);\r\n\t\t\tguilds[i].addEventListener(\"mouseleave\", this.mouseLeaveGuild);\r\n\t\t}\r\n\t}\r\n\r\n\ttooltip(guildId, element) {\r\n\t\tif (!guildId || !element || element.getBoundingClientRect().width == 0) return;\r\n\r\n\t\tlet tooltip = document.createElement(\"div\"),\r\n\t\t\tguild = this.guildModule.getGuild(guildId),\r\n\t\t\towner = this.userModule.getUser(guild.ownerId);\r\n\r\n\t\ttooltip.className = NeatoLib.getClass(\"tooltip\") + \" \" + NeatoLib.getClass(\"tooltip\", \"tooltipRight\") + \" dst-tooltip\";\r\n\t\tif (this.settings.minimalMode) tooltip.classList.add(\"dst-min\");\r\n\r\n\t\ttooltip.style.top = \"0\";\r\n\t\ttooltip.style.left = \"0\";\r\n\t\ttooltip.style.position = \"fixed\";\r\n\r\n\t\tlet creationDate = NeatoLib.getSnowflakeCreationDate(guild.id);\r\n\r\n\t\tvar imgURL = guild.getIconURL(guild && guild.icon && guild.icon.startsWith('a_') ? 'gif' : 'webp');\r\n\t\t\r\n\t\ttooltip.innerHTML = `${this.escapeHtml(guild.name)}\r\n\t\t\t\t<div class=\"dst-tooltip-icon\" style=\"background-image: url(${imgURL}?size=2048);\"></div>\r\n\t\t\t\t<div id=\"dst-tooltip-owner-label\" class=\"dst-tooltip-label\">Owner: ${owner ? this.escapeHtml(owner.tag) : \"unknown\"}</div>\r\n\t\t\t\t<div class=\"dst-tooltip-label\">Created at: ${creationDate.toLocaleDateString()}, ${creationDate.toLocaleTimeString()} (${Math.round(Math.abs(creationDate.getTime() - new Date().getTime()) / 86400000)} days ago)</div>\r\n\t\t\t\t${creationDate.toString() == guild.joinedAt.toString() ? \"\" : `<div class=\"dst-tooltip-label\">Joined at: ${guild.joinedAt.toLocaleDateString()}, ${guild.joinedAt.toLocaleTimeString()} (${Math.round(Math.abs(guild.joinedAt.getTime() - new Date().getTime()) / 86400000)} days ago)</div>`}\r\n\t\t\t\t<div id=\"dst-tooltip-member-count-label\" class=\"dst-tooltip-label\">${this.memberCountModule.getMemberCount(guildId)} members</div>\r\n\t\t\t\t<div class=\"dst-tooltip-label\">${Object.values(this.channelModule.getChannels()).filter(c => c.guild_id == guildId).length} channels</div>\r\n\t\t\t\t<div class=\"dst-tooltip-label\">${Object.keys(guild.roles).length} roles</div>\r\n\t\t\t\t<div class=\"dst-tooltip-label\">Region: ${guild.region}</div>`;\r\n\r\n\t\tif(!guild.getIconURL()) tooltip.find(\".dst-tooltip-icon\").outerHTML = \"\";\r\n\r\n\t\tif (guild.features.has(\"PARTNERED\")) tooltip.insertAdjacentHTML(\"beforeend\", `<div style=\"font-weight: bolder;\" class=\"dst-tooltip-label\"><div class=\"profileBadgePartner-SjK6L2 profileBadge-2BqF-Z\" style=\"display: inline-block;\"></div>PARTNERED SERVER</div>`);\r\n\r\n\t\tif(owner){\r\n\t\t\tthis.owners[guildId] = owner.tag;\r\n\t\t}else{\r\n\t\t\tNeatoLib.Modules.get(\"getAPIBaseURL\").get(NeatoLib.Modules.get([\"Permissions\", \"ActivityTypes\", \"StatusTypes\"]).Endpoints.USER(guild.ownerId)).then(result => {\r\n\t\t\t\tif(!result) return;\r\n\t\t\t\tlet res = JSON.parse(result.text);\r\n\t\t\t\tthis.owners[guildId] = res.username + \"#\" + res.discriminator;\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tlet updateMemberCount = false;\r\n\t\tif(this.memberCountModule.getMemberCount(guildId) < 500){\r\n\t\t\tNeatoLib.Modules.get(\"requestMembers\").requestMembers(guildId, \"\", 0);\r\n\t\t\tupdateMemberCount = true;\r\n\t\t}\r\n\r\n\t\tconst self = setInterval(() => {\r\n\t\t\tif (!Array.from(document.getElementsByClassName(NeatoLib.getClass(\"tooltip\"))).includes(tooltip)) return clearInterval(self);\r\n\t\t\tdocument.getElementById(\"dst-tooltip-owner-label\").innerHTML = \"Owner: \" + (this.escapeHtml(this.owners[guildId] || \"unknown\"));\r\n\t\t\tif(updateMemberCount) document.getElementById(\"dst-tooltip-member-count-label\").innerText = this.memberModule.getMembers(guildId).length + \" members\";\r\n\t\t}, 500);\r\n\r\n\t\ttooltip.updateLoop = self;\r\n\r\n\t\treturn tooltip;\r\n\t}\r\n\r\n\tstop() {\r\n\t\tlet tooltips = document.getElementsByClassName(\"dst-tooltip\");\r\n\r\n\t\tfor (let i = 0; i < tooltips.length; i++) {\r\n\t\t\tif (tooltips[i].updateLoop) clearInterval(tooltips[i].updateLoop);\r\n\t\t\ttooltips[i].remove();\r\n\t\t}\r\n\r\n\t\tthis.applyToGuilds(true);\r\n\r\n\t\tif (this.style) this.style.destroy();\r\n\r\n\t\tNeatoLib.Events.detach(\"switch\", this.switchEvent);\r\n\r\n\t\tthis.guildObserver.disconnect();\r\n\t}\r\n\r\n\tescapeHtml(txt){\r\n\t\treturn txt.replace(/&/g, \"&amp;\")\r\n\t\t\t\t  .replace(/</g, \"&lt;\")\r\n\t\t\t\t  .replace(/>/g, \"&gt;\")\r\n\t\t\t\t  .replace(/\"/g, \"&quot;\")\r\n\t\t\t\t  .replace(/'/g, \"&#039;\");\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "DoubleClickVoiceChannels.plugin.js",
    "content": "/**\r\n * @name DoubleClickVoiceChannels\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www.paypal.me/israelboone\r\n * @website https://metalloriff.github.io/toms-discord-stuff/\r\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/DoubleClickVoiceChannels.plugin.js\r\n */\r\n\r\nmodule.exports = (() => {\r\n\tconst config =\r\n\t{\r\n\t\tinfo:\r\n\t\t{\r\n\t\t\tname: \"DoubleClickVoiceChannels\",\r\n\t\t\tauthors:\r\n\t\t\t\t[\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tname: \"Metalloriff\",\r\n\t\t\t\t\t\tdiscord_id: \"264163473179672576\",\r\n\t\t\t\t\t\tgithub_username: \"metalloriff\",\r\n\t\t\t\t\t\ttwitter_username: \"Metalloriff\"\r\n\t\t\t\t\t}\r\n\t\t\t\t],\r\n\t\t\tversion: \"2.0.4\",\r\n\t\t\tdescription: \"Requires you to double click voice channels to join them.\",\r\n\t\t\tgithub: \"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/DoubleClickVoiceChannels.plugin.js\",\r\n\t\t\tgithub_raw: \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/DoubleClickVoiceChannels.plugin.js\"\r\n\t\t},\r\n\t\tchangelog:\r\n\t\t\t[\r\n\t\t\t\t{\r\n\t\t\t\t\ttitle: \"Patched\",\r\n\t\t\t\t\ttype: \"fixed\",\r\n\t\t\t\t\titems: [\"Fixed again. Thanks cmd430 for the help!\"]\r\n\t\t\t\t}\r\n\t\t\t]\r\n\t};\r\n\r\n\treturn !global.ZeresPluginLibrary ? class {\r\n\t\tconstructor() { this._config = config; }\r\n\r\n\t\tgetName = () => config.info.name;\r\n\t\tgetAuthor = () => config.info.description;\r\n\t\tgetVersion = () => config.info.version;\r\n\r\n\t\tload() {\r\n\t\t\tBdApi.showConfirmationModal(\"Library Missing\", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {\r\n\t\t\t\tconfirmText: \"Download Now\",\r\n\t\t\t\tcancelText: \"Cancel\",\r\n\t\t\t\tonConfirm: () => {\r\n\t\t\t\t\trequire(\"request\").get(\"https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js\", async (err, res, body) => {\r\n\t\t\t\t\t\tif (err) return require(\"electron\").shell.openExternal(\"https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js\");\r\n\t\t\t\t\t\tawait new Promise(r => require(\"fs\").writeFile(require(\"path\").join(BdApi.Plugins.folder, \"0PluginLibrary.plugin.js\"), body, r));\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tstart() { }\r\n\t\tstop() { }\r\n\t} : (([Plugin, Api]) => {\r\n\r\n\t\tconst plugin = (Plugin, Api) => {\r\n\t\t\tconst { WebpackModules, Patcher, Utilities, ReactComponents } = Api;\r\n\r\n\t\t\treturn class DoubleClickVoiceChannels extends Plugin {\r\n\t\t\t\tconstructor() {\r\n\t\t\t\t\tsuper();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tasync showChangelog(footer) {\r\n\t\t\t\t\ttry { footer = (await WebpackModules.getByProps(\"getUser\", \"acceptAgreements\").getUser(\"264163473179672576\")).tag + \" | https://discord.gg/yNqzuJa\"; }\r\n\t\t\t\t\tfinally { super.showChangelog(footer); }\r\n\t\t\t\t}\r\n\r\n\t\t\t\tasync onStart() {\r\n\t\t\t\t\tconst { component: ChannelItem } = await ReactComponents.getComponentByName(\"VoiceChannel\", \"*\");\r\n\t\t\t\t\r\n\t\t\t\t\tif (ChannelItem) {\r\n\t\t\t\t\t\tPatcher.after(ChannelItem.prototype, \"render\", (r, _, el) => {\r\n\t\t\t\t\t  const children_type0 = Utilities.getNestedProp(el, \"props.children.props.children.0.props.children.props.children\");\r\n\t\t\t\t\t  const children_type1 = Utilities.getNestedProp(el, \"props.children.0.props.children.props.children\");\r\n\t\t\t\t\r\n\t\t\t\t\t\t\tif (children_type0 || children_type1) {\r\n\t\t\t\t\t\tconst handleClick = (children) => {\r\n\t\t\t\t\t\t  const c = children();\r\n\t\t\t\t\r\n\t\t\t\t\t\t\t\t\tconst handler = c.props.children;\r\n\t\t\t\t\t\t\t\t\tc.props.children = () => {\r\n\t\t\t\t\t\t\t\t\t\tconst h = handler({});\r\n\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t\tif (!h.props.connected) {\r\n\t\t\t\t\t\t\t\t\t\t\t// for whatever reason, onDoubleClick stopped working, so here's a dumb workaround\r\n\t\t\t\t\t\t\t\t\t\t\tconst onClick = h.props.onClick;\r\n\t\t\t\t\t\t\t\t\t\t\tlet t = performance.now() - 200;\r\n\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t\t\th.props.onClick = () => {\r\n\t\t\t\t\t\t\t\t\t\t\t\tif (performance.now() - t < 200)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tonClick();\r\n\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t\t\t\tt = performance.now();\r\n\t\t\t\t\t\t\t\t\t\t\t};\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t\treturn h;\r\n\t\t\t\t\t\t\t\t\t};\r\n\t\t\t\t\r\n\t\t\t\t\t\t\t\t\treturn c;\r\n\t\t\t\t\t\t};\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tif (children_type0) {\r\n\t\t\t\t\t\t  el.props.children.props.children[0].props.children.props.children = () => {\r\n\t\t\t\t\t\t\treturn handleClick(children_type0);\r\n\t\t\t\t\t\t  };\r\n\t\t\t\t\t\t} \r\n\t\t\t\t\t\tif (children_type1) {\r\n\t\t\t\t\t\t  el.props.children[0].props.children.props.children = () => {\r\n\t\t\t\t\t\t\treturn handleClick(children_type1);\r\n\t\t\t\t\t\t  };\r\n\t\t\t\t\t\t\t\t};\r\n\t\t\t\t\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse {\r\n\t\t\t\t\t\t\t\tconsole.warn(\"DoubleClickVoiceChannel: Failed to get nested props!\");\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tonStop() {\r\n\t\t\t\t\tPatcher.unpatchAll();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\treturn plugin(Plugin, Api);\r\n\t})(global.ZeresPluginLibrary.buildPlugin(config));\r\n})();\r\n"
  },
  {
    "path": "FormattableMessageCopier.plugin.js",
    "content": "//META{\"name\":\"FormattableMessageCopier\"}*//\r\n\r\nclass FormattableMessageCopier {\r\n\t\r\n\tgetSettingsPanel(){\r\n            \r\n        let updateExample = () => {\r\n\r\n            let hints = document.getElementsByClassName(\"plugin-settings\")[0].getElementsByTagName(\"p\");\r\n\r\n            for(let i = 0; i < hints.length; i++) hints[i].style.color = `rgb(${this.settings.selectionColor})`;\r\n\r\n            let time = new Date().toLocaleTimeString(\"en-us\"),\r\n            example = document.getElementById(\"mc-example\"),\r\n            exampleMessages1 = [], exampleMessages2 = [];\r\n\r\n            for(let msg of [\"Hello.\", \"These are some messages.\", \"This is a third message, with a reaction.\"]) {\r\n\r\n                exampleMessages1.push(this.settings.messageFormat\r\n                .split(\"$time\").join(time)\r\n                .split(\"$messagetext\").join(msg));\r\n\r\n                exampleMessages1.push(this.settings.reactionFormat\r\n                .split(\"$emoji\").join(\"👌\")\r\n                .split(\"$count\").join(\"4\") + this.settings.reactionFormat\r\n                .split(\"$emoji\").join(\"💯\")\r\n                .split(\"$count\").join(\"20\"));\r\n\r\n                exampleMessages2.push(this.settings.messageFormat\r\n                .split(\"$time\").join(time)\r\n                .split(\"$messagetext\").join(\"This message has an image attached to it.\"));\r\n\r\n                exampleMessages2.push(this.settings.attachmentFormat\r\n                .split(\"$filename\").join(\"LUUL.jpg\")\r\n                .split(\"$fileurl\").join(\"https://i.imgur.com/cxWch9R.jpg\"));\r\n\r\n                example.innerText = (this.settings.headerFormat\r\n                    .split(\"$channel\").join(\"#general\")\r\n                    .split(\"$selectionstarttime\").join(time)\r\n                    .split(\"$selectionendtime\").join(time)\r\n                    .split(\"$selectiondate\").join(new Date().toLocaleDateString(\"en-us\"))\r\n                + \"\\n\" + this.settings.groupFormat\r\n                    .split(\"$time\").join(time)\r\n                    .split(\"$username\").join(\"Metalloriff\")\r\n                    .split(\"$usertag\").join(\"Metalloriff#2891\")\r\n                    .split(\"$jumplink\").join(\"https://discordapp.com/channels/serverid/channelid/messageid\")\r\n                    .split(\"$message\").join(exampleMessages1.join(\"\\n\"))\r\n                + \"\\n\" + this.settings.groupFormat\r\n                    .split(\"$time\").join(time)\r\n                    .split(\"$username\").join(\"Some Kid Named Nate\")\r\n                    .split(\"$usertag\").join(\"Some Kid Named Nate#0000\")\r\n                    .split(\"$jumplink\").join(\"https://discordapp.com/channels/serverid/channelid/messageid\")\r\n                    .split(\"$message\").join(exampleMessages2.join(\"\\n\")))\r\n                .split(\"$newline\").join(\"\\n\")\r\n                .split(\"$tab\").join(\"    \");\r\n\r\n            }\r\n\r\n        };\r\n\r\n        setTimeout(() => {\r\n            \r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField(\"Header format\", this.settings.headerFormat, e => {\r\n                this.settings.headerFormat = e.target.value;\r\n                updateExample();\r\n                this.saveSettings();\r\n            }), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createHint(`\r\n                Header variables:\\n\\n\r\n                $channel - The name of the selected channel\\n\r\n                $selectionstarttime - The timestamp of the first selected message\\n\r\n                $selectionendtime - The timestamp of the last selected message\\n\r\n                $selectiondate - The date of the selected messages\\n\r\n                $newline - New line\\n\r\n                $tab - Tab\r\n            `), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField(\"Message group format\", this.settings.groupFormat, e => {\r\n                this.settings.groupFormat = e.target.value;\r\n                updateExample();\r\n                this.saveSettings();\r\n            }), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createHint(`\r\n                $time - Message group timestamp\\n\r\n                $username - Message group sender's username\\n\r\n                $usertag - Message group sender's username and discriminator\\n\r\n                $message - Formatted message\\n\r\n                $newline - New line\\n\r\n                $tab - Tab\r\n            `), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField(\"Message format\", this.settings.messageFormat, e => {\r\n                this.settings.messageFormat = e.target.value;\r\n                updateExample();\r\n                this.saveSettings();\r\n            }), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createHint(`\r\n                $time - Message timestamp\\n\r\n                $jumplink - A link that redirects the user to the message, if they are in the server it was from, and have access to the channel\\n\r\n                $messagetext - Message text\\n\r\n                $newline - New line\\n\r\n                $tab - Tab\r\n            `), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField(\"Attachment format\", this.settings.attachmentFormat, e => {\r\n                this.settings.attachmentFormat = e.target.value;\r\n                updateExample();\r\n                this.saveSettings();\r\n            }), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createHint(`\r\n                $filename - Name of the uploaded file\\n\r\n                $fileurl - URL of the uploaded file\\n\r\n                $newline - New line\\n\r\n                $tab - Tab\r\n            `), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField(\"Reaction format\", this.settings.reactionFormat, e => {\r\n                this.settings.reactionFormat = e.target.value;\r\n                updateExample();\r\n                this.saveSettings();\r\n            }), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createHint(`\r\n                $emoji - Reaction's emoji\\n\r\n                $count - Reaction count\\n\r\n                $newline - New line\\n\r\n                $tab - Tab\r\n            `), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField(\"Selection color (R, G, B)\", this.settings.selectionColor, e => {\r\n                this.settings.selectionColor = e.target.value;\r\n                updateExample();\r\n                this.saveSettings();\r\n            }), this.getName());\r\n\r\n            NeatoLib.Settings.pushHTML(`\r\n                <div id=\"mc-example\" style=\"padding-top:30px;color:white;background-color:rgba(0,0,0,0.3);white-space:pre;border-radius:5px;\" class=\"message-group hide-overflow\">\r\n                    <div class=\"comment\">\r\n                        <div class=\"message\">\r\n                            <div class=\"body\">\r\n                                <div class=\"message-text\">\r\n                                    <div class=\"markup\">...</div>\r\n                                </div>\r\n                            </div>\r\n                        </div>\r\n                    </div>\r\n                </div>\r\n            `, this.getName());\r\n\r\n            let fields = document.getElementsByClassName(\"plugin-settings\")[0].getElementsByClassName(\"input-2YozMi\");\r\n\r\n            updateExample();\r\n\r\n        }, 0);\r\n        \r\n        return NeatoLib.Settings.Elements.pluginNameLabel(this.getName());\r\n\r\n\t}\r\n\r\n    saveSettings(){\r\n        NeatoLib.Settings.save(this);\r\n    }\r\n\t\r\n    getName() { return \"Formattable Message Copier\"; }\r\n    getDescription() { return \"Allows you to select messages in a chat to copy them in a customizable format. Double click the top of a message group to select it, then shift click to the next point to select all message groups between, or ctrl click another message group to append a single group to the selection.\"; }\r\n    getVersion() { return \"0.1.4\"; }\r\n    getAuthor() { return \"Metalloriff\"; }\r\n\r\n    load() {}\r\n\r\n    start() {\r\n\r\n        let libLoadedEvent = () => {\r\n            try{ this.onLibLoaded(); }\r\n            catch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n        };\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif(lib == undefined) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.setAttribute(\"id\", \"NeatoBurritoLibrary\");\r\n\t\t\tlib.setAttribute(\"type\", \"text/javascript\");\r\n\t\t\tlib.setAttribute(\"src\", \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\");\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n        if(typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n        else lib.addEventListener(\"load\", libLoadedEvent);\r\n\r\n\t}\r\n\t\r\n\tonLibLoaded(){\r\n\r\n        this.settings = NeatoLib.Settings.load(this, {\r\n            headerFormat : \"messages in $channel since $selectionstarttime, until $selectionendtime, on $selectiondate$newline$newline\",\r\n            groupFormat : \"[$time] $username:$newline$newline$message$newline$newline\",\r\n            messageFormat : \"$tab$messagetext\",\r\n            attachmentFormat : \"[$filename]: <$fileurl>\",\r\n            reactionFormat : \" [ $emoji : $count ] \",\r\n            selectionColor : \"114, 137, 218\"\r\n        });\r\n\r\n        NeatoLib.Updates.check(this);\r\n\r\n        NeatoLib.Events.onPluginLoaded(this);\r\n\r\n        this.userModule = NeatoLib.Modules.get(\"getUser\");\r\n        this.channelModule = NeatoLib.Modules.get(\"getChannel\");\r\n        this.selection = [];\r\n\r\n        this.applyCSS();\r\n\r\n        this.clickEvent = e => this.onClick(e);\r\n\r\n        document.addEventListener(\"dblclick\", this.clickEvent);\r\n        document.addEventListener(\"click\", this.clickEvent);\r\n        \r\n    }\r\n\r\n    applyCSS(){\r\n\r\n        if(this.styles) this.styles.destroy();\r\n\r\n        this.styles = NeatoLib.injectCSS(`\r\n\r\n            .mc-message-selected {\r\n                margin-left: 0px !important;\r\n                margin-right: 0px !important;\r\n                padding-left: 20px !important;\r\n                padding-right: 6px !important;\r\n                border: 2px solid rgb(${this.settings.selectionColor}) !important;\r\n                background-color: rgba(${this.settings.selectionColor}, 0.5) !important;\r\n            }\r\n\r\n            #mc-copy-button {\r\n                background-color: rgb(${this.settings.selectionColor}) !important;\r\n            }\r\n\r\n        `);\r\n        \r\n    }\r\n    \r\n    onClick(e){\r\n\r\n        if(!e.target.classList || (!e.target.classList.contains(\"message-group\") && e.target.className != \"old-h2\")) return;\r\n\r\n        let messageGroup;\r\n\r\n        if(e.target.className.includes(\"message-group\")) messageGroup = e.target;\r\n        else messageGroup = $(e.target).parents(\".message-group\")[0];\r\n        \r\n        if(messageGroup) {\r\n\r\n            if(e.type == \"dblclick\" || (e.type == \"click\" && e.ctrlKey)){\r\n                messageGroup.classList.add(\"mc-message-selected\");\r\n                this.selection.push(messageGroup);\r\n            }\r\n\r\n            if(e.type == \"click\" && !e.ctrlKey && !e.shiftKey) this.clearSelection();\r\n\r\n            if(e.type == \"click\" && e.shiftKey && this.selection.length > 0 && messageGroup != this.selection[0]) {\r\n\r\n                if(document.selection) document.selection.empty();\r\n                else if(window.getSelection) window.getSelection().removeAllRanges();\r\n\r\n                let messages = document.getElementsByClassName(\"message-group\"), selecting = false, newSelection = [];\r\n\r\n                let select = (i, reverse) => {\r\n                    if(messages[i] == (reverse ? messageGroup : this.selection[0])) selecting = true;\r\n                    if(selecting) {\r\n                        newSelection.push(messages[i]);\r\n                        messages[i].classList.add(\"mc-message-selected\");\r\n                    }\r\n                    if(messages[i] == (reverse ? this.selection[0] : messageGroup)) selecting = false;\r\n                };\r\n\r\n                if(this.selection.length > 1) {\r\n                    for(let i = 0; i < this.selection.length; i++) this.selection[i].classList.remove(\"mc-message-selected\");\r\n                    this.selection.splice(1, this.selection.length);\r\n                }\r\n\r\n                for(let i = 0; i < messages.length; i++) select(i);\r\n\r\n                if(selecting) {\r\n                    selecting = false;\r\n                    newSelection = [];\r\n                    for(let i = 0; i < messages.length; i++) {\r\n                        messages[i].classList.remove(\"mc-message-selected\");\r\n                        select(i, true);\r\n                    }\r\n                }\r\n\r\n                this.selection = newSelection;\r\n\r\n            }\r\n\r\n        }\r\n\r\n        if(document.getElementById(\"mc-copy-button\")) document.getElementById(\"mc-copy-button\").remove();\r\n\r\n        if(this.selection.length > 0){\r\n            this.selection[this.selection.length - 1].insertAdjacentHTML(\"beforeend\", `<button id=\"mc-copy-button\" style=\"display: inline-block; margin-right: 25px;\" type=\"button\" class=\"button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMedium-1AC_Sl grow-q77ONN\"><div class=\"contents-4L4hQM\">Copy Selection (${this.selection.length})</div></button>`);\r\n            document.getElementById(\"mc-copy-button\").addEventListener(\"click\", () => this.copySelection());\r\n        }\r\n\r\n    }\r\n\r\n    clearSelection(){\r\n        for(let i = 0; i < this.selection.length; i++) this.selection[i].classList.remove(\"mc-message-selected\");\r\n        this.selection = [];\r\n        if(document.getElementById(\"mc-copy-button\")) document.getElementById(\"mc-copy-button\").remove();\r\n    }\r\n\r\n    copySelection(){\r\n\r\n        let formatted = [],\r\n        firstMessage = NeatoLib.ReactData.getProps(this.selection[0]),\r\n        lastGroup = NeatoLib.ReactData.getProps(this.selection[this.selection.length - 1]).messages,\r\n        lastMessageTimestamp = lastGroup[lastGroup.length - 1].timestamp,\r\n        channelName = firstMessage.channel.name;\r\n        \r\n        if(this.settings.headerFormat != \"\" && this.selection.length > 1){\r\n            formatted.push(this.settings.headerFormat\r\n                .split(\"$channel\").join(channelName == \"\" ? \"DM\" : (\"#\" + channelName))\r\n                .split(\"$selectionstarttime\").join(firstMessage.messages[0].timestamp.toDate().toLocaleTimeString(\"en-us\"))\r\n                .split(\"$selectionendtime\").join(lastMessageTimestamp.toDate().toLocaleTimeString(\"en-us\"))\r\n                .split(\"$selectiondate\").join(lastMessageTimestamp.toDate().toLocaleDateString(\"en-us\"))\r\n                .split(\"$newline\").join(\"\\n\")\r\n                .split(\"$tab\").join(\"\\t\")\r\n            );\r\n        }\r\n\r\n        for(let i = 0; i < this.selection.length; i++) {\r\n\r\n            let props = NeatoLib.ReactData.getProps(this.selection[i]),\r\n            time = props.messages[0].timestamp.toDate().toLocaleTimeString(\"en-us\"),\r\n            messages = [];\r\n\r\n            for(let pi = 0; pi < props.messages.length; pi++) {\r\n\r\n                let message = props.messages[pi];\r\n\r\n                for(let mi = 0; mi < message.mentions.length; mi++) message.content = message.content.replace(\"<@\" + message.mentions[i] + \">\", \"`@\" + this.userModule.getUser(message.mentions[i]).tag + \"`\");\r\n\r\n                let content = this.settings.messageFormat\r\n                .split(\"$time\").join(message.timestamp.toDate().toLocaleTimeString(\"en-us\"))\r\n                .split(\"$newline\").join(\"\\n\")\r\n                .split(\"$tab\").join(\"\\t\")\r\n                .split(\"$messagetext\").join(message.content);\r\n                \r\n                if(content != \"\") messages.push(content);\r\n\r\n                let reactions = [];\r\n\r\n                for(let ri = 0; ri < message.reactions.length; ri++) {\r\n                    reactions.push(this.settings.reactionFormat\r\n                        .split(\"$emoji\").join(message.reactions[ri].emoji.name)\r\n                        .split(\"$count\").join(message.reactions[ri].count)\r\n                        .split(\"$newline\").join(\"\\n\")\r\n                        .split(\"$tab\").join(\"\\t\")\r\n                    );\r\n                }\r\n\r\n                if(reactions.length > 0) messages.push(reactions.join(\"\"));\r\n\r\n                for(let ai = 0; ai < message.attachments.length; ai++) {\r\n                    messages.push(this.settings.attachmentFormat\r\n                        .split(\"$filename\").join(message.attachments[ai].filename)\r\n                        .split(\"$fileurl\").join(message.attachments[ai].url)\r\n                        .split(\"$newline\").join(\"\\n\")\r\n                        .split(\"$tab\").join(\"\\t\")\r\n                    );\r\n                }\r\n\r\n            }\r\n            \r\n            formatted.push(this.settings.groupFormat\r\n                .split(\"$time\").join(time)\r\n                .split(\"$username\").join(props.messages[0].author.username)\r\n                .split(\"$usertag\").join(props.messages[0].author.tag)\r\n                .split(\"$jumplink\").join(props.channel.guild_id ? `<https://discordapp.com/channels/${props.channel.guild_id}/${props.channel.id}/${props.messages[0].id}>` : \"\")\r\n                .split(\"$message\").join(messages.join(\"\\n\"))\r\n                .split(\"$newline\").join(\"\\n\")\r\n                .split(\"$tab\").join(\"\\t\")\r\n            );\r\n        }\r\n        \r\n        this.clearSelection();\r\n\r\n        formatted = formatted.join(\"\\n\");\r\n        \r\n        NeatoLib.Modules.get(\"copy\").copy(formatted);\r\n\r\n        NeatoLib.showToast(formatted.length + \" characters copied!\", null, { color : `rgb(${this.settings.selectionColor})` });\r\n        \r\n    }\r\n\t\r\n    stop() {\r\n\r\n        document.removeEventListener(\"dblclick\", this.clickEvent);\r\n        document.removeEventListener(\"click\", this.clickEvent);\r\n\r\n        this.styles.destroy();\r\n        \r\n    }\r\n\t\r\n}\r\n"
  },
  {
    "path": "GuildAndFriendRemovalAlerts/GuildAndFriendRemovalAlerts.plugin.js",
    "content": "/**\n * @name GuildAndFriendRemovalAlerts\n * @version 3.2.1\n * @description Displays alerts when you are kicked/banned from a server, a server is deleted, and when a friend removes you.\n * @author Metalloriff\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/GuildAndFriendRemovalAlerts\n * @updateUrl https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/GuildAndFriendRemovalAlerts/GuildAndFriendRemovalAlerts.plugin.js\n * @website https://metalloriff.github.io/toms-discord-stuff/#/\n * @donate https://paypal.me/israelboone\n * @invite yNqzuJa\n */\n/*@cc_on\n@if (@_jscript)\n    \n    // Offer to self-install for clueless users that try to run this directly.\n    var shell = WScript.CreateObject(\"WScript.Shell\");\n    var fs = new ActiveXObject(\"Scripting.FileSystemObject\");\n    var pathPlugins = shell.ExpandEnvironmentStrings(\"%APPDATA%\\BetterDiscord\\plugins\");\n    var pathSelf = WScript.ScriptFullName;\n    // Put the user at ease by addressing them in the first person\n    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);\n    if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {\n        shell.Popup(\"I'm in the correct folder already.\", 0, \"I'm already installed\", 0x40);\n    } else if (!fs.FolderExists(pathPlugins)) {\n        shell.Popup(\"I can't find the BetterDiscord plugins folder.\\nAre you sure it's even installed?\", 0, \"Can't install myself\", 0x10);\n    } else if (shell.Popup(\"Should I copy myself to BetterDiscord's plugins folder for you?\", 0, \"Do you need some help?\", 0x34) === 6) {\n        fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);\n        // Show the user where to put plugins in the future\n        shell.Exec(\"explorer \" + pathPlugins);\n        shell.Popup(\"I'm installed!\", 0, \"Successfully installed\", 0x40);\n    }\n    WScript.Quit();\n@else@*/\n/* Generated Code */\nconst config = {\n\t\"info\": {\n\t\t\"name\": \"GuildAndFriendRemovalAlerts\",\n\t\t\"version\": \"3.2.1\",\n\t\t\"description\": \"Displays alerts when you are kicked/banned from a server, a server is deleted, and when a friend removes you.\",\n\t\t\"authors\": [{\n\t\t\t\"name\": \"Metalloriff\",\n\t\t\t\"discord_id\": \"264163473179672576\",\n\t\t\t\"github_username\": \"Metalloriff\"\n\t\t}],\n\t\t\"github\": \"https://github.com/Metalloriff/BetterDiscordPlugins/GuildAndFriendRemovalAlerts\",\n\t\t\"github_raw\": \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/GuildAndFriendRemovalAlerts/GuildAndFriendRemovalAlerts.plugin.js\",\n\t\t\"website\": \"https://metalloriff.github.io/toms-discord-stuff/#/\",\n\t\t\"donate\": \"https://paypal.me/israelboone\",\n\t\t\"invite\": \"yNqzuJa\"\n\t},\n\t\"changelog\": [{\n\t\t\"title\": \"3.0 rewrite\",\n\t\t\"type\": \"fixed\",\n\t\t\"items\": [\n\t\t\t\"This plugin has been rewritten. Functionality is simliar, and settings and data should still be valid.\",\n\t\t\t\"If you experience any bugs, please contact me.\"\n\t\t]\n\t}],\n\t\"build\": {\n\t\t\"zlibrary\": true,\n\t\t\"copy\": true,\n\t\t\"production\": false,\n\t\t\"scssHash\": false,\n\t\t\"alias\": {\n\t\t\t\"components\": \"components/index.js\"\n\t\t},\n\t\t\"release\": {\n\t\t\t\"source\": true,\n\t\t\t\"readme\": true,\n\t\t\t\"public\": true,\n\t\t\t\"contributors\": null\n\t\t}\n\t}\n};\nfunction buildPlugin([BasePlugin, PluginApi]) {\n\tconst module = {\n\t\texports: {}\n\t};\n\t(() => {\n\t\t\"use strict\";\n\t\tclass StyleLoader {\n\t\t\tstatic styles = \"\";\n\t\t\tstatic element = null;\n\t\t\tstatic append(module, css) {\n\t\t\t\tthis.styles += `/* ${module} */\\n${css}`;\n\t\t\t}\n\t\t\tstatic inject(name = config.info.name) {\n\t\t\t\tif (this.element) this.element.remove();\n\t\t\t\tthis.element = document.head.appendChild(Object.assign(document.createElement(\"style\"), {\n\t\t\t\t\tid: name,\n\t\t\t\t\ttextContent: this.styles\n\t\t\t\t}));\n\t\t\t}\n\t\t\tstatic remove() {\n\t\t\t\tif (this.element) {\n\t\t\t\t\tthis.element.remove();\n\t\t\t\t\tthis.element = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfunction ___createMemoize___(instance, name, value) {\n\t\t\tvalue = value();\n\t\t\tObject.defineProperty(instance, name, {\n\t\t\t\tvalue,\n\t\t\t\tconfigurable: true\n\t\t\t});\n\t\t\treturn value;\n\t\t};\n\t\tconst Modules = {\n\t\t\tget 'react-spring'() {\n\t\t\t\treturn ___createMemoize___(this, 'react-spring', () => BdApi.findModuleByProps('useSpring'))\n\t\t\t},\n\t\t\t'@discord/utils': {\n\t\t\t\tget 'joinClassNames'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'joinClassNames', () => BdApi.findModule(e => e.toString().indexOf('return e.join(\" \")') > 200))\n\t\t\t\t},\n\t\t\t\tget 'useForceUpdate'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'useForceUpdate', () => BdApi.findModuleByProps('useForceUpdate')?.useForceUpdate)\n\t\t\t\t},\n\t\t\t\tget 'Logger'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Logger', () => BdApi.findModuleByProps('setLogFn')?.default)\n\t\t\t\t},\n\t\t\t\tget 'Navigation'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Navigation', () => BdApi.findModuleByProps('replaceWith', 'currentRouteIsPeekView'))\n\t\t\t\t}\n\t\t\t},\n\t\t\t'@discord/components': {\n\t\t\t\tget 'Tooltip'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Tooltip', () => BdApi.findModuleByDisplayName('Tooltip'))\n\t\t\t\t},\n\t\t\t\tget 'TooltipContainer'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'TooltipContainer', () => BdApi.findModuleByProps('TooltipContainer')?.TooltipContainer)\n\t\t\t\t},\n\t\t\t\tget 'TextInput'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'TextInput', () => BdApi.findModuleByDisplayName('TextInput'))\n\t\t\t\t},\n\t\t\t\tget 'SlideIn'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'SlideIn', () => BdApi.findModuleByDisplayName('SlideIn'))\n\t\t\t\t},\n\t\t\t\tget 'SettingsNotice'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'SettingsNotice', () => BdApi.findModuleByDisplayName('SettingsNotice'))\n\t\t\t\t},\n\t\t\t\tget 'TransitionGroup'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'TransitionGroup', () => BdApi.findModuleByDisplayName('TransitionGroup'))\n\t\t\t\t},\n\t\t\t\tget 'Button'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Button', () => BdApi.findModuleByProps('DropdownSizes'))\n\t\t\t\t},\n\t\t\t\tget 'Popout'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Popout', () => BdApi.findModuleByDisplayName('Popout'))\n\t\t\t\t},\n\t\t\t\tget 'Flex'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Flex', () => BdApi.findModuleByDisplayName('Flex'))\n\t\t\t\t},\n\t\t\t\tget 'Text'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Text', () => BdApi.findModuleByDisplayName('Text'))\n\t\t\t\t},\n\t\t\t\tget 'Card'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Card', () => BdApi.findModuleByDisplayName('Card'))\n\t\t\t\t}\n\t\t\t},\n\t\t\t'@discord/modules': {\n\t\t\t\tget 'Dispatcher'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Dispatcher', () => BdApi.findModuleByProps('dispatch', 'subscribe'))\n\t\t\t\t},\n\t\t\t\tget 'ComponentDispatcher'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'ComponentDispatcher', () => BdApi.findModuleByProps('ComponentDispatch')?.ComponentDispatch)\n\t\t\t\t},\n\t\t\t\tget 'EmojiUtils'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'EmojiUtils', () => BdApi.findModuleByProps('uploadEmoji'))\n\t\t\t\t},\n\t\t\t\tget 'PermissionUtils'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'PermissionUtils', () => BdApi.findModuleByProps('computePermissions', 'canManageUser'))\n\t\t\t\t},\n\t\t\t\tget 'DMUtils'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'DMUtils', () => BdApi.findModuleByProps('openPrivateChannel'))\n\t\t\t\t}\n\t\t\t},\n\t\t\t'@discord/stores': {\n\t\t\t\tget 'Messages'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Messages', () => BdApi.findModuleByProps('getMessage', 'getMessages'))\n\t\t\t\t},\n\t\t\t\tget 'Channels'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Channels', () => BdApi.findModuleByProps('getChannel', 'getDMFromUserId'))\n\t\t\t\t},\n\t\t\t\tget 'Guilds'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Guilds', () => BdApi.findModuleByProps('getGuild'))\n\t\t\t\t},\n\t\t\t\tget 'SelectedGuilds'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'SelectedGuilds', () => BdApi.findModuleByProps('getGuildId', 'getLastSelectedGuildId'))\n\t\t\t\t},\n\t\t\t\tget 'SelectedChannels'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'SelectedChannels', () => BdApi.findModuleByProps('getChannelId', 'getLastSelectedChannelId'))\n\t\t\t\t},\n\t\t\t\tget 'Info'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Info', () => BdApi.findModuleByProps('getSessionId'))\n\t\t\t\t},\n\t\t\t\tget 'Status'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Status', () => BdApi.findModuleByProps('getStatus', 'getActivities', 'getState'))\n\t\t\t\t},\n\t\t\t\tget 'Users'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Users', () => BdApi.findModuleByProps('getUser', 'getCurrentUser'))\n\t\t\t\t},\n\t\t\t\tget 'SettingsStore'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'SettingsStore', () => BdApi.findModuleByProps('afkTimeout', 'status'))\n\t\t\t\t},\n\t\t\t\tget 'UserProfile'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'UserProfile', () => BdApi.findModuleByProps('getUserProfile'))\n\t\t\t\t},\n\t\t\t\tget 'Members'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Members', () => BdApi.findModuleByProps('getMember'))\n\t\t\t\t},\n\t\t\t\tget 'Activities'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Activities', () => BdApi.findModuleByProps('getActivities'))\n\t\t\t\t},\n\t\t\t\tget 'Games'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Games', () => BdApi.findModuleByProps('getGame', 'games'))\n\t\t\t\t},\n\t\t\t\tget 'Auth'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Auth', () => BdApi.findModuleByProps('getId', 'isGuest'))\n\t\t\t\t},\n\t\t\t\tget 'TypingUsers'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'TypingUsers', () => BdApi.findModuleByProps('isTyping'))\n\t\t\t\t}\n\t\t\t},\n\t\t\t'@discord/actions': {\n\t\t\t\tget 'ProfileActions'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'ProfileActions', () => BdApi.findModuleByProps('fetchProfile'))\n\t\t\t\t},\n\t\t\t\tget 'GuildActions'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'GuildActions', () => BdApi.findModuleByProps('requestMembersById'))\n\t\t\t\t}\n\t\t\t},\n\t\t\tget '@discord/i18n'() {\n\t\t\t\treturn ___createMemoize___(this, '@discord/i18n', () => BdApi.findModule(m => m.Messages?.CLOSE && typeof(m.getLocale) === 'function'))\n\t\t\t},\n\t\t\tget '@discord/constants'() {\n\t\t\t\treturn ___createMemoize___(this, '@discord/constants', () => BdApi.findModuleByProps('API_HOST'))\n\t\t\t},\n\t\t\tget '@discord/contextmenu'() {\n\t\t\t\treturn ___createMemoize___(this, '@discord/contextmenu', () => {\n\t\t\t\t\tconst ctx = Object.assign({}, BdApi.findModuleByProps('openContextMenu'), BdApi.findModuleByProps('MenuItem'));\n\t\t\t\t\tctx.Menu = ctx.default;\n\t\t\t\t\treturn ctx;\n\t\t\t\t})\n\t\t\t},\n\t\t\tget '@discord/forms'() {\n\t\t\t\treturn ___createMemoize___(this, '@discord/forms', () => BdApi.findModuleByProps('FormItem'))\n\t\t\t},\n\t\t\tget '@discord/scrollbars'() {\n\t\t\t\treturn ___createMemoize___(this, '@discord/scrollbars', () => BdApi.findModuleByProps('ScrollerAuto'))\n\t\t\t},\n\t\t\tget '@discord/native'() {\n\t\t\t\treturn ___createMemoize___(this, '@discord/native', () => BdApi.findModuleByProps('requireModule'))\n\t\t\t},\n\t\t\tget '@discord/flux'() {\n\t\t\t\treturn ___createMemoize___(this, '@discord/flux', () => Object.assign({}, BdApi.findModuleByProps('useStateFromStores').default, BdApi.findModuleByProps('useStateFromStores')))\n\t\t\t},\n\t\t\tget '@discord/modal'() {\n\t\t\t\treturn ___createMemoize___(this, '@discord/modal', () => Object.assign({}, BdApi.findModuleByProps('ModalRoot'), BdApi.findModuleByProps('openModal', 'closeAllModals')))\n\t\t\t},\n\t\t\tget '@discord/connections'() {\n\t\t\t\treturn ___createMemoize___(this, '@discord/connections', () => BdApi.findModuleByProps('get', 'isSupported', 'map'))\n\t\t\t},\n\t\t\tget '@discord/sanitize'() {\n\t\t\t\treturn ___createMemoize___(this, '@discord/sanitize', () => BdApi.findModuleByProps('stringify', 'parse', 'encode'))\n\t\t\t},\n\t\t\tget '@discord/icons'() {\n\t\t\t\treturn ___createMemoize___(this, '@discord/icons', () => BdApi.findAllModules(m => m.displayName && ~m.toString().indexOf('currentColor')).reduce((icons, icon) => (icons[icon.displayName] = icon, icons), {}))\n\t\t\t},\n\t\t\t'@discord/classes': {\n\t\t\t\tget 'Timestamp'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Timestamp', () => BdApi.findModuleByPrototypes('toDate', 'month'))\n\t\t\t\t},\n\t\t\t\tget 'Message'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Message', () => BdApi.findModuleByPrototypes('getReaction', 'isSystemDM'))\n\t\t\t\t},\n\t\t\t\tget 'User'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'User', () => BdApi.findModuleByPrototypes('tag'))\n\t\t\t\t},\n\t\t\t\tget 'Channel'() {\n\t\t\t\t\treturn ___createMemoize___(this, 'Channel', () => BdApi.findModuleByPrototypes('isOwner', 'isCategory'))\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tvar __webpack_modules__ = {\n\t\t\t86: (module, __webpack_exports__, __webpack_require__) => {\n\t\t\t\t__webpack_require__.d(__webpack_exports__, {\n\t\t\t\t\tZ: () => __WEBPACK_DEFAULT_EXPORT__\n\t\t\t\t});\n\t\t\t\tvar _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(246);\n\t\t\t\tvar _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);\n\t\t\t\tvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i) {\n\t\t\t\t\treturn i[1];\n\t\t\t\t}));\n\t\t\t\t___CSS_LOADER_EXPORT___.push([module.id, \".GuildAndFriendRemovalAlerts-item-item{display:flex;margin:5px;padding:10px;cursor:pointer;border-radius:5px}.GuildAndFriendRemovalAlerts-item-item:hover{background-color:var(--background-modifier-hover)}.GuildAndFriendRemovalAlerts-item-item .GuildAndFriendRemovalAlerts-item-image{width:60px;height:60px;border-radius:5px}.GuildAndFriendRemovalAlerts-item-item .GuildAndFriendRemovalAlerts-item-inner{display:flex;flex-direction:column;padding:5px;margin-left:10px}.GuildAndFriendRemovalAlerts-item-item .GuildAndFriendRemovalAlerts-item-inner .GuildAndFriendRemovalAlerts-item-title{margin:auto 0;max-width:400px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;font-weight:bolder;font-size:1.1rem}.GuildAndFriendRemovalAlerts-item-item .GuildAndFriendRemovalAlerts-item-inner .GuildAndFriendRemovalAlerts-item-description{margin:auto 0}\", \"\"]);\n\t\t\t\t___CSS_LOADER_EXPORT___.locals = {\n\t\t\t\t\titem: \"GuildAndFriendRemovalAlerts-item-item\",\n\t\t\t\t\timage: \"GuildAndFriendRemovalAlerts-item-image\",\n\t\t\t\t\tinner: \"GuildAndFriendRemovalAlerts-item-inner\",\n\t\t\t\t\ttitle: \"GuildAndFriendRemovalAlerts-item-title\",\n\t\t\t\t\tdescription: \"GuildAndFriendRemovalAlerts-item-description\"\n\t\t\t\t};\n\t\t\t\tStyleLoader.append(module.id, ___CSS_LOADER_EXPORT___.toString());\n\t\t\t\tconst __WEBPACK_DEFAULT_EXPORT__ = Object.assign(___CSS_LOADER_EXPORT___, ___CSS_LOADER_EXPORT___.locals);\n\t\t\t},\n\t\t\t377: (module, __webpack_exports__, __webpack_require__) => {\n\t\t\t\t__webpack_require__.d(__webpack_exports__, {\n\t\t\t\t\tZ: () => __WEBPACK_DEFAULT_EXPORT__\n\t\t\t\t});\n\t\t\t\tvar _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(246);\n\t\t\t\tvar _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);\n\t\t\t\tvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i) {\n\t\t\t\t\treturn i[1];\n\t\t\t\t}));\n\t\t\t\t___CSS_LOADER_EXPORT___.push([module.id, \".GuildAndFriendRemovalAlerts-styles-modal{color:var(--header-primary)}.GuildAndFriendRemovalAlerts-styles-modal .GuildAndFriendRemovalAlerts-styles-itemContainer .GuildAndFriendRemovalAlerts-styles-title{margin:20px;font-size:1.2rem;font-weight:bolder}.GuildAndFriendRemovalAlerts-styles-modal .GuildAndFriendRemovalAlerts-styles-nothingHere{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);font-size:2rem;font-weight:bolder;opacity:.5}.GuildAndFriendRemovalAlerts-styles-modal .GuildAndFriendRemovalAlerts-styles-clearButton{margin-top:20px;background-color:rgba(237,66,69,.1);color:#f66;border-radius:5px;padding:15px 10px;text-align:center;cursor:pointer}.GuildAndFriendRemovalAlerts-styles-floatRight{margin-left:auto}\", \"\"]);\n\t\t\t\t___CSS_LOADER_EXPORT___.locals = {\n\t\t\t\t\tmodal: \"GuildAndFriendRemovalAlerts-styles-modal\",\n\t\t\t\t\titemContainer: \"GuildAndFriendRemovalAlerts-styles-itemContainer\",\n\t\t\t\t\ttitle: \"GuildAndFriendRemovalAlerts-styles-title\",\n\t\t\t\t\tnothingHere: \"GuildAndFriendRemovalAlerts-styles-nothingHere\",\n\t\t\t\t\tclearButton: \"GuildAndFriendRemovalAlerts-styles-clearButton\",\n\t\t\t\t\tfloatRight: \"GuildAndFriendRemovalAlerts-styles-floatRight\"\n\t\t\t\t};\n\t\t\t\tStyleLoader.append(module.id, ___CSS_LOADER_EXPORT___.toString());\n\t\t\t\tconst __WEBPACK_DEFAULT_EXPORT__ = Object.assign(___CSS_LOADER_EXPORT___, ___CSS_LOADER_EXPORT___.locals);\n\t\t\t},\n\t\t\t234: (__unused_webpack_module, __webpack_exports__, __webpack_require__) => {\n\t\t\t\t__webpack_require__.r(__webpack_exports__);\n\t\t\t\t__webpack_require__.d(__webpack_exports__, {\n\t\t\t\t\tdefault: () => GuildAndFriendRemovalAlerts\n\t\t\t\t});\n\t\t\t\tconst external_BasePlugin_namespaceObject = BasePlugin;\n\t\t\t\tvar external_BasePlugin_default = __webpack_require__.n(external_BasePlugin_namespaceObject);\n\t\t\t\tconst external_PluginApi_namespaceObject = PluginApi;\n\t\t\t\tvar external_BdApi_React_ = __webpack_require__(832);\n\t\t\t\tvar external_BdApi_React_default = __webpack_require__.n(external_BdApi_React_);\n\t\t\t\tvar React = __webpack_require__(832);\n\t\t\t\tfunction _extends() {\n\t\t\t\t\t_extends = Object.assign || function(target) {\n\t\t\t\t\t\tfor (var i = 1; i < arguments.length; i++) {\n\t\t\t\t\t\t\tvar source = arguments[i];\n\t\t\t\t\t\t\tfor (var key in source)\n\t\t\t\t\t\t\t\tif (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn target;\n\t\t\t\t\t};\n\t\t\t\t\treturn _extends.apply(this, arguments);\n\t\t\t\t}\n\t\t\t\tconst createUpdateWrapper = (Component, valueProp = \"value\", changeProp = \"onChange\", valueIndex = 0) => props => {\n\t\t\t\t\tconst [value, setValue] = React.useState(props[valueProp]);\n\t\t\t\t\treturn React.createElement(Component, _extends({}, props, {\n\t\t\t\t\t\t[valueProp]: value,\n\t\t\t\t\t\t[changeProp]: (...args) => {\n\t\t\t\t\t\t\tconst value = args[valueIndex];\n\t\t\t\t\t\t\tif (\"function\" === typeof props[changeProp]) props[changeProp](value);\n\t\t\t\t\t\t\tsetValue(value);\n\t\t\t\t\t\t}\n\t\t\t\t\t}));\n\t\t\t\t};\n\t\t\t\tconst hooks_createUpdateWrapper = createUpdateWrapper;\n\t\t\t\tconst package_namespaceObject = JSON.parse('{\"um\":{\"u2\":\"GuildAndFriendRemovalAlerts\"}}');\n\t\t\t\tfunction _defineProperty(obj, key, value) {\n\t\t\t\t\tif (key in obj) Object.defineProperty(obj, key, {\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t\tconfigurable: true,\n\t\t\t\t\t\twritable: true\n\t\t\t\t\t});\n\t\t\t\t\telse obj[key] = value;\n\t\t\t\t\treturn obj;\n\t\t\t\t}\n\t\t\t\tclass Settings {}\n\t\t\t\t_defineProperty(Settings, \"settings\", external_PluginApi_namespaceObject.PluginUtilities.loadSettings(package_namespaceObject.um.u2, {}));\n\t\t\t\t_defineProperty(Settings, \"get\", ((key, defaultValue) => Settings.settings[key] ?? defaultValue));\n\t\t\t\t_defineProperty(Settings, \"set\", ((key, value) => {\n\t\t\t\t\tSettings.settings[key] = value;\n\t\t\t\t\tSettings.save();\n\t\t\t\t}));\n\t\t\t\t_defineProperty(Settings, \"save\", (() => external_PluginApi_namespaceObject.PluginUtilities.saveSettings(package_namespaceObject.um.u2, Settings.settings)));\n\t\t\t\tconst Switch = hooks_createUpdateWrapper(external_PluginApi_namespaceObject.WebpackModules.getByDisplayName(\"SwitchItem\"));\n\t\t\t\tfunction SettingsPanel() {\n\t\t\t\t\treturn external_BdApi_React_default().createElement(\"div\", {\n\t\t\t\t\t\tclassName: \"gafSettingsPanel\"\n\t\t\t\t\t}, external_BdApi_React_default().createElement(Switch, {\n\t\t\t\t\t\tnote: \"Whether or not to automatically show the modal when a guild/friend is removed.\",\n\t\t\t\t\t\tvalue: Settings.get(\"showModal\", true),\n\t\t\t\t\t\tonChange: value => Settings.set(\"showModal\", value)\n\t\t\t\t\t}, \"Auto Show Modal\"), external_BdApi_React_default().createElement(Switch, {\n\t\t\t\t\t\tnote: \"Whether or not to show desktop notifications when a guild/friend is removed.\",\n\t\t\t\t\t\tvalue: Settings.get(\"showDeskNotifs\", false),\n\t\t\t\t\t\tonChange: value => Settings.set(\"showDeskNotifs\", value)\n\t\t\t\t\t}, \"Show Desktop Notifications\"));\n\t\t\t\t}\n\t\t\t\tconst stores_namespaceObject = Modules[\"@discord/stores\"];\n\t\t\t\tvar stores_default = __webpack_require__.n(stores_namespaceObject);\n\t\t\t\tconst modal_namespaceObject = Modules[\"@discord/modal\"];\n\t\t\t\tconst external_StyleLoader_namespaceObject = StyleLoader;\n\t\t\t\tvar external_StyleLoader_default = __webpack_require__.n(external_StyleLoader_namespaceObject);\n\t\t\t\tvar styles = __webpack_require__(377);\n\t\t\t\tvar item = __webpack_require__(86);\n\t\t\t\tconst utils_namespaceObject = Modules[\"@discord/utils\"];\n\t\t\t\tconst PrivateModule = external_PluginApi_namespaceObject.WebpackModules.getByProps(\"openPrivateChannel\");\n\t\t\t\tfunction Item({\n\t\t\t\t\ttitle,\n\t\t\t\t\tdescription,\n\t\t\t\t\ticon,\n\t\t\t\t\tclickId,\n\t\t\t\t\tcloseModal\n\t\t\t\t}) {\n\t\t\t\t\treturn external_BdApi_React_default().createElement(\"div\", {\n\t\t\t\t\t\tclassName: (0, utils_namespaceObject.joinClassNames)(item.Z.item, item.Z.userItem),\n\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\tPrivateModule.openPrivateChannel(clickId);\n\t\t\t\t\t\t\tcloseModal();\n\t\t\t\t\t\t}\n\t\t\t\t\t}, external_BdApi_React_default().createElement(\"img\", {\n\t\t\t\t\t\tclassName: item.Z.image,\n\t\t\t\t\t\tsrc: icon || \"/assets/485a854d5171c8dc98088041626e6fea.png\",\n\t\t\t\t\t\talt: \"image\"\n\t\t\t\t\t}), external_BdApi_React_default().createElement(\"div\", {\n\t\t\t\t\t\tclassName: item.Z.inner\n\t\t\t\t\t}, external_BdApi_React_default().createElement(\"div\", {\n\t\t\t\t\t\tclassName: item.Z.title\n\t\t\t\t\t}, title), description?.length ? external_BdApi_React_default().createElement(\"div\", {\n\t\t\t\t\t\tclassName: item.Z.description\n\t\t\t\t\t}, description) : null));\n\t\t\t\t}\n\t\t\t\tconst contextmenu_namespaceObject = Modules[\"@discord/contextmenu\"];\n\t\t\t\tvar contextmenu_default = __webpack_require__.n(contextmenu_namespaceObject);\n\t\t\t\tconst constants_namespaceObject = Modules[\"@discord/constants\"];\n\t\t\t\tconst modules_namespaceObject = Modules[\"@discord/modules\"];\n\t\t\t\tvar GuildAndFriendRemovalAlerts_React = __webpack_require__(832);\n\t\t\t\tfunction GuildAndFriendRemovalAlerts_extends() {\n\t\t\t\t\tGuildAndFriendRemovalAlerts_extends = Object.assign || function(target) {\n\t\t\t\t\t\tfor (var i = 1; i < arguments.length; i++) {\n\t\t\t\t\t\t\tvar source = arguments[i];\n\t\t\t\t\t\t\tfor (var key in source)\n\t\t\t\t\t\t\t\tif (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn target;\n\t\t\t\t\t};\n\t\t\t\t\treturn GuildAndFriendRemovalAlerts_extends.apply(this, arguments);\n\t\t\t\t}\n\t\t\t\tfunction GuildAndFriendRemovalAlerts_defineProperty(obj, key, value) {\n\t\t\t\t\tif (key in obj) Object.defineProperty(obj, key, {\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t\tconfigurable: true,\n\t\t\t\t\t\twritable: true\n\t\t\t\t\t});\n\t\t\t\t\telse obj[key] = value;\n\t\t\t\t\treturn obj;\n\t\t\t\t}\n\t\t\t\tconst {\n\t\t\t\t\tgetFriendIDs\n\t\t\t\t} = external_PluginApi_namespaceObject.WebpackModules.getByProps(\"getFriendIDs\");\n\t\t\t\tconst HomeButton = external_PluginApi_namespaceObject.WebpackModules.getByProps(\"HomeButton\");\n\t\t\t\tconst events = [\"GUILD_CREATE\", \"GUILD_DELETE\", \"GUILD_UPDATE\", \"RELATIONSHIP_ADD\", \"RELATIONSHIP_REMOVE\", \"RELATIONSHIP_UPDATE\", \"FRIEND_REQUEST_ACCEPTED\"];\n\t\t\t\tclass GuildAndFriendRemovalAlerts extends(external_BasePlugin_default()) {\n\t\t\t\t\tconstructor(...args) {\n\t\t\t\t\t\tsuper(...args);\n\t\t\t\t\t\tGuildAndFriendRemovalAlerts_defineProperty(this, \"history\", {\n\t\t\t\t\t\t\tguilds: Settings.get(\"removedGuildHistory\", []),\n\t\t\t\t\t\t\tfriends: Settings.get(\"removedFriendHistory\", []),\n\t\t\t\t\t\t\tupdate: () => {\n\t\t\t\t\t\t\t\tSettings.set(\"removedGuildHistory\", this.history.guilds);\n\t\t\t\t\t\t\t\tSettings.set(\"removedFriendHistory\", this.history.friends);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tclear: () => {\n\t\t\t\t\t\t\t\tObject.assign(this.history, {\n\t\t\t\t\t\t\t\t\tguilds: [],\n\t\t\t\t\t\t\t\t\tfriends: []\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tthis.history.update();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t\tGuildAndFriendRemovalAlerts_defineProperty(this, \"snapshots\", {\n\t\t\t\t\t\t\tguilds: Settings.get(\"guildsSnapshot\", []),\n\t\t\t\t\t\t\tfriends: Settings.get(\"friendsSnapshot\", []),\n\t\t\t\t\t\t\tupdate: ({\n\t\t\t\t\t\t\t\tguilds,\n\t\t\t\t\t\t\t\tfriends\n\t\t\t\t\t\t\t}) => {\n\t\t\t\t\t\t\t\tSettings.set(\"guildsSnapshot\", this.snapshots.guilds = guilds);\n\t\t\t\t\t\t\t\tSettings.set(\"friendsSnapshot\", this.snapshots.friends = friends);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t\tGuildAndFriendRemovalAlerts_defineProperty(this, \"getSettingsPanel\", (() => GuildAndFriendRemovalAlerts_React.createElement(SettingsPanel, null)));\n\t\t\t\t\t\tGuildAndFriendRemovalAlerts_defineProperty(this, \"main\", (() => {\n\t\t\t\t\t\t\tconsole.log(\"main()\");\n\t\t\t\t\t\t\tconst guilds = Object.keys(stores_default().Guilds.getGuilds()).map((guildId => this.serializeGuild(guildId)));\n\t\t\t\t\t\t\tconst friends = getFriendIDs().map((uid => this.serializeUser(uid)));\n\t\t\t\t\t\t\tconst removedGuilds = this.snapshots.guilds.filter((snapshot => !guilds.some((guild => snapshot.id === guild.id))));\n\t\t\t\t\t\t\tconst removedFriends = this.snapshots.friends.filter((snapshot => !friends.some((friend => snapshot.id === friend.id))));\n\t\t\t\t\t\t\tif (removedGuilds.length || removedFriends.length) {\n\t\t\t\t\t\t\t\tif (Settings.get(\"showModal\", true)) this.openModal(removedGuilds, removedFriends);\n\t\t\t\t\t\t\t\tremovedGuilds.forEach((guild => this.history.guilds.unshift(guild)));\n\t\t\t\t\t\t\t\tremovedFriends.forEach((friend => this.history.friends.unshift(friend)));\n\t\t\t\t\t\t\t\tif (Settings.get(\"showDeskNotifs\", false)) {\n\t\t\t\t\t\t\t\t\tremovedGuilds.forEach((guild => new Notification(guild.name, {\n\t\t\t\t\t\t\t\t\t\tsilent: true,\n\t\t\t\t\t\t\t\t\t\tbody: \"Server removed\",\n\t\t\t\t\t\t\t\t\t\ticon: guild.iconUrl\n\t\t\t\t\t\t\t\t\t})));\n\t\t\t\t\t\t\t\t\tremovedFriends.forEach((friend => new Notification(friend.name, {\n\t\t\t\t\t\t\t\t\t\tsilent: true,\n\t\t\t\t\t\t\t\t\t\tbody: \"Friend removed\",\n\t\t\t\t\t\t\t\t\t\ticon: friend.avatarUrl\n\t\t\t\t\t\t\t\t\t})));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (guilds.length !== this.snapshots.guilds.length || friends.length !== this.snapshots.friends.length) {\n\t\t\t\t\t\t\t\tthis.history.update();\n\t\t\t\t\t\t\t\tthis.snapshots.update({\n\t\t\t\t\t\t\t\t\tguilds,\n\t\t\t\t\t\t\t\t\tfriends\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tconsole.log(\"update\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}));\n\t\t\t\t\t}\n\t\t\t\t\tonStart() {\n\t\t\t\t\t\tconst PatchedHomeButton = ({\n\t\t\t\t\t\t\toriginalType,\n\t\t\t\t\t\t\t...props\n\t\t\t\t\t\t}) => {\n\t\t\t\t\t\t\tconst returnValue = Reflect.apply(originalType, this, [props]);\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\treturnValue.props.onContextMenu = e => {\n\t\t\t\t\t\t\t\t\t(0, contextmenu_namespaceObject.openContextMenu)(e, (() => GuildAndFriendRemovalAlerts_React.createElement(contextmenu_default().default, {\n\t\t\t\t\t\t\t\t\t\tnavId: package_namespaceObject.um.u2,\n\t\t\t\t\t\t\t\t\t\tonClose: contextmenu_namespaceObject.closeContextMenu\n\t\t\t\t\t\t\t\t\t}, GuildAndFriendRemovalAlerts_React.createElement(contextmenu_namespaceObject.MenuItem, {\n\t\t\t\t\t\t\t\t\t\tlabel: \"View GFR Logs\",\n\t\t\t\t\t\t\t\t\t\taction: () => this.openModal(this.history.guilds, this.history.friends, true),\n\t\t\t\t\t\t\t\t\t\tid: package_namespaceObject.um.u2 + \"-logs\"\n\t\t\t\t\t\t\t\t\t}), GuildAndFriendRemovalAlerts_React.createElement(contextmenu_namespaceObject.MenuItem, {\n\t\t\t\t\t\t\t\t\t\tlabel: \"View All Guilds and Friends\",\n\t\t\t\t\t\t\t\t\t\taction: () => this.openModal(),\n\t\t\t\t\t\t\t\t\t\tid: package_namespaceObject.um.u2 + \"-view-all\"\n\t\t\t\t\t\t\t\t\t}))));\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\texternal_PluginApi_namespaceObject.Logger.error(\"Error in DefaultHomeButton patch:\", error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn returnValue;\n\t\t\t\t\t\t};\n\t\t\t\t\t\texternal_PluginApi_namespaceObject.Patcher.after(HomeButton, \"HomeButton\", ((_, __, component) => {\n\t\t\t\t\t\t\tconst originalType = component.type;\n\t\t\t\t\t\t\tcomponent.type = PatchedHomeButton;\n\t\t\t\t\t\t\tObject.assign(component.props, {\n\t\t\t\t\t\t\t\toriginalType\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}));\n\t\t\t\t\t\texternal_StyleLoader_default().inject();\n\t\t\t\t\t\tevents.forEach((eventType => modules_namespaceObject.Dispatcher.subscribe(eventType, this.main)));\n\t\t\t\t\t\tthis.main();\n\t\t\t\t\t}\n\t\t\t\t\tserializeGuild(guildId) {\n\t\t\t\t\t\tconst serialized = {\n\t\t\t\t\t\t\tid: guildId,\n\t\t\t\t\t\t\tinvalid: true,\n\t\t\t\t\t\t\tname: \"Unknown Guild\",\n\t\t\t\t\t\t\ticonUrl: \"/assets/1531b79c2f2927945582023e1edaaa11.png\"\n\t\t\t\t\t\t};\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst guild = stores_default().Guilds.getGuild(guildId);\n\t\t\t\t\t\t\tif (guild) Object.assign(serialized, {\n\t\t\t\t\t\t\t\tinvalid: false,\n\t\t\t\t\t\t\t\tname: guild.name,\n\t\t\t\t\t\t\t\townerId: guild.ownerId,\n\t\t\t\t\t\t\t\ticonUrl: \"function\" === typeof guild.getIconURL ? guild.getIconURL(\"webp\") : serialized.iconUrl\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} finally {}\n\t\t\t\t\t\treturn serialized;\n\t\t\t\t\t}\n\t\t\t\t\tserializeUser(userId) {\n\t\t\t\t\t\tconst serialized = {\n\t\t\t\t\t\t\tid: userId,\n\t\t\t\t\t\t\tinvalid: true,\n\t\t\t\t\t\t\ttag: \"Unknown User\",\n\t\t\t\t\t\t\tavatarURL: \"/assets/1cbd08c76f8af6dddce02c5138971129.png\"\n\t\t\t\t\t\t};\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst user = stores_default().Users.getUser(userId);\n\t\t\t\t\t\t\tif (user) Object.assign(serialized, {\n\t\t\t\t\t\t\t\tinvalid: false,\n\t\t\t\t\t\t\t\ttag: user.tag,\n\t\t\t\t\t\t\t\tavatarUrl: \"function\" === typeof user.getAvatarURL ? user.getAvatarURL(\"webp\") : serialized.avatarUrl\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} finally {}\n\t\t\t\t\t\treturn serialized;\n\t\t\t\t\t}\n\t\t\t\t\topenModal(guilds, friends, showClearButton = false) {\n\t\t\t\t\t\tif (!guilds && !friends) {\n\t\t\t\t\t\t\tguilds = this.snapshots.guilds;\n\t\t\t\t\t\t\tfriends = this.snapshots.friends;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst clearLogs = () => BdApi.showConfirmationModal(\"Are you sure?\", \"Do you really want to clear the logs?\\nThis action cannot be undone.\", {\n\t\t\t\t\t\t\tdanger: true,\n\t\t\t\t\t\t\tonConfirm: () => {\n\t\t\t\t\t\t\t\tthis.history.clear();\n\t\t\t\t\t\t\t\t(0, modal_namespaceObject.closeModal)(modalId);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tconfirmText: \"Clear\"\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst modalId = (0, modal_namespaceObject.openModal)((props => GuildAndFriendRemovalAlerts_React.createElement(modal_namespaceObject.ModalRoot, GuildAndFriendRemovalAlerts_extends({}, props, {\n\t\t\t\t\t\t\tsize: \"large\",\n\t\t\t\t\t\t\tclassName: styles.Z.modal\n\t\t\t\t\t\t}), GuildAndFriendRemovalAlerts_React.createElement(modal_namespaceObject.ModalHeader, null, \"Guild And Friend Removal Alerts \", GuildAndFriendRemovalAlerts_React.createElement(modal_namespaceObject.ModalCloseButton, {\n\t\t\t\t\t\t\tclassName: styles.Z.floatRight,\n\t\t\t\t\t\t\tonClick: props.onClose\n\t\t\t\t\t\t})), GuildAndFriendRemovalAlerts_React.createElement(modal_namespaceObject.ModalContent, props, guilds?.length || friends?.length ? GuildAndFriendRemovalAlerts_React.createElement(GuildAndFriendRemovalAlerts_React.Fragment, null, showClearButton ? GuildAndFriendRemovalAlerts_React.createElement(\"div\", {\n\t\t\t\t\t\t\tclassName: styles.Z.clearButton,\n\t\t\t\t\t\t\tonClick: clearLogs\n\t\t\t\t\t\t}, \"Clear Logs\") : null, guilds?.length ? GuildAndFriendRemovalAlerts_React.createElement(\"div\", {\n\t\t\t\t\t\t\tclassName: styles.Z.itemContainer\n\t\t\t\t\t\t}, GuildAndFriendRemovalAlerts_React.createElement(\"div\", {\n\t\t\t\t\t\t\tclassName: styles.Z.title\n\t\t\t\t\t\t}, \"Guilds - \", GuildAndFriendRemovalAlerts_React.createElement(\"span\", {\n\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\tcolor: \"var(--control-brand-foreground-new)\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, guilds.length)), GuildAndFriendRemovalAlerts_React.createElement(\"div\", {\n\t\t\t\t\t\t\tclassName: styles.Z.items\n\t\t\t\t\t\t}, guilds.map(((guild, i) => GuildAndFriendRemovalAlerts_React.createElement(Item, {\n\t\t\t\t\t\t\tkey: i,\n\t\t\t\t\t\t\ttitle: guild.name,\n\t\t\t\t\t\t\tdescription: \"Owner ID - \" + guild.ownerId,\n\t\t\t\t\t\t\ticon: guild.iconUrl,\n\t\t\t\t\t\t\tclickId: guild.ownerId,\n\t\t\t\t\t\t\tcloseModal: props.onClose\n\t\t\t\t\t\t}))))) : null, friends?.length ? GuildAndFriendRemovalAlerts_React.createElement(\"div\", {\n\t\t\t\t\t\t\tclassName: styles.Z.itemContainer\n\t\t\t\t\t\t}, GuildAndFriendRemovalAlerts_React.createElement(\"div\", {\n\t\t\t\t\t\t\tclassName: styles.Z.title\n\t\t\t\t\t\t}, \"Friends - \", GuildAndFriendRemovalAlerts_React.createElement(\"span\", {\n\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\tcolor: \"var(--control-brand-foreground-new)\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, friends.length)), GuildAndFriendRemovalAlerts_React.createElement(\"div\", {\n\t\t\t\t\t\t\tclassName: styles.Z.items\n\t\t\t\t\t\t}, friends.map(((friend, i) => GuildAndFriendRemovalAlerts_React.createElement(Item, {\n\t\t\t\t\t\t\tkey: i,\n\t\t\t\t\t\t\ttitle: friend.tag,\n\t\t\t\t\t\t\ticon: friend.avatarUrl,\n\t\t\t\t\t\t\tclickId: friend.id,\n\t\t\t\t\t\t\tcloseModal: props.onClose\n\t\t\t\t\t\t}))))) : null) : GuildAndFriendRemovalAlerts_React.createElement(\"div\", {\n\t\t\t\t\t\t\tclassName: styles.Z.nothingHere\n\t\t\t\t\t\t}, \"No logs to show.\")))));\n\t\t\t\t\t}\n\t\t\t\t\tonStop() {\n\t\t\t\t\t\texternal_PluginApi_namespaceObject.Patcher.unpatchAll();\n\t\t\t\t\t\texternal_StyleLoader_default().remove();\n\t\t\t\t\t\tevents.forEach((eventType => modules_namespaceObject.Dispatcher.unsubscribe(eventType, this.main)));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t246: module => {\n\t\t\t\tmodule.exports = function(cssWithMappingToString) {\n\t\t\t\t\tvar list = [];\n\t\t\t\t\tlist.toString = function() {\n\t\t\t\t\t\treturn this.map((function(item) {\n\t\t\t\t\t\t\tvar content = cssWithMappingToString(item);\n\t\t\t\t\t\t\tif (item[2]) return \"@media \".concat(item[2], \" {\").concat(content, \"}\");\n\t\t\t\t\t\t\treturn content;\n\t\t\t\t\t\t})).join(\"\");\n\t\t\t\t\t};\n\t\t\t\t\tlist.i = function(modules, mediaQuery, dedupe) {\n\t\t\t\t\t\tif (\"string\" === typeof modules) modules = [\n\t\t\t\t\t\t\t[null, modules, \"\"]\n\t\t\t\t\t\t];\n\t\t\t\t\t\tvar alreadyImportedModules = {};\n\t\t\t\t\t\tif (dedupe)\n\t\t\t\t\t\t\tfor (var i = 0; i < this.length; i++) {\n\t\t\t\t\t\t\t\tvar id = this[i][0];\n\t\t\t\t\t\t\t\tif (null != id) alreadyImportedModules[id] = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (var _i = 0; _i < modules.length; _i++) {\n\t\t\t\t\t\t\tvar item = [].concat(modules[_i]);\n\t\t\t\t\t\t\tif (dedupe && alreadyImportedModules[item[0]]) continue;\n\t\t\t\t\t\t\tif (mediaQuery)\n\t\t\t\t\t\t\t\tif (!item[2]) item[2] = mediaQuery;\n\t\t\t\t\t\t\t\telse item[2] = \"\".concat(mediaQuery, \" and \").concat(item[2]);\n\t\t\t\t\t\t\tlist.push(item);\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\treturn list;\n\t\t\t\t};\n\t\t\t},\n\t\t\t832: module => {\n\t\t\t\tmodule.exports = BdApi.React;\n\t\t\t}\n\t\t};\n\t\tvar __webpack_module_cache__ = {};\n\t\tfunction __webpack_require__(moduleId) {\n\t\t\tvar cachedModule = __webpack_module_cache__[moduleId];\n\t\t\tif (void 0 !== cachedModule) return cachedModule.exports;\n\t\t\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t\t\tid: moduleId,\n\t\t\t\texports: {}\n\t\t\t};\n\t\t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\t\t\treturn module.exports;\n\t\t}\n\t\t(() => {\n\t\t\t__webpack_require__.n = module => {\n\t\t\t\tvar getter = module && module.__esModule ? () => module[\"default\"] : () => module;\n\t\t\t\t__webpack_require__.d(getter, {\n\t\t\t\t\ta: getter\n\t\t\t\t});\n\t\t\t\treturn getter;\n\t\t\t};\n\t\t})();\n\t\t(() => {\n\t\t\t__webpack_require__.d = (exports, definition) => {\n\t\t\t\tfor (var key in definition)\n\t\t\t\t\tif (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, {\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t\tget: definition[key]\n\t\t\t\t\t});\n\t\t\t};\n\t\t})();\n\t\t(() => {\n\t\t\t__webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);\n\t\t})();\n\t\t(() => {\n\t\t\t__webpack_require__.r = exports => {\n\t\t\t\tif (\"undefined\" !== typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports, Symbol.toStringTag, {\n\t\t\t\t\tvalue: \"Module\"\n\t\t\t\t});\n\t\t\t\tObject.defineProperty(exports, \"__esModule\", {\n\t\t\t\t\tvalue: true\n\t\t\t\t});\n\t\t\t};\n\t\t})();\n\t\tvar __webpack_exports__ = __webpack_require__(234);\n\t\tmodule.exports.LibraryPluginHack = __webpack_exports__;\n\t})();\n\tconst PluginExports = module.exports.LibraryPluginHack;\n\treturn PluginExports?.__esModule ? PluginExports.default : PluginExports;\n}\nmodule.exports = window.hasOwnProperty(\"ZeresPluginLibrary\") ?\n\tbuildPlugin(window.ZeresPluginLibrary.buildPlugin(config)) :\n\tclass {\n\t\tgetName() {\n\t\t\treturn config.info.name;\n\t\t}\n\t\tgetAuthor() {\n\t\t\treturn config.info.authors.map(a => a.name).join(\", \");\n\t\t}\n\t\tgetDescription() {\n\t\t\treturn `${config.info.description}. __**ZeresPluginLibrary was not found! This plugin will not work!**__`;\n\t\t}\n\t\tgetVersion() {\n\t\t\treturn config.info.version;\n\t\t}\n\t\tload() {\n\t\t\tBdApi.showConfirmationModal(\n\t\t\t\t\"Library plugin is needed\",\n\t\t\t\t[`The library plugin needed for ${config.info.name} is missing. Please click Download to install it.`], {\n\t\t\t\t\tconfirmText: \"Download\",\n\t\t\t\t\tcancelText: \"Cancel\",\n\t\t\t\t\tonConfirm: () => {\n\t\t\t\t\t\trequire(\"request\").get(\"https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js\", async (error, response, body) => {\n\t\t\t\t\t\t\tif (error) return require(\"electron\").shell.openExternal(\"https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js\");\n\t\t\t\t\t\t\tawait new Promise(r => require(\"fs\").writeFile(require(\"path\").join(BdApi.Plugins.folder, \"0PluginLibrary.plugin.js\"), body, r));\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t\tstart() {}\n\t\tstop() {}\n\t};\n/*@end@*/\n"
  },
  {
    "path": "GuildAndFriendRemovalAlerts/README.md",
    "content": "# GuildAndFriendRemovalAlerts\n\n> Displays alerts when you are kicked/banned from a server, a server is deleted, and when a friend removes you.\n<hr/>\n\n<br/>\n<span>Made with <img src=\"https://discord.com/assets/0483f2b648dcc986d01385062052ae1c.svg\" width=\"15\" /> by <a href=\"https://github.com/Kyza/bdbuilder\">BDBuilder</a></span>"
  },
  {
    "path": "GuildAndFriendRemovalAlerts/src/components/item.jsx",
    "content": "﻿import React from \"react\";\nimport styles from \"./item.scss\";\nimport { joinClassNames } from \"@discord/utils\";\nimport { WebpackModules } from \"@zlibrary\";\n\nconst PrivateModule = WebpackModules.getByProps(\"openPrivateChannel\");\n\nexport default function Item({ title, description, icon, clickId, closeModal }) {\n    const handleClick = () => {\n        PrivateModule.openPrivateChannel(clickId);\n        \n        closeModal();\n    };\n    \n    return (\n        <div className={joinClassNames(styles.item, styles.userItem)} onClick={handleClick}>\n            <img className={styles.image} src={icon || \"/assets/485a854d5171c8dc98088041626e6fea.png\"} alt=\"image\"/>\n            \n            <div className={styles.inner}>\n                <div className={styles.title}>{ title }</div>\n                { description?.length ? <div className={styles.description}>{ description }</div> : null }\n            </div>\n        </div>\n    );\n}"
  },
  {
    "path": "GuildAndFriendRemovalAlerts/src/components/item.scss",
    "content": "﻿.item {\n  display: flex;\n  margin: 5px;\n  padding: 10px;\n  cursor: pointer;\n  border-radius: 5px;\n  \n  &:hover {\n    background-color: var(--background-modifier-hover);\n  }\n  \n  .image {\n    width: 60px;\n    height: 60px;\n    border-radius: 5px;\n  }\n  \n  .inner {\n    display: flex;\n    flex-direction: column;\n    padding: 5px;\n    margin-left: 10px;\n    \n    .title {\n      margin: auto 0;\n      \n      max-width: 400px;\n      white-space: nowrap;\n      text-overflow: ellipsis;\n      overflow: hidden;\n      \n      font-weight: bolder;\n      font-size: 1.1rem;\n    }\n    \n    .description {\n      margin: auto 0;\n    }\n  }\n  \n  &.userItem {\n    \n  }\n  \n  &.guildItem {\n    \n  }\n}"
  },
  {
    "path": "GuildAndFriendRemovalAlerts/src/components/settings.jsx",
    "content": "﻿import React from \"react\";\nimport createUpdateWrapper from \"common/hooks/createUpdateWrapper\";\nimport { WebpackModules } from \"@zlibrary\";\nimport Settings from \"../modules/settings\";\n\nconst Switch = createUpdateWrapper(WebpackModules.getByDisplayName(\"SwitchItem\"));\n\nexport default function SettingsPanel() {\n    return (\n        <div className=\"gafSettingsPanel\">\n            <Switch note=\"Whether or not to automatically show the modal when a guild/friend is removed.\"\n                    value={Settings.get(\"showModal\", true)}\n                    onChange={value => Settings.set(\"showModal\", value)}>Auto Show Modal</Switch>\n            \n            <Switch note=\"Whether or not to show desktop notifications when a guild/friend is removed.\"\n                    value={Settings.get(\"showDeskNotifs\", false)}\n                    onChange={value => Settings.set(\"showDeskNotifs\", value)}>Show Desktop Notifications</Switch>\n        </div>\n    );\n}\n"
  },
  {
    "path": "GuildAndFriendRemovalAlerts/src/index.js",
    "content": "import BasePlugin from \"@zlibrary/plugin\";\nimport { Logger, Patcher, PluginUtilities, ReactComponents, WebpackModules } from \"@zlibrary\";\nimport SettingsPanel from \"./components/settings\";\nimport Settings from \"./modules/settings\";\nimport Stores from \"@discord/stores\";\nimport { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal, closeModal } from \"@discord/modal\";\nimport stylesheet from \"styles\";\nimport styles from \"./styles.scss\";\nimport Item from \"./components/item\";\nimport ContextMenu, { closeContextMenu, MenuItem, openContextMenu } from \"@discord/contextmenu\";\nimport pkg from \"./package.json\";\nimport { ActionTypes } from \"@discord/constants\";\nimport { Dispatcher } from \"@discord/modules\";\n\nconst { getFriendIDs } = WebpackModules.getByProps(\"getFriendIDs\");\nconst HomeButton = WebpackModules.getByProps(\"HomeButton\");\nconst events = [ ActionTypes.GUILD_CREATE, ActionTypes.GUILD_DELETE, ActionTypes.GUILD_UPDATE, ActionTypes.RELATIONSHIP_ADD,\n\t\t\t\t ActionTypes.RELATIONSHIP_REMOVE, ActionTypes.RELATIONSHIP_UPDATE, ActionTypes.FRIEND_REQUEST_ACCEPTED ];\n\nexport default class GuildAndFriendRemovalAlerts extends BasePlugin {\n\thistory = {\n\t\tguilds: Settings.get(\"removedGuildHistory\", []),\n\t\tfriends: Settings.get(\"removedFriendHistory\", []),\n\t\tupdate: () => {\n\t\t\tSettings.set(\"removedGuildHistory\", this.history.guilds);\n\t\t\tSettings.set(\"removedFriendHistory\", this.history.friends);\n\t\t},\n\t\tclear: () => {\n\t\t\tObject.assign(this.history, {\n\t\t\t\tguilds: [],\n\t\t\t\tfriends: []\n\t\t\t});\n\t\t\t\n\t\t\tthis.history.update();\n\t\t}\n\t};\n\t\n\tsnapshots = {\n\t\tguilds: Settings.get(\"guildsSnapshot\", []),\n\t\tfriends: Settings.get(\"friendsSnapshot\", []),\n\t\tupdate: ({ guilds, friends }) => {\n\t\t\tSettings.set(\"guildsSnapshot\", this.snapshots.guilds = guilds);\n\t\t\tSettings.set(\"friendsSnapshot\", this.snapshots.friends = friends);\n\t\t}\n\t};\n\t\n\tgetSettingsPanel = () => <SettingsPanel/>;\n\t\n\tonStart() {\n\t\tconst PatchedHomeButton = ({originalType, ...props}) => {\n\t\t\tconst returnValue = Reflect.apply(originalType, this, [props]);\n\t\t\t\n\t\t\ttry {\n\t\t\t\treturnValue.props.onContextMenu = e => {\n\t\t\t\t\topenContextMenu(e, () => (\n\t\t\t\t\t\t<ContextMenu.default navId={pkg.info.name} onClose={closeContextMenu}>\n\t\t\t\t\t\t\t<MenuItem label=\"View GFR Logs\" action={() => this.openModal(this.history.guilds, this.history.friends, true)} id={pkg.info.name + \"-logs\"}/>\n\t\t\t\t\t\t\t<MenuItem label=\"View All Guilds and Friends\" action={() => this.openModal()} id={pkg.info.name + \"-view-all\"}/>\n\t\t\t\t\t\t</ContextMenu.default>\n\t\t\t\t\t));\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tLogger.error(\"Error in DefaultHomeButton patch:\", error);\n\t\t\t}\n\n\t\t\treturn returnValue;\n\t\t}\n\n\t\tPatcher.after(HomeButton, \"HomeButton\", (_, __, component) => {\n\t\t\tconst originalType = component.type;\n\n\t\t\tcomponent.type = PatchedHomeButton;\n\t\t\tObject.assign(component.props, {originalType: originalType});\n\t\t});\n\t\t\n\t\tstylesheet.inject();\n\t\t\n\t\tevents.forEach(eventType => Dispatcher.subscribe(eventType, this.main));\n\t\tthis.main();\n\t}\n\t\n\tserializeGuild(guildId) {\n\t\tconst serialized = { id: guildId, invalid: true, name: \"Unknown Guild\", iconUrl: \"/assets/1531b79c2f2927945582023e1edaaa11.png\" };\n\t\t\n\t\ttry {\n\t\t\tconst guild = Stores.Guilds.getGuild(guildId);\n\t\t\t\n\t\t\tif (guild) {\n\t\t\t\tObject.assign(serialized, {\n\t\t\t\t\tinvalid: false,\n\t\t\t\t\tname: guild.name,\n\t\t\t\t\townerId: guild.ownerId,\n\t\t\t\t\ticonUrl: typeof(guild.getIconURL) === \"function\" ? guild.getIconURL(\"webp\") : serialized.iconUrl\n\t\t\t\t});\n\t\t\t}\n\t\t} finally { }\n\t\t\n\t\treturn serialized;\n\t}\n\t\n\tserializeUser(userId) {\n\t\tconst serialized = { id: userId, invalid: true, tag: \"Unknown User\", avatarURL: \"/assets/1cbd08c76f8af6dddce02c5138971129.png\" };\n\n\t\ttry {\n\t\t\tconst user = Stores.Users.getUser(userId);\n\t\t\t\n\t\t\tif (user) {\n\t\t\t\tObject.assign(serialized, {\n\t\t\t\t\tinvalid: false,\n\t\t\t\t\ttag: user.tag,\n\t\t\t\t\tavatarUrl: typeof(user.getAvatarURL) === \"function\" ? user.getAvatarURL(\"webp\") : serialized.avatarUrl\n\t\t\t\t});\n\t\t\t}\n\t\t} finally { }\n\n\t\treturn serialized;\n\t}\n\t\n\tmain = () => {\n\t\tconsole.log(\"main()\");\n\t\t\n\t\tconst guilds = Object.keys(Stores.Guilds.getGuilds()).map(guildId => this.serializeGuild(guildId));\n\t\tconst friends = getFriendIDs().map(uid => this.serializeUser(uid));\n\t\t\n\t\tconst removedGuilds = this.snapshots.guilds.filter(snapshot => !guilds.some(guild => snapshot.id === guild.id));\n\t\tconst removedFriends = this.snapshots.friends.filter(snapshot => !friends.some(friend => snapshot.id === friend.id));\n\t\t\n\t\tif (removedGuilds.length || removedFriends.length) {\n\t\t\tif (Settings.get(\"showModal\", true))\n\t\t\t\tthis.openModal(removedGuilds, removedFriends);\n\t\t\t\n\t\t\tremovedGuilds.forEach(guild => this.history.guilds.unshift(guild));\n\t\t\tremovedFriends.forEach(friend => this.history.friends.unshift(friend));\n\t\t\t\n\t\t\tif (Settings.get(\"showDeskNotifs\", false)) {\n\t\t\t\tremovedGuilds.forEach(guild =>\n\t\t\t\t\tnew Notification(guild.name, {\n\t\t\t\t\t\tsilent: true,\n\t\t\t\t\t\tbody: \"Server removed\",\n\t\t\t\t\t\ticon: guild.iconUrl\n\t\t\t\t\t}));\n\t\t\t\t\n\t\t\t\tremovedFriends.forEach(friend =>\n\t\t\t\t\tnew Notification(friend.name, {\n\t\t\t\t\t\tsilent: true,\n\t\t\t\t\t\tbody: \"Friend removed\",\n\t\t\t\t\t\ticon: friend.avatarUrl\n\t\t\t\t\t}));\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (guilds.length !== this.snapshots.guilds.length || friends.length !== this.snapshots.friends.length) {\n\t\t\tthis.history.update();\n\t\t\tthis.snapshots.update({ guilds, friends });\n\t\t\t\n\t\t\tconsole.log(\"update\");\n\t\t}\n\t};\n\t\n\topenModal(guilds, friends, showClearButton = false) {\n\t\tif (!guilds && !friends) {\n\t\t\tguilds = this.snapshots.guilds;\n\t\t\tfriends = this.snapshots.friends;\n\t\t}\n\t\t\n\t\tconst clearLogs = () =>\n\t\t\tBdApi.showConfirmationModal(\"Are you sure?\", \"Do you really want to clear the logs?\\nThis action cannot be undone.\", {\n\t\t\t\tdanger: true,\n\t\t\t\tonConfirm: () => {\n\t\t\t\t\tthis.history.clear();\n\t\t\t\t\tcloseModal(modalId);\n\t\t\t\t},\n\t\t\t\tconfirmText: \"Clear\"\n\t\t\t});\n\t\t\n\t\tconst modalId = openModal(props => (\n\t\t\t<ModalRoot {...props} size=\"large\" className={styles.modal}>\n\t\t\t\t<ModalHeader>Guild And Friend Removal Alerts <ModalCloseButton className={styles.floatRight} onClick={props.onClose}/></ModalHeader>\n\t\t\t\t\n\t\t\t\t<ModalContent {...props}>\n\t\t\t\t\t{ guilds?.length || friends?.length ? (\n\t\t\t\t\t\t<React.Fragment>\n\t\t\t\t\t\t\t{ showClearButton ? (\n\t\t\t\t\t\t\t\t<div className={styles.clearButton} onClick={clearLogs}>\n\t\t\t\t\t\t\t\t\tClear Logs\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t) : null }\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{ guilds?.length ? (\n\t\t\t\t\t\t\t\t<div className={styles.itemContainer}>\n\t\t\t\t\t\t\t\t\t<div className={styles.title}>Guilds - <span style={{ color: \"var(--control-brand-foreground-new)\" }}>{guilds.length}</span></div>\n\n\t\t\t\t\t\t\t\t\t<div className={styles.items}>\n\t\t\t\t\t\t\t\t\t\t{ guilds.map((guild, i) =>\n\t\t\t\t\t\t\t\t\t\t\t<Item key={i} title={guild.name} description={\"Owner ID - \" + guild.ownerId} icon={guild.iconUrl} clickId={guild.ownerId} closeModal={props.onClose}/>) }\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t) : null }\n\n\t\t\t\t\t\t\t{ friends?.length ? (\n\t\t\t\t\t\t\t\t<div className={styles.itemContainer}>\n\t\t\t\t\t\t\t\t\t<div className={styles.title}>Friends - <span style={{ color: \"var(--control-brand-foreground-new)\" }}>{friends.length}</span></div>\n\n\t\t\t\t\t\t\t\t\t<div className={styles.items}>\n\t\t\t\t\t\t\t\t\t\t{ friends.map((friend, i) =>\n\t\t\t\t\t\t\t\t\t\t\t<Item key={i} title={friend.tag} icon={friend.avatarUrl} clickId={friend.id} closeModal={props.onClose}/>) }\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t) : null }\n\t\t\t\t\t\t</React.Fragment>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className={styles.nothingHere}>\n\t\t\t\t\t\t\tNo logs to show.\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) }\n\t\t\t\t</ModalContent>\n\t\t\t</ModalRoot>\n\t\t));\n\t}\n\t\n\tonStop() {\n\t\tPatcher.unpatchAll();\n\t\tstylesheet.remove();\n\n\t\tevents.forEach(eventType => Dispatcher.unsubscribe(eventType, this.main));\n\t}\n}\n"
  },
  {
    "path": "GuildAndFriendRemovalAlerts/src/modules/settings.js",
    "content": "﻿import pkg from \"../package.json\";\nimport { PluginUtilities } from \"@zlibrary\";\n\nexport default class Settings {\n    static settings = PluginUtilities.loadSettings(pkg.info.name, {});\n    \n    static get = (key, defaultValue) => Settings.settings[key] ?? defaultValue;\n    static set = (key, value) => {\n        Settings.settings[key] = value;\n        Settings.save();\n    };\n    \n    static save = () => PluginUtilities.saveSettings(pkg.info.name, Settings.settings);\n}"
  },
  {
    "path": "GuildAndFriendRemovalAlerts/src/package.json",
    "content": "{\n\t\"info\": {\n\t\t\"name\": \"GuildAndFriendRemovalAlerts\",\n\t\t\"version\": \"3.0.0\",\n\t\t\"description\": \"Displays alerts when you are kicked/banned from a server, a server is deleted, and when a friend removes you.\",\n\t\t\"authors\": [\n\t\t\t{\n\t\t\t\t\"name\": \"Metalloriff\",\n\t\t\t\t\"discord_id\": \"264163473179672576\",\n\t\t\t\t\"github_username\": \"Metalloriff\"\n\t\t\t}\n\t\t],\n\t\t\"github\": \"https://github.com/Metalloriff/BetterDiscordPlugins/GuildAndFriendRemovalAlerts\",\n\t\t\"github_raw\": \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/GuildAndFriendRemovalAlerts/GuildAndFriendRemovalAlerts.plugin.js\",\n\t\t\"website\": \"https://metalloriff.github.io/toms-discord-stuff/#/\",\n\t\t\"donate\": \"https://paypal.me/israelboone\",\n\t\t\"invite\": \"yNqzuJa\"\n\t},\n\t\"changelog\": [{\n\t\t\"title\": \"3.0 rewrite\",\n\t\t\"type\": \"fixed\",\n\t\t\"items\": [\n\t\t\t\"This plugin has been rewritten. Functionality is simliar, and settings and data should still be valid.\",\n\t\t\t\"If you experience any bugs, please contact me.\"\n\t\t]\n\t}],\n\t\"build\": {\n\t\t\"zlibrary\": true,\n\t\t\"copy\": true,\n\t\t\"production\": false,\n\t\t\"scssHash\": false,\n\t\t\"alias\": {\n\t\t\t\"components\": \"components/index.js\"\n\t\t},\n\t\t\"release\": {\n\t\t\t\"source\": true,\n\t\t\t\"readme\": true,\n\t\t\t\"public\": true,\n\t\t\t\"contributors\": null\n\t\t}\n\t}\n}"
  },
  {
    "path": "GuildAndFriendRemovalAlerts/src/styles.scss",
    "content": "﻿.modal {\n  color: white;\n  \n  .itemContainer {\n    .title {\n      margin: 20px;\n      font-size: 1.2rem;\n      font-weight: bolder;\n    }\n    \n    .items {\n      \n    }\n  }\n\n  .nothingHere {\n    position: absolute;\n    top: 50%; left: 50%;\n    transform: translate(-50%, -50%);\n    \n    font-size: 2rem;\n    font-weight: bolder;\n    opacity: 0.5;\n  }\n  \n  .clearButton {\n    margin-top: 20px;\n    \n    background-color: rgba(237, 66, 69, 0.1);\n    color: #ff6666;\n    \n    border-radius: 5px;\n    padding: 15px 10px;\n    \n    text-align: center;\n    cursor: pointer;\n  }\n}\n\n.floatRight {\n  margin-left: auto;\n}"
  },
  {
    "path": "GuildAndFriendRemovalAlerts.plugin.js",
    "content": "/**\r\n * @name GuildAndFriendRemovalAlerts\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www.paypal.me/israelboone\r\n * @website https://metalloriff.github.io/toms-discord-stuff/\r\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/GuildAndFriendRemovalAlerts.plugin.js\r\n */\r\n\r\nconst name = \"GuildAndFriendRemovalAlerts\";\r\nconst newUrl = \"https://github.com/Metalloriff/BetterDiscordPlugins/tree/master/\" + name;\r\nconst rawUrl = `https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/${name}/${name}.plugin.js`;\r\n\r\nmodule.exports = (() => {\r\n    const config = {\r\n        info: {\r\n            name: \"0 GuildAndFriendRemovalAlerts OUTDATED\",\r\n            authors: [{\r\n                name: \"Metalloriff\",\r\n                discord_id: \"264163473179672576\",\r\n                github_username: \"metalloriff\",\r\n                twitter_username: \"Metalloriff\"\r\n            }],\r\n            version: \"999.999.999\",\r\n            description: `Please delete this plugin and download the new one at ${newUrl}.`\r\n        }\r\n    };\r\n    \r\n    return class {\r\n        getName = () => config.info.name;\r\n        getAuthor = () => config.info.authors[0].name;\r\n        getDescription = () => config.info.description;\r\n        getVersion = () => config.info.version;\r\n        \r\n        load() {\r\n            BdApi.showConfirmationModal(\"Outdated Plugin\", `This version of ${name} is outdated, consider installing the newest one by clicking the \"Update Now\" button.`, {\r\n                onConfirm: () => {\r\n                    const https = require(\"https\");\r\n                    const fs = require(\"fs\");\r\n                    const path = require(\"path\");\r\n\r\n                    https.get(rawUrl, res => {\r\n                        const chunks = [];\r\n                        res.on(\"data\", chunk => chunks.push(chunk));\r\n\r\n                        res.on(\"end\", () => {\r\n                            try {\r\n                                fs.writeFileSync(path.resolve(BdApi.Plugins.folder, path.basename(rawUrl)), chunks.join(\"\"), \"utf8\");\r\n                            } catch (error) {\r\n                                console.error(name, error);\r\n                                BdApi.showToast(\"Failed to download new update - check the console for details\", { type: \"error\" });\r\n                            }\r\n                        });\r\n                    });\r\n                }\r\n            });\r\n        }\r\n        \r\n        start() {  }\r\n        stop() {  }\r\n    };\r\n})();\r\n"
  },
  {
    "path": "GuildCounter.plugin.js",
    "content": "//META{\"name\":\"GuildCounter\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/GuildCounter.plugin.js\"}*//\r\n\r\nclass GuildCounter {\r\n\t\r\n    getName() { return \"Guild Counter\"; }\r\n    getDescription() { return \"Displays a guild counter below the online friend counter.\"; }\r\n    getVersion() { return \"1.0.4\"; }\r\n    getAuthor() { return \"Metalloriff\"; }\r\n\r\n    load() {}\r\n\r\n    start() {\r\n\r\n        let libLoadedEvent = () => {\r\n            try{ this.onLibLoaded(); }\r\n            catch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n        };\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif(!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n        if(typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\r\n\t}\r\n\t\r\n\tonLibLoaded(){\r\n\r\n\t\tNeatoLib.Updates.check(this);\r\n\r\n\t\t(this.guildsScroller = document.getElementsByClassName(NeatoLib.getClass(\"unreadMentionsBar\", \"scroller\"))[0]).addEventListener(\"DOMNodeInserted\", this.count = () => {\r\n\t\t\tif (typeof event !== \"undefined\" && event.relatedNode.id === \"gc-counter\") return;\r\n\t\t\tlet existing = document.getElementById(\"gc-counter\"), count = Object.keys(NeatoLib.Modules.get(\"getGuilds\").getGuilds()).length;\r\n\t\r\n\t\t\tif(existing) existing.innerText = count + \" guilds\";\r\n\t\t\telse this.guildsScroller.getElementsByClassName(NeatoLib.getClass(\"guildSeparator\"))[0].parentElement.insertAdjacentElement(\"beforebegin\", NeatoLib.DOM.createElement({ id : \"gc-counter\", className : NeatoLib.getClass(\"friendsOnline\") + \" \" + NeatoLib.getClass([\"badgeIcon\", \"guildSeparator\", \"guildsError\"], \"listItem\"), innerText : count + \" guilds\" }));\r\n\r\n\t\t});\r\n\r\n\t\tthis.count();\r\n\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\r\n\t}\r\n\t\r\n    stop() {\r\n\t\tthis.guildsScroller.removeEventListener(\"DOMNodeInserted\", this.count);\r\n\t\tdocument.getElementById(\"gc-counter\").remove();\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "IdleGuildlistScroller.plugin.js",
    "content": "//META{\"name\":\"IdleGuildlistScroller\"}*//\r\n\r\nclass IdleGuildlistScroller {\r\n\t\r\n    getName() { return \"Idle Guildlist Scroller\"; }\r\n    getDescription() { return \"Automatically scrolls to the top of the guilds list after the specified amount of time that your mouse isn't over it.\"; }\r\n    getVersion() { return \"0.1.1\"; }\r\n    getAuthor() { return \"Metalloriff\"; }\r\n\r\n    load() {}\r\n\t\r\n\tgetSettingsPanel(){\r\n\t\treturn \"Delay (ms):<br><input id='igs-delay' type='number' value='\" +  IdleGuildlistScroller.delay + \"'>ms<br><br><input id='igs-includeChannels' type='checkbox' class='ui-switch-checkbox'\" + (IdleGuildlistScroller.includeChannels ? \" checked\" : \"\") + \">Check if mouse is over channel list</input><br><br><button onclick='IdleGuildlistScroller.resetSettings();'>Reset Settings</button><br><br><button onclick='IdleGuildlistScroller.saveSettings();'>Save & Update</button>\";\r\n\t}\r\n\t\r\n\tstatic saveSettings(){\r\n\t\tIdleGuildlistScroller.delay = document.getElementById(\"igs-delay\").value;\r\n\t\tIdleGuildlistScroller.includeChannels = document.getElementById(\"igs-includeChannels\").checked;\r\n\t\tPluginUtilities.saveData(\"IdleGuildlistScroller\", \"settings\", {delay : IdleGuildlistScroller.delay, includeChannels : IdleGuildlistScroller.includeChannels});\r\n\t\tPluginUtilities.showToast(\"Settings saved! You will need to switch channels for them to take effect.\");\r\n\t}\r\n\t\r\n\tstatic resetSettings(){\r\n\t\tIdleGuildlistScroller.delay = 1000;\r\n\t\tIdleGuildlistScroller.includeChannels = true;\r\n\t\tPluginUtilities.showToast(\"Settings reset! You will still have to save and update.\");\r\n\t}\r\n\r\n    start() {\r\n\t\tvar libraryScript = document.getElementById('zeresLibraryScript');\r\n\t\tif (!libraryScript) {\r\n\t\t\tlibraryScript = document.createElement(\"script\");\r\n\t\t\tlibraryScript.setAttribute(\"type\", \"text/javascript\");\r\n\t\t\tlibraryScript.setAttribute(\"src\", \"https://rauenzi.github.io/BetterDiscordAddons/Plugins/PluginLibrary.js\");\r\n\t\t\tlibraryScript.setAttribute(\"id\", \"zeresLibraryScript\");\r\n\t\t\tdocument.head.appendChild(libraryScript);\r\n\t\t}\r\n\t\tif (typeof window.ZeresLibrary !== \"undefined\") this.initialize();\r\n\t\telse libraryScript.addEventListener(\"load\", () => { this.initialize(); });\r\n\t}\r\n\t\r\n\tinitialize(){\r\n\t\tPluginUtilities.checkForUpdate(this.getName(), this.getVersion(), \"https://github.com/Metalloriff/BetterDiscordPlugins/raw/master/IdleGuildlistScroller.plugin.js\");\r\n\t\tIdleGuildlistScroller.resetSettings();\r\n\t\tvar data = PluginUtilities.loadData(\"IdleGuildlistScroller\", \"settings\", {delay : 1000, includeChannels : true});\r\n\t\tIdleGuildlistScroller.delay = data[\"delay\"];\r\n\t\tIdleGuildlistScroller.includeChannels = data[\"includeChannels\"];\r\n\t\tthis.guildsList.addEventListener(\"mouseenter\", this.onHover, false);\r\n\t\tthis.guildsList.addEventListener(\"mouseleave\", this.offHover, false);\r\n\t\tthis.onSwitch();\r\n\t}\r\n\t\r\n\tonSwitch(){\r\n\t\tif(IdleGuildlistScroller.includeChannels && this.channelList){\r\n\t\t\tthis.channelList.addEventListener(\"mouseenter\", this.onHover, false);\r\n\t\t\tthis.channelList.addEventListener(\"mouseleave\", this.offHover, false);\r\n\t\t}else if(this.channelList){\r\n\t\t\tthis.channelList.removeEventListener(\"mouseenter\", this.onHover);\r\n\t\t\tthis.channelList.removeEventListener(\"mouseleave\", this.offHover);\r\n\t\t}\r\n\t}\r\n\t\r\n\tonHover(){\r\n\t\tif(IdleGuildlistScroller.sttFunc)\r\n\t\t\tclearTimeout(IdleGuildlistScroller.sttFunc);\r\n\t}\r\n\t\r\n\toffHover(){\r\n\t\tIdleGuildlistScroller.sttFunc = setTimeout(function(){\r\n\t\t\t$(document.getElementsByClassName(\"guilds scroller\")[0]).animate({scrollTop : 0}, \"fast\");\r\n\t\t}, IdleGuildlistScroller.delay);\r\n\t}\r\n\t\r\n    stop() {\r\n\t\tthis.guildsList.removeEventListener(\"mouseenter\", this.onHover);\r\n\t\tthis.guildsList.removeEventListener(\"mouseleave\", this.offHover);\r\n\t\tif(this.channelList){\r\n\t\t\tthis.channelList.removeEventListener(\"mouseenter\", this.onHover);\r\n\t\t\tthis.channelList.removeEventListener(\"mouseleave\", this.offHover);\r\n\t\t}\r\n\t}\r\n\r\n\tget guildsList(){\r\n\t\treturn document.getElementsByClassName(\"guilds-wrapper\")[0];\r\n\t}\r\n\t\r\n\tget channelList(){\r\n\t\treturn document.getElementsByClassName(\"scroller-fzNley scroller-NXV0-d\")[0];\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "ImageBrowser.plugin.js",
    "content": "//META{\"name\":\"ImageBrowser\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/ImageBrowser.plugin.js\"}*//\r\n\r\nclass ImageBrowser {\r\n\t\r\n    getName() { return \"ImageBrowser\"; }\r\n    getDescription() { return \"Displays a next and previous button on image popouts for browsing through images in a channel.\"; }\r\n    getVersion() { return \"0.1.1\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\tgetChanges() {\r\n\t\treturn {\r\n\t\t\t\"0.1.1\":\r\n\t\t\t`\r\n\t\t\t\tYou can now use the arrow keys to navigate through images.\r\n\t\t\t`\r\n\t\t};\r\n\t}\r\n\r\n    load() {}\r\n\r\n    start() {\r\n\r\n        let libLoadedEvent = () => {\r\n            try{ this.onLibLoaded(); }\r\n            catch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n        };\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif(!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n        if(typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\r\n\t\tsetTimeout(() => {\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField(\"Zoom speed\", this.settings.zoomSpeed, e => {\r\n\t\t\t\tif(isNaN(e.target.value)) return NeatoLib.showToast(\"Value must be a number\", \"error\");\r\n\t\t\t\tthis.settings.zoomSpeed = e.target.value;\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t}), this.getName());\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createToggleSwitch(\"Display image loading filter\", this.settings.loadingFilter, () => {\r\n\t\t\t\tthis.settings.loadingFilter = !this.settings.loadingFilter;\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t}), this.getName(), { hint : \"Grayscale, blurred, slightly darker, when an image is loading\" });\r\n\t\t\t\r\n\t\t\tNeatoLib.Settings.pushChangelogElements(this);\r\n\r\n\t\t}, 0);\r\n\r\n\t\treturn NeatoLib.Settings.Elements.pluginNameLabel(this.getName());\r\n\t\t\r\n\t}\r\n\r\n\tsaveSettings() {\r\n\t\tNeatoLib.Settings.save(this);\r\n\t}\r\n\r\n\tonLibLoaded() {\r\n\t\t\r\n\t\tthis.settings = NeatoLib.Settings.load(this, {\r\n\t\t\tdisplayUpdateNotes : true,\r\n\t\t\tzoomSpeed : 0.2,\r\n\t\t\tloadingFilter : true\r\n\t\t});\r\n\t\t\r\n\t\tNeatoLib.Updates.check(this);\r\n\t\t\r\n\t\tconst classes = NeatoLib.getClasses([\"imageWrapper\"], false);\r\n\r\n\t\tthis.style = NeatoLib.injectCSS(`\r\n\t\t\t\r\n\t\t\t.ib-arrow {\r\n\t\t\t\tposition: fixed;\r\n\t\t\t\ttop: calc(50% - 100px);\r\n\t\t\t\twidth: 200px;\r\n\t\t\t\theight: 200px;\r\n\t\t\t\tfilter: invert(50%);\r\n\t\t\t\tanimation: ib-arrow-fade-in 1s;\r\n\t\t\t\ttransition: filter 0.3s;\r\n\t\t\t\tz-index: 10000;\r\n\t\t\t}\r\n\r\n\t\t\t.ib-arrow:hover {\r\n\t\t\t\tfilter: invert(100%);\r\n\t\t\t}\r\n\r\n\t\t\t@keyframes ib-arrow-fade-in {\r\n\t\t\t\t0%{opacity:0}\r\n\t\t\t\t100%{opacity:1}\r\n\t\t\t}\r\n\t\t\t\r\n\t\t`);\r\n\t\t\r\n\t\tif(this.settings.displayUpdateNotes) NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());\r\n\r\n\t\tlet images = [], selectedImage = -1, zoomLevel = 1;\r\n\r\n\t\tdocument.addEventListener(\"keydown\", this.keyDownEvent = e => {\r\n\t\t\tif(e.key == \"ArrowRight\" && document.getElementById(\"ib-next-arrow\")) document.getElementById(\"ib-next-arrow\").click();\r\n\t\t\telse if(e.key == \"ArrowLeft\" && document.getElementById(\"ib-prev-arrow\")) document.getElementById(\"ib-prev-arrow\").click();\r\n\t\t});\r\n\t\t\r\n\t\tconst selectImage = (wrapper, idx, initial) => {\r\n\r\n\t\t\tselectedImage = idx;\r\n\r\n\t\t\tif(!initial) images = Array.filter(document.getElementsByClassName(\"chat\")[0].getElementsByTagName(\"img\"), e => e.parentElement.className.includes(\"imageWrapper\"));\r\n\r\n\t\t\tconst image = wrapper.lastChild;\r\n\r\n\t\t\timage.style = \"\";\r\n\r\n\t\t\timage.addEventListener(\"mousewheel\", e => {\r\n\t\t\t\tif(e.wheelDelta > 0) zoomLevel += this.settings.zoomSpeed;\r\n\t\t\t\telse if(zoomLevel > 1) zoomLevel -= this.settings.zoomSpeed;\r\n\t\t\t\telse {\r\n\t\t\t\t\tclicked = false;\r\n\t\t\t\t\timage.style.top = 0;\r\n\t\t\t\t\timage.style.left = 0;\r\n\t\t\t\t\timage.style.transform = \"\";\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tif(zoomLevel > 1) {\r\n\t\t\t\t\timage.draggable = false;\r\n\t\t\t\t\timage.style.transform = `scale(${zoomLevel})`;\r\n\t\t\t\t} else image.draggable = true;\r\n\t\t\t});\r\n\r\n\t\t\tlet clicked = false, origPos;\r\n\r\n\t\t\timage.addEventListener(\"mousedown\", function(e) {\r\n\t\t\t\torigPos = { x : e.clientX, y : e.clientY, top : parseInt(image.style.top), left : parseInt(image.style.left) };\r\n\t\t\t\tclicked = true;\r\n\t\t\t});\r\n\t\t\timage.addEventListener(\"mouseup\", function() { clicked = false; });\r\n\r\n\t\t\timage.style.top = 0;\r\n\t\t\timage.style.left = 0;\r\n\t\t\timage.addEventListener(\"mousemove\", function(e) {\r\n\t\t\t\tif(!(clicked && zoomLevel > 1)) return;\r\n\t\t\t\timage.style.top = (origPos.top - (origPos.y - e.clientY)) + \"px\";\r\n\t\t\t\timage.style.left = (origPos.left - (origPos.x - e.clientX)) + \"px\";\r\n\t\t\t});\r\n\r\n\t\t\tlet prevArrow = document.getElementById(\"ib-prev-arrow\"), nextArrow = document.getElementById(\"ib-next-arrow\");\r\n\r\n\t\t\tif(images[idx - 1]) {\r\n\t\t\t\tif(!prevArrow) wrapper.parentElement.insertAdjacentElement(\"afterbegin\", NeatoLib.DOM.createElement({ id : \"ib-prev-arrow\", className : \"ib-arrow\", src : \"https://material.io/tools/icons/static/icons/baseline-arrow_right-24px.svg\", style : \"left:100px;transform:rotate(180deg)\", onclick : () => selectImage(wrapper, selectedImage - 1), draggable : false }, { type : \"img\" }));\r\n\t\t\t} else if(prevArrow) prevArrow.remove();\r\n\r\n\t\t\tif(images[idx + 1]) {\r\n\t\t\t\tif(!nextArrow) wrapper.parentElement.insertAdjacentElement(\"afterbegin\", NeatoLib.DOM.createElement({ id : \"ib-next-arrow\", className : \"ib-arrow\", src : \"https://material.io/tools/icons/static/icons/baseline-arrow_right-24px.svg\", style : \"right:100px;\", onclick : () => selectImage(wrapper, selectedImage + 1), draggable : false }, { type : \"img\" }));\r\n\t\t\t} else if(nextArrow) nextArrow.remove();\r\n\r\n\t\t\timage.src = images[idx].src;\r\n\r\n\t\t\tif(this.settings.loadingFilter) image.style.filter = \"grayscale(100%) brightness(0.8) blur(3px)\";\r\n\t\t\timages[idx].scrollIntoViewIfNeeded();\r\n\r\n\t\t\timage.setAttribute(\"width\", \"\");\r\n\t\t\timage.setAttribute(\"height\", \"\");\r\n\r\n\t\t\timage.style.width = \"\";\r\n\t\t\timage.style.height = \"\";\r\n\r\n\t\t\tconst resize = function() {\r\n\r\n\t\t\t\timage.width *= 3;\r\n\r\n\t\t\t\tconst tryBegin = performance.now();\r\n\t\t\t\twhile((image.width > window.innerWidth * 0.65 || image.height > window.innerHeight * 0.65) && performance.now() - tryBegin < 500) image.width *= 0.9;\r\n\r\n\t\t\t\tdocument.getElementById(\"app-mount\").lastChild.lastChild.firstChild.style.width = image.width + \"px\";\r\n\t\t\t\tdocument.getElementById(\"app-mount\").lastChild.lastChild.firstChild.style.height = image.height + \"px\";\r\n\t\t\t\twrapper.style.width = image.width + \"px\";\r\n\t\t\t\twrapper.style.height = image.height + \"px\";\r\n\r\n\t\t\t};\r\n\r\n\t\t\timage.onload = function() {\r\n\r\n\t\t\t\tresize();\r\n\r\n\t\t\t\timage.src = images[idx].src.split(\"?\")[0];\r\n\r\n\t\t\t\timage.onload = e => {\r\n\t\t\t\t\tresize();\r\n\t\t\t\t\timage.style.transition = \"all 0.1s\";\r\n\t\t\t\t\te.target.style.filter = \"\";\r\n\t\t\t\t};\r\n\r\n\t\t\t};\r\n\t\t\tif(image.complete) image.onload();\r\n\r\n\t\t};\r\n\r\n\t\tthis.mutationObserver = new MutationObserver(async function(m) {\r\n\r\n\t\t\tif(m[1] && m[1].addedNodes.length) {\r\n\r\n\t\t\t\timages = Array.filter(document.getElementsByClassName(\"chat\")[0].getElementsByTagName(\"img\"), e => e.parentElement.className.includes(\"imageWrapper\"));\r\n\r\n\t\t\t\tconst wrapper = m[1].addedNodes[0].getElementsByClassName(classes.imageWrapper)[0];\r\n\t\t\t\tif(!wrapper) return;\r\n\t\t\t\t\r\n\t\t\t\tconst tryBegin = performance.now();\r\n\t\t\t\twhile(!wrapper.lastChild.src && performance.now() - tryBegin < 3000) await NeatoLib.Thread.sleep();\r\n\r\n\t\t\t\tselectImage(wrapper, images.findIndex(i => i.src && i.src.split(\"?\")[0] == wrapper.getElementsByTagName(\"img\")[0].src.split(\"?\")[0]), true);\r\n\r\n\t\t\t}\r\n\r\n\t\t});\r\n\t\t\r\n\t\tthis.mutationObserver.observe(document.getElementById(\"app-mount\").lastChild, { childList : true });\r\n\t\t\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\r\n\t}\r\n\t\r\n    stop() {\r\n\t\tthis.mutationObserver.disconnect();\r\n\t\tthis.style.destroy();\r\n\t\tdocument.removeEventListener(\"keydown\", this.keyDownEvent);\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "Lib/NeatoBurritoLibrary.js",
    "content": "var NeatoLib = {\r\n\r\n\tversion: \"0.9.29\",\r\n\r\n\tparseVersion: function(version) {\r\n\r\n\t\tlet numbers = Array.from(version.split(\".\"), n => parseInt(n)),\r\n\t\t\tmajor = numbers[0],\r\n\t\t\tminor = numbers[1],\r\n\t\t\tpatch = numbers[2];\r\n\r\n\t\treturn {\r\n\t\t\tmajor: major,\r\n\t\t\tminor: minor,\r\n\t\t\tpatch: patch,\r\n\t\t\tcompareTo: otherVersion => {\r\n\t\t\t\tif (patch > otherVersion.patch || minor > otherVersion.minor || major > otherVersion.major) return \"newer\";\r\n\t\t\t\tif (patch < otherVersion.patch || minor < otherVersion.minor || major < otherVersion.major) return \"older\";\r\n\t\t\t\treturn \"equal\";\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t},\r\n\r\n\thasRequiredLibVersion: function(plugin, requiredVersion) {\r\n\t\tif (NeatoLib.parseVersion(NeatoLib.version).compareTo(NeatoLib.parseVersion(requiredVersion)) == \"older\") {\r\n\t\t\tif (plugin.ready) plugin.ready = false;\r\n\r\n\t\t\tconst updateLibrary = () => {\r\n\t\t\t\tconst vm = require(\"vm\");\r\n\r\n\t\t\t\tfetch(\"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\", { cache: \"no-cache\" }).then(r => r.text()).then(data => {\r\n\t\t\t\t\tlet lib = new vm.Script(data, {\r\n\t\t\t\t\t\tfilename: \"NeatoBurritoLibrary.js\",\r\n\t\t\t\t\t\tdisplayErrors: true\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\tnew Promise(exec => exec(lib.runInThisContext())).then(() => {\r\n\t\t\t\t\t\tNeatoLib.showToast(`[${plugin.getName()}]: Library updated!`, \"success\");\r\n\t\t\t\t\t\tsetTimeout(() => plugin.start(), 1000);\r\n\t\t\t\t\t});\r\n\t\t\t\t});\r\n\t\t\t};\r\n\r\n\t\t\tNeatoLib.showToast(`[${plugin.getName()}]: Library update required! Click this notification to update it.`, \"error\", {\r\n\t\t\t\ttimeout: 30000,\r\n\t\t\t\tonClick: updateLibrary,\r\n\t\t\t\tdestroyOnClick: true\r\n\t\t\t});\r\n\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\treturn true;\r\n\t},\r\n\r\n\tforceLibUpdate: function() {\r\n\t\tconst vm = require(\"vm\");\r\n\r\n\t\tfetch(\"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\", { cache: \"no-cache\" }).then(r => r.text()).then(data => {\r\n\t\t\tconst lib = new vm.Script(data, { filename: \"NeatoBurritoLibrary.js\", displayErrors: true });\r\n\r\n\t\t\tnew Promise(e => e(lib.runInThisContext())).then(() => NeatoLib.showToast(\"Lib updated!\", \"success\"));\r\n\t\t});\r\n\t},\r\n\r\n\tChangelog: {\r\n\r\n\t\tcompareVersions: function(name, changes) {\r\n\r\n\t\t\tvar spacelessName = name.split(\" \").join(\"\"),\r\n\t\t\t\tupdateData = NeatoLib.Data.load(\"MetalloriffUpdateData\", spacelessName, {}),\r\n\t\t\t\tunreadChanges = [],\r\n\t\t\t\tthisUpdateData = updateData[spacelessName],\r\n\t\t\t\tfirst = false;\r\n\r\n\t\t\tif (thisUpdateData != undefined) {\r\n\r\n\t\t\t\tif (thisUpdateData.readChanges == undefined) thisUpdateData.readChanges = [];\r\n\r\n\t\t\t\tfor (var i in changes) {\r\n\r\n\t\t\t\t\tif (!thisUpdateData.readChanges.includes(i)) {\r\n\r\n\t\t\t\t\t\tunreadChanges.push(i);\r\n\t\t\t\t\t\tthisUpdateData.readChanges.push(i);\r\n\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t} else {\r\n\r\n\t\t\t\tupdateData[spacelessName] = {\r\n\t\t\t\t\treadChanges: Object.keys(changes)\r\n\t\t\t\t};\r\n\t\t\t\tfirst = true;\r\n\r\n\t\t\t}\r\n\r\n\t\t\tif (unreadChanges.length > 0 || first) {\r\n\t\t\t\tNeatoLib.Changelog.createChangeWindow(name, unreadChanges, changes, updateData);\r\n\t\t\t}\r\n\r\n\t\t},\r\n\r\n\t\tcreateChangeWindow: function(name, changes, allChanges, newUpdateData) {\r\n\r\n\t\t\tlet changeKeys = Object.keys(allChanges);\r\n\r\n\t\t\tif (changeKeys.length == 0) {\r\n\t\t\t\tNeatoLib.showToast(\"There are no updates notes for this plugin yet!\", \"error\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tlet spacelessName = name.split(\" \").join(\"\");\r\n\r\n\t\t\tif (document.getElementById(spacelessName + \"-changelog\")) document.getElementById(spacelessName + \"-changelog\").remove();\r\n\r\n\t\t\tdocument.getElementsByClassName(NeatoLib.getClass(\"app\"))[0].insertAdjacentHTML(\"beforeend\", `\r\n\r\n\t\t\t\t<div id=\"${spacelessName}-changelog\">\r\n\r\n\t\t\t\t<style>\r\n\r\n\t\t\t\t.metalloriff-update-item {\r\n\t\t\t\t\tpadding: 10px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t.metalloriff-update-label {\r\n\t\t\t\t\tcolor: white;\r\n\t\t\t\t\tfont-size: 35px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t.metalloriff-update-note {\r\n\t\t\t\t\tcolor: white;\r\n\t\t\t\t\tfont-size: 25px;\r\n\t\t\t\t\topacity: 0.75;\r\n\t\t\t\t\tline-height: 25px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t.metalloriff-changelog-backdrop {\r\n\t\t\t\t\topacity: 0.85;\r\n\t\t\t\t\tbackground-color: black;\r\n\t\t\t\t\tz-index: 1000;\r\n\t\t\t\t\tposition: fixed;\r\n\t\t\t\t\tcontain: strict;\r\n\t\t\t\t\tbottom: 0;\r\n\t\t\t\t\tleft: 0;\r\n\t\t\t\t\ttop: 0;\r\n\t\t\t\t\tright: 0;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t.metalloriff-changelog-scroller {\r\n\t\t\t\t\twidth: 800px;\r\n\t\t\t\t\tmin-height: 800px;\r\n\t\t\t\t\tmax-height: 800px;\r\n\t\t\t\t\tposition: fixed;\r\n\t\t\t\t\ttop: 50%;\r\n\t\t\t\t\tleft: 50%;\r\n\t\t\t\t\ttransform: translate(-50%, -50%);\r\n\t\t\t\t\toverflow-y: scroll;\r\n\t\t\t\t\tbackground: #2f3136;\r\n\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t\tz-index: 10000;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t#${spacelessName}-changelog *::-webkit-scrollbar {\r\n\t\t\t\t\tmax-width: 10px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t#${spacelessName}-changelog *::-webkit-scrollbar-track-piece {\r\n\t\t\t\t\tbackground: transparent;\r\n\t\t\t\t\tborder: none;\r\n\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t#${spacelessName}-changelog *:hover::-webkit-scrollbar-track-piece {\r\n\t\t\t\t\tbackground: #2F3136;\r\n\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t#${spacelessName}-changelog *::-webkit-scrollbar-thumb {\r\n\t\t\t\t\tbackground: #1E2124;\r\n\t\t\t\t\tborder: none;\r\n\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t#${spacelessName}-changelog *::-webkit-scrollbar-button {\r\n\t\t\t\t\tdisplay: none;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t.metalloriff-changelog-label {\r\n\t\t\t\t\tflex: 1 1 auto;\r\n\t\t\t\t\ttext-align: center;\r\n\t\t\t\t\tcolor: white;\r\n\t\t\t\t\tpadding-top: 10px;\r\n\t\t\t\t\tfont-size: 20px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t</style>\r\n\r\n\t\t\t\t<div class=\"metalloriff-changelog-backdrop\"></div>\r\n\t\t\t\t\t<div class=\"metalloriff-changelog-scroller\">\r\n\t\t\t\t\t\t<div class=\"metalloriff-changelog-label\">\r\n\t\t\t\t\t\t\t<h2>${name} Update Notes</h2>\r\n\t\t\t\t\t\t\t<br>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t<div id=\"${spacelessName}-changelog-scroller\"></div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</div>\r\n\r\n\t\t\t\t</div>\r\n\r\n\t\t\t`);\r\n\r\n\t\t\tdocument.getElementById(spacelessName + \"-changelog\").getElementsByClassName(\"metalloriff-changelog-backdrop\")[0].addEventListener(\"click\", () => {\r\n\t\t\t\tif (newUpdateData != undefined) NeatoLib.Data.save(\"MetalloriffUpdateData\", spacelessName, newUpdateData);\r\n\t\t\t\tdocument.getElementById(spacelessName + \"-changelog\").remove();\r\n\t\t\t});\r\n\r\n\t\t\tlet scroller = document.getElementById(spacelessName + \"-changelog-scroller\");\r\n\r\n\t\t\tif (changes.length == 0) changes = changeKeys;\r\n\r\n\t\t\tfor (let i = 0; i < changes.length; i++) {\r\n\t\t\t\tscroller.insertAdjacentHTML(\"afterbegin\", `\r\n\t\t\t\t\t<div class=\"metalloriff-update-item\">\r\n\t\t\t\t\t\t<p class=\"metalloriff-update-label\">` + changes[i] + `</p><p class=\"metalloriff-update-note\">` +\r\n\t\t\t\t\tallChanges[changes[i]].split(\"\\n\").join(\"<br><br>\") +\r\n\t\t\t\t\t`</p>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t`);\r\n\t\t\t}\r\n\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tSettings: {\r\n\r\n\t\tcreatePanel: function(plugin) {\r\n\t\t\tsetTimeout(() => {\r\n\t\t\t\tthis.create(plugin);\r\n\t\t\t\tthis.pushChangelogElements(plugin);\r\n\t\t\t});\r\n\r\n\t\t\treturn this.Elements.pluginNameLabel(plugin.getName());\r\n\t\t},\r\n\r\n\t\tcreate: function(plugin) {\r\n\t\t\tconst panel = document.getElementById(`plugin-settings-${plugin.getName()}`), fields = plugin.settingFields;\r\n\t\t\tpanel.classList.add(\"neato-settings\");\r\n\r\n\t\t\tpanel.insertAdjacentHTML(\"beforeEnd\", `\r\n\t\t\t\t<style>\r\n\t\t\t\t\t.neato-setting-label {\r\n\t\t\t\t\t\tcolor: white;\r\n\t\t\t\t\t\tline-height: 24px;\r\n\t\t\t\t\t\tmargin-left: 5px;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t:not(.neato-setting-array-items) > .neato-setting {\r\n\t\t\t\t\t\tmargin-top: 20px;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.neato-setting-array-items > .neato-setting {\r\n\t\t\t\t\t\tmargin-top: 10px;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.neato-textfield {\r\n\t\t\t\t\t\tdisplay: block;\r\n\t\t\t\t\t\tcolor: white;\r\n\t\t\t\t\t\tbackground-color: rgba(0, 0, 0, 0.2);\r\n\t\t\t\t\t\tborder: none;\r\n\t\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t\t\theight: 40px;\r\n\t\t\t\t\t\tpadding: 10px;\r\n\t\t\t\t\t\twidth: 100%;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.neato-setting-array .neato-textfield {\r\n\t\t\t\t\t\tdisplay: inline;\r\n\t\t\t\t\t\ttransition: width 0.3s;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.neato-setting-array .neato-setting:hover .neato-textfield {\r\n\t\t\t\t\t\twidth: calc(100% - 45px);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.neato-setting-array .neato-array-remove-button {\r\n\t\t\t\t\t\twidth: 0;\r\n\t\t\t\t\t\tcolor: white;\r\n\t\t\t\t\t\tfloat: right;\r\n\t\t\t\t\t\tcursor: pointer;\r\n\t\t\t\t\t\tfont-size: 40px;\r\n\t\t\t\t\t\ttransition: width 0.3s;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.neato-setting-array .neato-setting:hover .neato-array-remove-button {\r\n\t\t\t\t\t\twidth: 40px;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.neato-setting-array-buttons {\r\n\t\t\t\t\t\tmargin-top: 10px;\r\n\t\t\t\t\t\ttext-align: center;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.neato-radio-button {\r\n\t\t\t\t\t\tdisplay: flex;\r\n\t\t\t\t\t\tbackground: rgba(0, 0, 0, 0.2);\r\n\t\t\t\t\t\tpadding: 10px;\r\n\t\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t\t\tcursor: pointer;\r\n\t\t\t\t\t\tposition: relative;\r\n\t\t\t\t\t\tmargin: 10px;\r\n\t\t\t\t\t\ttransition: all 0.3s;\r\n\t\t\t\t\t\tuser-select: none;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.neato-radio-button .nrb-box {\r\n\t\t\t\t\t\twidth: 24px;\r\n\t\t\t\t\t\theight: 24px;\r\n\t\t\t\t\t\tborder: none;\r\n\t\t\t\t\t\tbackground: rgba(0, 0, 0, 0.5);\r\n\t\t\t\t\t\tborder-radius: 100%;\r\n\t\t\t\t\t\tmargin-right: 5px;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.neato-radio-button.nrb-selected {\r\n\t\t\t\t\t\tbackground: #7289da;\r\n\t\t\t\t\t\tbox-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.neato-radio-button.nrb-selected .nrb-box {\r\n\t\t\t\t\t\tbackground: white;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.neato-radio-button * {\r\n\t\t\t\t\t\tcursor: pointer;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.neato-settings [data-description]::after {\r\n\t\t\t\t\t\tcontent: attr(data-description);\r\n\t\t\t\t\t\tmargin-left: 10px;\r\n\t\t\t\t\t\topacity: 0.5;\r\n\t\t\t\t\t}\r\n\t\t\t\t</style>\r\n\t\t\t`);\r\n\r\n\t\t\tconst createLabel = (title, description, tooltip) => {\r\n\t\t\t\tconst element = document.createElement(\"label\");\r\n\t\t\t\telement.className = \"neato-setting-label\";\r\n\t\t\t\telement.textContent = title;\r\n\t\t\t\tif(description) element.dataset.description = description || \"\";\r\n\t\t\t\tif(tooltip) NeatoLib.Tooltip.attach(tooltip, element);\r\n\t\t\t\treturn element;\r\n\t\t\t};\r\n\r\n\t\t\tconst createToggleSwitch = (title, description, tooltip, value, callback) => { //<input id=\"1\" class=\"checkboxEnabled-CtinEn checkbox-2tyjJg da-checkboxEnabled da-checkbox\" type=\"checkbox\" tabindex=\"-1\">\r\n\t\t\t\tconst element = document.createElement(\"div\");\r\n\t\t\t\telement.className = \"neato-setting\";\r\n\t\t\t\tif (title) element.appendChild(createLabel(title, description, tooltip));\r\n\r\n\t\t\t\tconst tswitch = document.createElement(\"div\");\r\n\t\t\t\ttswitch.className = [NeatoLib.getClass(\"flexChild\"), NeatoLib.getClass(\"switchEnabled\"), NeatoLib.getClass(\"switch\"), value ? NeatoLib.getClass(\"valueChecked\") : NeatoLib.getClass(\"valueUnchecked\"), NeatoLib.getClass(\"value\"), NeatoLib.getClass(\"sizeDefault\"), NeatoLib.getClass(\"sizeDefault\", \"size\"), NeatoLib.getClass(\"themeDefault\")].join(\" \");\r\n\t\t\t\ttswitch.style.float = \"right\";\r\n\r\n\t\t\t\tconst tcheckbox = document.createElement(\"input\");\r\n\t\t\t\ttcheckbox.type = \"checkbox\";\r\n\t\t\t\ttcheckbox.className = [NeatoLib.getClass(\"checkboxEnabled\"), NeatoLib.getClass(\"checkboxEnabled\", \"checkbox\")].join(\" \");\r\n\r\n\t\t\t\ttswitch.appendChild(tcheckbox);\r\n\t\t\t\ttswitch.addEventListener(\"click\", e => {\r\n\t\t\t\t\tvalue = !value;\r\n\r\n\t\t\t\t\tif (value == true) tswitch.className = tswitch.className.replace(NeatoLib.getClass(\"valueUnchecked\"), NeatoLib.getClass(\"valueChecked\"));\r\n\t\t\t\t\telse tswitch.className = tswitch.className.replace(NeatoLib.getClass(\"valueChecked\"), NeatoLib.getClass(\"valueUnchecked\"));\r\n\r\n\t\t\t\t\tcallback(value, e);\r\n\t\t\t\t});\r\n\r\n\t\t\t\telement.appendChild(tswitch);\r\n\r\n\t\t\t\treturn element;\r\n\t\t\t};\r\n\r\n\t\t\tconst createTextField = (title, description, tooltip, value, type, callback) => {\r\n\t\t\t\tconst element = document.createElement(\"div\");\r\n\t\t\t\telement.className = \"neato-setting\";\r\n\t\t\t\tif (title) element.appendChild(createLabel(title, description, tooltip));\r\n\r\n\t\t\t\tconst input = document.createElement(\"input\");\r\n\t\t\t\tinput.className = \"neato-textfield\";\r\n\t\t\t\tinput.type = \"text\";\r\n\t\t\t\tinput.value = value || \"\";\r\n\r\n\t\t\t\tlet last = input.value;\r\n\r\n\t\t\t\tinput.addEventListener(\"focusout\", e => {\r\n\t\t\t\t\tswitch (type) {\r\n\t\t\t\t\t\tcase \"number\": case \"float\":\r\n\t\t\t\t\t\t\tif (!input.value) {\r\n\t\t\t\t\t\t\t\tcallback(input.value = last = 0, e);\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tif (isNaN(input.value)) {\r\n\t\t\t\t\t\t\t\tNeatoLib.showToast(\"Value must be a number!\", \"error\");\r\n\t\t\t\t\t\t\t\tinput.value = last;\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tcallback(last = parseFloat(input.value), e);\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\tcase \"whole number\": case \"int\": case \"integer\":\r\n\t\t\t\t\t\t\tif (!input.value) {\r\n\t\t\t\t\t\t\t\tcallback(input.value = last = 0, e);\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tif (isNaN(input.value)) {\r\n\t\t\t\t\t\t\t\tNeatoLib.showToast(\"Value must be a number!\", \"error\");\r\n\t\t\t\t\t\t\t\tinput.value = last;\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tcallback(last = parseInt(input.value), e);\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\tdefault: callback(input.value, e);\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\r\n\t\t\t\telement.appendChild(input);\r\n\r\n\t\t\t\treturn element;\r\n\t\t\t};\r\n\r\n\t\t\tconst createRadioGroup = (title, description, tooltip, value, choices, callback) => {\r\n\t\t\t\tconst element = document.createElement(\"div\");\r\n\t\t\t\telement.className = \"neato-setting\";\r\n\t\t\t\tif (title) element.appendChild(createLabel(title, description, tooltip));\r\n\r\n\t\t\t\tconst list = document.createElement(\"div\");\r\n\r\n\t\t\t\tconst update = () => {\r\n\t\t\t\t\tfor (let choice of element.getElementsByClassName(\"neato-radio-button\")) {\r\n\t\t\t\t\t\tif (choice.dataset.key == value) choice.classList.add(\"nrb-selected\");\r\n\t\t\t\t\t\telse choice.classList.remove(\"nrb-selected\");\r\n\t\t\t\t\t}\r\n\t\t\t\t};\r\n\r\n\t\t\t\tconst createRadioButton = choice => {\r\n\t\t\t\t\tconst c = document.createElement(\"div\");\r\n\t\t\t\t\tc.className = \"neato-radio-button\";\r\n\t\t\t\t\tc.dataset.key = choice;\r\n\t\t\t\t\tif (value == choice) c.classList.add(\"nrb-selected\");\r\n\r\n\t\t\t\t\tconst t = document.createElement(\"span\");\r\n\t\t\t\t\tt.className = \"nrb-box\";\r\n\r\n\t\t\t\t\tc.appendChild(t);\r\n\t\t\t\t\tc.appendChild(createLabel(choices[choice].label, choices[choice].description, choices[choice].tooltip));\r\n\r\n\t\t\t\t\tc.addEventListener(\"click\", e => {\r\n\t\t\t\t\t\tcallback(value = choice, e);\r\n\t\t\t\t\t\tupdate();\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\treturn c;\r\n\t\t\t\t};\r\n\r\n\t\t\t\tfor (let choice in choices) element.appendChild(createRadioButton(choice));\r\n\r\n\t\t\t\tupdate();\r\n\r\n\t\t\t\treturn element;\r\n\t\t\t};\r\n\r\n\t\t\tif (!plugin.settings) plugin.settings = plugin.defaultSettings;\r\n\r\n\t\t\tfor (let setting in fields) {\r\n\t\t\t\tconst field = fields[setting];\r\n\t\t\t\ttry {\r\n\t\t\t\t\tlet repop, array, insertDeleteButton;\r\n\r\n\t\t\t\t\tif (field.array || field.list) {\r\n\t\t\t\t\t\tconst list = document.createElement(\"div\");\r\n\t\t\t\t\t\tlist.className = \"neato-setting neato-setting-array\";\r\n\t\t\t\t\t\tlist.appendChild(createLabel(field.label, field.description, field.tooltip));\r\n\r\n\t\t\t\t\t\tarray = document.createElement(\"div\");\r\n\t\t\t\t\t\tarray.className = \"neato-setting-array-items\";\r\n\t\t\t\t\t\tlist.appendChild(array);\r\n\r\n\t\t\t\t\t\tif (field.array) {\r\n\t\t\t\t\t\t\tconst addButton = document.createElement(\"button\"), clearButton = document.createElement(\"button\");\r\n\t\t\t\t\t\t\taddButton.className = clearButton.className = [NeatoLib.getClass(\"button\"), NeatoLib.getClass(\"lookFilled\"), NeatoLib.getClass(\"colorBrand\"), NeatoLib.getClass(\"sizeMedium\"), NeatoLib.getClass(\"grow\")].join(\" \");\r\n\t\t\t\t\t\t\taddButton.style = clearButton.style = \"display:inline;margin:0 10px\";\r\n\r\n\t\t\t\t\t\t\taddButton.textContent = \"Add\";\r\n\t\t\t\t\t\t\taddButton.addEventListener(\"click\", e => {\r\n\t\t\t\t\t\t\t\tplugin.settings[setting].push(\"\");\r\n\t\t\t\t\t\t\t\tarray.innerHTML = \"\";\r\n\t\t\t\t\t\t\t\trepop();\r\n\t\t\t\t\t\t\t\tif (typeof field.callback == \"function\") field.callback({ type: \"add\", event: e, array: array, panel: panel });\r\n\t\t\t\t\t\t\t\tplugin.saveSettings();\r\n\t\t\t\t\t\t\t});\r\n\r\n\t\t\t\t\t\t\tclearButton.textContent = \"Clear\";\r\n\t\t\t\t\t\t\tclearButton.addEventListener(\"click\", e => {\r\n\t\t\t\t\t\t\t\tplugin.settings[setting] = [];\r\n\t\t\t\t\t\t\t\tarray.innerHTML = \"\";\r\n\t\t\t\t\t\t\t\trepop();\r\n\t\t\t\t\t\t\t\tif (typeof field.callback == \"function\") field.callback({ type: \"clear\", event: e, array: array, panel: panel });\r\n\t\t\t\t\t\t\t\tplugin.saveSettings();\r\n\t\t\t\t\t\t\t});\r\n\r\n\t\t\t\t\t\t\tinsertDeleteButton = i => {\r\n\t\t\t\t\t\t\t\tconst button = document.createElement(\"span\");\r\n\t\t\t\t\t\t\t\tbutton.className = \"material-icons neato-array-remove-button\";\r\n\t\t\t\t\t\t\t\tbutton.textContent = \"close\";\r\n\r\n\t\t\t\t\t\t\t\tbutton.addEventListener(\"click\", e => {\r\n\t\t\t\t\t\t\t\t\tplugin.settings[setting].splice(i, 1);\r\n\t\t\t\t\t\t\t\t\tarray.innerHTML = \"\";\r\n\t\t\t\t\t\t\t\t\trepop();\r\n\t\t\t\t\t\t\t\t\tif (typeof field.callback == \"function\") field.callback({ type: \"remove\", event: e, array: array, panel: panel });\r\n\t\t\t\t\t\t\t\t\tplugin.saveSettings();\r\n\t\t\t\t\t\t\t\t});\r\n\r\n\t\t\t\t\t\t\t\tNeatoLib.Tooltip.attach(\"Remove\", button, { delay : 250 });\r\n\r\n\t\t\t\t\t\t\t\tarray.children[i].appendChild(button);\r\n\t\t\t\t\t\t\t};\r\n\r\n\t\t\t\t\t\t\tconst buttons = document.createElement(\"div\");\r\n\t\t\t\t\t\t\tbuttons.className = \"neato-setting-array-buttons\";\r\n\r\n\t\t\t\t\t\t\tbuttons.appendChild(addButton);\r\n\t\t\t\t\t\t\tbuttons.appendChild(clearButton);\r\n\r\n\t\t\t\t\t\t\tlist.appendChild(buttons);\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tpanel.appendChild(list);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tconst set = (nv, e) => {\r\n\t\t\t\t\t\tif (typeof field.callback == \"function\") field.callback({ type: \"change\", oldValue: plugin.settings[setting], newValue: nv, event: e, panel: panel });\r\n\t\t\t\t\t\tplugin.settings[setting] = nv;\r\n\t\t\t\t\t\tplugin.saveSettings();\r\n\t\t\t\t\t};\r\n\r\n\t\t\t\t\tswitch (field.type) {\r\n\t\t\t\t\t\tcase \"bool\": case \"boolean\":\r\n\t\t\t\t\t\t\tif (array) {\r\n\t\t\t\t\t\t\t\t(repop = () => {\r\n\t\t\t\t\t\t\t\t\tfor (let i in plugin.settings[setting]) {\r\n\t\t\t\t\t\t\t\t\t\tconst element = createToggleSwitch(null, null, null, plugin.settings[setting][i], (nv, e) => {\r\n\t\t\t\t\t\t\t\t\t\t\tif (typeof field.callback == \"function\") field.callback({ type: \"change\", oldValue: plugin.settings[setting][i], newValue: nv, event: e, panel: panel });\r\n\t\t\t\t\t\t\t\t\t\t\tplugin.settings[setting][i] = nv;\r\n\t\t\t\t\t\t\t\t\t\t\tplugin.saveSettings();\r\n\t\t\t\t\t\t\t\t\t\t});\r\n\r\n\t\t\t\t\t\t\t\t\t\tif (field.events)\r\n\t\t\t\t\t\t\t\t\t\t\tfor (let event in field.events)\r\n\t\t\t\t\t\t\t\t\t\t\t\tif (event == \"start\" || event == \"init\") field.events[i](element);\r\n\t\t\t\t\t\t\t\t\t\t\t\telse element.addEventListener(event, field.events[i]);\r\n\r\n\t\t\t\t\t\t\t\t\t\tarray.appendChild(element);\r\n\r\n\t\t\t\t\t\t\t\t\t\tif (field.array) insertDeleteButton(i);\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t})();\r\n\t\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\t\tconst element = createToggleSwitch(field.label, field.description, field.tooltip, plugin.settings[setting], set);\r\n\r\n\t\t\t\t\t\t\t\tif (field.events)\r\n\t\t\t\t\t\t\t\t\tfor (let event in field.events)\r\n\t\t\t\t\t\t\t\t\t\tif (event == \"start\" || event == \"init\") field.events[i](element);\r\n\t\t\t\t\t\t\t\t\t\telse element.addEventListener(event, field.events[i]);\r\n\r\n\t\t\t\t\t\t\t\tpanel.appendChild(element);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\tcase \"custom\":\r\n\t\t\t\t\t\t\tif (field.element) panel.appendChild(field.element);\r\n\t\t\t\t\t\t\tif (field.html) panel.insertAdjacentHTML(\"beforeEnd\", field.html);\r\n\t\t\t\t\t\t\tif (field.createElement) field.createElement(panel);\r\n\r\n\t\t\t\t\t\t\tif (field.events)\r\n\t\t\t\t\t\t\t\tfor (let event in field.events)\r\n\t\t\t\t\t\t\t\t\tif (event == \"start\" || event == \"init\") field.events[i](panel.lastChild);\r\n\t\t\t\t\t\t\t\t\telse panel.lastChild.addEventListener(event, field.events[i]);\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\tcase \"radio\": case \"radio group\":\r\n\t\t\t\t\t\t\tconst element = createRadioGroup(field.label, field.description, field.tooltip, plugin.settings[setting], field.choices, set);\r\n\r\n\t\t\t\t\t\t\tif (field.events)\r\n\t\t\t\t\t\t\t\tfor (let event in field.events)\r\n\t\t\t\t\t\t\t\t\tif (event == \"start\" || event == \"init\") field.events[i](element);\r\n\t\t\t\t\t\t\t\t\telse element.addEventListener(event, field.events[i]);\r\n\r\n\t\t\t\t\t\t\tpanel.appendChild(element);\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\tdefault:\r\n\t\t\t\t\t\t\tif (array) {\r\n\t\t\t\t\t\t\t\t(repop = () => {\r\n\t\t\t\t\t\t\t\t\tfor (let i in plugin.settings[setting]) {\r\n\t\t\t\t\t\t\t\t\t\tconst element = createTextField(null, null, null, plugin.settings[setting][i], field.type, (nv, e) => {\r\n\t\t\t\t\t\t\t\t\t\tif (typeof field.callback == \"function\") field.callback({ type: \"change\", oldValue: plugin.settings[i], newValue: nv, event: e, panel: panel });\r\n\t\t\t\t\t\t\t\t\t\t\tplugin.settings[setting][i] = nv;\r\n\t\t\t\t\t\t\t\t\t\t\tplugin.saveSettings();\r\n\t\t\t\t\t\t\t\t\t\t});\r\n\r\n\t\t\t\t\t\t\t\t\t\tif (field.events)\r\n\t\t\t\t\t\t\t\t\t\t\tfor (let event in field.events)\r\n\t\t\t\t\t\t\t\t\t\t\t\tif (event == \"start\" || event == \"init\") field.events[i](element);\r\n\t\t\t\t\t\t\t\t\t\t\t\telse element.addEventListener(event, field.events[i]);\r\n\r\n\t\t\t\t\t\t\t\t\t\tarray.appendChild(element);\r\n\r\n\t\t\t\t\t\t\t\t\t\tif (field.array) insertDeleteButton(i);\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t})();\r\n\t\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\t\tconst element = createTextField(field.label, field.description, field.tooltip, plugin.settings[setting], field.type, set);\r\n\r\n\t\t\t\t\t\t\t\tif (field.events)\r\n\t\t\t\t\t\t\t\t\tfor (let event in field.events)\r\n\t\t\t\t\t\t\t\t\t\tif (event == \"start\" || event == \"init\") field.events[i](element);\r\n\t\t\t\t\t\t\t\t\t\telse element.addEventListener(event, field.events[i]);\r\n\r\n\t\t\t\t\t\t\t\tpanel.appendChild(element);\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t}\r\n\t\t\t\t} catch(err) { console.error(err); }\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tStyles: {\r\n\t\t\ttextField: `color: white; background-color: rgba(0, 0, 0, 0.2); border: none; border-radius: 5px; height: 40px; padding: 10px; width: 100%;`\r\n\t\t},\r\n\r\n\t\tElements: {\r\n\r\n\t\t\tpluginNameLabel: function(name, authorName = \"Metalloriff\") {\r\n\t\t\t\treturn `\r\n\t\t\t\t\t<style>\r\n\t\t\t\t\t\t#bd-settingspane-container *::-webkit-scrollbar {\r\n\t\t\t\t\t\t\tmax-width: 10px;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t#bd-settingspane-container *::-webkit-scrollbar-track-piece {\r\n\t\t\t\t\t\t\tbackground: transparent;\r\n\t\t\t\t\t\t\tborder: none;\r\n\t\t\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t#bd-settingspane-container *:hover::-webkit-scrollbar-track-piece {\r\n\t\t\t\t\t\t\tbackground: #2F3136;\r\n\t\t\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t#bd-settingspane-container *::-webkit-scrollbar-thumb {\r\n\t\t\t\t\t\t\tbackground: #1E2124;\r\n\t\t\t\t\t\t\tborder: none;\r\n\t\t\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t#bd-settingspane-container *::-webkit-scrollbar-button {\r\n\t\t\t\t\t\t\tdisplay: none;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t</style>\r\n\t\t\t\t\t<h style=\"color: white;font-size: 30px;font-weight: bold;\">${name.replace(/([A-Z])/g, ' $1').trim()} by ${authorName}</h>`;\r\n\t\t\t},\r\n\r\n\t\t\tcreateRadioGroup: function(id, label, choices, selectedChoice, callback, description = \"\") {\r\n\r\n\t\t\t\tlet element = document.createElement(\"div\");\r\n\r\n\t\t\t\telement.style.paddingTop = \"20px\";\r\n\r\n\t\t\t\telement.innerHTML = `\r\n\t\t\t\t<h5 style=\"color:white;padding-bottom:10px;\">${label}</h5>\r\n\t\t\t\t<h5 style=\"Color:white;padding-bottom:10px;opacity:0.5;\">${description}<h5>\r\n\t\t\t\t<div id=\"${id}\" style=\"color:white;\"></div>`;\r\n\r\n\t\t\t\tfor (let i = 0; i < choices.length; i++) {\r\n\r\n\t\t\t\t\tif (choices[i].description == undefined) choices[i].description = \"\";\r\n\r\n\t\t\t\t\tlet choiceButton = document.createElement(\"div\");\r\n\r\n\t\t\t\t\tchoiceButton.setAttribute(\"id\", `${id}-${i}`);\r\n\t\t\t\t\tchoiceButton.setAttribute(\"data-value\", choices[i].value);\r\n\t\t\t\t\tchoiceButton.setAttribute(\"data-index\", i);\r\n\t\t\t\t\tchoiceButton.setAttribute(\"class\", \"metalloriff-checkbox-item\");\r\n\t\t\t\t\tchoiceButton.setAttribute(\"style\", `padding:10px;border-radius:5px !important;background-color:rgba(0, 0, 0, 0.3);cursor:pointer;position:relative;margin-bottom:10px;display:flex;`);\r\n\r\n\t\t\t\t\tchoiceButton.innerHTML =\r\n\t\t\t\t\t\t`<label>\r\n\t\t\t\t\t\t<div style=\"width:24px;height:24px;border:3px solid white;border-radius:100%;\"></div>\r\n\t\t\t\t\t</label>\r\n\t\t\t\t\t<div style=\"margin: 0 8px;color:white;\">\r\n\t\t\t\t\t\t<div style=\"display:inline;padding-right:10px;line-height:24px;\">${choices[i].title}</div>\r\n\t\t\t\t\t\t<div style=\"display:inline;line-height:24px;opacity:0.5;\">${choices[i].description}</div>\r\n\t\t\t\t\t</div>`;\r\n\r\n\t\t\t\t\telement.insertAdjacentElement(\"beforeend\", choiceButton);\r\n\r\n\t\t\t\t\tif (selectedChoice != undefined && choices[i].value == selectedChoice) choiceButton.querySelector(`label > div`).style.backgroundColor = \"white\";\r\n\r\n\t\t\t\t\tchoiceButton.addEventListener(\"click\", e => {\r\n\r\n\t\t\t\t\t\tlet i = e.currentTarget.getAttribute(\"data-index\");\r\n\r\n\t\t\t\t\t\tlet checkboxes = e.currentTarget.parentElement.querySelectorAll(`.metalloriff-checkbox-item > label > div`);\r\n\r\n\t\t\t\t\t\tfor (let ii = 0; ii < checkboxes.length; ii++) checkboxes[ii].style.backgroundColor = \"\";\r\n\r\n\t\t\t\t\t\telement.querySelector(`#${id}-${i} > label > div`).style.backgroundColor = \"white\";\r\n\r\n\t\t\t\t\t\tcallback(choiceButton, choices[i]);\r\n\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn element;\r\n\r\n\t\t\t},\r\n\r\n\t\t\tcreateToggleGroup: function(id, label, choices, callback, description = \"\") {\r\n\r\n\t\t\t\tlet element = document.createElement(\"div\");\r\n\r\n\t\t\t\telement.style.paddingTop = \"10px\";\r\n\r\n\t\t\t\telement.insertAdjacentHTML(\"beforeend\", `\r\n\t\t\t\t\t<h5 style=\"color:white;padding-bottom:5px;\">${label}</h5>\r\n\t\t\t\t\t<h5 style=\"Color:white;padding-bottom:5px;opacity:0.5;\">${description}<h5>\r\n\t\t\t\t\t<div id=\"${id}\" style=\"color:white;\"></div>\r\n\t\t\t\t`);\r\n\r\n\t\t\t\tfor (let i = 0; i < choices.length; i++) {\r\n\r\n\t\t\t\t\tlet choiceButton = NeatoLib.Settings.Elements.createToggleSwitch(choices[i].title, choices[i].setValue, e => {\r\n\t\t\t\t\t\tcallback(choices[i], e);\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\tchoiceButton.setAttribute(\"id\", `${id}-${i}`);\r\n\t\t\t\t\tchoiceButton.setAttribute(\"data-value\", choices[i].value);\r\n\t\t\t\t\tchoiceButton.setAttribute(\"data-index\", i);\r\n\r\n\t\t\t\t\telement.insertAdjacentElement(\"beforeend\", choiceButton);\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn element;\r\n\r\n\t\t\t},\r\n\r\n\t\t\tcreateTextField: function(label, type, value, callback, options = {}) {\r\n\r\n\t\t\t\tlet element = document.createElement(\"div\");\r\n\r\n\t\t\t\telement.style.paddingTop = options.spacing || \"20px\";\r\n\r\n\t\t\t\telement.insertAdjacentHTML(\"beforeend\", `\r\n\t\t\t\t\t<p style=\"color:white;font-size:20px;\">${label}</p>\r\n\t\t\t\t\t<input value=\"${value}\" type=\"${type}\" class=\"inputDefault-_djjkz input-cIJ7To size16-14cGz5\">\r\n\t\t\t\t`);\r\n\r\n\t\t\t\tif (options.tooltip) NeatoLib.Tooltip.attach(options.tooltip, element, {\r\n\t\t\t\t\tside: \"left\"\r\n\t\t\t\t});\r\n\r\n\t\t\t\telement.querySelector(\"input\").addEventListener(options.callbackType || \"focusout\", e => callback(e));\r\n\r\n\t\t\t\treturn element;\r\n\r\n\t\t\t},\r\n\r\n\t\t\tcreateNewTextField: function(label, value, callback, options = {}) {\r\n\r\n\t\t\t\tlet element = document.createElement(\"div\");\r\n\r\n\t\t\t\telement.style.paddingTop = options.spacing || \"20px\";\r\n\r\n\t\t\t\telement.insertAdjacentHTML(\"beforeend\", `\r\n\t\t\t\t\t<style>\r\n\t\t\t\t\t\t.neato-text-field-p {\r\n\t\t\t\t\t\t\tcolor: white;\r\n\t\t\t\t\t\t\tfont-size: 20px;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t</style>\r\n\t\t\t\t\t<p class=\"neato-text-field-p\">${label}</p>\r\n\t\t\t\t\t<p class=\"neato-text-field-p\" style=\"opacity:0.5;font-size:17px;\">${options.description || \"\"}</p>\r\n\t\t\t\t\t<input value=\"${value}\" type=\"${options.type || \"text\"}\" style=\"${NeatoLib.Settings.Styles.textField}\">\r\n\t\t\t\t`);\r\n\r\n\t\t\t\telement.querySelector(\"input\").addEventListener(options.callbackType || \"focusout\", e => callback(e));\r\n\r\n\t\t\t\treturn element;\r\n\r\n\t\t\t},\r\n\r\n\t\t\tcreateHint: function(text, options = {}) {\r\n\r\n\t\t\t\tlet element = document.createElement(\"p\");\r\n\r\n\t\t\t\telement.style.color = options.color || \"white\";\r\n\t\t\t\telement.style.fontSize = options.fontSize || \"17px\";\r\n\r\n\t\t\t\telement.innerText = text;\r\n\r\n\t\t\t\treturn element;\r\n\r\n\t\t\t},\r\n\r\n\t\t\tcreateButton: function(label, callback, style = \"\", attributes = {}) {\r\n\r\n\t\t\t\tlet element = document.createElement(\"button\");\r\n\r\n\t\t\t\telement.setAttribute(\"style\", `display:inline-block;${style}`);\r\n\t\t\t\telement.setAttribute(\"class\", [NeatoLib.getClass(\"button\"), NeatoLib.getClass(\"lookFilled\"), NeatoLib.getClass(\"colorBrand\"), NeatoLib.getClass(\"sizeMedium\"), NeatoLib.getClass(\"grow\")].join(\" \"));\r\n\r\n\t\t\t\tfor (let key in attributes) element.setAttribute(key, attributes[key]);\r\n\r\n\t\t\t\telement.innerText = label;\r\n\r\n\t\t\t\telement.addEventListener(\"click\", e => callback(e));\r\n\r\n\t\t\t\treturn element;\r\n\r\n\t\t\t},\r\n\r\n\t\t\tcreateToggleSwitch: function(label, value, callback, spacing = \"20px\") {\r\n\r\n\t\t\t\tvar element = document.createElement(\"div\");\r\n\r\n\t\t\t\telement.style.paddingTop = spacing;\r\n\r\n\t\t\t\telement.innerHTML =\r\n\t\t\t\t\t`<div class=\"flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2NDFzi alignStart-H-X2h- noWrap-3jynv6\" style=\"flex: 1 1 auto;\">\r\n\t\t\t\t\t<h3 class=\"titleDefault-a8-ZSr title-31JmR4 marginReset-236NPn weightMedium-2iZe9B size16-14cGz5 height24-3XzeJx flexChild-faoVW3\" style=\"flex: 1 1 auto;\">${label}</h3>\r\n\t\t\t\t\t<div class=\"flexChild-faoVW3 switchEnabled-V2WDBB switch-3wwwcV ${value == true ? \"valueChecked-m-4IJZ\" : \"valueUnchecked-2lU_20\"} value-2hFrkk sizeDefault-2YlOZr size-3rFEHg themeDefault-24hCdX\" style=\"flex: 0 0 auto;\">\r\n\t\t\t\t\t\t<input class=\"checkboxEnabled-CtinEn checkbox-2tyjJg\" type=\"checkbox\">\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</div>`;\r\n\r\n\t\t\t\telement.querySelector(\"input\").addEventListener(\"click\", e => {\r\n\r\n\t\t\t\t\tvar b = e.currentTarget.parentElement;\r\n\r\n\t\t\t\t\tif (b.classList.contains(\"valueChecked-m-4IJZ\")) {\r\n\t\t\t\t\t\tb.classList.add(\"valueUnchecked-2lU_20\");\r\n\t\t\t\t\t\tb.classList.remove(\"valueChecked-m-4IJZ\");\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tb.classList.add(\"valueChecked-m-4IJZ\");\r\n\t\t\t\t\t\tb.classList.remove(\"valueUnchecked-2lU_20\");\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tcallback(e);\r\n\r\n\t\t\t\t});\r\n\r\n\t\t\t\treturn element;\r\n\r\n\t\t\t},\r\n\r\n\t\t\tcreateLabel: function(title, spacing = \"20px\", style = \"\") {\r\n\t\t\t\treturn `<div style=\"color:white;margin: ${spacing} 0px;${style}\">${title}</div>`;\r\n\t\t\t},\r\n\r\n\t\t\tcreateGroup: function(title, options = {}) {\r\n\r\n\t\t\t\tlet element = document.createElement(\"div\");\r\n\r\n\t\t\t\telement.setAttribute(\"style\", `color:white;margin:${options.spacing || \"20px\"};${options.style || \"\"}`);\r\n\r\n\t\t\t\telement.insertAdjacentHTML(\"beforeend\", `<div style=\"margin: ${options.spacing || \"20px\"} 0px;\">${title}</div><div></div>`);\r\n\r\n\t\t\t\treturn element;\r\n\r\n\t\t\t},\r\n\r\n\t\t\tcreateKeybindInput: function(title, value, callback, options = {}) {\r\n\r\n\t\t\t\tlet element = document.createElement(\"div\"),\r\n\t\t\t\t\tv = value.primaryKey || \"\",\r\n\t\t\t\t\toldValue = value;\r\n\r\n\t\t\t\tif (value.modifiers && value.modifiers[0]) v = (value.modifiers.join(\" + \") || \"\") + \" + \" + (value.primaryKey || \"\");\r\n\r\n\t\t\t\tif (options.global) v = value;\r\n\r\n\t\t\t\telement.insertAdjacentHTML(\"beforeend\", `\r\n\t\t\t\t\t<style>\r\n\t\t\t\t\t\t#app-mount .card-FDVird.active-nvdKfC:before, .card-FDVird:before {\r\n\t\t\t\t\t\t\topacity: 0.5 !important;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t</style>\r\n\t\t\t\t\t<div class=\"row-2okwlC\">\r\n\t\t\t\t\t\t<div class=\"flex-1xMQg5 flex-1O1GKY vertical-V37hAW flex-1O1GKY directionColumn-35P_nr justifyStart-2NDFzi alignStretch-DpGPf3 noWrap-3jynv6 keybindGroup-JQs9x_ card-FDVird\" style=\"flex: 1 1 auto; margin: 10px 25px\">\r\n\t\t\t\t\t\t\t<div class=\"flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2NDFzi alignStretch-DpGPf3 noWrap-3jynv6 marginBottom8-AtZOdT\" style=\"flex: 1 1 auto;\">\r\n\t\t\t\t\t\t\t\t<div class=\"item-rJ_Cmt flexChild-faoVW3\" style=\"flex: 1 1 50%;\">\r\n\t\t\t\t\t\t\t\t\t<h5 class=\"h5-18_1nd title-3sZWYQ size12-3R0845 height16-2Lv3qA weightSemiBold-NJexzi defaultMarginh5-2mL-bP marginBottom8-AtZOdT\">${title}</h5>\r\n\t\t\t\t\t\t\t\t\t<div class=\"container-CpszHS container-1nZlH6 hasValue-3pdcdm\">\r\n\t\t\t\t\t\t\t\t\t\t<div class=\"flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2NDFzi alignStretch-DpGPf3 noWrap-3jynv6 layout-FSaTy9 layout-eEMo5y\" style=\"flex: 1 1 auto;\"><input placeholder=\"No Keybind Set\" type=\"text\" readonly=\"\" class=\"input-1G2o7i input-1UhAnY base-96ewKC\" value=\"${v.replace(\"Key\", \"\")}\" style=\"flex: 1 1 auto;\">\r\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2NDFzi alignStretch-DpGPf3 noWrap-3jynv6\"\r\n\t\t\t\t\t\t\t\t\t\t\t\tstyle=\"flex: 0 1 auto; margin: 0px;\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t<button type=\"button\" class=\"button-34kXw5 button-3tQuzi button-38aScr lookGhost-2Fn_0- colorGrey-2DXtkV sizeMin-1mJd1x grow-q77ONN nbl-keybind-button\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"contents-18-Yxp nbl-keybind-button\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"text-2sI5Sd nbl-keybind-button\">Edit Keybind</span>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"editIcon-13gaox nbl-keybind-button\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\r\n\t\t\t\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t<div class=\"description-3_Ncsb formText-3fs7AJ keybindMessage-20JT9A flexChild-faoVW3 modeDefault-3a2Ph1 primary-jw0I4K\" style=\"flex: 1 1 auto;\">${options.description || \"\"}</div>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t`);\r\n\r\n\t\t\t\tlet isRecording = false,\r\n\t\t\t\t\tprimaryKey = \"\",\r\n\t\t\t\t\tmodifiers = [],\r\n\t\t\t\t\tglobalKeys = [];\r\n\r\n\t\t\t\tlet keyEvent = e => {\r\n\r\n\t\t\t\t\te.preventDefault();\r\n\r\n\t\t\t\t\tif (options.global) {\r\n\r\n\t\t\t\t\t\tlet key = e.key;\r\n\r\n\t\t\t\t\t\tif (key.length == 1) key = key.toUpperCase();\r\n\r\n\t\t\t\t\t\tif (globalKeys.indexOf(key) == -1) globalKeys.push(key);\r\n\t\t\t\t\t\tif (globalKeys[0] == \"\") globalKeys.splice(0, 1);\r\n\t\t\t\t\t\tinput.value = globalKeys.join(\" + \");\r\n\r\n\t\t\t\t\t\tif (e.location == 0 && globalKeys.length > 1) button.click();\r\n\t\t\t\t\t\telse input.value += \" + ...\";\r\n\r\n\t\t\t\t\t} else {\r\n\r\n\t\t\t\t\t\tif (e.location == 0) primaryKey = e.code;\r\n\t\t\t\t\t\telse if (modifiers.indexOf(e.code) == -1) modifiers.push(e.code);\r\n\r\n\t\t\t\t\t\tif (primaryKey && modifiers[0]) {\r\n\t\t\t\t\t\t\tinput.value = `${modifiers.join(\" + \")} + ${primaryKey}`;\r\n\t\t\t\t\t\t\tbutton.click();\r\n\t\t\t\t\t\t} else if (primaryKey) input.value = primaryKey;\r\n\t\t\t\t\t\telse if (modifiers[0]) input.value = modifiers.join(\" + \") + \" + ...\";\r\n\t\t\t\t\t\telse input.value = \"\";\r\n\r\n\t\t\t\t\t\tinput.value = input.value.replace(\"Key\", \"\");\r\n\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t};\r\n\r\n\t\t\t\tlet keyUpEvent = e => {\r\n\r\n\t\t\t\t\te.preventDefault();\r\n\r\n\t\t\t\t\tif (options.global) {\r\n\r\n\t\t\t\t\t\tlet key = e.key;\r\n\r\n\t\t\t\t\t\tif (key.length == 1) key = key.toUpperCase();\r\n\r\n\t\t\t\t\t\tif (globalKeys.indexOf(key) != -1) globalKeys.splice(globalKeys.indexOf(key), 1);\r\n\t\t\t\t\t\tif (globalKeys[0] == \"\") globalKeys.splice(0, 1);\r\n\t\t\t\t\t\tinput.value = globalKeys.join(\" + \");\r\n\r\n\t\t\t\t\t} else {\r\n\r\n\t\t\t\t\t\tif (e.location == 0) primaryKey = undefined;\r\n\t\t\t\t\t\telse if (modifiers.indexOf(e.code) != -1) modifiers.splice(modifiers.indexOf(e.code), 1);\r\n\r\n\t\t\t\t\t\tif (primaryKey && modifiers[0]) input.value = `${modifiers.join(\" + \")} + ${primaryKey}`;\r\n\t\t\t\t\t\telse if (primaryKey) input.value = primaryKey;\r\n\t\t\t\t\t\telse if (modifiers[0]) input.value = modifiers.join(\" + \") + \" + ...\";\r\n\t\t\t\t\t\telse input.value = \"\";\r\n\r\n\t\t\t\t\t\tinput.value = input.value.replace(\"Key\", \"\");\r\n\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t};\r\n\r\n\t\t\t\tlet toggleRecording = () => {\r\n\r\n\t\t\t\t\tisRecording = !isRecording;\r\n\r\n\t\t\t\t\tif (isRecording) {\r\n\t\t\t\t\t\tif (options.global) NeatoLib.Keybinds.unregisterGlobal(oldValue);\r\n\t\t\t\t\t\tdocument.addEventListener(\"keydown\", keyEvent);\r\n\t\t\t\t\t\tdocument.addEventListener(\"keyup\", keyUpEvent);\r\n\t\t\t\t\t\tdocument.addEventListener(\"click\", documentClick);\r\n\t\t\t\t\t\tcontainer.classList.add(\"recording-1H2dS7\");\r\n\t\t\t\t\t\tlabel.innerText = \"Save Keybind\";\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\toldValue = globalKeys.join(\" + \");\r\n\t\t\t\t\t\tif (options.global) callback(oldValue);\r\n\t\t\t\t\t\telse callback({\r\n\t\t\t\t\t\t\tprimaryKey: primaryKey,\r\n\t\t\t\t\t\t\tmodifiers: modifiers\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\t\tprimaryKey = undefined;\r\n\t\t\t\t\t\tmodifiers = [];\r\n\t\t\t\t\t\tglobalKeys = [];\r\n\t\t\t\t\t\tdocument.removeEventListener(\"keydown\", keyEvent);\r\n\t\t\t\t\t\tdocument.removeEventListener(\"keyup\", keyUpEvent);\r\n\t\t\t\t\t\tdocument.removeEventListener(\"click\", documentClick);\r\n\t\t\t\t\t\tcontainer.classList.remove(\"recording-1H2dS7\");\r\n\t\t\t\t\t\tlabel.innerText = \"Edit Keybind\";\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t};\r\n\r\n\t\t\t\tlet documentClick = e => {\r\n\t\t\t\t\tif (!e.target.classList.contains(\"nbl-keybind-button\")) toggleRecording();\r\n\t\t\t\t};\r\n\r\n\t\t\t\tlet input = element.getElementsByTagName(\"input\")[0],\r\n\t\t\t\t\tcontainer = element.getElementsByClassName(\"container-CpszHS\")[0],\r\n\t\t\t\t\tbutton = element.getElementsByTagName(\"button\")[0],\r\n\t\t\t\t\tlabel = element.getElementsByClassName(\"text-2sI5Sd\")[0];\r\n\r\n\t\t\t\tbutton.addEventListener(\"click\", toggleRecording);\r\n\r\n\t\t\t\treturn element;\r\n\r\n\t\t\t}\r\n\r\n\t\t},\r\n\r\n\t\tpushChangelogElements: function(plugin) {\r\n\r\n\t\t\tvar element = document.createElement(\"div\");\r\n\r\n\t\t\telement.style.padding = \"10px\";\r\n\t\t\telement.style.marginTop = \"10px\";\r\n\t\t\telement.style.backgroundColor = \"rgba(0,0,0,0.2)\";\r\n\t\t\telement.style.borderRadius = \"5px\";\r\n\r\n\t\t\telement.insertAdjacentHTML(\"beforeend\", `<div style=\"text-align:center;color:white;\">Other</div>`);\r\n\r\n\t\t\telement.insertAdjacentElement(\"beforeend\", NeatoLib.Settings.Elements.createToggleSwitch(\"Display changes for every update\", plugin.settings.displayUpdateNotes, () => {\r\n\t\t\t\tplugin.settings.displayUpdateNotes = !plugin.settings.displayUpdateNotes;\r\n\t\t\t\tplugin.saveSettings();\r\n\t\t\t}));\r\n\r\n\t\t\tvar right = document.createElement(\"div\");\r\n\r\n\t\t\tright.style.textAlign = \"right\";\r\n\r\n\t\t\tright.style.paddingTop = \"20px\";\r\n\r\n\t\t\tright.insertAdjacentElement(\"beforeend\", NeatoLib.Settings.Elements.createButton(\"View Changelog\", () => {\r\n\t\t\t\tNeatoLib.Changelog.createChangeWindow(plugin.getName(), [], plugin.getChanges());\r\n\t\t\t}));\r\n\r\n\t\t\tright.insertAdjacentElement(\"afterbegin\", NeatoLib.Settings.Elements.createButton(\"Join Support Server\", () => {\r\n\t\t\t\twindow.open(\"https://discord.gg/yNqzuJa\");\r\n\t\t\t}, \"float:left\"));\r\n\r\n\t\t\telement.insertAdjacentElement(\"beforeend\", right);\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(element, plugin.getName());\r\n\r\n\t\t},\r\n\r\n\t\tpushElement: function(element, name, options = {}) {\r\n\r\n\t\t\tconst {\r\n\t\t\t\ttooltip,\r\n\t\t\t\ttooltipSide\r\n\t\t\t} = options;\r\n\r\n\t\t\tdocument.getElementById(`plugin-settings-${name}`).appendChild(element);\r\n\r\n\t\t\tif (tooltip) NeatoLib.Tooltip.attach(tooltip, element, {\r\n\t\t\t\tside: tooltipSide || \"left\"\r\n\t\t\t});\r\n\r\n\t\t},\r\n\r\n\t\tpushElements: function(elements, name) {\r\n\t\t\tlet panel = document.getElementById(`plugin-settings-${name}`);\r\n\t\t\tfor (let i = 0; i < elements.length; i++) panel.appendChild(elements[i]);\r\n\t\t},\r\n\r\n\t\tpushHTML: function(html, name) {\r\n\t\t\tdocument.getElementById(`plugin-settings-${name}`).insertAdjacentHTML(\"beforeend\", html);\r\n\t\t},\r\n\r\n\t\tshowPluginSettings: function(name) {\r\n\r\n\t\t\tdocument.querySelector(\".button-2b6hmh:nth-child(3)\").click();\r\n\r\n\t\t\tsetTimeout(() => {\r\n\r\n\t\t\t\tvar bdActions = document.querySelectorAll(\"#bd-settings-sidebar .ui-tab-bar-item\");\r\n\r\n\t\t\t\tfor (var i = 0; i < bdActions.length; i++) {\r\n\t\t\t\t\tif (bdActions[i].textContent == \"Plugins\") bdActions[i].click();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tsetTimeout(() => {\r\n\r\n\t\t\t\t\tvar settingsBox = document.querySelector(`li[data-name=\"${name}\"]`),\r\n\t\t\t\t\t\tsettingsButton = settingsBox.getElementsByClassName(\"bda-settings-button\")[0];\r\n\r\n\t\t\t\t\tsettingsBox.scrollIntoView();\r\n\r\n\t\t\t\t\tif (settingsButton != undefined) settingsButton.click();\r\n\r\n\t\t\t\t}, 100);\r\n\r\n\t\t\t}, 100);\r\n\r\n\t\t},\r\n\r\n\t\tsave: function(plugin) {\r\n\t\t\tNeatoLib.Data.save(plugin.getName().split(\" \").join(\"\"), \"settings\", plugin.settings);\r\n\t\t},\r\n\r\n\t\tload: function(plugin, defaultSettings) {\r\n\t\t\treturn plugin.settings = NeatoLib.Data.load(plugin.getName().split(\" \").join(\"\"), \"settings\", defaultSettings || plugin.defaultSettings);\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tUI: {\r\n\r\n\t\tcreatePrompt: function(id, title, description, yesCallback, noCallback = \"close\", options = {}) {\r\n\r\n\t\t\tdocument.getElementsByClassName(NeatoLib.getClass(\"app\"))[0].insertAdjacentHTML(\"beforeend\", `\r\n\r\n\t\t\t<div id=\"neato-prompt-${id}\" style=\"z-index:10000;\">\r\n\t\t\t\t<div class=\"backdrop-1wrmKB\" style=\"opacity: 0.85; background-color: rgb(0, 0, 0); transform: translateZ(0px);\"></div>\r\n\t\t\t\t<div class=\"modal-36zFtW\" style=\"opacity: 1; transform: scale(1) translateZ(0px);\">\r\n\t\t\t\t\t<div class=\"inner-2VEzy9\">\r\n\t\t\t\t\t\t<div class=\"modal-3v8ziU sizeSmall-2-_smo\">\r\n\t\t\t\t\t\t\t<div class=\"flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2NDFzi alignCenter-1dQNNs noWrap-3jynv6 header-2nhbou\" style=\"flex: 0 0 auto;\">\r\n\t\t\t\t\t\t\t\t<h4 class=\"h4-AQvcAz title-3sZWYQ size16-14cGz5 height20-mO2eIN weightSemiBold-NJexzi defaultColor-1_ajX0 defaultMarginh4-2vWMG5 marginReset-236NPn\">${title}</h4>\r\n\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t<div class=\"scrollerWrap-2lJEkd content-2BXhLs scrollerThemed-2oenus themeGhostHairline-DBD-2d\">\r\n\t\t\t\t\t\t\t\t<div class=\"scroller-2FKFPG inner-3wn6Q5\">\r\n\t\t\t\t\t\t\t\t\t<div class=\"card-1SJYqw marginBottom20-32qID7 card-3Qj_Yx\" style=\"background-color:${options.color || \"transparent\"};border:none;\">\r\n\t\t\t\t\t\t\t\t\t\t<div class=\"medium-zmzTW- size16-14cGz5 height20-mO2eIN white-2qwKC7\">${description}</div>\r\n\t\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t<div class=\"flex-1xMQg5 flex-1O1GKY horizontalReverse-2eTKWD horizontalReverse-3tRjY7 flex-1O1GKY directionRowReverse-m8IjIq justifyStart-2NDFzi alignStretch-DpGPf3 noWrap-3jynv6 footer-30ewN8\" style=\"flex: 0 0 auto;\"><button type=\"submit\" class=\"button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMedium-1AC_Sl grow-q77ONN prompt-yes\"><div class=\"contents-18-Yxp\">${options.yesText || \"Yes\"}</div></button><button type=\"button\" class=\"button-38aScr lookLink-9FtZy- colorPrimary-3b3xI6 sizeMedium-1AC_Sl grow-q77ONN prompt-no\"><div class=\"contents-18-Yxp\">${options.noText || \"No\"}</div></button></div>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t`);\r\n\r\n\t\t\tlet prompt = document.getElementById(\"neato-prompt-\" + id),\r\n\t\t\t\tbackdrop = prompt.getElementsByClassName(\"backdrop-1wrmKB\")[0],\r\n\t\t\t\tyesButton = prompt.getElementsByClassName(\"prompt-yes\")[0],\r\n\t\t\t\tnoButton = prompt.getElementsByClassName(\"prompt-no\")[0];\r\n\r\n\t\t\tprompt.close = () => prompt.outerHTML = \"\";\r\n\r\n\t\t\tbackdrop.addEventListener(\"click\", () => prompt.close());\r\n\r\n\t\t\tyesButton.addEventListener(\"click\", () => yesCallback(prompt));\r\n\t\t\tnoButton.addEventListener(\"click\", noCallback == \"close\" ? () => prompt.close() : () => noCallback(prompt));\r\n\r\n\t\t\tprompt.addEventListener(\"keydown\", e => {\r\n\t\t\t\tif (e.key == \"Escape\") prompt.close();\r\n\t\t\t\tif (e.key == \"Enter\") yesButton.click();\r\n\t\t\t});\r\n\r\n\t\t\treturn prompt;\r\n\r\n\t\t},\r\n\r\n\t\tcreateTextPrompt: function(id, title, callback, value = \"\", options = {}) {\r\n\r\n\t\t\tdocument.getElementsByClassName(NeatoLib.getClass(\"app\"))[0].insertAdjacentHTML(\"beforeend\", `\r\n\r\n\t\t\t<div id=\"neato-text-prompt-${id}\" style=\"z-index:10000;\">\r\n\t\t\t\t<div class=\"backdrop-1wrmKB\" style=\"opacity: 0.85; background-color: rgb(0, 0, 0); transform: translateZ(0px);\"></div>\r\n\t\t\t\t<div class=\"modal-1UGdnR\" style=\"opacity: 1; transform: scale(1) translateZ(0px);\">\r\n\t\t\t\t\t<div class=\"inner-1JeGVc\">\r\n\t\t\t\t\t\t<div class=\"modal-3HD5ck sizeSmall-Sf4iOi\">\r\n\t\t\t\t\t\t\t<div class=\"flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2NDFzi alignCenter-1dQNNs noWrap-3jynv6 header-1R_AjF\" style=\"flex: 0 0 auto;\">\r\n\t\t\t\t\t\t\t\t<h4 class=\"h4-AQvcAz title-3sZWYQ size16-14cGz5 height20-mO2eIN weightSemiBold-NJexzi defaultColor-1_ajX0 defaultMarginh4-2vWMG5 marginReset-236NPn\">${title}</h4>\r\n\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t<div class=\"content-2BXhLs\">\r\n\t\t\t\t\t\t\t\t<div class=\"inner-3wn6Q5\" style=\"flex:1;-webkit-box-flex:1;padding-right:20px;\">\r\n\t\t\t\t\t\t\t\t\t<div class=\"input-2JOcvO marginBottom8-AtZOdT\">\r\n\t\t\t\t\t\t\t\t\t\t<h5 class=\"h5-18_1nd title-3sZWYQ size12-3R0845 height16-2Lv3qA weightSemiBold-NJexzi defaultMarginh5-2mL-bP marginBottom8-AtZOdT\">${options.description || \"\"}</h5>\r\n\t\t\t\t\t\t\t\t\t\t<div class=\"inputWrapper-31_8H8 vertical-V37hAW flex-1O1GKY directionColumn-35P_nr\"><input placeholder=\"${options.placeholder || \"\"}\" value=\"${value.split(\"\\\"\").join(\"&quot;\")}\" class=\"inputDefault-_djjkz input-cIJ7To size16-14cGz5\" type=\"text\"></div>\r\n\t\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t\t\t<div class=\"reset-2ikQ30 marginBottom20-32qID7 small-29zrCQ size12-3R0845 height16-2Lv3qA primary-jw0I4K weightSemiBold-NJexzi prompt-second-option\">${options.secondOptionText || \"\"}</div>\r\n\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t<div class=\"flex-1xMQg5 flex-1O1GKY horizontalReverse-2eTKWD horizontalReverse-3tRjY7 flex-1O1GKY directionRowReverse-m8IjIq justifyStart-2NDFzi alignStretch-DpGPf3 noWrap-3jynv6 footer-2yfCgX\" style=\"flex: 0 0 auto;\"><button class=\"button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMedium-1AC_Sl grow-q77ONN prompt-confirm\"><div class=\"contents-18-Yxp\">${options.confirmText || \"Save\"}</div></button><button type=\"button\" class=\"button-38aScr lookLink-9FtZy- colorPrimary-3b3xI6 sizeMedium-1AC_Sl grow-q77ONN prompt-cancel\"><div class=\"contents-18-Yxp\">Cancel</div></button></div>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t`);\r\n\r\n\t\t\tlet prompt = document.getElementById(\"neato-text-prompt-\" + id),\r\n\t\t\t\tbackdrop = prompt.getElementsByClassName(\"backdrop-1wrmKB\")[0],\r\n\t\t\t\tconfirmButton = prompt.getElementsByClassName(\"prompt-confirm\")[0],\r\n\t\t\t\tcancelButton = prompt.getElementsByClassName(\"prompt-cancel\")[0],\r\n\t\t\t\tsecondOption = prompt.getElementsByClassName(\"prompt-second-option\")[0],\r\n\t\t\t\tfield = prompt.getElementsByTagName(\"input\")[0];\r\n\r\n\t\t\tfield.focus();\r\n\t\t\tfield.selectionStart = field.selectionEnd = field.value.length;\r\n\r\n\t\t\tprompt.close = () => prompt.outerHTML = \"\";\r\n\r\n\t\t\tbackdrop.addEventListener(\"click\", () => prompt.close());\r\n\r\n\t\t\tconfirmButton.addEventListener(\"click\", () => callback(field.value, prompt));\r\n\t\t\tcancelButton.addEventListener(\"click\", () => prompt.close());\r\n\r\n\t\t\tif (options.secondOptionCallback != undefined) secondOption.addEventListener(\"click\", () => options.secondOptionCallback(prompt));\r\n\r\n\t\t\tprompt.addEventListener(\"keydown\", e => {\r\n\t\t\t\tif (e.key == \"Escape\") prompt.close();\r\n\t\t\t\tif (e.key == \"Enter\") confirmButton.click();\r\n\t\t\t});\r\n\r\n\t\t\treturn prompt;\r\n\r\n\t\t},\r\n\r\n\t\tcreateBasicScrollList: function(id, title, options = {}) {\r\n\r\n\t\t\tdocument.getElementsByClassName(NeatoLib.getClass(\"app\"))[0].insertAdjacentHTML(\"beforeend\", `\r\n\r\n\t\t\t<div id=\"${id}\">\r\n\r\n\t\t\t<style>\r\n\r\n\t\t\t\t${options.style || \"\"}\r\n\r\n\t\t\t\t.${id}-item {\r\n\t\t\t\t\tpadding: 10px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t.${id}-backdrop {\r\n\t\t\t\t\topacity: 0.85;\r\n\t\t\t\t\tbackground-color: black;\r\n\t\t\t\t\tz-index: 1000;\r\n\t\t\t\t\tposition: fixed;\r\n\t\t\t\t\tcontain: strict;\r\n\t\t\t\t\tbottom: 0;\r\n\t\t\t\t\tleft: 0;\r\n\t\t\t\t\ttop: 0;\r\n\t\t\t\t\tright: 0;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t.${id}-scroller-wrapper {\r\n\t\t\t\t\twidth: ${options.width || 800}px;\r\n\t\t\t\t\tposition: fixed;\r\n\t\t\t\t\ttop: 50%;\r\n\t\t\t\t\tleft: 50%;\r\n\t\t\t\t\ttransform: translate(-50%, -50%);\r\n\t\t\t\t\tbackground: #2f3136;\r\n\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t\tz-index: 10000;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t.${id}-scroller {\r\n\t\t\t\t\twidth: 100%;\r\n\t\t\t\t\tmax-height: calc(100vh - 300px);\r\n\t\t\t\t\toverflow-y: scroll;\r\n\t\t\t\t\toverflow-x: hidden;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t#${id} *::-webkit-scrollbar {\r\n\t\t\t\t\tmax-width: 10px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t#${id} *::-webkit-scrollbar-track-piece {\r\n\t\t\t\t\tbackground: transparent;\r\n\t\t\t\t\tborder: none;\r\n\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t#${id} *:hover::-webkit-scrollbar-track-piece {\r\n\t\t\t\t\tbackground: #2F3136;\r\n\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t#${id} *::-webkit-scrollbar-thumb {\r\n\t\t\t\t\tbackground: #1E2124;\r\n\t\t\t\t\tborder: none;\r\n\t\t\t\t\tborder-radius: 5px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t#${id} *::-webkit-scrollbar-button {\r\n\t\t\t\t\tdisplay: none;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t.${id}-label {\r\n\t\t\t\t\tcolor: white;\r\n\t\t\t\t\tfont-size: 35px;\r\n\t\t\t\t\tflex: 1 1 auto;\r\n\t\t\t\t\ttext-align: center;\r\n\t\t\t\t\tpadding-top: 10px;\r\n\t\t\t\t\tfont-size: 20px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t.${id}-label span {\r\n\t\t\t\t\tvertical-align: middle;\r\n\t\t\t\t\tmarign-left: 10px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t.${id}-scroller > :last-child {\r\n\t\t\t\t\tmargin-bottom: 10px;\r\n\t\t\t\t}\r\n\r\n\t\t\t</style>\r\n\r\n\t\t\t<div class=\"${id}-backdrop\"></div>\r\n\t\t\t\t<div class=\"${id}-scroller-wrapper\">\r\n\t\t\t\t\t<div class=\"${id}-label\">\r\n\t\t\t\t\t\t<h2>${title}</h2>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<div class=\"${id}-scroller\"></div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\r\n\t\t\t</div>\r\n\r\n\t\t\t`);\r\n\r\n\t\t\tlet window = document.getElementById(id),\r\n\t\t\t\tscroller = window.getElementsByClassName(`${id}-scroller`)[0],\r\n\t\t\t\tbackdrop = window.getElementsByClassName(`${id}-backdrop`)[0];\r\n\r\n\t\t\tbackdrop.addEventListener(\"click\", () => window.outerHTML = \"\");\r\n\t\t\twindow.addEventListener(\"keydown\", e => {\r\n\t\t\t\tif (key == \"Escape\") backdrop.click();\r\n\t\t\t});\r\n\r\n\t\t\treturn {\r\n\t\t\t\twindow: window,\r\n\t\t\t\tscroller: scroller,\r\n\t\t\t\tbackdrop: backdrop\r\n\t\t\t};\r\n\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tKeybinds: {\r\n\r\n\t\tglobalShortcut: require(\"electron\").remote.globalShortcut,\r\n\r\n\t\tattachListener: function(id, key, event, options = {}) {\r\n\r\n\t\t\tif (key == undefined) return console.warn(id, \"The passed key object is null!\", key);\r\n\r\n\t\t\tif (window.activeNeatoKeyListeners == undefined) window.activeNeatoKeyListeners = {};\r\n\r\n\t\t\tlet node = options.node || document;\r\n\r\n\t\t\tif (window.activeNeatoKeyListeners[id]) {\r\n\t\t\t\tconsole.warn(\"There is already a keybind listener with the id '\" + id + \"'!\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\twindow.activeNeatoKeyListeners[id] = {\r\n\t\t\t\theldKeys: [],\r\n\t\t\t\tkeydown: e => {\r\n\t\t\t\t\tif (window.activeNeatoKeyListeners[id].heldKeys.indexOf(e.code) == -1) window.activeNeatoKeyListeners[id].heldKeys.push(e.code);\r\n\t\t\t\t\tif (window.activeNeatoKeyListeners[id].heldKeys.indexOf(key.primaryKey) != -1) {\r\n\t\t\t\t\t\tlet heldModifiers = 0;\r\n\t\t\t\t\t\tfor (let i = 0; i < key.modifiers.length; i++)\r\n\t\t\t\t\t\t\tif (window.activeNeatoKeyListeners[id].heldKeys.indexOf(key.modifiers[i]) != -1) heldModifiers++;\r\n\t\t\t\t\t\tif (key.modifiers.length == heldModifiers && window.activeNeatoKeyListeners[id].heldKeys.length == heldModifiers + 1) event(e);\r\n\t\t\t\t\t}\r\n\t\t\t\t},\r\n\t\t\t\tkeyup: e => {\r\n\t\t\t\t\tif (window.activeNeatoKeyListeners[id].heldKeys.indexOf(e.code) != -1) window.activeNeatoKeyListeners[id].heldKeys.splice(window.activeNeatoKeyListeners[id].heldKeys.indexOf(e.code), 1);\r\n\t\t\t\t},\r\n\t\t\t\twindowFocusLoss: () => {\r\n\t\t\t\t\twindow.activeNeatoKeyListeners[id].heldKeys = [];\r\n\t\t\t\t}\r\n\t\t\t};\r\n\r\n\t\t\tnode.addEventListener(\"keydown\", window.activeNeatoKeyListeners[id].keydown);\r\n\t\t\tnode.addEventListener(\"keyup\", window.activeNeatoKeyListeners[id].keyup);\r\n\r\n\t\t\twindow.addEventListener(\"blur\", window.activeNeatoKeyListeners[id].windowFocusLoss);\r\n\r\n\t\t\treturn window.activeNeatoKeyListeners[id];\r\n\r\n\t\t},\r\n\r\n\t\tdetachListener: function(id, node = document) {\r\n\r\n\t\t\tif (window.activeNeatoKeyListeners == undefined) window.activeNeatoKeyListeners = {};\r\n\r\n\t\t\tif (!window.activeNeatoKeyListeners[id]) {\r\n\t\t\t\tconsole.warn(\"There is no keybind listener with the id '\" + id + \"'!\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tnode.removeEventListener(\"keydown\", window.activeNeatoKeyListeners[id].keydown);\r\n\t\t\tnode.removeEventListener(\"keyup\", window.activeNeatoKeyListeners[id].keyup);\r\n\r\n\t\t\twindow.removeEventListener(\"blur\", window.activeNeatoKeyListeners[id].windowFocusLoss);\r\n\r\n\t\t\tdelete window.activeNeatoKeyListeners[id];\r\n\r\n\t\t},\r\n\r\n\t\tregisterGlobal: function(key, event, debug = false) {\r\n\t\t\ttry {\r\n\t\t\t\tthis.globalShortcut.register(key, event);\r\n\t\t\t} catch (e) {\r\n\t\t\t\tif (debug) console.error(e);\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tunregisterGlobal: function(key, debug = false) {\r\n\t\t\ttry {\r\n\t\t\t\tthis.globalShortcut.unregister(key);\r\n\t\t\t} catch (e) {\r\n\t\t\t\tif (debug) console.error(e);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tChatbox: {\r\n\r\n\t\tget: function() {\r\n\t\t\tlet chat = document.getElementsByClassName(NeatoLib.getClass(\"chat\"))[0];\r\n\t\t\treturn chat ? chat.getElementsByTagName(\"textarea\")[0] : null;\r\n\t\t},\r\n\r\n\t\tsetText: function(newText) {\r\n\t\t\tNeatoLib.Chatbox.get().select();\r\n\t\t\tdocument.execCommand(\"insertText\", false, newText);\r\n\t\t},\r\n\r\n\t\tappendText: function(text) {\r\n\t\t\tlet chatbox = NeatoLib.Chatbox.get();\r\n\t\t\tif (!chatbox) return;\r\n\t\t\tchatbox.select();\r\n\t\t\tdocument.execCommand(\"insertText\", false, chatbox.value + text);\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tModules: { //Based off of Zerebos' PluginLibrary. https://rauenzi.github.io/BetterDiscordAddons/docs/PluginLibrary.js\r\n\r\n\t\treq: webpackJsonp.push([\r\n\t\t\t[], {\r\n\t\t\t\t\"__extra_id__\": (m, e, r) => m.exports = r\r\n\t\t\t},\r\n\t\t\t[\r\n\t\t\t\t[\"__extra_id__\"]\r\n\t\t\t]\r\n\t\t]),\r\n\r\n\t\tfind: function(filter) {\r\n\t\t\tfor (let i in this.req.c) {\r\n\t\t\t\tif (this.req.c.hasOwnProperty(i)) {\r\n\t\t\t\t\tconst m = this.req.c[i].exports;\r\n\t\t\t\t\tif (m && m.__esModule && m.default && filter(m.default)) return m.default;\r\n\t\t\t\t\tif (m && filter(m)) return m;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tconsole.warn(\"No module found with this filter!\", filter);\r\n\r\n\t\t\treturn null;\r\n\t\t},\r\n\r\n\t\tfindAll: function(filter) {\r\n\r\n\t\t\tlet found = [];\r\n\r\n\t\t\tfor (let i in this.req.c) {\r\n\r\n\t\t\t\tif (this.req.c.hasOwnProperty(i)) {\r\n\t\t\t\t\tlet m = this.req.c[i].exports;\r\n\t\t\t\t\tif (m && m.__esModule && m.default && filter(m.default)) found.push(m.default);\r\n\t\t\t\t\telse if (m && filter(m)) found.push(m);\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t\treturn found;\r\n\r\n\t\t},\r\n\r\n\t\tfindAllByPropertyName: function(propName, filter) {\r\n\r\n\t\t\tif (!filter) filter = m => m[propName];\r\n\r\n\t\t\tlet found = [];\r\n\r\n\t\t\tfor (let i in this.req.c) {\r\n\r\n\t\t\t\tif (this.req.c.hasOwnProperty(i)) {\r\n\t\t\t\t\tlet m = this.req.c[i].exports;\r\n\t\t\t\t\tif (m && m.__esModule && m.default && filter(m.default)) found.push(m.default[propName]);\r\n\t\t\t\t\telse if (m && filter(m)) found.push(m[propName]);\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t\treturn found;\r\n\r\n\t\t},\r\n\r\n\t\tfindIndex: function(filter) {\r\n\r\n\t\t\tfor (let i in this.req.c) {\r\n\r\n\t\t\t\tif (this.req.c.hasOwnProperty(i)) {\r\n\t\t\t\t\tlet m = this.req.c[i].exports;\r\n\t\t\t\t\tif (m && m.__esModule && m.default && filter(m.default)) return i;\r\n\t\t\t\t\tif (m && filter(m)) return i;\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t\tconsole.warn(\"No module found with this filter!\", filter);\r\n\r\n\t\t\treturn null;\r\n\r\n\t\t},\r\n\r\n\t\tget: function(props) {\r\n\t\t\tconst cacheKey = typeof props == \"string\" ? props : props.join(\",\");\r\n\t\t\tif (!this.cached) this.cached = {};\r\n\t\t\tif (!this.cached[cacheKey]) this.cached[cacheKey] = typeof props == \"string\" ? this.find(module => module[props] != undefined) : this.find(module => props.every(prop => module[prop] != undefined));\r\n\t\t\treturn this.cached[cacheKey];\r\n\t\t},\r\n\r\n\t\tgetById: function(id) {\r\n\t\t\treturn this.find(x => x._dispatchToken == \"ID_\" + id);\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tUpdates: { //Based off of Zerebos' PluginLibrary. https://rauenzi.github.io/BetterDiscordAddons/docs/PluginLibrary.js\r\n\r\n\t\trequestUpdateCheck: function(pluginName, url) {\r\n\r\n\t\t\trequire(\"request\")(url, (err, response, res) => {\r\n\r\n\t\t\t\tif (err) return console.error(pluginName, \"Failed to check for updates!\", err);\r\n\r\n\t\t\t\tlet latestVersion = res.match(/['\"][0-9]+\\.[0-9]+\\.[0-9]+['\"]/i);\r\n\t\t\t\tif (!latestVersion) return;\r\n\t\t\t\tlatestVersion = latestVersion.toString().replace(/['\"]/g, \"\").trim();\r\n\r\n\t\t\t\tif(!window.PluginUpdates.plugins[url]) return NeatoLib.Updates.hideNotice(pluginName);\r\n\r\n\t\t\t\tlet versionOld = window.PluginUpdates.plugins[url].version.split(\".\");\r\n\t\t\t\tlet versionNew = latestVersion.split(\".\");\r\n\r\n\t\t\t\tif(versionNew[0] > versionOld[0] || (versionNew[0] == versionOld[0] && versionNew[1] > versionOld[1]) || (versionNew[0] == versionOld[0] && versionNew[1] == versionOld[1] && versionNew[2] > versionOld[2]))\r\n\t\t\t\t\tNeatoLib.Updates.displayNotice(pluginName, url);\r\n\t\t\t\telse\r\n\t\t\t\t\tNeatoLib.Updates.hideNotice(pluginName);\r\n\r\n\t\t\t});\r\n\r\n\t\t},\r\n\r\n\t\tdisplayNotice: function(pluginName, url) {\r\n\r\n\t\t\tif (document.getElementById(\"pluginNotice\") == undefined) {\r\n\r\n\t\t\t\tlet classes = NeatoLib.Modules.get(\"noticeInfo\");\r\n\r\n\t\t\t\tdocument.getElementsByClassName(NeatoLib.getClass(\"app\"))[0].insertAdjacentHTML(\"afterbegin\", `<div class=\"${classes.notice} ${classes.noticeInfo}\" id=\"pluginNotice\"><div class=\"${classes.dismiss}\" id=\"pluginNoticeDismiss\"></div><span class=\"notice-message\">The following plugins have updates:</span>&nbsp;&nbsp;<strong id=\"outdatedPlugins\"></strong></div>`);\r\n\r\n\t\t\t\tdocument.getElementById(\"pluginNoticeDismiss\").addEventListener(\"click\", () => document.getElementById(\"pluginNotice\").outerHTML = \"\");\r\n\r\n\t\t\t}\r\n\r\n\t\t\tif (document.getElementById(pluginName + \"-notice\") == undefined) {\r\n\r\n\t\t\t\tlet element = document.createElement(\"span\"),\r\n\t\t\t\t\toutdated = document.getElementById(\"outdatedPlugins\");\r\n\r\n\t\t\t\telement.setAttribute(\"id\", pluginName + \"-notice\");\r\n\t\t\t\telement.innerText = pluginName;\r\n\r\n\t\t\t\telement.addEventListener(\"click\", () => NeatoLib.Updates.download(pluginName, url));\r\n\r\n\t\t\t\tif (outdated.getElementsByTagName(\"span\")[0] != undefined) outdated.insertAdjacentHTML(\"beforeend\", \"<span class='separator'>, </span>\");\r\n\t\t\t\toutdated.appendChild(element);\r\n\r\n\t\t\t}\r\n\r\n\t\t},\r\n\r\n\t\thideNotice: function(pluginName) {\r\n\r\n\t\t\tlet notice = document.getElementById(pluginName + \"-notice\");\r\n\r\n\t\t\tif (notice) {\r\n\t\t\t\tif (notice.nextSibling && notice.nextSibling.classList.contains(\"separator\")) notice.nextSibling.remove();\r\n\t\t\t\telse if (notice.previousSibling && notice.previousSibling.classList.contains(\"separator\")) notice.previousSibling.remove();\r\n\t\t\t\tnotice.remove();\r\n\t\t\t} else if (!document.querySelector(\"#outdatedPluings > span\") && document.querySelector(\"#pluginNotice > .btn-reload\") && document.querySelector(\"#pluginNotice .notice-message\")) document.querySelector(\"#pluginNotice .notice-message\").innerText = \"To finish updating you need to reload.\";\r\n\r\n\t\t},\r\n\r\n\t\tdownload: function(pluginName, url) {\r\n\r\n\t\t\tlet req = require(\"request\"),\r\n\t\t\t\tfs = require(\"fs\"),\r\n\t\t\t\tpath = require(\"path\");\r\n\r\n\t\t\treq(url, (err, response, res) => {\r\n\r\n\t\t\t\tif (err) return console.error(pluginName, \"Failed to download update!\", err);\r\n\r\n\t\t\t\tlet latestVersion = res.match(/['\"][0-9]+\\.[0-9]+\\.[0-9]+['\"]/i).toString().replace(/['\"]/g, \"\").trim(),\r\n\t\t\t\t\tfileName = url.split(\"/\");\r\n\t\t\t\tfileName = fileName[fileName.length - 1];\r\n\r\n\t\t\t\tlet file = path.join(NeatoLib.getPluginsFolderPath(), fileName);\r\n\r\n\t\t\t\tfs.writeFileSync(file, res);\r\n\r\n\t\t\t\tNeatoLib.showToast(`${pluginName} was updated to v${latestVersion}.`, \"success\");\r\n\r\n\t\t\t\t\tif (!window.PluginUpdates.downloaded) {\r\n\r\n\t\t\t\t\t\twindow.PluginUpdates.downloaded = [];\r\n\r\n\t\t\t\t\t\tlet button = document.createElement(\"button\");\r\n\r\n\t\t\t\t\t\tbutton.className = \"btn btn-reload btn-2o56RF button-1MICoQ size14-3iUx6q weightMedium-2iZe9B\";\r\n\t\t\t\t\t\tbutton.innerText = \"Reload\";\r\n\r\n\t\t\t\t\t\tbutton.addEventListener(\"click\", e => {\r\n\t\t\t\t\t\t\te.preventDefault();\r\n\t\t\t\t\t\t\twindow.location.reload(false);\r\n\t\t\t\t\t\t});\r\n\r\n\t\t\t\t\t\tlet tooltip = document.createElement(\"div\");\r\n\t\t\t\t\t\ttooltip.className = NeatoLib.getClass(\"tooltip\") + \" \" + NeatoLib.getClass(\"tooltip\", \"tooltipBottom\") + \" \" + NeatoLib.getClass(\"tooltip\", \"tooltipBlack\");\r\n\r\n\t\t\t\t\t\ttooltip.style.maxWidth = \"400px\";\r\n\r\n\t\t\t\t\t\tbutton.addEventListener(\"mouseenter\", () => {\r\n\t\t\t\t\t\t\tdocument.getElementsByClassName(NeatoLib.getClass(\"tooltip\"))[0].appendChild(tooltip);\r\n\t\t\t\t\t\t\ttooltip.innerText = window.PluginUpdates.downloaded.join(\", \");\r\n\t\t\t\t\t\t\ttooltip.style.left = button.getBoundingClientRect().left + (button.offsetWidth / 2) - (tooltip.offsetWidth / 2) + \"px\";\r\n\t\t\t\t\t\t\ttooltip.style.top = button.getBoundingClientRect().top + button.offsetHeight + \"px\";\r\n\t\t\t\t\t\t});\r\n\r\n\t\t\t\t\t\tbutton.addEventListener(\"mouseleave\", () => tooltip.remove());\r\n\r\n\t\t\t\t\t\tdocument.getElementById(\"pluginNotice\").appendChild(button);\r\n\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\twindow.PluginUpdates.plugins[url].version = latestVersion;\r\n\t\t\t\t\twindow.PluginUpdates.downloaded.push(pluginName);\r\n\t\t\t\t\tNeatoLib.Updates.hideNotice(pluginName);\r\n\r\n\t\t\t});\r\n\r\n\t\t},\r\n\r\n\t\tcheck: function(plugin, path) {\r\n\r\n\t\t\tlet url = path ? path : \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/\" + plugin.getName().split(\" \").join(\"\") + \".plugin.js\";\r\n\r\n\t\t\tif (typeof window.PluginUpdates == \"undefined\") window.PluginUpdates = {\r\n\t\t\t\tplugins: {}\r\n\t\t\t};\r\n\t\t\twindow.PluginUpdates.plugins[url] = {\r\n\t\t\t\tname: plugin.getName(),\r\n\t\t\t\traw: url,\r\n\t\t\t\tversion: plugin.getVersion()\r\n\t\t\t};\r\n\r\n\t\t\tNeatoLib.Updates.requestUpdateCheck(plugin.getName(), url);\r\n\r\n\t\t\tif (typeof window.PluginUpdates.interval == \"undefined\") {\r\n\t\t\t\twindow.PluginUpdates.interval = setInterval(() => {\r\n\t\t\t\t\twindow.PluginUpdates.checkAll();\r\n\t\t\t\t}, 7200000);\r\n\t\t\t}\r\n\r\n\t\t\tif (typeof window.PluginUpdates.checkAll == \"undefined\") {\r\n\t\t\t\twindow.PluginUpdates.checkAll = function() {\r\n\t\t\t\t\tfor (let key in this.plugins) {\r\n\t\t\t\t\t\tNeatoLib.Updates.requestUpdateCheck(this.plugins[key].name, this.plugins[key].raw);\r\n\t\t\t\t\t}\r\n\t\t\t\t};\r\n\t\t\t}\r\n\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tData: {\r\n\r\n\t\tsave: function(name, key, data) {\r\n\t\t\ttry {\r\n\t\t\t\tBdApi.setData(name, key, data);\r\n\t\t\t} catch (err) {\r\n\t\t\t\tconsole.warn(name, \"failed to save data.\", err);\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tload: function(name, key, fallback) {\r\n\t\t\ttry {\r\n\t\t\t\treturn $.extend(true, fallback ? fallback : {}, BdApi.getData(name, key));\r\n\t\t\t} catch (err) {\r\n\t\t\t\tconsole.warn(name, \"failed to load data.\", err);\r\n\t\t\t}\r\n\t\t\treturn {};\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tEvents: {\r\n\r\n\t\tonPluginLoaded: function(plugin) {\r\n\r\n\t\t\tNeatoLib.showToast(`[${plugin.getName()}]: Plugin loaded.`, \"success\");\r\n\t\t\tconsole.log(plugin.getName(), \"loaded.\");\r\n\r\n\t\t\tplugin.ready = true;\r\n\r\n\t\t\tif (plugin.forceLoadTimeout) {\r\n\t\t\t\tclearTimeout(plugin.forceLoadTimeout);\r\n\t\t\t\tplugin.forceLoadTimeout = null;\r\n\t\t\t\tdelete plugin.forceLoadTimeout;\r\n\t\t\t}\r\n\r\n\t\t},\r\n\r\n\t\tattach: function(eventType, event, options = {}) {\r\n\t\t\twindow.activeNeatoEvents.push({\r\n\t\t\t\tcallback: event,\r\n\t\t\t\ttype: eventType,\r\n\t\t\t\toptions: options\r\n\t\t\t});\r\n\t\t},\r\n\r\n\t\tdetach: function(eventType, event) {\r\n\t\t\tlet idx = window.activeNeatoEvents.findIndex(e => e.callback == event && e.type == eventType);\r\n\t\t\tif (idx != -1) window.activeNeatoEvents.splice(idx, 1);\r\n\t\t\telse console.warn(\"Event could not be found.\", event);\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tReactData: {\r\n\r\n\t\tget: function(element) {\r\n\r\n\t\t\tif (!(element instanceof Element)) return null;\r\n\r\n\t\t\treturn element[Object.keys(element).find(key => key.startsWith(\"__reactInternalInstance\"))];\r\n\r\n\t\t},\r\n\r\n\t\tgetEvents: function(element) {\r\n\r\n\t\t\tif (!(element instanceof Element)) return null;\r\n\r\n\t\t\treturn element[Object.keys(element).find(key => key.startsWith(\"__reactEventHandlers\"))];\r\n\r\n\t\t},\r\n\r\n\t\tgetOwner: function(element) {\r\n\r\n\t\t\tif (!(element instanceof Element)) return null;\r\n\r\n\t\t\tlet reactData = this.get(element);\r\n\r\n\t\t\tif (reactData == undefined) return null;\r\n\r\n\t\t\tfor (let c = reactData.return; !_.isNil(c); c = c.return) {\r\n\t\t\t\tif (_.isNil(c)) continue;\r\n\t\t\t\tlet owner = c.stateNode;\r\n\t\t\t\tif (!_.isNil(owner) && !(owner instanceof HTMLElement)) return owner;\r\n\t\t\t}\r\n\r\n\t\t},\r\n\r\n\t\tgetProps: function(element) {\r\n\r\n\t\t\tif (!(element instanceof Element)) return null;\r\n\r\n\t\t\tlet owner = this.getOwner(element);\r\n\r\n\t\t\treturn owner ? owner.props : null;\r\n\r\n\t\t},\r\n\r\n\t\tgetProp: function(element, propKey) {\r\n\r\n\t\t\tif (!(element instanceof Element)) return null;\r\n\r\n\t\t\tlet owner = this.getOwner(element);\r\n\r\n\t\t\tif (!owner || !owner.props) return null;\r\n\r\n\t\t\tlet split = propKey.split(\".\"),\r\n\t\t\t\tobj = owner.props;\r\n\r\n\t\t\tfor (let i = 0; i < split.length; i++) {\r\n\t\t\t\tobj = obj[split[i]];\r\n\t\t\t\tif (!obj) return null;\r\n\t\t\t}\r\n\r\n\t\t\treturn obj;\r\n\r\n\t\t},\r\n\r\n\t},\r\n\r\n\tContextMenu: {\r\n\r\n\t\tcreate: function(items, event, options = {}) {\r\n\r\n\t\t\tthis.close();\r\n\r\n\t\t\tlet menu = document.createElement(\"div\");\r\n\r\n\t\t\tmenu.classList.add(this.classes.contextMenu.split(\" \")[0], document.getElementsByClassName(\"theme-dark\")[0] != undefined ? \"theme-dark\" : \"theme-light\");\r\n\r\n\t\t\tfor (let i = 0; i < items.length; i++) menu.appendChild(items[i]);\r\n\r\n\t\t\tif (options.style) menu.style = options.style;\r\n\r\n\t\t\tmenu.style.zIndex = 10000;\r\n\t\t\tmenu.style.top = event.clientY + \"px\";\r\n\t\t\tmenu.style.left = event.clientX + \"px\";\r\n\t\t\tmenu.style.position = 'relative';\r\n\r\n\t\t\tlet close = () => {\r\n\t\t\t\tmenu.remove();\r\n\t\t\t\tdocument.removeEventListener(\"click\", onClick);\r\n\t\t\t\tdocument.removeEventListener(\"contextmenu\", onClick);\r\n\t\t\t\tdocument.removeEventListener(\"keyup\", onKeyUp);\r\n\t\t\t};\r\n\r\n\t\t\tlet onClick = e => {\r\n\t\t\t\tif (!menu.contains(e.target)) close();\r\n\t\t\t};\r\n\r\n\t\t\tlet onKeyUp = e => {\r\n\t\t\t\tif (e.key == \"Escape\") close();\r\n\t\t\t};\r\n\r\n\t\t\tdocument.addEventListener(\"click\", onClick);\r\n\t\t\tsetTimeout(() => {\r\n\t\t\t\tdocument.addEventListener(\"contextmenu\", onClick);\r\n\t\t\t}, 0);\r\n\t\t\tdocument.addEventListener(\"keyup\", onKeyUp);\r\n\r\n\t\t\tdocument.getElementById(\"app-mount\").appendChild(menu);\r\n\r\n\t\t\treturn menu;\r\n\r\n\t\t},\r\n\r\n\t\tcreateGroup: function(items, options = {}) {\r\n\r\n\t\t\tlet element = document.createElement(\"div\");\r\n\r\n\t\t\telement.classList.add(this.classes.itemGroup.split(\" \")[0]);\r\n\r\n\t\t\tfor (let i = 0; i < items.length; i++) element.appendChild(items[i]);\r\n\r\n\t\t\treturn element;\r\n\r\n\t\t},\r\n\r\n\t\tcreateItem: function(label, callback, options = {}) {\r\n\r\n\t\t\tlet element = document.createElement(\"div\");\r\n\r\n\t\t\telement.classList.add(this.classes.item.split(\" \")[0], this.classes.itemBase.split(\" \")[0], this.classes.clickable.split(\" \")[0]);\r\n\r\n\t\t\telement.innerHTML = \"<span>\" + label + \"</span>\";\r\n\r\n\t\t\tif (options.color) element.firstChild.style.color = options.color;\r\n\r\n\t\t\tif (options.hint) NeatoLib.Tooltip.attach(options.hint, element);\r\n\r\n\t\t\tif (options.description) element.innerHTML += `<div class=\"${this.classes.hint}\">${options.description}</div>`;\r\n\r\n\t\t\tif (callback) element.addEventListener(\"click\", callback);\r\n\r\n\t\t\treturn element;\r\n\r\n\t\t},\r\n\r\n\t\tcreateSubMenu: function(label, items, options = {}) {\r\n\r\n\t\t\tlet element = document.createElement(\"div\");\r\n\t\t\telement.classList.add(this.classes.itemSubMenu.split(\" \")[0], this.classes.itemBase.split(\" \")[0], this.classes.clickable.split(\" \")[0]);\r\n\r\n\t\t\tlet le = document.createElement(\"div\");\r\n\t\t\tle.classList.add(this.classes.label.split(\" \")[0]);\r\n\t\t\tle.innerText = label;\r\n\r\n\t\t\telement.appendChild(le);\r\n\t\t\t\r\n\t\t\telement.insertAdjacentHTML(\"beforeend\", `\r\n\t\t\t\t<svg class=\"caret-UIZBlm da-caret\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\r\n\t\t\t\t\t<path fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M16.59 8.59004L12 13.17L7.41 8.59004L6 10L12 16L18 10L16.59 8.59004Z\"></path>\r\n\t\t\t\t</svg>\r\n\t\t\t`);\r\n\r\n\t\t\tif (options.color) element.style.color = options.color;\r\n\t\t\tif (options.hint) element.innerHTML += `<div class=\"${this.classes.hint}\">${options.hint}</div>`;\r\n\t\t\tif (options.callback) \r\n\t\t\t\telement.addEventListener(\"click\", e => {\r\n\t\t\t\t\tif (e.target.parentElement == element) options.callback(e);\r\n\t\t\t\t});\r\n\r\n\t\t\tlet sm, hoveringOver;\r\n\r\n\t\t\telement.addEventListener(\"mouseenter\", () => {\r\n\t\t\t\tlet layer = document.createElement(\"div\");\r\n\t\t\t\tlayer.classList.add(NeatoLib.getClass(\"layer\"), \"neato-cl\");\r\n\r\n\t\t\t\tlayer.style.left = (element.getBoundingClientRect().width) + \"px\";\r\n\t\t\t\tlayer.style.top = (element.getBoundingClientRect().y - NeatoLib.DOM.searchForParentElementByClassName(element, NeatoLib.getClass(\"itemSubMenu\")).getBoundingClientRect().y) + \"px\";\r\n\r\n\t\t\t\tlet subMenu = document.createElement(\"div\");\r\n\t\t\t\tsubMenu.classList.add(this.classes.subMenuContext.split(\" \")[0]);\r\n\r\n\t\t\t\tlet menu = document.createElement(\"div\");\r\n\r\n\t\t\t\tmenu.classList.add(this.classes.contextMenu.split(\" \")[0]);\r\n\r\n\t\t\t\tfor (let i = 0; i < items.length; i++) menu.appendChild(items[i]);\r\n\r\n\t\t\t\tsubMenu.appendChild(menu);\r\n\r\n\t\t\t\tlayer.appendChild(subMenu);\r\n\r\n\t\t\t\tdocument.getElementsByClassName(NeatoLib.getClass(\"layerContainer\"))[0].appendChild(layer);\r\n\r\n\t\t\t\tsm = layer;\r\n\r\n\t\t\t\tsubMenu.addEventListener(\"mouseenter\", e => {\r\n\t\t\t\t\thoveringOver = subMenu;\r\n\t\t\t\t});\r\n\r\n\t\t\t\tsubMenu.addEventListener(\"mouseleave\", () => {\r\n\t\t\t\t\tsetTimeout(() => {\r\n\t\t\t\t\t\tif (hoveringOver == subMenu) hoveringOver = null;\r\n\t\t\t\t\t}, 0);\r\n\t\t\t\t});\r\n\r\n\t\t\t\telement.appendChild(layer);\r\n\t\t\t});\r\n\r\n\t\t\telement.addEventListener(\"mouseleave\", () => {\r\n\t\t\t\tif (sm && !hoveringOver) sm.remove();\r\n\t\t\t});\r\n\r\n\t\t\treturn element;\r\n\r\n\t\t},\r\n\r\n\t\tcreateToggle: function(label, value, callback, options = {}) {\r\n\r\n\t\t\tlet element = document.createElement(\"div\");\r\n\r\n\t\t\telement.classList.add(this.classes.item.split(\" \")[0], this.classes.itemBase.split(\" \")[0], this.classes.itemToggle.split(\" \")[0]);\r\n\r\n\t\t\telement.innerHTML = `\r\n\t\t\t\t<div class=\"${this.classes.label}\">${label}</div>\r\n\t\t\t\t<div class=\"checkbox\">\r\n\t\t\t\t\t<div class=\"checkbox-inner\">\r\n\t\t\t\t\t\t<input type=\"checkbox\">\r\n\t\t\t\t\t\t<span></span>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</div>\r\n\t\t\t`;\r\n\r\n\t\t\tlet checkbox = element.getElementsByTagName(\"input\")[0];\r\n\r\n\t\t\tcheckbox.checked = value;\r\n\r\n\t\t\tif (options.color) element.style.color = options.color;\r\n\r\n\t\t\tif (callback) element.addEventListener(\"click\", () => {\r\n\t\t\t\tcheckbox.checked = !checkbox.checked;\r\n\t\t\t\tcallback(checkbox.checked);\r\n\t\t\t});\r\n\r\n\t\t\treturn element;\r\n\r\n\t\t},\r\n\r\n\t\tget: function() {\r\n\t\t\treturn Array.from(document.getElementsByClassName(this.classes.contextMenu.split(\" \")[0])).filter(x => x.style.display != \"none\")[0];\r\n\t\t},\r\n\r\n\t\tclose: function() {\r\n\t\t\tlet cm = NeatoLib.ContextMenu.get();\r\n\t\t\tif (cm) cm.style.display = \"none\";\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tTooltip: {\r\n\r\n\t\tattach: function(content, element, options = {}) {\r\n\r\n\t\t\tif (element.tooltip != undefined) element.tooltip.detach();\r\n\r\n\t\t\tconst { side = \"top\", color, onShow, onHide, delay } = options;\r\n\r\n\t\t\tlet domChecker, delayTimeout;\r\n\r\n\t\t\telement.tooltip = {\r\n\t\t\t\ttooltip: undefined,\r\n\t\t\t\tnode: element,\r\n\t\t\t\tevent: {\r\n\t\t\t\t\tmouseenter: () => {\r\n\t\t\t\t\t\tlet tooltip = document.createElement(\"div\");\r\n\t\t\t\t\t\ttooltip.classList.add(NeatoLib.getClass(\"tooltip\"), NeatoLib.getClass(\"tooltip\", \"tooltip\" + side.substr(0,1).toUpperCase() + side.substr(1)), NeatoLib.getClass(\"tooltip\", \"tooltipBlack\"));\r\n                        tooltip.innerText = content;\r\n\t\t\t\t\t\ttooltip.style.pointerEvents = \"none\";\r\n\t\t\t\t\t\ttooltip.style.zIndex = 15000;\r\n                        if (color) tooltip.style.backgroundColor = color;\r\n                        let tooltipContainer = document.createElement(\"div\");\r\n                        tooltipContainer.classList.add(NeatoLib.getClass(\"layerContainer\", \"layer\"));\r\n                        document.getElementsByClassName(NeatoLib.getClass(\"layerContainer\"))[0].appendChild(tooltipContainer);\r\n                        tooltipContainer.appendChild(tooltip);\r\n                        tooltip.insertAdjacentHTML(\"afterbegin\", `<div class=\"${NeatoLib.getClass(\"tooltip\", \"tooltipPointer\")}\"></div>`);\r\n\t\t\t\t\t\telement.tooltip.tooltip = tooltipContainer;\r\n\t\t\t\t\t\tlet elementRect = element.getBoundingClientRect();\r\n\t\t\t\t\t\tswitch (side) {\r\n\t\t\t\t\t\t\tcase \"top\":\r\n\t\t\t\t\t\t\t\ttooltipContainer.style.top = (elementRect.top - tooltipContainer.offsetHeight) + \"px\";\r\n\t\t\t\t\t\t\t\ttooltipContainer.style.left = ((elementRect.left + (element.offsetWidth / 2)) - (tooltipContainer.offsetWidth / 2)) + \"px\";\r\n\t\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\t\tcase \"bottom\":\r\n\t\t\t\t\t\t\t\ttooltipContainer.style.top = (elementRect.top + element.offsetHeight) + \"px\";\r\n\t\t\t\t\t\t\t\ttooltipContainer.style.left = ((elementRect.left + (element.offsetWidth / 2)) - (tooltipContainer.offsetWidth / 2)) + \"px\";\r\n\t\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\t\tcase \"right\":\r\n\t\t\t\t\t\t\t\ttooltipContainer.style.left = (elementRect.left + element.offsetWidth) + \"px\";\r\n\t\t\t\t\t\t\t\ttooltip.style.top = ((elementRect.top + (element.offsetHeight / 2)) - (tooltipContainer.offsetHeight / 2)) + \"px\";\r\n\t\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\t\tcase \"left\":\r\n\t\t\t\t\t\t\t\ttooltipContainer.style.left = (elementRect.left - tooltipContainer.offsetWidth) + \"px\";\r\n\t\t\t\t\t\t\t\ttooltipContainer.style.top = ((elementRect.top + (element.offsetHeight / 2)) - (tooltipContainer.offsetHeight / 2)) + \"px\";\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (typeof onShow == \"function\") onShow(element.tooltip);\r\n\t\t\t\t\t\tdomChecker = setInterval(() => {\r\n\t\t\t\t\t\t\tif (!document.contains(element)) {\r\n\t\t\t\t\t\t\t\ttooltip.remove();\r\n\t\t\t\t\t\t\t\tclearInterval(domChecker);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}, 200);\r\n\t\t\t\t\t},\r\n\t\t\t\t\tmouseleave: () => {\r\n\t\t\t\t\t\tif (element.tooltip.tooltip) {\r\n\t\t\t\t\t\t\telement.tooltip.tooltip.remove();\r\n\t\t\t\t\t\t\tif (typeof onHide == \"function\") onHide(element.tooltip);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tclearInterval(domChecker);\r\n\t\t\t\t\t\tclearTimeout(delayTimeout);\r\n\t\t\t\t\t}\r\n\t\t\t\t},\r\n\t\t\t\tdetach: () => {\r\n\t\t\t\t\telement.tooltip.event.mouseleave();\r\n\t\t\t\t\telement.removeEventListener(\"mouseenter\", element.tooltip.event.mouseenter);\r\n\t\t\t\t\telement.removeEventListener(\"mouseleave\", element.tooltip.event.mouseleave);\r\n\t\t\t\t\tdelete element.tooltip;\r\n\t\t\t\t}\r\n\t\t\t};\r\n\r\n\t\t\tif (delay) {\r\n\t\t\t\tconst display = element.tooltip.event.mouseenter;\r\n\t\t\t\telement.tooltip.event.mouseenter = () => delayTimeout = setTimeout(display, delay);\r\n\t\t\t}\r\n\r\n\t\t\telement.addEventListener(\"mouseenter\", element.tooltip.event.mouseenter);\r\n\t\t\telement.addEventListener(\"mouseleave\", element.tooltip.event.mouseleave);\r\n\r\n\t\t\treturn element.tooltip;\r\n\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tColors: {\r\n\r\n\t\tDiscordDefaults: {\r\n\t\t\tred: \"#f04747\",\r\n\t\t\tblue: \"#7289da\",\r\n\t\t\tgreen: \"#43b581\"\r\n\t\t},\r\n\r\n\t\thexToRGB: function(hex, format = \"R, G, B\") {\r\n\t\t\treturn format.replace(\"R\", parseInt(hex.substring(1, 7).substring(0, 2), 16)).replace(\"G\", parseInt(hex.substring(1, 7).substring(2, 4), 16)).replace(\"B\", parseInt(parseInt(hex.substring(1, 7).substring(4, 6), 16)));\r\n\t\t},\r\n\r\n\t\tgetBrightness: function(color) {\r\n\t\t\tif (!color) return 0;\r\n\t\t\tlet c = Array.from(color.split(\",\"), n => parseInt(n.replace(/[^0-9]/g, \"\")));\r\n\t\t\treturn Math.sqrt(c[0] * c[0] * 0.241 + c[1] * c[1] * 0.691 + c[2] * c[2] * 0.068) / 255;\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tDOM: {\r\n\r\n\t\tsearchForParentElement: function(element, filter) {\r\n\t\t\tif (!element) return null;\r\n\r\n\t\t\tif (filter(element)) return element;\r\n\r\n\t\t\twhile (element && element.parentElement && element.parentElement != document) {\r\n\t\t\t\telement = element.parentElement;\r\n\r\n\t\t\t\tif (filter(element)) return element;\r\n\r\n\t\t\t\tfor (let i = 0; i < element.children.length; i++)\r\n\t\t\t\t\tif (filter(element.children[i])) return element.children[i];\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tsearchForParentElementByClassName: function(element, className) {\r\n\t\t\tif (!element) return null;\r\n\r\n\t\t\tif (element.classList.contains(className)) return element;\r\n\r\n\t\t\twhile (element && element.parentElement && element.parentElement != document) {\r\n\t\t\t\telement = element.parentElement;\r\n\r\n\t\t\t\tif (element.classList.contains(className)) return element;\r\n\r\n\t\t\t\tfor (let i = 0; i < element.children.length; i++)\r\n\t\t\t\t\tif (element.children[i].classList.contains(className)) return element.children[i];\r\n\t\t\t}\r\n\r\n\t\t\treturn null;\r\n\t\t},\r\n\r\n\t\tcreateElement: function(values, options = {}) {\r\n\r\n\t\t\tlet element = document.createElement(options.type || \"div\");\r\n\r\n\t\t\tfor (let key in values) element[key] = values[key];\r\n\r\n\t\t\treturn element;\r\n\r\n\t\t},\r\n\r\n\t\tsortChildren: function(element, sortFunc) {\r\n\r\n\t\t\tlet children = Array.from(element.children).sort(sortFunc || function(a, b) {\r\n\t\t\t\tlet x = a.innerText.toLowerCase(),\r\n\t\t\t\t\ty = b.innerText.toLowerCase();\r\n\t\t\t\tif (x < y) return -1;\r\n\t\t\t\telse if (x > y) return 1;\r\n\t\t\t\treturn 0;\r\n\t\t\t});\r\n\r\n\t\t\tfor (let i = 0; i < children.length; i++) element.appendChild(children[i]);\r\n\r\n\t\t},\r\n\r\n\t\tinsertHTMLBefore: function(element, html) {\r\n\r\n\t\t\tlet e = document.createElement(\"div\");\r\n\r\n\t\t\telement.parentElement.insertBefore(e, element);\r\n\r\n\t\t\te.outerHTML = html;\r\n\r\n\t\t\treturn e;\r\n\r\n\t\t},\r\n\r\n\t\tinsertAtIndex: function(idx, element, parent) {\r\n\t\t\tif (idx >= parent.children.length) parent.appendChild(element);\r\n\t\t\telse parent.insertBefore(element, parent.children[idx]);\r\n\t\t},\r\n\r\n\t\tinsertHTMLAtIndex: function(idx, html, parent) {\r\n\r\n\t\t\tlet e = document.createElement(\"div\");\r\n\r\n\t\t\tthis.insertAtIndex(idx, e, parent);\r\n\r\n\t\t\te.outerHTML = html;\r\n\r\n\t\t\treturn e;\r\n\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tThread: {\r\n\r\n\t\tsleep: function(timeout = 0) {\r\n\t\t\treturn new Promise(p => setTimeout(p, timeout));\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tdownloadFile: async function(url, path, filename, onCompleted) {\r\n\t\tfilename = filename.split(/[:|?|%]/)[0];\r\n\r\n\t\tconst def = [url, path, filename, onCompleted];\r\n\r\n\t\tlet progressToast, id = path.replace(/[^a-z0-9]/g, \"\");\r\n\r\n\t\tconst error = function(err) {\r\n\t\t\tif (!err) return;\r\n\t\t\tif (id) NeatoLib.showProgressToast(id, \"Error saving \" + filename + \". Click to retry.\", 1, 1, {\r\n\t\t\t\t\tcolor: NeatoLib.Colors.DiscordDefaults.red,\r\n\t\t\t\t\tprogressText: \"ERROR\",\r\n\t\t\t\t\ttimeout: 5000\r\n\t\t\t\t})\r\n\t\t\t\t.addEventListener(\"click\", function(e) {\r\n\t\t\t\t\tNeatoLib.downloadFile(...def);\r\n\t\t\t\t\te.currentTarget.close();\r\n\t\t\t\t});\r\n\t\t\tthrow err;\r\n\t\t};\r\n\r\n\t\ttry {\r\n\t\t\tconst fs = require(\"fs\"),\r\n\t\t\t\tprotocol = require(url.match(/[http&https]+/)[0]);\r\n\r\n\t\t\tif (!path.endsWith(\"/\")) path += \"/\";\r\n\r\n\t\t\tpath = path.split(\"?\")[0] + filename;\r\n\r\n\t\t\tif (fs.existsSync(path)) {\r\n\t\t\t\tNeatoLib.showToast(`\"${filename}\" already exists, random characters will be appended to the file name!`, \"error\");\r\n\t\t\t\tconst fileExtension = \".\" + path.split(\".\")[path.split(\".\").length - 1];\r\n\t\t\t\tpath = path.split(fileExtension).join(`${Math.random().toString(36).substring(10)}${fileExtension}`);\r\n\t\t\t}\r\n\r\n\t\t\tid = path.replace(/[^a-z0-9]/g, \"\");\r\n\r\n\t\t\tconst startingToast = NeatoLib.showToast(`[<span style=\"color:${NeatoLib.Colors.DiscordDefaults.blue}\">${filename}</span>]: Preparing download...`);\r\n\r\n\t\t\tconst request = protocol.get(url, function(req) {\r\n\t\t\t\tlet data = [],\r\n\t\t\t\t\tprogress = 0,\r\n\t\t\t\t\tlength;\r\n\r\n\t\t\t\tstartingToast.close();\r\n\t\t\t\tif (length = req.headers[\"content-length\"]) progressToast = NeatoLib.showProgressToast(id, \"Downloading \" + filename + \"...\", progress, length, {\r\n\t\t\t\t\ttimeout: 10000\r\n\t\t\t\t});\r\n\t\t\t\telse progressToast = NeatoLib.showProgressToast(id, \"Downloading \" + filename + \"...\", 1, 1, {\r\n\t\t\t\t\tcolor: NeatoLib.Colors.DiscordDefaults.blue,\r\n\t\t\t\t\tprogressText: \"File size unknown\",\r\n\t\t\t\t\ttimeout: 10000\r\n\t\t\t\t});\r\n\r\n\t\t\t\treq.on(\"data\", function(dataChunk) {\r\n\t\t\t\t\tdata.push(dataChunk);\r\n\t\t\t\t\tprogress += dataChunk.length;\r\n\t\t\t\t\tif (length) progressToast = NeatoLib.showProgressToast(id, \"Downloading \" + filename + \"...\", progress, length, {\r\n\t\t\t\t\t\ttimeout: 10000\r\n\t\t\t\t\t});\r\n\t\t\t\t});\r\n\r\n\t\t\t\treq.on(\"end\", function() {\r\n\t\t\t\t\tif (data.length == 0) return error(\"URL is invalid\");\r\n\t\t\t\t\tprogressToast = NeatoLib.showProgressToast(id, \"Finished downloading \" + filename, progress, length, {\r\n\t\t\t\t\t\ttimeout: 3000\r\n\t\t\t\t\t});\r\n\t\t\t\t\tprogressToast.addEventListener(\"click\", () => {\r\n\t\t\t\t\t\twindow.open(\"file:///\" + path.substring(0, path.lastIndexOf(\"/\")));\r\n\t\t\t\t\t});\r\n\t\t\t\t\tfs.writeFile(path, Buffer.concat(data), error);\r\n\t\t\t\t\tif (onCompleted) onCompleted(path, url);\r\n\t\t\t\t});\r\n\t\t\t});\r\n\r\n\t\t\trequest.on(\"error\", error);\r\n\t\t\trequest.end();\r\n\t\t} catch (err) {\r\n\t\t\terror(err);\r\n\t\t}\r\n\t},\r\n\r\n\trequestFile: function(url, name = \"unknown.png\", onCompleted) {\r\n\t\tconst http = require(\"https\");\r\n\r\n\t\tconst request = http.request(url, x => {\r\n\t\t\tconst data = [];\r\n\r\n\t\t\tx.on(\"data\", d => data.push(d));\r\n\r\n\t\t\tx.on(\"end\", () => {\r\n\t\t\t\tif (onCompleted != undefined) onCompleted(new File([Buffer.concat(data)], name));\r\n\t\t\t});\r\n\t\t});\r\n\r\n\t\trequest.on(\"error\", error => {\r\n\t\t\tNeatoLib.showToast(\"Failed to request file! Error: \" + error.message, \"error\");\r\n\t\t});\r\n\r\n\t\trequest.end();\r\n\t},\r\n\r\n\tgetClass: function(moduleName, className = moduleName, index = 0) {\r\n\t\tlet temp = NeatoLib.Modules.get(moduleName);\r\n\t\tif(!temp || typeof temp[className] !== \"string\") return;\r\n\t\tif(!temp[className]) return temp[moduleName].split(\" \")[index];\r\n\t\treturn temp[className].split(\" \")[index];\r\n\t},\r\n\r\n\tgetClasses: function(classes, returnAll = true) {\r\n\r\n\t\tvar found = {};\r\n\r\n\t\tfor (var i = 0; i < classes.length; i++) {\r\n\r\n\t\t\tvar module = NeatoLib.Modules.get(classes[i]);\r\n\r\n\t\t\tif (module != undefined) {\r\n\r\n\t\t\t\tfor (var ii in module) {\r\n\r\n\t\t\t\t\tif (!returnAll && classes[i] != ii) continue;\r\n\r\n\t\t\t\t\tfound[ii] = module[ii];\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t}\r\n\r\n\t\treturn found;\r\n\r\n\t},\r\n\r\n\tgetSelectedGuild: function() {\r\n\t\treturn NeatoLib.Modules.get(\"getGuild\").getGuild(NeatoLib.Modules.get(\"getLastSelectedGuildId\").getGuildId());\r\n\t},\r\n\r\n\tgetSelectedGuildId: function() {\r\n\t\treturn NeatoLib.Modules.get(\"getLastSelectedGuildId\").getGuildId();\r\n\t},\r\n\r\n\tgetSelectedTextChannel: function() {\r\n\t\treturn NeatoLib.Modules.Stores.Channels.getChannel(NeatoLib.Modules.Stores.SelectedChannels.getChannelId());\r\n\t},\r\n\r\n\tgetSelectedVoiceChannel: function() {\r\n\t\treturn NeatoLib.Modules.Stores.Channels.getChannel(NeatoLib.Modules.Stores.SelectedChannels.getVoiceChannelId());\r\n\t},\r\n\r\n\tmonkeyPatchInternal: function(module, funcName, newFunc) {\r\n\r\n\t\tconst unpatched = module[funcName];\r\n\r\n\t\tmodule[funcName] = function() {\r\n\t\t\treturn newFunc({\r\n\t\t\t\tmodule: this,\r\n\t\t\t\targs: arguments,\r\n\t\t\t\tunpatch: () => module[funcName] = unpatched,\r\n\t\t\t\tunpatched: unpatched,\r\n\t\t\t\tcallDefault: () => unpatched.apply(this, arguments),\r\n\t\t\t\tcallDefaultWithArgs: function() {\r\n\t\t\t\t\tthis.unpatched.apply(this.module, arguments);\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t};\r\n\r\n\t\treturn module[funcName].unpatch = () => module[funcName] = unpatched;\r\n\r\n\t},\r\n\r\n\tpatchInternalFunction: function(functionName, newFunction, pluginName, replace = false) {\r\n\r\n\t\tlet module = NeatoLib.Modules.get(functionName);\r\n\r\n\t\tif (module == undefined) return console.warn(\"No module with function '\" + functionName + \"' found!\");\r\n\r\n\t\tif (module[functionName + \"_unpatched_\" + pluginName] != undefined) return console.warn(\"This function is already patched by this plugin!\");\r\n\r\n\t\tmodule[functionName + \"_unpatched_\" + pluginName] = module[functionName];\r\n\r\n\t\tmodule[functionName] = replace ? newFunction : function() {\r\n\t\t\tnewFunction.apply(module, arguments);\r\n\t\t\treturn module[functionName + \"_unpatched_\" + pluginName].apply(module, arguments);\r\n\t\t};\r\n\r\n\t},\r\n\r\n\tunpatchInternalFunction: function(functionName, pluginName) {\r\n\r\n\t\tlet module = NeatoLib.Modules.get(functionName);\r\n\r\n\t\tif (module == undefined) {\r\n\r\n\t\t\tconsole.log(\"There are no modules that contain this function!\");\r\n\r\n\t\t\treturn;\r\n\r\n\t\t}\r\n\r\n\t\tif (module[functionName + \"_unpatched_\" + pluginName] == undefined) {\r\n\r\n\t\t\tconsole.log(\"This function is not patched!\");\r\n\r\n\t\t\treturn;\r\n\r\n\t\t}\r\n\r\n\t\tmodule[functionName] = module[functionName + \"_unpatched_\" + pluginName];\r\n\t\tdelete module[functionName + \"_unpatched_\" + pluginName];\r\n\r\n\t},\r\n\r\n\tinternalFunctionIsPatched: function(functionName, pluginName) {\r\n\r\n\t\tlet module = NeatoLib.Modules.get(functionName);\r\n\r\n\t\tif (module == undefined) {\r\n\r\n\t\t\tconsole.log(\"There are no modules that contain this function!\");\r\n\r\n\t\t\treturn;\r\n\r\n\t\t}\r\n\r\n\t\treturn module[functionName + \"_unpatched_\" + pluginName] != undefined;\r\n\r\n\t},\r\n\r\n\tpatchInternalFunctions: function(functionNames, newFunction, pluginName, replace = false) {\r\n\t\tfor (let i = 0; i < functionNames.length; i++) NeatoLib.patchInternalFunction(functionNames[i], newFunction, pluginName, replace);\r\n\t},\r\n\r\n\tunpatchInternalFunctions: function(functionNames, pluginName) {\r\n\t\tfor (let i = 0; i < functionNames.length; i++) NeatoLib.unpatchInternalFunction(functionNames[i], pluginName);\r\n\t},\r\n\r\n\tgetLocalUser: function() {\r\n\t\treturn NeatoLib.Modules.Stores.Users.getCurrentUser();\r\n\t},\r\n\r\n\tgetLocalStatus: function() {\r\n\t\treturn NeatoLib.Modules.get(\"getApplicationActivity\").getStatus(NeatoLib.getLocalUser().id);\r\n\t},\r\n\r\n\tbrowseForFile: function(callback, options = {}) {\r\n\r\n\t\tlet fileBrowser = document.createElement(\"input\");\r\n\r\n\t\tfileBrowser.type = \"file\";\r\n\t\tfileBrowser.style.display = \"none\";\r\n\r\n\t\tif (options.directory == true) {\r\n\t\t\tfileBrowser.setAttribute(\"webkitdirectory\", true);\r\n\t\t\tfileBrowser.setAttribute(\"directory\", true);\r\n\t\t}\r\n\r\n\t\tif (options.multiple == true) fileBrowser.setAttribute(\"multiple\", true);\r\n\r\n\t\tdocument.head.appendChild(fileBrowser);\r\n\r\n\t\tfileBrowser.click();\r\n\r\n\t\tfileBrowser.addEventListener(\"change\", () => {\r\n\r\n\t\t\tcallback(options.multiple == true ? fileBrowser.files : fileBrowser.files[0]);\r\n\r\n\t\t\tfileBrowser.outerHTML = \"\";\r\n\r\n\t\t});\r\n\r\n\t},\r\n\r\n\tshuffleArray: function(array) {\r\n\r\n\t\tlet idx = array.length,\r\n\t\t\ttemp, random;\r\n\r\n\t\twhile (idx != 0) {\r\n\t\t\trandom = Math.floor(Math.random() * idx);\r\n\t\t\tidx--;\r\n\t\t\ttemp = array[idx];\r\n\t\t\tarray[idx] = array[random];\r\n\t\t\tarray[random] = temp;\r\n\t\t}\r\n\r\n\t\treturn array;\r\n\r\n\t},\r\n\r\n\tgetPluginsFolderPath: function() {\r\n\r\n\t\tlet proc = require(\"process\"),\r\n\t\t\tpath = require(\"path\");\r\n\r\n\t\tswitch (proc.platform) {\r\n\t\t\tcase \"win32\":\r\n\t\t\t\treturn path.resolve(proc.env.appdata, \"BetterDiscord/plugins/\");\r\n\t\t\tcase \"darwin\":\r\n\t\t\t\treturn path.resolve(proc.env.HOME, \"Library/Preferences/\", \"BetterDiscord/plugins/\");\r\n\t\t\tdefault:\r\n\t\t\t\treturn path.resolve(proc.env.HOME, \".config/\", \"BetterDiscord/plugins/\");\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tgetThemesFolderPath: function() {\r\n\r\n\t\tlet proc = require(\"process\"),\r\n\t\t\tpath = require(\"path\");\r\n\r\n\t\tswitch (proc.platform) {\r\n\t\t\tcase \"win32\":\r\n\t\t\t\treturn path.resolve(proc.env.appdata, \"BetterDiscord/themes/\");\r\n\t\t\tcase \"darwin\":\r\n\t\t\t\treturn path.resolve(proc.env.HOME, \"Library/Preferences/\", \"BetterDiscord/themes/\");\r\n\t\t\tdefault:\r\n\t\t\t\treturn path.resolve(proc.env.HOME, \".config/\", \"BetterDiscord/themes/\");\r\n\t\t}\r\n\r\n\t},\r\n\r\n\ttryCreateToastContainer: function() {\r\n\r\n\t\tif (!document.getElementsByClassName(\"toasts\").length) {\r\n\r\n\t\t\tconst container = document.getElementsByClassName(NeatoLib.Modules.get(['sidebar', 'guilds']).guilds.split(\" \")[0])[0].nextSibling,\r\n\t\t\t\tmemberlist = container.getElementsByClassName(NeatoLib.Modules.get(\"membersWrap\").membersWrap)[0],\r\n\t\t\t\tform = container ? container.getElementsByTagName(\"form\")[0] : undefined,\r\n\t\t\t\tleft = container ? container.getBoundingClientRect().left : 310,\r\n\t\t\t\tright = memberlist ? memberlist.getBoundingClientRect().left : 0,\r\n\t\t\t\twidth = right ? right - container.getBoundingClientRect().left : container.offsetWidth,\r\n\t\t\t\tbottom = form ? form.offsetHeight : 80,\r\n\t\t\t\ttoastWrapper = document.createElement(\"div\");\r\n\r\n\t\t\ttoastWrapper.classList.add(\"toasts\");\r\n\r\n\t\t\ttoastWrapper.style.left = left + \"px\";\r\n\t\t\ttoastWrapper.style.width = width + \"px\";\r\n\t\t\ttoastWrapper.style.bottom = bottom + \"px\";\r\n\r\n\t\t\tdocument.getElementsByClassName(NeatoLib.getClass(\"app\"))[0].appendChild(toastWrapper);\r\n\r\n\t\t}\r\n\r\n\t},\r\n\r\n\tshowToast: function(text, type, options = {}) {\r\n\r\n\t\tthis.tryCreateToastContainer();\r\n\r\n\t\tconst toast = document.createElement(\"div\");\r\n\r\n\t\ttoast.classList.add(\"toast\");\r\n\t\tif (typeof type == \"string\") toast.classList.add(\"toast-\" + type);\r\n\t\tif (options.icon) toast.classList.add(\"icon\");\r\n\t\tif (options.color) toast.style.background = options.color;\r\n\r\n\t\tconst destroy = toast.close = function() {\r\n\t\t\ttoast.classList.add(\"closing\");\r\n\t\t\tsetTimeout(function() {\r\n\t\t\t\ttoast.remove();\r\n\t\t\t\tif (!document.getElementsByClassName(\"toast\").length) document.getElementsByClassName(\"toasts\")[0].remove();\r\n\t\t\t}, 300);\r\n\t\t};\r\n\r\n\t\tif (options.onClick) toast.addEventListener(\"click\", options.onClick);\r\n\t\tif (options.destroyOnClick) toast.addEventListener(\"click\", destroy);\r\n\r\n\t\ttoast.innerHTML = text;\r\n\r\n\t\tdocument.getElementsByClassName(\"toasts\")[0].appendChild(toast);\r\n\r\n\t\tsetTimeout(destroy, options.timeout || 3000);\r\n\r\n\t\treturn toast;\r\n\r\n\t},\r\n\r\n\tshowProgressToast: function(id, label, val, max, options = {}) {\r\n\r\n\t\tlet bar;\r\n\r\n\t\tconst destroy = function() {\r\n\t\t\tclearTimeout(bar.destroyTimeout);\r\n\t\t\tbar.classList.add(\"closing\");\r\n\t\t\tsetTimeout(function() {\r\n\t\t\t\tbar.remove();\r\n\t\t\t\tif (!document.getElementsByClassName(\"toast\").length) document.getElementsByClassName(\"toasts\")[0].remove();\r\n\t\t\t}, 300);\r\n\t\t};\r\n\r\n\t\tconst updateBar = function() {\r\n\r\n\t\t\tbar.getElementsByClassName(\"toast-prog-bar-label\")[0].innerHTML = label;\r\n\r\n\t\t\tconst prog = bar.getElementsByClassName(\"toast-prog-bar-progress\")[0];\r\n\t\t\tif (options.progressText) prog.innerHTML = options.progressText;\r\n\t\t\telse if (!isNaN(parseInt((val / max) * 100))) prog.innerText = parseInt((val / max) * 100) + \"%\";\r\n\t\t\telse prog.innerText = \"ERROR\";\r\n\t\t\tprog.style.width = ((val / max) * 500) + \"px\";\r\n\r\n\t\t\tif (options.color) prog.style.background = options.color;\r\n\t\t\telse prog.style.background = NeatoLib.Colors.DiscordDefaults.green;\r\n\r\n\t\t\tclearTimeout(bar.destroyTimeout);\r\n\t\t\tbar.destroyTimeout = setTimeout(destroy, options.timeout || 1500);\r\n\r\n\t\t};\r\n\r\n\t\tif (bar = document.getElementById(\"neato-toast-prog-bar-\" + id)) {\r\n\t\t\tupdateBar();\r\n\t\t\treturn bar;\r\n\t\t}\r\n\r\n\t\tthis.tryCreateToastContainer();\r\n\r\n\t\tdocument.getElementsByClassName(\"toasts\")[0].insertAdjacentHTML(\"beforeend\",\r\n\t\t\t`<div id=\"neato-toast-prog-bar-${id}\" class=\"toast has-prog-bar\">\r\n\t\t\t<div class=\"toast-prog-bar-label\">${label}</div>\r\n\t\t\t<div class=\"toast-prog-bar-background\">\r\n\t\t\t\t<div class=\"toast-prog-bar-progress\" style=\"width:0\">0%</div>\r\n\t\t\t</div>\r\n\t\t</div>`);\r\n\r\n\t\tbar = document.getElementById(\"neato-toast-prog-bar-\" + id);\r\n\t\tbar.close = destroy;\r\n\r\n\t\tif (options.backgroundColor) bar.style.backgroundColor = options.backgroundColor;\r\n\r\n\t\tif (options.onClick) bar.addEventListener(\"click\", options.onClick);\r\n\t\tif (options.destroyOnClick) bar.addEventListener(\"click\", destroy);\r\n\r\n\t\tupdateBar();\r\n\r\n\t\treturn bar;\r\n\r\n\t},\r\n\r\n\tinjectCSS: function(css) {\r\n\r\n\t\tlet element = document.createElement(\"style\");\r\n\r\n\t\telement.type = \"text/css\";\r\n\r\n\t\telement.innerText = css;\r\n\r\n\t\tdocument.head.appendChild(element);\r\n\r\n\t\treturn {\r\n\t\t\telement: element,\r\n\t\t\tgetStyle: selector => {\r\n\t\t\t\tlet selectorIDX = css.indexOf(selector);\r\n\t\t\t\tif (selectorIDX == -1) return null;\r\n\t\t\t\treturn css.substring(selectorIDX, selectorIDX + css.substring(selectorIDX, css.length).indexOf(\"}\")).split(\"{\")[1].trim();\r\n\t\t\t},\r\n\t\t\tappend: toAppend => {\r\n\t\t\t\tcss += toAppend;\r\n\t\t\t\telement.innerText = css;\r\n\t\t\t},\r\n\t\t\tdestroy: () => {\r\n\t\t\t\telement.remove();\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t},\r\n\r\n\tgetSnowflakeCreationDate: function(id) {\r\n\t\tconst epoch = 1420070400000;\r\n\r\n\t\tconst toBinary = sf => {\r\n\t\t\tlet binary = \"\",\r\n\t\t\t\thigh = parseInt(sf.slice(0, -10)) || 0,\r\n\t\t\t\tlow = parseInt(sf.slice(-10));\r\n\r\n\t\t\twhile (low > 0 || high > 0) {\r\n\t\t\t\tbinary = String(low & 1) + binary;\r\n\r\n\t\t\t\tlow = Math.floor(low / 2);\r\n\r\n\t\t\t\tif (high > 0) {\r\n\t\t\t\t\tlow += 5000000000 * (high % 2);\r\n\t\t\t\t\thigh = Math.floor(high / 2);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\treturn binary;\r\n\t\t};\r\n\r\n\t\treturn new Date(parseInt(toBinary(id).padStart(64).substring(0, 42), 2) + epoch);\r\n\t},\r\n\r\n\tsetTimeout: function(func, delay) {\r\n\t\ttry {\r\n\t\t\tconst setTimeout = NeatoLib.Modules.get(\"_wrappedBuiltIns\")._wrappedBuiltIns.find(([obj, name, func]) => obj == global && name == \"setTimeout\")[2];\r\n\t\t\treturn setTimeout(func, delay);\r\n\t\t} finally {\r\n\t\t\treturn global.setTimeout(func, delay);\r\n\t\t}\r\n\t}\r\n\r\n};\r\n\r\nvar Metalloriff = NeatoLib;\r\n\r\nvar mesquite = BdApi.Plugins.getAll().map(x => x.getName());\r\n\r\nfor (let pluginName of mesquite) {\r\n\tif (typeof BdApi.Plugins.get(pluginName).onLibLoaded == \"function\" && !BdApi.Plugins.get(pluginName).ready) {\r\n\t\tsetTimeout(() => {\r\n\t\t\tif (BdApi.Plugins.get(pluginName).onLibLoaded.toString().indexOf(\"NeatoLib.Events.onPluginLoaded\") == -1) NeatoLib.Events.onPluginLoaded(BdApi.Plugins.get(pluginName));\r\n\t\t}, 100);\r\n\t}\r\n}\r\n\r\nif (window.activeNeatoEvents == undefined) window.activeNeatoEvents = [];\r\n\r\nif (window.neatoObserver) window.neatoObserver.disconnect();\r\nwindow.neatoObserver = new MutationObserver(mutations => {\r\n\r\n\tlet call = (type, ...args) => {\r\n\t\tfor (let i = 0; i < window.activeNeatoEvents.length; i++) {\r\n\t\t\tif (window.activeNeatoEvents[i].type == type) {\r\n\t\t\t\tif (typeof(window.activeNeatoEvents[i].callback) == \"function\") {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\twindow.activeNeatoEvents[i].callback(...args);\r\n\t\t\t\t\t} catch (err) {\r\n\t\t\t\t\t\tconsole.warn(\"Unable to call \" + window.activeNeatoEvents[i].type + \" event.\", window.activeNeatoEvents[i].callback, err);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t};\r\n\r\n\tfor (let i = 0; i < mutations.length; i++) {\r\n\r\n\t\tif (mutations[i].removedNodes[0] != undefined && mutations[i].removedNodes[0] instanceof Element) {\r\n\t\t\tif (mutations[i].removedNodes[0].id == \"friends\") {\r\n\t\t\t\tcall(\"switch\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tlet added = mutations[i].addedNodes[0];\r\n\r\n\t\tif (added == undefined || !(added instanceof Element)) continue;\r\n\r\n\t\tif (added.classList.contains(NeatoLib.Events.classes.layer)) call(\"settings\");\r\n\r\n\t\tif (added.id == \"friends\") call(\"switch\");\r\n\r\n\t\tif (added.classList.contains(NeatoLib.getClass(\"messagesWrapper\")) || added.getElementsByClassName(NeatoLib.getClass(\"messagesWrapper\"))[0] != undefined) call(\"switch\");\r\n\r\n\t\tif ((added.classList.contains(NeatoLib.getClass(\"message\")) && !added.className.includes(\"sending\")) || added.classList.contains(NeatoLib.getClass(\"cozyMessage\"))) call(\"message\");\r\n\r\n\t\tif (window.neatoObserver.addedTextarea != (window.neatoObserver.addedTextarea = added.getElementsByClassName(NeatoLib.getClass(\"textArea\"))[0]) && window.neatoObserver.addedTextarea) call(\"chatbox\", window.neatoObserver.addedTextarea);\r\n\r\n\t}\r\n\r\n});\r\nwindow.neatoObserver.observe(document, {\r\n\tchildList: true,\r\n\tsubtree: true\r\n});\r\n\r\nNeatoLib.Modules.Stores = {\r\n\tGuilds: NeatoLib.Modules.get([\"getGuild\", \"getGuilds\"]),\r\n\tChannels: NeatoLib.Modules.get([\"getChannel\", \"getChannels\"]),\r\n\tSelectedChannels: NeatoLib.Modules.get([\"getChannelId\", \"getVoiceChannelId\"]),\r\n\tUsers: NeatoLib.Modules.get([\"getUser\", \"getUsers\"]),\r\n\tMembers: NeatoLib.Modules.get([\"getMember\", \"getMembers\"]),\r\n};\r\n\r\nNeatoLib.Events.classes = {\r\n\tlayer: NeatoLib.Modules.get(\"layer\").layer.split(\" \")[0],\r\n\tsocialLinks: NeatoLib.Modules.get(\"socialLinks\").socialLinks.split(\" \")[0]\r\n};\r\n\r\nNeatoLib.ContextMenu.classes = NeatoLib.Modules.get(\"contextMenu\");\r\n\r\nNeatoLib.getSelectedServer = NeatoLib.getSelectedGuild;\r\nNeatoLib.getSelectedServerId = NeatoLib.getSelectedGuildId;\r\n\r\nif (window.neatoStyles) window.neatoStyles.destroy();\r\nwindow.neatoStyles = NeatoLib.injectCSS(`\r\n\r\n\t.toast.has-prog-bar {\r\n\t\tpadding-top: 30px;\r\n\t}\r\n\r\n\t.toast-prog-bar-label {\r\n\t\tposition: absolute;\r\n\t\ttop: 8px;\r\n\t}\r\n\r\n\t.toast-prog-bar-background {\r\n\t\theight: 25px;\r\n\t\twidth: 500px;\r\n\t\tborder-radius: 5px;\r\n\t\tbackground: rgba(0,0,0,0.3);\r\n\t\ttext-align: center;\r\n\t\tline-height: 25px;\r\n\t}\r\n\r\n\t.toast-prog-bar-progress {\r\n\t\theight: 25px;\r\n\t\tborder-radius: 5px;\r\n\t\tbackground: green;\r\n\t\ttext-align: center;\r\n\t\tline-height: 25px;\r\n\t\tposition: relative;\r\n\t\ttop: 0;\r\n\t\tpadding: 0;\r\n\t\ttransition: all 0.3s;\r\n\t\toverflow: hidden;\r\n\t}\r\n\r\n\t/* Below is CSS from Zerebos' PluginLibrary. https://rauenzi.github.io/BetterDiscordAddons/docs/PluginLibrary.js */\r\n\r\n\t#pluginNotice {-webkit-app-region: drag;border-radius:0;} #outdatedPlugins {font-weight:700;} #outdatedPlugins>span {-webkit-app-region: no-drag;color:#fff;cursor:pointer;} #outdatedPlugins>span:hover {text-decoration:underline;}\r\n\r\n\t.toasts{position:fixed;display:flex;top:0;flex-direction:column;align-items:center;justify-content:flex-end;pointer-events:none;z-index:4000}@keyframes toast-up{from{transform:translateY(0);opacity:0}}.toast{animation:toast-up .3s ease;transform:translateY(-10px);background:#36393F;padding:10px;border-radius:5px;box-shadow:0 0 0 1px rgba(32,34,37,.6),0 2px 10px 0 rgba(0,0,0,.2);font-weight:500;color:#fff;user-select:text;font-size:14px;opacity:1;margin-top:10px}@keyframes toast-down{to{transform:translateY(0);opacity:0}}.toast.closing{animation:toast-down .2s ease;animation-fill-mode:forwards;opacity:1;transform:translateY(-10px)}.toast.icon{padding-left:30px;background-size:20px 20px;background-repeat:no-repeat;background-position:6px 50%}.toast.toast-info{background-color:#4a90e2}.toast.toast-info.icon{background-image:url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPiAgICA8cGF0aCBkPSJNMTIgMkM2LjQ4IDIgMiA2LjQ4IDIgMTJzNC40OCAxMCAxMCAxMCAxMC00LjQ4IDEwLTEwUzE3LjUyIDIgMTIgMnptMSAxNWgtMnYtNmgydjZ6bTAtOGgtMlY3aDJ2MnoiLz48L3N2Zz4=)}.toast.toast-success{background-color:#43b581}.toast.toast-success.icon{background-image:url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPiAgICA8cGF0aCBkPSJNMTIgMkM2LjQ4IDIgMiA2LjQ4IDIgMTJzNC40OCAxMCAxMCAxMCAxMC00LjQ4IDEwLTEwUzE3LjUyIDIgMTIgMnptLTIgMTVsLTUtNSAxLjQxLTEuNDFMMTAgMTQuMTdsNy41OS03LjU5TDE5IDhsLTkgOXoiLz48L3N2Zz4=)}.toast.toast-danger,.toast.toast-error{background-color:#f04747}.toast.toast-danger.icon,.toast.toast-error.icon{background-image:url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gICAgPHBhdGggZD0iTTEyIDJDNi40NyAyIDIgNi40NyAyIDEyczQuNDcgMTAgMTAgMTAgMTAtNC40NyAxMC0xMFMxNy41MyAyIDEyIDJ6bTUgMTMuNTlMMTUuNTkgMTcgMTIgMTMuNDEgOC40MSAxNyA3IDE1LjU5IDEwLjU5IDEyIDcgOC40MSA4LjQxIDcgMTIgMTAuNTkgMTUuNTkgNyAxNyA4LjQxIDEzLjQxIDEyIDE3IDE1LjU5eiIvPiAgICA8cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PC9zdmc+)}.toast.toast-warn,.toast.toast-warning{background-color:#FFA600;color:#fff}.toast.toast-warn.icon,.toast.toast-warning.icon{background-image:url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPiAgICA8cGF0aCBkPSJNMSAyMWgyMkwxMiAyIDEgMjF6bTEyLTNoLTJ2LTJoMnYyem0wLTRoLTJ2LTRoMnY0eiIvPjwvc3ZnPg==)}\r\n\r\n`);\r\n\r\nif (!document.getElementById(\"material-icons\")) {\r\n\tconst link = document.createElement(\"link\");\r\n\r\n\tlink.id = \"material-icons\";\r\n\tlink.rel = \"stylesheet\";\r\n\tlink.href = \"https://fonts.googleapis.com/icon?family=Material+Icons\";\r\n\r\n\tdocument.head.appendChild(link);\r\n}\r\n"
  },
  {
    "path": "MentionAliases.plugin.js",
    "content": "/**\r\n * @name MentionAliases\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www.paypal.me/israelboone\r\n * @website https://metalloriff.github.io/toms-discord-stuff/\r\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/MentionAliases.plugin.js\r\n */\r\n\r\nmodule.exports = (() => {\r\n\tconst config =\r\n\t{\r\n\t\tinfo: {\r\n\t\t\tname: \"MentionAliases\",\r\n\t\t\tauthors: [{\r\n\t\t\t\tname: \"Metalloriff\",\r\n\t\t\t\tdiscord_id: \"264163473179672576\",\r\n\t\t\t\tgithub_username: \"metalloriff\",\r\n\t\t\t\ttwitter_username: \"Metalloriff\"\r\n\t\t\t}, {\r\n\t\t\t\tname: \"Strencher\",\r\n\t\t\t\tdiscord_id: \"415849376598982656\",\r\n\t\t\t\tgithub_username: \"Strencher\",\r\n\t\t\t\ttwitter_username: \"Strencher3\"\r\n\t\t\t}],\r\n\t\t\tversion: \"2.0.0\",\r\n\t\t\tdescription: \"Allows you to define custom aliases for users that you can @mention them with, with the option to display the alias next to their username.\",\r\n\t\t\tgithub: \"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/MentionAliases.plugin.js\",\r\n\t\t\tgithub_raw: \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/MentionAliases.plugin.js\"\r\n\t\t},\r\n\t\tdefaultConfig: [{\r\n\t\t\tid: \"general\",\r\n\t\t\ttitle: \"general settings\",\r\n\t\t\ttype: \"category\",\r\n\t\t\tcollapsible: true,\r\n\t\t\tshown: false,\r\n\t\t\tsettings: [{\r\n\t\t\t\tid: \"displayTags\",\r\n\t\t\t\ttitle: \"Display Alias Tags\",\r\n\t\t\t\tnote: \"Whether or not to display the alias tag next to the relevant username in chat.\",\r\n\t\t\t\ttype: \"switch\",\r\n\t\t\t\tvalue: true\r\n\t\t\t}]\r\n\t\t}],\r\n\t\tchangelog: [{\r\n\t\t\ttitle: \"2.0 rewrite\",\r\n\t\t\ttype: \"fixed\",\r\n\t\t\titems: [\r\n\t\t\t\t\"MentionAliases has been rewritten and should work better in the long run. If you notice any bugs that are not listed below, please report them to me.\",\r\n\t\t\t]\r\n\t\t}, {\r\n\t\t\ttitle: \"known bugs\",\r\n\t\t\ttype: \"progress\",\r\n\t\t\titems: [\r\n\t\t\t\t\"Tags will not refresh after modifying a user's alias until you switch channels.\"\r\n\t\t\t]\r\n\t\t}]\r\n\t};\r\n\r\n\treturn !global.ZeresPluginLibrary ? class {\r\n\t\tconstructor() { this._config = config; }\r\n\r\n\t\tgetName = () => config.info.name;\r\n\t\tgetAuthor = () => config.info.description;\r\n\t\tgetVersion = () => config.info.version;\r\n\r\n\t\tload() {\r\n\t\t\tBdApi.showConfirmationModal(\"Library Missing\", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {\r\n\t\t\t\tconfirmText: \"Download Now\",\r\n\t\t\t\tcancelText: \"Cancel\",\r\n\t\t\t\tonConfirm: () => {\r\n\t\t\t\t\trequire(\"request\").get(\"https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js\", async (err, res, body) => {\r\n\t\t\t\t\t\tif (err) return require(\"electron\").shell.openExternal(\"https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js\");\r\n\t\t\t\t\t\tawait new Promise(r => require(\"fs\").writeFile(require(\"path\").join(BdApi.Plugins.folder, \"0PluginLibrary.plugin.js\"), body, r));\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tstart() { }\r\n\t\tstop() { }\r\n\t} : (([Plugin, Api]) => {\r\n\r\n\t\tconst plugin = (Plugin, Api) => { try {\r\n\t\t\tconst {\r\n\t\t\t\tWebpackModules,\r\n\t\t\t\tDiscordModules: { UserStore, UserStatusStore, React },\r\n\t\t\t\tPluginUtilities,\r\n\t\t\t\tUtilities,\r\n\t\t\t\tPatcher\r\n\t\t\t} = Api;\r\n\r\n\t\t\tconst { getCurrentUser } = UserStore;\r\n\r\n\t\t\tvar f = s => WebpackModules.find(s), d = s => WebpackModules.getByDisplayName(s), p = function() { return WebpackModules.getByProps(...arguments); };\r\n\r\n\t\t\tconst { MenuItem, MenuGroup } = f(m => m.MenuItem && !m.default);\r\n\t\t\tconst Popouts = p(\"openPopout\");\r\n\t\t\tconst Modals = p(\"openModal\");\r\n\t\t\tconst { ComponentDispatch } = p(\"ComponentDispatch\");\r\n\t\t\tconst insertText = content => ComponentDispatch.dispatchToLastSubscribed(\"INSERT_TEXT\", { content });\r\n\r\n\t\t\tlet fetchedModules = {};\r\n\t\t\tconst Components = {\r\n\t\t\t\t...p(\"Title\"),\r\n\t\t\t\tModalRoot: p(\"ModalRoot\").ModalRoot,\r\n\t\t\t\tAutocompletePopout: d(\"FluxContainer(AutocompletePopout)\"),\r\n\t\t\t\tAvatar: p(\"AnimatedAvatar\").default,\r\n\t\t\t\tTooltip: d(\"Tooltip\"),\r\n\t\t\t\t_input: d(\"TextInput\"),\r\n\t\t\t\tFlex: d(\"Flex\"),\r\n\t\t\t\tButton: p(\"DropdownSizes\"),\r\n\t\t\t\tIcon: props => {\r\n\t\t\t\t\tif (!fetchedModules[props.name])\r\n\t\t\t\t\t\tfetchedModules[props.name] = WebpackModules.find(m => m.id && typeof m.keys === \"function\" && m.keys().includes(\"./Activity\"))(\"./\" + props.name);\r\n\t\t\t\t\treturn React.createElement(fetchedModules[props.name].default, props);\r\n\t\t\t\t},\r\n\t\t\t\tInput: (props, ref) =>\r\n\t\t\t\t\tReact.createElement(Components._input, {\r\n\t\t\t\t\t\tvalue: props.value,\r\n\t\t\t\t\t\tplaceholder: props.placeholder,\r\n\t\t\t\t\t\tref: e => e && (ref = e),\r\n\t\t\t\t\t\tonChange: val => {\r\n\t\t\t\t\t\t\tref.props.value = val;\r\n\t\t\t\t\t\t\tref.forceUpdate();\r\n\t\t\t\t\t\t\tprops.onChange(val);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t})\r\n\t\t\t};\r\n\r\n\t\t\tconst classes = {\r\n\t\t\t\t...p(\"header\", \"botTag\", \"listAvatar\"),\r\n\t\t\t\t...p(\"textAreaHeight\", \"channelTextArea\", \"highlighted\"),\r\n\t\t\t\t...p(\"botTagRegular\", \"botTag\")\r\n\t\t\t};\r\n\t\t\t\r\n\t\t\tf = d = p = null;\r\n\r\n\t\t\tconst Item = ({ user, alias }) =>\r\n\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\"div\",\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tstyle: { display: \"flex\" },\r\n\t\t\t\t\t\tchildren: [\r\n\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\tComponents.Avatar,\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tsrc: user.getAvatarURL(),\r\n\t\t\t\t\t\t\t\t\tisMobile: false,\r\n\t\t\t\t\t\t\t\t\tisTyping: false,\r\n\t\t\t\t\t\t\t\t\tsize: \"SIZE_40\"\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t),\r\n\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\"div\",\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tclassName: classes.listRowContent,\r\n\t\t\t\t\t\t\t\t\tstyle: { marginLeft: 5 },\r\n\t\t\t\t\t\t\t\t\tchildren: [\r\n\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\"div\",\r\n\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\tstyle: {\r\n\t\t\t\t\t\t\t\t\t\t\t\t\ttextTransform: \"none\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tmarginTop: 12\r\n\t\t\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\t\tclassName: classes.listName,\r\n\t\t\t\t\t\t\t\t\t\t\t\tchildren: alias\r\n\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t]\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t]\r\n\t\t\t\t\t}\r\n\t\t\t\t);\r\n\t\t\t\r\n\t\t\tclass Popout extends React.Component {\r\n\t\t\t\trenderItem(item) {\r\n\t\t\t\t\tif (!item)\r\n\t\t\t\t\t\treturn null;\r\n\t\t\t\t\t\r\n\t\t\t\t\treturn React.createElement(\r\n\t\t\t\t\t\tItem,\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tuser: item.user,\r\n\t\t\t\t\t\t\talias: item.alias,\r\n\t\t\t\t\t\t\tonRemove: () => {\r\n\t\t\t\t\t\t\t\tthis.items.splice(this.items.indexOf(item), 1);\r\n\t\t\t\t\t\t\t\tthis.forceUpdate();\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t);\r\n\t\t\t\t}\r\n\r\n\t\t\t\trender() {\r\n\t\t\t\t\tconst { label, onClose, onSelect, placeholder, items } = this.props;\r\n\r\n\t\t\t\t\treturn React.createElement(\r\n\t\t\t\t\t\tComponents.AutocompletePopout,\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tlabel,\r\n\t\t\t\t\t\t\tonClose,\r\n\t\t\t\t\t\t\tonSelect,\r\n\t\t\t\t\t\t\tplaceholder,\r\n\t\t\t\t\t\t\tkeyboardModeEnabled: false,\r\n\t\t\t\t\t\t\tonFilterResults: filter =>\r\n\t\t\t\t\t\t\t\tthis.items = items.filter(e =>\r\n\t\t\t\t\t\t\t\t\t(filter(e.alias.toLowerCase()) ||\r\n\t\t\t\t\t\t\t\t\tfilter(e.user.username.toLowerCase()))),\r\n\t\t\t\t\t\t\tonRenderResult: item => this.renderItem(item),\r\n\t\t\t\t\t\t\tposition: \"top\",\r\n\t\t\t\t\t\t\tsections: [null]\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tconst AliasTag = ({ alias }) =>\r\n\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\"span\",\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tclassName: [classes.botTagRegular, classes.botTag, classes.px].join(\" \"),\r\n\t\t\t\t\t\tchildren:\r\n\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\"span\",\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tclassName: classes.botText,\r\n\t\t\t\t\t\t\t\t\tchildren: alias.value\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t}\r\n\t\t\t\t);\r\n\r\n\t\t\treturn class MentionAliases extends Plugin {\r\n\t\t\t\tconstructor() {\r\n\t\t\t\t\tsuper();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tgetDataName = () => this.getName() + \".\" + getCurrentUser().id;\r\n\t\t\t\tloadSettings = s => PluginUtilities.loadSettings(this.getDataName(), this.defaultSettings || s);\r\n\t\t\t\tsaveSettings = s => PluginUtilities.saveSettings(this.getDataName(), this.settings || s);\r\n\r\n\t\t\t\tasync showChangelog(footer) {\r\n\t\t\t\t\ttry { footer = (await WebpackModules.getByProps(\"getUser\", \"acceptAgreements\").getUser(\"264163473179672576\")).tag + \" | https://discord.gg/yNqzuJa\"; }\r\n\t\t\t\t\tfinally { super.showChangelog(footer); }\r\n\t\t\t\t}\r\n\t\r\n\t\t\t\tonStart() {\r\n\t\t\t\t\tif (!Array.isArray(this.settings.aliases))\r\n\t\t\t\t\t\tthis.settings.aliases = [];\r\n\t\t\t\t\t\t\r\n\t\t\t\t\tthis.patchAutoComplete();\r\n\t\t\t\t\tthis.patchMessageHeader();\r\n\t\t\t\t\tthis.patchUserContextMenu();\r\n\t\t\t\t\tthis.patchMentions();\r\n\t\t\t\t\tthis.patchTextAreaContainer();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tpatchAutoComplete() {\r\n\t\t\t\t\tconst { MENTIONS } = WebpackModules.getByProps(\"AUTOCOMPLETE_OPTIONS\").AUTOCOMPLETE_OPTIONS;\r\n\r\n\t\t\t\t\tPatcher.after(MENTIONS, \"queryResults\", (_, [, query], props) => {\r\n\t\t\t\t\t\tfor (let alias of this.settings.aliases) {\r\n\t\t\t\t\t\t\tconst user = UserStore.getUser(alias.userId);\r\n\t\t\t\t\t\t\tconst renderer = props.users.find(u => u.user.id == alias.userId);\r\n\r\n\t\t\t\t\t\t\tif (user && query && alias.value.toLowerCase().includes(query.toLowerCase())) {\r\n\t\t\t\t\t\t\t\tif (renderer)\r\n\t\t\t\t\t\t\t\t\trenderer.nick = alias.value;\r\n\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t\tprops.users.push({\r\n\t\t\t\t\t\t\t\t\t\tcomparator: user.usernameNormalized,\r\n\t\t\t\t\t\t\t\t\t\tnick: alias.value,\r\n\t\t\t\t\t\t\t\t\t\tscore: 10,\r\n\t\t\t\t\t\t\t\t\t\tstatus: UserStatusStore.getStatus(alias.userId),\r\n\t\t\t\t\t\t\t\t\t\tuser\r\n\t\t\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\r\n\t\t\t\tpatchMessageHeader() {\r\n\t\t\t\t\tPatcher.after(WebpackModules.getByProps(\"MessageTimestamp\"), \"default\", (_, [props], re) => {\r\n\t\t\t\t\t\tif (!this.settings.general.displayTags)\r\n\t\t\t\t\t\t\treturn;\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tconst children = Utilities.getNestedProp(re, \"props.children.1.props.children\");\r\n\t\t\t\t\t\tconst alias = this.settings.aliases.find(u => u.userId == props.message.author.id);\r\n\t\t\t\t\t\tif (!Array.isArray(children) || !alias)\r\n\t\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\t\t\tchildren.splice(\r\n\t\t\t\t\t\t\t2, 0,\r\n\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\tAliasTag,\r\n\t\t\t\t\t\t\t\t{ alias }\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t);\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\r\n\t\t\t\tpatchUserContextMenu() {\r\n\t\t\t\t\tconst menus = [\r\n\t\t\t\t\t\tWebpackModules.find(m => m.default && m.default.displayName == \"GuildChannelUserContextMenu\"),\r\n\t\t\t\t\t\tWebpackModules.find(m => m.default && m.default.displayName == \"DMUserContextMenu\")\r\n\t\t\t\t\t];\r\n\r\n\t\t\t\t\tfor (let menu of menus)\r\n\t\t\t\t\t\tPatcher.after(menu, \"default\", (_, [args], re) => {\r\n\t\t\t\t\t\t\tconst contextMenu = Utilities.getNestedProp(re, \"props.children.props.children\");\r\n\t\t\t\t\t\t\tconst registered = this.settings.aliases.findIndex(u => u.userId == args.user.id);\r\n\r\n\t\t\t\t\t\t\tif (!Array.isArray(contextMenu))\r\n\t\t\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\t\t\t\tcontextMenu.push(React.createElement(\r\n\t\t\t\t\t\t\t\tMenuGroup,\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tchildren:\r\n\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\tMenuItem,\r\n\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: \"Mention Aliases\",\r\n\t\t\t\t\t\t\t\t\t\t\t\tid: \"ma-submenu\",\r\n\t\t\t\t\t\t\t\t\t\t\t\tchildren: [\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tregistered > -1 ?\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tMenuItem,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlabel: \"Remove Alias\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid: \"ma-remove\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolor: \"colorDanger\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taction: () => {\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tthis.settings.aliases.splice(registered, 1);\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tthis.saveSettings();\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t) : null,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tMenuItem,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlabel: (registered > -1 ? \"Edit\" : \"Set\") + \" Alias\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid: \"ma-edit-remove\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taction: () => this.openAliasModal(args.user)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t\t\t\t]\r\n\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t));\r\n\t\t\t\t\t\t});\r\n\t\t\t\t}\r\n\r\n\t\t\t\tpatchMentions() {\r\n\t\t\t\t\tPatcher.after(WebpackModules.getByProps(\"UserMention\"), \"UserMention\", (_, [args], re) => {\r\n\t\t\t\t\t\tconst alias = this.settings.aliases.find(u => u.userId == args.id);\r\n\r\n\t\t\t\t\t\tif (alias) {\r\n\t\t\t\t\t\t\tconst old = re.props.children;\r\n\r\n\t\t\t\t\t\t\tre.props.children = e => {\r\n\t\t\t\t\t\t\t\tconst render = old(e);\r\n\r\n\t\t\t\t\t\t\t\trender.props.children = \"@\" + alias.value;\r\n\r\n\t\t\t\t\t\t\t\treturn render;\r\n\t\t\t\t\t\t\t};\r\n\r\n\t\t\t\t\t\t\treturn re;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\tPatcher.after(WebpackModules.getByDisplayName(\"DeprecatedPopout\").prototype, \"render\", (self, _, re) => {\r\n\t\t\t\t\t\tif (!re.props.className.includes(\"mention\"))\r\n\t\t\t\t\t\t\treturn re;\r\n\r\n\t\t\t\t\t\tconst props = self.props.render();\r\n\r\n\t\t\t\t\t\tif (!props || !props.props.userId)\r\n\t\t\t\t\t\t\treturn re;\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tconst alias = this.settings.aliases.find(u => u.userId == props.props.userId);\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tif (alias)\r\n\t\t\t\t\t\t\tre.props.children = [\"@\", alias.value];\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\treturn re;\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\r\n\t\t\t\tpatchTextAreaContainer() {\r\n\t\t\t\t\tconst TextAreaContainer = WebpackModules.find(m => m.type && m.type.render && m.type.render.displayName == \"ChannelTextAreaContainer\");\r\n\r\n\t\t\t\t\tPatcher.after(TextAreaContainer.type, \"render\", (_, [args], re) => {\r\n\t\t\t\t\t\tconst children = Utilities.getNestedProp(re, \"props.children.props.children.1.props.children.props.children.2.props.children\");\r\n\r\n\t\t\t\t\t\tif (!Array.isArray(children))\r\n\t\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\t\t\tchildren.unshift(React.createElement(\r\n\t\t\t\t\t\t\tComponents.Tooltip,\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\ttext: \"Open Aliases Menu\",\r\n\t\t\t\t\t\t\t\tposition: \"top\",\r\n\t\t\t\t\t\t\t\tcolor: \"black\"\r\n\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t_props => React.createElement(\r\n\t\t\t\t\t\t\t\t\"div\",\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tclassName: classes.buttonContainer,\r\n\t\t\t\t\t\t\t\t\tchildren:\r\n\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\tComponents.Icon,\r\n\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t..._props,\r\n\t\t\t\t\t\t\t\t\t\t\t\tclassName: classes.button,\r\n\t\t\t\t\t\t\t\t\t\t\t\tname: \"At\",\r\n\t\t\t\t\t\t\t\t\t\t\t\tstyle: {\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolor: \"var(--interactive-normal)\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tcursor: \"pointer\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tmarginTop: 6\r\n\t\t\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\t\tonClick: e =>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tPopouts.openPopout(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\te.target.parentElement,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trender: props =>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tPopout,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClose: () => props.onClose(),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonSelect: item => {\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprops.onClose();\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tinsertText(`<@${item.user.id}>`);\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlabel: \"User:\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplaceholder: \"Search for user\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\titems: [\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t...this.settings.aliases.map(e => ({\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tuser: UserStore.getUser(e.userId),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\talias: e.value\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}))\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t].filter(e => e && e.user)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"mention-aliases\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t));\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\r\n\t\t\t\topenAliasModal(user) {\r\n\t\t\t\t\tconst settings = this.settings.aliases.find(u => u.userId == user.id);\r\n\t\t\t\t\tlet value;\r\n\r\n\t\t\t\t\tModals.openModal(props =>\r\n\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\tComponents.ModalRoot,\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t...props,\r\n\t\t\t\t\t\t\t\tchildren: [\r\n\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\"h1\",\r\n\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\tstyle: {\r\n\t\t\t\t\t\t\t\t\t\t\t\tfontWeight: \"bold\",\r\n\t\t\t\t\t\t\t\t\t\t\t\ttextAlign: \"center\",\r\n\t\t\t\t\t\t\t\t\t\t\t\tcolor: \"white\",\r\n\t\t\t\t\t\t\t\t\t\t\t\tmargin: 5\r\n\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\"Define custom alias for \" + user.username\r\n\t\t\t\t\t\t\t\t\t),\r\n\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\"div\",\r\n\t\t\t\t\t\t\t\t\t\t{ style: { alignSelf: \"center\" } },\r\n\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\"div\",\r\n\t\t\t\t\t\t\t\t\t\t\t{ style: { margin: 15 } },\r\n\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\tComponents.Input, {\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tvalue: settings ? settings.value : user.username,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tplaceholder: \"Set Alias\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tonChange: v => (value = v)\r\n\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t),\r\n\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\"div\",\r\n\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\tstyle: {\r\n\t\t\t\t\t\t\t\t\t\t\t\tposition: \"absolute\",\r\n\t\t\t\t\t\t\t\t\t\t\t\tbottom: 5,\r\n\t\t\t\t\t\t\t\t\t\t\t\tright: 5,\r\n\t\t\t\t\t\t\t\t\t\t\t\tdisplay: \"flex\",\r\n\t\t\t\t\t\t\t\t\t\t\t\tflexDirection: \"row\"\r\n\t\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\tchildren: [\r\n\t\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tComponents.Button,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tchildren: \"Save\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick: () => {\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tthis.settings.aliases.push({\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvalue,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tuserId: user.id\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t});\r\n\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tthis.saveSettings();\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprops.onClose();\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstyle: { margin: 5 }\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t\t),\r\n\t\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tComponents.Button,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tchildren: \"Cancel\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick: () => props.onClose(),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstyle: { margin: 5 }\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t\t\t]\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t]\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t));\r\n\t\t\t\t}\r\n\t\r\n\t\t\t\tonStop() {\r\n\t\t\t\t\tPatcher.unpatchAll();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} catch (e) { console.error(e); }};\r\n\r\n\t\treturn plugin(Plugin, Api);\r\n\t})(global.ZeresPluginLibrary.buildPlugin(config));\r\n})();\r\n"
  },
  {
    "path": "NateUtilities.plugin.js",
    "content": "//META{\"name\":\"NateUtilities\"}*//\r\n\r\nclass NateUtilities {\r\n\t\r\n    getName() { return \"NateUtilities\"; }\r\n    getDescription() { return \"For all of your ear and brain saving needs! If you don't know what this plugin is, it's not for you, just ignore it.\"; }\r\n    getVersion() { return \"0.0.2\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\tgetChanges() {\r\n\t\treturn {\r\n\t\t\t\r\n\t\t};\r\n\t}\r\n\r\n    load() {}\r\n\r\n    start() {\r\n\r\n        let libLoadedEvent = () => {\r\n            try{ this.onLibLoaded(); }\r\n            catch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); }\r\n        };\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif(lib == undefined) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.setAttribute(\"id\", \"NeatoBurritoLibrary\");\r\n\t\t\tlib.setAttribute(\"type\", \"text/javascript\");\r\n\t\t\tlib.setAttribute(\"src\", \"https://rawgit.com/NeatoLib/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\");\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n        if(typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n        else lib.addEventListener(\"load\", libLoadedEvent);\r\n\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\r\n\t\tsetTimeout(() => {\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createKeybindInput(\"local mute nate\", this.settings.muteHotkey, newKey => {\r\n                this.unregisterKeybinds();\r\n                if(newKey) {\r\n                    this.settings.muteHotkey = newKey;\r\n                    this.registerKeybinds();\r\n                    this.saveSettings();\r\n                } else PluginUtilities.showToast(\"You did not input anything!\", { type : \"error\" });\r\n            }, { description : \"For when your ears and/or brain need a break.\", global : true }), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createKeybindInput(\"server mute nate\", this.settings.serverMuteHotkey, newKey => {\r\n                this.unregisterKeybinds();\r\n                if(newKey) {\r\n                    this.settings.serverMuteHotkey = newKey;\r\n                    this.registerKeybinds();\r\n                    this.saveSettings();\r\n                } else PluginUtilities.showToast(\"You did not input anything!\", { type : \"error\" });\r\n            }, { description : \"For when you feel like being a hero.\", global : true }), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createKeybindInput(\"server deafen nate\", this.settings.serverDeafenHotkey, newKey => {\r\n                this.unregisterKeybinds();\r\n                if(newKey) {\r\n                    this.settings.serverDeafenHotkey = newKey;\r\n                    this.registerKeybinds();\r\n                    this.saveSettings();\r\n                } else PluginUtilities.showToast(\"You did not input anything!\", { type : \"error\" });\r\n            }, { description : \"For when you need to talk behind Nate's back.\", global : true }), this.getName());\r\n\r\n            NeatoLib.Settings.pushChangelogElements(this);\r\n\r\n\t\t}, 0);\r\n\r\n\t\treturn NeatoLib.Settings.Elements.pluginNameLabel(this.getName());\r\n\t\t\r\n\t}\r\n\r\n\tsaveSettings() {\r\n\t\tNeatoLib.Settings.save(this);\t\t\r\n\t}\r\n\r\n\tonLibLoaded() {\r\n\r\n\t\tNeatoLib.Updates.check(this);\r\n\t\t\r\n\t\tthis.settings = NeatoLib.Settings.load(this, {\r\n            displayUpdateNotes : true,\r\n            muteHotkey : \"Alt + N\",\r\n            serverMuteHotkey : \"Shift + Alt + N\",\r\n            serverDeafenHotkey : \"Control + Alt + N\"\r\n\t\t});\r\n\r\n        NeatoLib.Events.onPluginLoaded(this);\r\n\t\t\r\n        //if(this.settings.displayUpdateNotes) NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());\r\n        \r\n        this.registerKeybinds();\r\n\r\n        this.initialized = true;\r\n\r\n        this.onSwitch();\r\n\r\n    }\r\n    \r\n    onSwitch() {\r\n\r\n        if(!this.initialized) return;\r\n\r\n        this.selectedServer = NeatoLib.getSelectedTextChannel();\r\n        this.selectedVoiceChannel = NeatoLib.getSelectedVoiceChannel();\r\n\r\n    }\r\n    \r\n    registerKeybinds() {\r\n\r\n        let nate = \"209024642697003008\",\r\n        toggleLocalMute = NeatoLib.Modules.get(\"toggleLocalMute\").toggleLocalMute,\r\n        isLocalMuted = NeatoLib.Modules.get(\"isLocalMute\").isLocalMute,\r\n        serverActionModule = NeatoLib.Modules.get([\"setServerMute\", \"setServerDeaf\"]),\r\n        getVoiceStates = NeatoLib.Modules.get(\"getVoiceStates\").getVoiceStates;\r\n\r\n        NeatoLib.Keybinds.registerGlobal(this.settings.muteHotkey, () => {\r\n            toggleLocalMute(nate);\r\n            if(isLocalMuted(nate)) NeatoLib.showToast(\"I got ya, fam!\", \"success\");\r\n            else NeatoLib.showToast(\"You're gonna regret that.\", \"error\");\r\n        });\r\n\r\n        NeatoLib.Keybinds.registerGlobal(this.settings.serverMuteHotkey, () => {\r\n            \r\n            if(this.selectedServer && this.selectedVoiceChannel) {\r\n\r\n                let voiceStates = getVoiceStates(this.selectedServer.id);\r\n\r\n                if(voiceStates[nate] == undefined) {\r\n                    NeatoLib.showToast(\"Nate is not here, you're safe!\", \"success\");\r\n                    return;\r\n                }\r\n\r\n                serverActionModule.setServerMute(this.selectedServer.id, nate, !voiceStates[nate].mute);\r\n\r\n                if(!voiceStates[nate].mute) NeatoLib.showToast(\"You should be proud!\", \"success\");\r\n                else NeatoLib.showToast(\"Sick fuck!\", \"error\");\r\n\r\n            }\r\n\r\n        });\r\n\r\n        NeatoLib.Keybinds.registerGlobal(this.settings.serverDeafenHotkey, () => {\r\n\r\n            if(this.selectedServer && this.selectedVoiceChannel) {\r\n\r\n                let voiceStates = getVoiceStates(this.selectedServer.id);\r\n\r\n                if(voiceStates[nate] == undefined) {\r\n                    NeatoLib.showToast(\"Nate is not here, you're safe!\", \"success\");\r\n                    return;\r\n                }\r\n\r\n                serverActionModule.setServerDeaf(this.selectedServer.id, nate, !voiceStates[nate].deaf);\r\n\r\n                if(!voiceStates[nate].deaf) NeatoLib.showToast(\"That Nate kid is an idiot.\", \"success\");\r\n                else NeatoLib.showToast(\"Shut up guys!\", \"error\");\r\n\r\n            }\r\n\r\n        });\r\n\r\n    }\r\n\r\n    unregisterKeybinds() {\r\n        NeatoLib.Keybinds.unregisterGlobal(this.settings.muteHotkey);\r\n        NeatoLib.Keybinds.unregisterGlobal(this.settings.serverMuteHotkey);\r\n        NeatoLib.Keybinds.unregisterGlobal(this.settings.serverDeafenHotkey);\r\n    }\r\n\t\r\n    stop() {\r\n        this.unregisterKeybinds();\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "OpenLinksInDiscord.plugin.js",
    "content": "//META{\"name\":\"OpenLinksInDiscord\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/OpenLinksInDiscord.plugin.js\"}*//\r\n\r\nclass OpenLinksInDiscord {\r\n\t\r\n    getName() { return \"OpenLinksInDiscord\"; }\r\n    getDescription() { return \"Opens links in a new window in Discord, instead of in your web browser. Hold shift to open links normally.\"; }\r\n    getVersion() { return \"1.1.4\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\r\n    load() {}\r\n\r\n    start() {\r\n\r\n        let libLoadedEvent = () => {\r\n            try{ this.onLibLoaded(); }\r\n            catch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n        };\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif(!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n        if(typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\r\n\t\tlet save = () => NeatoLib.Settings.save(this);\r\n\r\n\t\tsetTimeout(() => {\r\n\t\t\t\r\n\t\t\tNeatoLib.Settings.pushElements([\r\n\t\t\t\tNeatoLib.Settings.Elements.createToggleGroup(\"ld-tg\", \"Keys\", [\r\n\t\t\t\t\t{ title : \"Control/command\", value : \"ctrlKey\", setValue : this.settings.ctrlKey },\r\n\t\t\t\t\t{ title : \"Shift\", value : \"shiftKey\", setValue : this.settings.shiftKey },\r\n\t\t\t\t\t{ title : \"Alt\", value : \"altKey\", setValue : this.settings.altKey }\r\n\t\t\t\t], c => {\r\n\t\t\t\t\tthis.settings[c.value] = !this.settings[c.value];\r\n\t\t\t\t\tsave();\r\n\t\t\t\t}),\r\n\t\t\t\tNeatoLib.Settings.Elements.createToggleSwitch(\"Open in browser by default (holding keys above will open them in Discord)\", this.settings.reverse, () => {\r\n\t\t\t\t\tthis.settings.reverse = !this.settings.reverse;\r\n\t\t\t\t\tsave();\r\n\t\t\t\t})\r\n\t\t\t], this.getName());\r\n\r\n\t\t}, 0);\r\n\r\n\t\treturn NeatoLib.Settings.Elements.pluginNameLabel(this.getName());\r\n\r\n\t}\r\n\t\r\n\tonLibLoaded() {\r\n\t\t\r\n\t\tNeatoLib.Updates.check(this);\r\n\r\n\t\tthis.settings = NeatoLib.Settings.load(this, {\r\n\t\t\tctrlKey : false,\r\n\t\t\tshiftKey : true,\r\n\t\t\taltKey : false,\r\n\t\t\treverse : false\r\n\t\t});\r\n\r\n\t\tthis.event = e => {\r\n            if(e.target.localName == \"a\" && e.target.href && e.target.href.startsWith(\"http\") && !e.target.parentElement.className.includes(\"channel\") && !e.target.href.includes(\"/channels/\")) {\r\n\t\t\t\tif((!this.settings.ctrlKey || e.ctrlKey) && (!this.settings.shiftKey || e.shiftKey) && (!this.settings.altKey || e.altKey)) {\r\n\t\t\t\t\tif(this.settings.reverse) {\r\n\t\t\t\t\t\tthis.onClickLink(e);\r\n\t\t\t\t\t}\r\n\t\t\t\t} else if(!this.settings.reverse) {\r\n\t\t\t\t\tthis.onClickLink(e);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\t\t\r\n\t\tdocument.addEventListener(\"click\", this.event);\r\n\r\n\t\tthis.electron = require(\"electron\");\r\n\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\t\t\r\n\t}\r\n    \r\n    onClickLink(e) {\r\n\r\n\t\tlet window = new this.electron.remote.BrowserWindow({ frame : true, resizeable : true, show : true, webPreferences : { nodeIntegration : false, nodeIntegrationInWorker : false }});\r\n\r\n\t\twindow.maximize();\r\n\t\twindow.setMenu(null);\r\n        window.loadURL(e.target.href);\r\n        \r\n        e.preventDefault();\r\n\r\n    }\r\n\t\r\n    stop() {\r\n        document.removeEventListener(\"click\", this.event);\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "PinCollapsedChannels.plugin.js",
    "content": "//META{\"name\":\"PinCollapsedChannels\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/PinCollapsedChannels.plugin.js\"}*//\r\n\r\nclass PinCollapsedChannels {\r\n\r\n\tgetName() { return \"PinCollapsedChannels\"; }\r\n\tgetDescription() { return \"Allows you to pin channels on collapsed categories, similar to if there was an unread message.\"; }\r\n\tgetVersion() { return \"0.0.2\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\tgetChanges() {\r\n\t\treturn {\r\n\r\n\t\t};\r\n\t}\r\n\r\n\tload() {}\r\n\r\n\tstart() {\r\n\t\tconst libLoadedEvent = () => {\r\n\t\t\ttry{ this.onLibLoaded(); }\r\n\t\t\tcatch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n\t\t};\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif (!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n\t\tif (typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\t}\r\n\r\n\tget settingFields() {\r\n\t\treturn {\r\n\t\t\tpinnedChannels: { label: \"Pinned Channel IDs\", description: \"Hint: you can right click on channels to pin and unpin them.\", type: \"string\", array: true }\r\n\t\t};\r\n\t}\r\n\r\n\tget defaultSettings() {\r\n\t\treturn {\r\n\t\t\tdisplayUpdateNotes: true,\r\n\t\t\tpinnedChannels: []\r\n\t\t};\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\t\treturn NeatoLib.Settings.createPanel(this);\r\n\t}\r\n\r\n\tsaveSettings() {\r\n\t\tNeatoLib.Settings.save(this);\r\n\t}\r\n\r\n\tonLibLoaded() {\r\n\t\tthis.settings = NeatoLib.Settings.load(this);\r\n\r\n\t\tif (!NeatoLib.hasRequiredLibVersion(this, \"0.5.19\")) return;\r\n\r\n\t\tNeatoLib.Updates.check(this);\r\n\r\n\t\tthis.unpatch = NeatoLib.monkeyPatchInternal(NeatoLib.Modules.find(m => m.default && m.default.toString().search(/return!0;return \\w&&\\w\\.default\\.isGuildCollapsed\\(\\w\\)}/) !== -1), \"default\", e => {\r\n\t\t\tif (this.settings.pinnedChannels.includes(e.args[0].id)){\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\telse return e.callDefault();\r\n\t\t});\r\n\r\n\t\tdocument.addEventListener(\"contextmenu\", this.contextEvent = e => this.onContextMenu(e));\r\n\r\n\t\t//if (this.settings.displayUpdateNotes) NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());\r\n\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\t}\r\n\r\n\tonContextMenu(e) {\r\n\t\tconst channel = NeatoLib.ReactData.getProp(NeatoLib.DOM.searchForParentElement(e.target, element => element.className && element.className.includes && element.className.includes(\"containerDefault\")), \"channel\"),\r\n\t\t\tcontextMenu = NeatoLib.ContextMenu.get();\r\n\r\n\t\tif (!channel || !contextMenu || !channel.parent_id || channel.type != 0) return;\r\n\r\n\t\tconst subItems = [];\r\n\r\n\t\tif (this.settings.pinnedChannels.includes(channel.id)) {\r\n\t\t\tsubItems.push(NeatoLib.ContextMenu.createItem(\"Unpin Channel\", () => {\r\n\t\t\t\tthis.settings.pinnedChannels.splice(this.settings.pinnedChannels.indexOf(channel.id), 1);\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t\tNeatoLib.ContextMenu.close();\r\n\t\t\t}));\r\n\t\t} else {\r\n\t\t\tsubItems.push(NeatoLib.ContextMenu.createItem(\"Pin Channel\", () => {\r\n\t\t\t\tthis.settings.pinnedChannels.push(channel.id);\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t\tNeatoLib.ContextMenu.close();\r\n\t\t\t}));\r\n\t\t}\r\n\r\n\t\tcontextMenu.firstChild.appendChild(NeatoLib.ContextMenu.createSubMenu(\"Pin Collapsed Channels\", subItems));\r\n\t}\r\n\r\n\tstop() {\r\n\t\tthis.unpatch();\r\n\t\tdocument.removeEventListener(\"contextmenu\", this.contextEvent);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "PinPluginsAndThemes.plugin.js",
    "content": "//META{\"name\":\"PinPluginsAndThemes\",\"website\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/README.md\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/PinPluginsAndThemes.plugin.js\"}*//\r\n\r\nclass PinPluginsAndThemes {\r\n\t\r\n    getName() { return \"PinPluginsAndThemes\"; }\r\n    getDescription() { return \"Allows you to pin plugins and themes via the context menu.\"; }\r\n    getVersion() { return \"1.0.2\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\tgetChanges() {\r\n\t\treturn {\r\n            \"1.0.1\": `\r\n                Fixed plugins and themes not pinning after switching settings tabs.\r\n                Fixed incompatibility with DevilBro's RepoControls plugin. Changing sorting mode of RepoControls will temporarily break the plugin, but simply switching tabs will fix it.\r\n            `,\r\n            \"1.0.2\": `\r\n                Fixed plugin\r\n            `\r\n\t\t};\r\n\t}\r\n\r\n    load() {}\r\n\r\n    start() {\r\n\r\n        let libLoadedEvent = () => {\r\n            try{ this.onLibLoaded(); }\r\n            catch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n        };\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif(lib == undefined) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.setAttribute(\"id\", \"NeatoBurritoLibrary\");\r\n\t\t\tlib.setAttribute(\"type\", \"text/javascript\");\r\n\t\t\tlib.setAttribute(\"src\", \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\");\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n        if(typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n        else lib.addEventListener(\"load\", libLoadedEvent);\r\n\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\r\n\t\tsetTimeout(() => {\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField(\"Pin color (R, G, B) or (hex)\", this.settings.pinColor, e => {\r\n                if(e.target.value.trim()[0] == \"#\") e.target.value = NeatoLib.Colors.hexToRGB(e.target.value.trim());\r\n                this.settings.pinColor = e.target.value;\r\n                this.applyStyles();\r\n                this.saveSettings();\r\n            }), this.getName());\r\n\t\t\t\r\n\t\t\tNeatoLib.Settings.pushChangelogElements(this);\r\n\r\n\t\t}, 0);\r\n\r\n\t\treturn NeatoLib.Settings.Elements.pluginNameLabel(this.getName());\r\n\t\t\r\n    }\r\n    \r\n    applyStyles() {\r\n        \r\n        if(this.styles) this.styles.destroy();\r\n\r\n        this.styles = NeatoLib.injectCSS(`\r\n            #ptap-pinned-items {\r\n                border-bottom: 10px solid rgb(${this.settings.pinColor});\r\n                margin-bottom: 20px;\r\n            }\r\n            #ptap-pinned-items li {\r\n                border-color: rgb(${this.settings.pinColor});\r\n            }\r\n            #ptap-pinned-items .bda-footer button, #ptap-pinned-items .ui-switch.checked {\r\n                background: rgb(${this.settings.pinColor}) !important;\r\n            }\r\n            #ptap-pinned-items .bda-footer a, #ptap-pinned-items .bda-name {\r\n                color: rgb(${this.settings.pinColor}) !important;\r\n            }\r\n            #plugin-settings-PinPluginsAndThemes input, #plugin-settings-PinPluginsAndThemes .themeDefault-24hCdX.valueChecked-m-4IJZ, #plugin-settings-PinPluginsAndThemes button {\r\n                background-color: rgb(${this.settings.pinColor}) !important;\r\n            }\r\n        `);\r\n\r\n    }\r\n\r\n\tsaveSettings() {\r\n        this.applyStyles();\r\n\t\tNeatoLib.Settings.save(this);\r\n\t}\r\n\r\n\tonLibLoaded() {\r\n\r\n        if(NeatoLib.hasRequiredLibVersion(this, \"0.0.2\") == false) return;\r\n\t\t\r\n\t\tthis.settings = NeatoLib.Settings.load(this, {\r\n            displayUpdateNotes : true,\r\n            pinned : [],\r\n            pinColor : \"142, 112, 216\"\r\n\t\t});\r\n\t\t\r\n\t\tNeatoLib.Updates.check(this);\r\n\t\t\r\n        if(this.settings.displayUpdateNotes) NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());\r\n\r\n        this.applyStyles();\r\n\r\n        this.updatePinned = () => {\r\n\r\n            let items = document.getElementsByTagName(\"li\"), pinnedItems = document.getElementById(\"ptap-pinned-items\");\r\n\r\n            for(let i = 0; i < items.length; i++) {\r\n\r\n                if(!items[i].getAttribute(\"data-idx\")) items[i].setAttribute(\"data-idx\", i);\r\n\r\n                items[i].addEventListener(\"contextmenu\", this.onItemContext);\r\n\r\n                if(this.settings.pinned.indexOf(items[i].getAttribute(\"data-name\")) != -1) {\r\n\r\n                    if(!pinnedItems) {\r\n                        document.getElementsByClassName(\"bda-slist\")[0].insertAdjacentHTML(\"afterbegin\", `<div id=\"ptap-pinned-items\"></div>`);\r\n                        pinnedItems = document.getElementById(\"ptap-pinned-items\");\r\n                    }\r\n\r\n                    pinnedItems.appendChild(items[i]);\r\n\r\n                }\r\n\r\n            }\r\n\r\n        };\r\n        \r\n        this.onItemContext = e => {\r\n\r\n            let name = e.currentTarget.getAttribute(\"data-name\"), idx = parseInt(e.currentTarget.getAttribute(\"data-idx\")) + 1;\r\n\r\n            if(name) {\r\n                NeatoLib.ContextMenu.create([NeatoLib.ContextMenu.createGroup([NeatoLib.ContextMenu.createItem(this.settings.pinned.indexOf(name) == -1 ? \"Pin\" : \"Unpin\", e => {\r\n                    if(this.settings.pinned.indexOf(name) != -1) {\r\n                        let list = document.getElementsByClassName(\"bda-slist\")[0];\r\n                        list.insertBefore(document.querySelector(`[data-name=\"${name}\"]`), list.childNodes[idx]);\r\n                        this.settings.pinned.splice(this.settings.pinned.indexOf(name), 1);\r\n                        NeatoLib.showToast(name + \" unpinned\");\r\n                        let pinnedItems = document.getElementById(\"ptap-pinned-items\");\r\n                        if(pinnedItems.childElementCount == 0) pinnedItems.outerHTML = \"\";\r\n                    } else {\r\n                        this.settings.pinned.push(name);\r\n                        this.updatePinned();\r\n                        NeatoLib.showToast(name + \" pinned\", null, { color : `rgb(${this.settings.pinColor})` });\r\n                    }\r\n                    this.saveSettings();\r\n                    NeatoLib.ContextMenu.close();\r\n                })])], e);\r\n            }\r\n\r\n        };\r\n\r\n        this.settingsObserver = new MutationObserver(mutations => {\r\n\r\n            for(let mi = 0; mi < mutations.length; mi++) {\r\n\r\n                let added = mutations[mi].addedNodes[0];\r\n\r\n                if(added && added instanceof Element && (added.classList.contains(NeatoLib.getClass(\"scrollerWrap\"))))\r\n                    this.updatePinned();\r\n\r\n            }\r\n\r\n        });\r\n\r\n        this.settingsPanelEvent = () => {\r\n            this.settingsObserver.observe(document.getElementsByClassName(\"layers-3iHuyZ\")[0], { childList : true, subtree : true });\r\n        };\r\n\r\n        NeatoLib.Events.attach(\"settings\", this.settingsPanelEvent);\r\n\t\t\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\r\n\t}\r\n\t\r\n    stop() {\r\n        NeatoLib.Events.detach(\"settings\", this.settingsPanelEvent);\r\n        this.styles.destroy();\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "PreventSpotifyAutoPause.plugin.js",
    "content": "//META{\"name\":\"PreventSpotifyAutoPause\",\"displayName\":\"PreventSpotifyAutoPause\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/PreventSpotifyAutoPause.plugin.js\"}*//\r\n/*@cc_on\r\n@if (@_jscript)\r\n\t\r\n\t// Offer to self-install for clueless users that try to run this directly.\r\n\tvar shell = WScript.CreateObject(\"WScript.Shell\");\r\n\tvar fs = new ActiveXObject(\"Scripting.FileSystemObject\");\r\n\tvar pathPlugins = shell.ExpandEnvironmentStrings(\"%APPDATA%\\BetterDiscord\\plugins\");\r\n\tvar pathSelf = WScript.ScriptFullName;\r\n\t// Put the user at ease by addressing them in the first person\r\n\tshell.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);\r\n\tif (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {\r\n\t\tshell.Popup(\"I'm in the correct folder already.\", 0, \"I'm already installed\", 0x40);\r\n\t} else if (!fs.FolderExists(pathPlugins)) {\r\n\t\tshell.Popup(\"I can't find the BetterDiscord plugins folder.\\nAre you sure it's even installed?\", 0, \"Can't install myself\", 0x10);\r\n\t} else if (shell.Popup(\"Should I copy myself to BetterDiscord's plugins folder for you?\", 0, \"Do you need some help?\", 0x34) === 6) {\r\n\t\tfs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);\r\n\t\t// Show the user where to put plugins in the future\r\n\t\tshell.Exec(\"explorer \" + pathPlugins);\r\n\t\tshell.Popup(\"I'm installed!\", 0, \"Successfully installed\", 0x40);\r\n\t}\r\n\tWScript.Quit();\r\n@else@*/\r\n\r\nvar PreventSpotifyAutoPause = (() => {\r\n\tconst config = {\r\n\t\tinfo: {\r\n\t\t\tname: \"PreventSpotifyAutoPause\",\r\n\t\t\tauthors: [{\r\n\t\t\t\tname: \"Metalloriff\",\r\n\t\t\t\tdiscord_id: \"264163473179672576\",\r\n\t\t\t\tgithub_username: \"Metalloriff\",\r\n\t\t\t\ttwitter_username: \"Metalloriff\"\r\n\t\t\t}],\r\n\t\t\tversion: \"1.0.4\",\r\n\t\t\tdescription: \"Prevents Discord from automatically pausing Spotify after transmitting your microphone for 30 seconds.\",\r\n\t\t\tgithub: \"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/PreventSpotifyAutoPause.plugin.js\",\r\n\t\t\tgithub_raw: \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/PreventSpotifyAutoPause.plugin.js\"\r\n\t\t},\r\n\t\tchangelog: [{\r\n\t\t\ttitle: \"fixed\",\r\n\t\t\ttype: \"fixed\",\r\n\t\t\titems: [\"Fixed not working\"]\r\n\t\t}],\r\n\t\tmain: \"index.js\",\r\n\t\tdefaultConfig: []\r\n\t};\r\n\r\n\treturn !global.ZeresPluginLibrary ? class {\r\n\t\tgetName() { return config.info.name; }\r\n\t\tgetAuthor() { return config.info.authors.map(x => x.name).join(\", \"); }\r\n\t\tgetDescription() { return config.info.description; }\r\n\t\tgetVersion() { return config.info.version; }\r\n\t\t\r\n\t\tload() {\r\n            const title = \"Library Missing\";\r\n            const ModalStack = BdApi.findModuleByProps(\"push\", \"update\", \"pop\", \"popWithKey\");\r\n            const TextElement = BdApi.findModuleByProps(\"Sizes\", \"Weights\");\r\n            const ConfirmationModal = BdApi.findModule(m => m.defaultProps && m.key && m.key() == \"confirm-modal\");\r\n            if (!ModalStack || !ConfirmationModal || !TextElement) return BdApi.alert(title, `The library plugin needed for ${config.info.name} is missing.<br /><br /> <a href=\"https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js\" target=\"_blank\">Click here to download the library!</a>`);\r\n            ModalStack.push(function(props) {\r\n                return BdApi.React.createElement(ConfirmationModal, Object.assign({\r\n                    header: title,\r\n                    children: [BdApi.React.createElement(TextElement, {color: TextElement.Colors.PRIMARY, children: [`The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`]})],\r\n                    red: false,\r\n                    confirmText: \"Download Now\",\r\n                    cancelText: \"Cancel\",\r\n                    onConfirm: () => {\r\n                        require(\"request\").get(\"https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js\", async (error, response, body) => {\r\n                            if (error) return require(\"electron\").shell.openExternal(\"https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js\");\r\n                            await new Promise(r => require(\"fs\").writeFile(require(\"path\").join(ContentManager.pluginsFolder, \"0PluginLibrary.plugin.js\"), body, r));\r\n                        });\r\n                    }\r\n                }, props));\r\n            });\r\n        }\r\n  \r\n\t\t\r\n\t\tstart() {}\r\n\t\tstop() {}\r\n\t} : (([Plugin, Api]) => {\r\n\t\tconst plugin = (Plugin, Api) => {\r\n\t\t\treturn class PreventSpotifyAutoPause extends Plugin {\r\n\t\t\t\tonStart() {\r\n                    this.unpatch = Api.Patcher.instead(Api.WebpackModules.getByProps(\"SpotifyAPI\", \"pause\"), \"pause\", () => {});\r\n\t\t\t\t}\r\n\t\r\n\t\t\t\tonStop() {\r\n                    this.unpatch();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\treturn plugin(Plugin, Api);\r\n\t})(global.ZeresPluginLibrary.buildPlugin(config));\r\n})();\r\n/*@end@*/\r\n"
  },
  {
    "path": "README.md",
    "content": "# BetterDiscordPlugins\n\n## NOTE: Due to loss of interest in BetterDiscord development, I'm pursuing other projects and no longer maintaining this repository. I will try to keep up with pull requests, but I will likely not be fixing any plugins myself. I apologize for the inconvenience. You can view my other projects at https://kinzoku.one, if you're interested.\n\n## If you have any questions, feel free to contact me on any of my social platforms\n- https://kinzoku.one/contact\n## My support server\n- https://discord.gg/yNqzuJa\n\n# You can view information on my plugins [here](https://metalloriff.github.io/toms-discord-stuff/).\n- https://metalloriff.github.io/toms-discord-stuff/\n# You can also view my other projects here:\n- https://kinzoku.one/\n"
  },
  {
    "path": "ReactionImages.plugin.js",
    "content": "/**\r\n * @name ReactionImages\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www.paypal.me/israelboone\r\n * @website https://metalloriff.github.io/toms-discord-stuff/\r\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/ReactionImages.plugin.js\r\n */\r\n\r\nmodule.exports = (() => {\r\n\tconst config = {\r\n\t\tinfo: {\r\n\t\t\tname: \"ReactionImages\",\r\n\t\t\tauthors: [{\r\n\t\t\t\tname: \"Metalloriff\",\r\n\t\t\t\tdiscord_id: \"264163473179672576\",\r\n\t\t\t\tgithub_username: \"metalloriff\",\r\n\t\t\t\ttwitter_username: \"Metalloriff\"\r\n\t\t\t}],\r\n\t\t\tversion: \"2.0.1\",\r\n\t\t\tdescription: \"Allows you to define custom reaction image folders and send images with 'foldername/imagesearch'. Example: 'reactions/goodmeme'\",\r\n\t\t\tgithub: \"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/ReactionImages.plugin.js\",\r\n\t\t\tgithub_raw: \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/ReactionImages.plugin.js\"\r\n\t\t},\r\n\t\tchangelog: [{\r\n\t\t\ttype: \"fixed\",\r\n\t\t\ttitle: \"Shitcord patch\",\r\n\t\t\titems: [\r\n\t\t\t\t\"Shitcord broke, I fixed. Thanks Strencher for letting me know.\"\r\n\t\t\t]\r\n\t\t}],\r\n\t\tdefaultConfig: [{\r\n\t\t\ttype: \"category\",\r\n\t\t\tid: \"general\",\r\n\t\t\tname: \"general settings\",\r\n\t\t\tcollapsible: true,\r\n\t\t\tshown: true,\r\n\t\t\tsettings: [{\r\n\t\t\t\ttype: \"textbox\",\r\n\t\t\t\tid: \"paths\",\r\n\t\t\t\tname: \"Folder Sources\",\r\n\t\t\t\tnote: \"Note: Separate folder paths with commas.\",\r\n\t\t\t\tplaceholder: \"Example: C:/Folder1, C:/Folder2/Some Other Folder, C:/Folder3\",\r\n\t\t\t\tvalue: \"\"\r\n\t\t\t}, {\r\n\t\t\t\ttype: \"slider\",\r\n\t\t\t\tid: \"acSize\",\r\n\t\t\t\tname: \"Autocomplete Image Size\",\r\n\t\t\t\tvalue: 200, min: 50, max: 500\r\n\t\t\t}, {\r\n\t\t\t\ttype: \"slider\",\r\n\t\t\t\tid: \"acCap\",\r\n\t\t\t\tname: \"Autocomplete Result Cap\",\r\n\t\t\t\tnote: \"The maximum number of images that will be displayed at the same time. Reduce this if you experience freezes when searching.\",\r\n\t\t\t\tvalue: 20, min: 5, max: 100\r\n\t\t\t}]\r\n\t\t}]\r\n\t};\r\n\r\n\treturn !global.ZeresPluginLibrary ? class {\r\n\t\tconstructor() { this._config = config; }\r\n\r\n\t\tgetName = () => config.info.name;\r\n\t\tgetAuthor = () => config.info.description;\r\n\t\tgetVersion = () => config.info.version;\r\n\r\n\t\tload() {\r\n\t\t\tBdApi.showConfirmationModal(\"Library Missing\", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {\r\n\t\t\t\tconfirmText: \"Download Now\",\r\n\t\t\t\tcancelText: \"Cancel\",\r\n\t\t\t\tonConfirm: () => {\r\n\t\t\t\t\trequire(\"request\").get(\"https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js\", async (err, res, body) => {\r\n\t\t\t\t\t\tif (err) return require(\"electron\").shell.openExternal(\"https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js\");\r\n\t\t\t\t\t\tawait new Promise(r => require(\"fs\").writeFile(require(\"path\").join(BdApi.Plugins.folder, \"0PluginLibrary.plugin.js\"), body, r));\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tstart() { }\r\n\t\tstop() { }\r\n\t} : (([Plugin, Api]) => {\r\n\r\n\t\tconst plugin = (Plugin, Api) => {\r\n\t\t\tconst { WebpackModules, DiscordModules, PluginUtilities, Toasts, Patcher, ReactTools } = Api;\r\n\t\t\tconst { UserStore, React } = DiscordModules;\r\n\r\n\t\t\tconst { getCurrentUser } = UserStore;\r\n\r\n\t\t\tconst Autocomplete = WebpackModules.getByDisplayName(\"Autocomplete\");\r\n\t\t\tconst { AUTOCOMPLETE_OPTIONS } = WebpackModules.getByProps(\"AUTOCOMPLETE_OPTIONS\");\r\n\t\t\tconst { upload } = WebpackModules.getByProps(\"cancel\", \"upload\");\r\n\r\n\t\t\tconst fs = require(\"fs\");\r\n\t\t\tconst path = require(\"path\");\r\n\r\n\t\t\tconst classes = {\r\n\t\t\t\t...WebpackModules.getByProps(\"horizontalAutocomplete\"),\r\n\t\t\t\t...WebpackModules.getByProps(\"horizontal\", \"directionRowReverse\", \"noWrap\"),\r\n\t\t\t\t...WebpackModules.getByProps(\"justifyStart\", \"alignStretch\", \"noWrap\")\r\n\t\t\t};\r\n\r\n\t\t\tclass AutocompleteItem extends React.Component {\r\n\t\t\t\trender() {\r\n\t\t\t\t\tconst { fp, data, onClick, selected, index, height } = this.props;\r\n\r\n\t\t\t\t\treturn React.createElement(\r\n\t\t\t\t\t\t\"div\",\r\n\t\t\t\t\t\t{},\r\n\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\"div\",\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tstyle: {\r\n\t\t\t\t\t\t\t\t\tcolor: \"white\",\r\n\t\t\t\t\t\t\t\t\ttextAlign: \"center\"\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\tpath.basename(fp).split(\".\")[0],\r\n\t\t\t\t\t\t),\r\n\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\tAutocomplete.GIFIntegration,\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tclassName: classes.horizontalAutocomplete,\r\n\t\t\t\t\t\t\t\tsrc: `data:image/${path.extname(fp)};base64, ${data}`,\r\n\t\t\t\t\t\t\t\theight, onClick, selected, index\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\treturn class ReactionImages extends Plugin {\r\n\t\t\t\tfolders = {};\r\n\r\n\t\t\t\tconstructor() {\r\n\t\t\t\t\tsuper();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tgetDataName = () => this.getName() + \".\" + getCurrentUser().id;\r\n\t\t\t\tloadSettings = s => PluginUtilities.loadSettings(this.getDataName(), this.defaultSettings || s);\r\n\t\t\t\tsaveSettings = s => PluginUtilities.saveSettings(this.getDataName(), this.settings || s);\r\n\r\n\t\t\t\tasync showChangelog(footer) {\r\n\t\t\t\t\ttry { footer = (await WebpackModules.getByProps(\"getUser\", \"acceptAgreements\").getUser(\"264163473179672576\")).tag + \" | https://discord.gg/yNqzuJa\"; }\r\n\t\t\t\t\tfinally { super.showChangelog(footer); }\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tgetSettingsPanel() {\r\n\t\t\t\t\tconst panel = this.buildSettingsPanel();\r\n\r\n\t\t\t\t\tpanel.addListener(() => {\r\n\t\t\t\t\t\tthis.term();\r\n\t\t\t\t\t\tthis.init();\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\treturn panel.getElement();\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tasync init() {\r\n\t\t\t\t\tthis.fetchAllFolders();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tasync fetchAllFolders() {\r\n\t\t\t\t\tToasts.show(\"ReactionImages: Loading folder data...\");\r\n\r\n\t\t\t\t\tthis.folders = {};\r\n\r\n\t\t\t\t\tconst folderPaths = this.settings.general.paths.split(\",\").map(p => p.trim());\r\n\r\n\t\t\t\t\tfor (let folder of folderPaths) {\r\n\t\t\t\t\t\tconst folderName = path.basename(folder).toLowerCase().replace(/ /g, \"\");\r\n\r\n\t\t\t\t\t\tfs.readdir(folder, async (err, fileNames) => {\r\n\t\t\t\t\t\t\tif (err) {\r\n\t\t\t\t\t\t\t\tToasts.show(\"ReactionImages: Failed to load folder named '\" + folderName + \"'!\", { type: \"error\" });\r\n\r\n\t\t\t\t\t\t\t\treturn console.error(err);\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tthis.folders[folderName] = [];\r\n\r\n\t\t\t\t\t\t\tfor (let fileName of fileNames) {\r\n\t\t\t\t\t\t\t\tconst ext = fileName.split(\".\")[fileName.split(\".\").length - 1];\r\n\r\n\t\t\t\t\t\t\t\tif (!fileName.includes(\".\") || ![\"jpg\", \"jpeg\", \"png\", \"gif\", \"bmp\"].includes(ext))\r\n\t\t\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\tconst fp = path.join(folder, fileName);\r\n\t\t\t\t\t\t\t\tconst data = await new Promise(r => fs.readFile(fp, \"base64\", (_, d) => r(d)));\r\n\r\n\t\t\t\t\t\t\t\tthis.folders[folderName].push({\r\n\t\t\t\t\t\t\t\t\tdata,\r\n\t\t\t\t\t\t\t\t\tfp,\r\n\t\t\t\t\t\t\t\t\tsize: data.length\r\n\t\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tToasts.show(\"ReactionImages: Successfully loaded folder named '\" + folderName + \"'.\", { type: \"success\" });\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tgetFolders() {\r\n\t\t\t\t\treturn this.settings.general.paths.split(\",\").map(fp => ([ fp.trim(), path.basename(fp.trim()) ]));\r\n\t\t\t\t}\r\n\r\n\t\t\t\tterm() {\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tholdingShift = false;\r\n\t\t\t\tupdateHoldingShift = e => this.holdingShift = e.shiftKey;\r\n\r\n\t\t\t\tasync onStart() {\r\n\t\t\t\t\tthis.init();\r\n\r\n\t\t\t\t\tdocument.addEventListener(\"keydown\", this.updateHoldingShift);\r\n\t\t\t\t\tdocument.addEventListener(\"keyup\", this.updateHoldingShift);\r\n\r\n\t\t\t\t\tAUTOCOMPLETE_OPTIONS.REACTION_IMAGES = {\r\n\t\t\t\t\t\tautoSelect: true,\r\n\r\n\t\t\t\t\t\tgetPlainText: () => \"\",\r\n\t\t\t\t\t\tgetRawText: () => \"\",\r\n\t\t\t\t\t\tgetSentinel: () => \"\",\r\n\r\n\t\t\t\t\t\tmatches: (channel, {}, query, {}, config, queryRaw) => {\r\n\t\t\t\t\t\t\tfor (const [ fp, dirName ] of this.getFolders()) {\r\n\t\t\t\t\t\t\t\tif (query.split(\"/\")[0] == dirName.toLowerCase()) {\r\n\t\t\t\t\t\t\t\t\treturn true;\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\treturn false;\r\n\t\t\t\t\t\t},\r\n\r\n\t\t\t\t\t\tqueryResults: (channel, query, config, queryRaw) => {\r\n\t\t\t\t\t\t\tconst [ folderName, reactionQuery ] = query.split(\"/\");\r\n\t\t\t\t\t\t\tconst results = [];\r\n\t\t\t\t\t\t\tconst folder = this.folders[folderName];\r\n\r\n\t\t\t\t\t\t\tif (folder && folder.length) {\r\n\t\t\t\t\t\t\t\tfor (const file of folder) {\r\n\t\t\t\t\t\t\t\t\tconst fn = path.basename(file.fp);\r\n\r\n\t\t\t\t\t\t\t\t\tif (fn.toLowerCase().replace(/ /g, \"\").startsWith(reactionQuery)) {\r\n\t\t\t\t\t\t\t\t\t\tresults.push(file);\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\treturn { results };\r\n\t\t\t\t\t\t},\r\n\r\n\t\t\t\t\t\trenderResults: (channel, query, selectedIndex, select, choose, config, _, { results }) => {\r\n\t\t\t\t\t\t\tconst strong = c => React.createElement(\"strong\", {}, c);\r\n\r\n\t\t\t\t\t\t\treturn [\r\n\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\tAutocomplete.Title,\r\n\t\t\t\t\t\t\t\t\t{ title: [ \"Searching \", strong(`\"${query.split(\"/\")[1]}\"`), \" in \", strong(query.split(\"/\")[0]) ] }\r\n\t\t\t\t\t\t\t\t),\r\n\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\"div\",\r\n\t\t\t\t\t\t\t\t\t{ className: [ classes.flex, classes.horizontal, classes.justifyStart, classes.alignStretch, classes.noWrap, classes.horizontalAutocompletes ].join(\" \") },\r\n\t\t\t\t\t\t\t\t\tresults.map(({ data, fp, size }, index) => React.createElement(\r\n\t\t\t\t\t\t\t\t\t\tAutocompleteItem,\r\n\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\tfp, data,\r\n\t\t\t\t\t\t\t\t\t\t\tonClick: () => {\r\n\t\t\t\t\t\t\t\t\t\t\t\tconst { deserialize } = WebpackModules.getByProps(\"serialize\", \"deserialize\");\r\n\t\t\t\t\t\t\t\t\t\t\t\tconst { channelTextArea } = WebpackModules.getByProps(\"channelTextArea\", \"channelTextAreaDisabled\");\r\n\t\t\t\t\t\t\t\t\t\t\t\tconst [ cta ] = document.getElementsByClassName(channelTextArea);\r\n\t\t\t\t\t\t\t\t\t\t\t\tconst owner = ReactTools.getOwnerInstance(cta);\r\n\t\t\t\t\t\t\t\t\t\t\t\tconst { state: { textValue } } = owner;\r\n\r\n\t\t\t\t\t\t\t\t\t\t\t\tupload(channel.id, new File([ fs.readFileSync(fp) ], path.basename(fp)), {\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tcontent: textValue.split(\" \").splice(0, textValue.split(\" \").length - 1).join(\" \")\r\n\t\t\t\t\t\t\t\t\t\t\t\t});\r\n\r\n\t\t\t\t\t\t\t\t\t\t\t\tif (!this.holdingShift) {\r\n\t\t\t\t\t\t\t\t\t\t\t\t\towner.setState({ textValue: \"\", richValue: deserialize(\"\") });\r\n\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (!this.settings.hasShownShiftNotice) {\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tToasts.show(\"Note: You can hold shift to prevent the menu from closing when sending a reaction!\", { type: \"success\" });\r\n\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tthis.settings.hasShownShiftNotice = true;\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tthis.saveSettings();\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\tselected: selectedIndex == index,\r\n\t\t\t\t\t\t\t\t\t\t\theight: this.settings.general.acSize,\r\n\t\t\t\t\t\t\t\t\t\t\tindex,\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t)).slice(0, this.settings.general.acCap)\r\n\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t];\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t};\r\n\t\t\t\t}\r\n\r\n\t\t\t\tonStop() {\r\n\t\t\t\t\tthis.term();\r\n\r\n\t\t\t\t\tdocument.removeEventListener(\"keydown\", this.updateHoldingShift);\r\n\t\t\t\t\tdocument.removeEventListener(\"keyup\", this.updateHoldingShift);\r\n\r\n\t\t\t\t\tdelete AUTOCOMPLETE_OPTIONS.REACTION_IMAGES;\r\n\r\n\t\t\t\t\tPatcher.unpatchAll();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\treturn plugin(Plugin, Api);\r\n\t})(global.ZeresPluginLibrary.buildPlugin(config));\r\n})();\r\n"
  },
  {
    "path": "SaveTo.plugin.js",
    "content": "//META{\"name\":\"SaveTo\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/SaveTo.plugin.js\"}*//\r\n\r\nclass SaveTo {\r\n\r\n\tgetName() { return \"Save To\"; }\r\n\tgetDescription() { return \"Allows you to save images, videos, files, server icons and user avatars to your defined folders, or browse to a folder, via the context menu.\"; }\r\n\tgetVersion() { return \"0.7.9\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\tgetChanges() {\r\n\t\treturn {\r\n\t\t\t\"0.1.2\" :\r\n\t\t\t`\r\n\t\t\t\tAdded sort mode settings, including custom sorting.\r\n\t\t\t\tYou can now add, remove and modify folders via the settings menu.\r\n\t\t\t`,\r\n\t\t\t\"0.2.2\" :\r\n\t\t\t`\r\n\t\t\t\tInstead of just random numbers, the random file names are now random characters.\r\n\t\t\t\tAdded a changelog toggle setting.\r\n\t\t\t\tAdded a view changelog button.\r\n\t\t\t`,\r\n\t\t\t\"0.3.2\" :\r\n\t\t\t`\r\n\t\t\t\tFixed the context menu being slightly delayed, causing an ugly flash effect.\r\n\t\t\t\tAdded a \"Save and Open File\" choice to the folder submenus.\r\n\t\t\t`,\r\n\t\t\t\"0.4.2\" :\r\n\t\t\t`\r\n\t\t\t\tAdded a setting to move the dropdown menu to the top of the context menu.\r\n\t\t\t`,\r\n\t\t\t\"0.5.4\" :\r\n\t\t\t`\r\n\t\t\t\tFixed a bunch of bugs.\r\n\t\t\t\tEmotes are now saved as the emote name instead of ID.\r\n\t\t\t`,\r\n\t\t\t\"0.5.5\" :\r\n\t\t\t`\r\n\t\t\t\tFixed \"Save File Here\" and \"Save and Open File\" not working.\r\n\t\t\t`,\r\n\t\t\t\"0.6.7\" :\r\n\t\t\t`\r\n\t\t\t\tAdded a \"Save File Here As\" option to folder context menus.\r\n\t\t\t`,\r\n\t\t\t\"0.7.8\" :\r\n\t\t\t`\r\n\t\t\t\tYou can now save emojis from the emoji picker.\r\n\t\t\t\tFixed saving avatars.\r\n\t\t\t\tFixed saving server icons.\r\n\t\t\t`\r\n\t\t};\r\n\t}\r\n\r\n\tsaveSettings() {\r\n\t\tNeatoLib.Settings.save(this);\r\n\t}\r\n\t\r\n\tsaveData() {\r\n\t\tNeatoLib.Data.save(\"SaveTo\", \"data\", this.data);\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\r\n\t\tsetTimeout(() => {\r\n\r\n\t\t\tlet date = new Date();\r\n\r\n\t\t\tlet refreshFolders = () => {\r\n\r\n\t\t\t\tlet fields = [];\r\n\r\n\t\t\t\tfor (let i = 0; i < this.data.folders.length; i++) {\r\n\r\n\t\t\t\t\tlet textField = NeatoLib.Settings.Elements.createNewTextField(\"\", this.data.folders[i].path, e => {\r\n\t\t\t\t\t\tlet name = e.target.value.substring(e.target.value.split(\"\\\\\").join(\"/\").lastIndexOf(\"/\") + 1, e.target.value.length);\r\n\t\t\t\t\t\tif (e.target.value.trim().length == 0 || name.length == 0) {\r\n\t\t\t\t\t\t\tthis.data.folders.splice(i, 1);\r\n\t\t\t\t\t\t\te.target.parentElement.outerHTML = \"\";\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\tthis.data.folders[i].path = e.target.value;\r\n\t\t\t\t\t\t\tthis.data.folders[i].name = name;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\trefreshFolders();\r\n\t\t\t\t\t\tthis.saveData();\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\ttextField.insertAdjacentElement(\"beforeend\", NeatoLib.Settings.Elements.createButton(\"Browse\", e => {\r\n\t\t\t\t\t\tNeatoLib.browseForFile(folder => {\r\n\t\t\t\t\t\t\te.target.parentElement.getElementsByClassName(\"input\")[0].value = folder.path;\r\n\t\t\t\t\t\t\tthis.data.folders[i].path = folder.path;\r\n\t\t\t\t\t\t\tthis.data.folders[i].name = folder.name;\r\n\t\t\t\t\t\t\trefreshFolders();\r\n\t\t\t\t\t\t\tthis.saveData();\r\n\t\t\t\t\t\t}, {\r\n\t\t\t\t\t\t\tdirectory: true\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\t}, \"float:right;\"));\r\n\r\n\t\t\t\t\tlet positionField = NeatoLib.Settings.Elements.createNewTextField(\"\", this.data.folders[i].position, e => {\r\n\t\t\t\t\t\tif (e.target.value.length == 0) e.target.value = this.data.folders[i].position;\r\n\t\t\t\t\t\telse this.data.folders[i].position = e.target.value;\r\n\t\t\t\t\t\trefreshFolders();\r\n\t\t\t\t\t\tthis.saveData();\r\n\t\t\t\t\t}).getElementsByTagName(\"input\")[0];\r\n\r\n\t\t\t\t\tpositionField.style.paddingRight = \"10px\";\r\n\t\t\t\t\tpositionField.style.width = \"100px\";\r\n\t\t\t\t\tpositionField.style.float = \"left\";\r\n\r\n\t\t\t\t\ttextField.setAttribute(\"data-path\", this.data.folders[i].path);\r\n\t\t\t\t\ttextField.setAttribute(\"data-name\", this.data.folders[i].name);\r\n\t\t\t\t\ttextField.setAttribute(\"data-priority\", this.data.folders[i].position);\r\n\r\n\t\t\t\t\ttextField.getElementsByTagName(\"input\")[0].style.width = \"430px\";\r\n\r\n\t\t\t\t\ttextField.addEventListener(\"click\", () => this.selectedFolder = i);\r\n\r\n\t\t\t\t\ttextField.insertAdjacentElement(\"afterbegin\", positionField);\r\n\r\n\t\t\t\t\tfields.push(textField);\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (this.settings.sortMode == \"a-z\" || this.settings.sortMode == \"z-a\") fields = fields.sort((x, y) => {\r\n\t\t\t\t\tif (x.getAttribute(\"data-name\").toLowerCase() < y.getAttribute(\"data-name\").toLowerCase()) return -1;\r\n\t\t\t\t\tif (x.getAttribute(\"data-name\").toLowerCase() > y.getAttribute(\"data-name\").toLowerCase()) return 1;\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\t});\r\n\r\n\t\t\t\tif (this.settings.sortMode == \"z-a\" || this.settings.sortMode == \"new-old\") fields = fields.reverse();\r\n\r\n\t\t\t\tif (this.settings.sortMode == \"custom\") fields = fields.sort((x, y) => {\r\n\t\t\t\t\tif (parseFloat(x.getAttribute(\"data-priority\")) > parseFloat(y.getAttribute(\"data-priority\"))) return -1;\r\n\t\t\t\t\tif (parseFloat(x.getAttribute(\"data-priority\")) < parseFloat(y.getAttribute(\"data-priority\"))) return 1;\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\t});\r\n\r\n\t\t\t\tdocument.getElementById(\"st-folders\").innerHTML = \"\";\r\n\r\n\t\t\t\tfor (let i = 0; i < fields.length; i++) document.getElementById(\"st-folders\").insertAdjacentElement(\"beforeend\", fields[i]);\r\n\r\n\t\t\t};\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createRadioGroup(\"st-file-name-type\", \"File name type:\", [{\r\n\t\t\t\t\ttitle: \"Original\",\r\n\t\t\t\t\tvalue: \"original\",\r\n\t\t\t\t\tdescription: `Example: unknown.png`\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\ttitle: \"Date\",\r\n\t\t\t\t\tvalue: \"date\",\r\n\t\t\t\t\tdescription: `Example: ${date.toLocaleDateString().split(\"/\").join(\"-\")} ${date.getMinutes()}${date.getSeconds()}${date.getMilliseconds()}.png`\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\ttitle: \"Random\",\r\n\t\t\t\t\tvalue: \"random\",\r\n\t\t\t\t\tdescription: `Example: ${Math.random().toString(36).substring(10)}.png`\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\ttitle: \"Original + random\",\r\n\t\t\t\t\tvalue: \"original+random\",\r\n\t\t\t\t\tdescription: `Example: unknown ${Math.random().toString(36).substring(10)}.png`\r\n\t\t\t\t}\r\n\t\t\t], this.settings.fileNameType, (e, choiceItem) => {\r\n\t\t\t\tthis.settings.fileNameType = choiceItem.value;\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t}), this.getName());\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createRadioGroup(\"st-sort-type\", \"Sort by:\", [{\r\n\t\t\t\t\ttitle: \"A - Z\",\r\n\t\t\t\t\tvalue: \"a-z\"\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\ttitle: \"Z - A\",\r\n\t\t\t\t\tvalue: \"z-a\"\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\ttitle: \"Newest - Oldest\",\r\n\t\t\t\t\tvalue: \"new-old\"\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\ttitle: \"Oldest - Newest\",\r\n\t\t\t\t\tvalue: \"old-new\"\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\ttitle: \"By priority\",\r\n\t\t\t\t\tvalue: \"custom\"\r\n\t\t\t\t}\r\n\t\t\t], this.settings.sortMode, (e, choiceItem) => {\r\n\t\t\t\tthis.settings.sortMode = choiceItem.value;\r\n\t\t\t\trefreshFolders();\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t}), this.getName());\r\n\r\n\t\t\tlet folderLabels = document.createElement(\"div\");\r\n\r\n\t\t\tfolderLabels.insertAdjacentHTML(\"beforeend\", `<h5 style=\"color:white;padding-bottom:10px;padding-top:20px;\">Folders:</h5>`);\r\n\t\t\tfolderLabels.insertAdjacentHTML(\"beforeend\", `<h5 style=\"color:white;padding-bottom:10px;width:100px;display:inline-block;\">Prioirty</h5>`);\r\n\t\t\tfolderLabels.insertAdjacentHTML(\"beforeend\", `<h5 style=\"color:white;padding-bottom:10px;width:430px;display:inline-block;\">Path</h5>`);\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(folderLabels, this.getName());\r\n\r\n\t\t\tlet foldersParentDiv = document.createElement(\"div\");\r\n\r\n\t\t\tfoldersParentDiv.setAttribute(\"id\", \"st-folders\");\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(foldersParentDiv, this.getName());\r\n\r\n\t\t\trefreshFolders();\r\n\r\n\t\t\tNeatoLib.Settings.pushHTML(`<div id=\"st-settings-buttons\" style=\"text-align:center;padding-top:20px;\"></div>`, this.getName());\r\n\r\n\t\t\tlet buttonParent = document.getElementById(\"st-settings-buttons\");\r\n\r\n\t\t\tbuttonParent.insertAdjacentElement(\"beforeend\", NeatoLib.Settings.Elements.createButton(\"Add Folder\", () => {\r\n\t\t\t\tthis.browseForFolder(() => refreshFolders());\r\n\t\t\t}));\r\n\r\n\t\t\tbuttonParent.insertAdjacentElement(\"beforeend\", NeatoLib.Settings.Elements.createButton(\"Remove Selected Folder\", () => {\r\n\t\t\t\tthis.data.folders.splice(this.selectedFolder, 1);\r\n\t\t\t\trefreshFolders();\r\n\t\t\t\tthis.saveData();\r\n\t\t\t}, \"margin-left:15px;\", {\r\n\t\t\t\tid: \"st-settings-remove-folder\"\r\n\t\t\t}));\r\n\r\n\t\t\tbuttonParent.insertAdjacentElement(\"beforeend\", NeatoLib.Settings.Elements.createButton(\"Open Selected Folder\", () => {\r\n\t\t\t\twindow.open(`file:///${this.data.folders[this.selectedFolder].path}`);\r\n\t\t\t}, \"margin-left:15px;\", {\r\n\t\t\t\tid: \"st-settings-open-folder\"\r\n\t\t\t}));\r\n\r\n\t\t\tNeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createToggleSwitch(\"Move dropdown menu to the top of the context menu\", this.settings.dropdownOnTop, () => {\r\n\t\t\t\tthis.settings.dropdownOnTop = !this.settings.dropdownOnTop;\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t}), this.getName());\r\n\r\n\t\t\tNeatoLib.Settings.pushChangelogElements(this);\r\n\r\n\t\t}, 0);\r\n\r\n\t\treturn NeatoLib.Settings.Elements.pluginNameLabel(this.getName());\r\n\r\n\t}\r\n\r\n\tload() {}\r\n\r\n\tstart() {\r\n\t\tconst libLoadedEvent = () => {\r\n\t\t\ttry{ this.onLibLoaded(); }\r\n\t\t\tcatch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n\t\t};\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif (!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n\t\tif (typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\t}\r\n\r\n\tonLibLoaded() {\r\n\t\tNeatoLib.Updates.check(this);\r\n\r\n\t\tthis.data = NeatoLib.Data.load(\"SaveTo\", \"data\", {\r\n\t\t\tfolders: []\r\n\t\t});\r\n\r\n\t\tlet updated = [];\r\n\r\n\t\tfor (let i = 0; i < this.data.folders.length; i++) {\r\n\t\t\tif (typeof this.data.folders[i] !== \"object\") {\r\n\t\t\t\tlet p = this.data.folders[i];\r\n\r\n\t\t\t\tupdated.push({\r\n\t\t\t\t\tpath: p,\r\n\t\t\t\t\tname: p.substring(p.split(\"\\\\\").join(\"/\").lastIndexOf(\"/\") + 1, p.length),\r\n\t\t\t\t\tposition: i\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (updated.length > 0) this.data.folders = updated;\r\n\r\n\t\tthis.settings = NeatoLib.Settings.load(this, {\r\n\t\t\tfileNameType: \"original\",\r\n\t\t\tsortMode: \"a-z\",\r\n\t\t\tdisplayUpdateNotes: true,\r\n\t\t\tdropdownOnTop: true\r\n\t\t});\r\n\r\n\t\tthis.selectedFolder = -1;\r\n\r\n\t\tdocument.addEventListener(\"contextmenu\", this.contextMenuEvent = e => {\r\n\t\t\tif (document.getElementsByClassName(NeatoLib.getClass(\"contextMenu\")).length == 0) setTimeout(() => this.onContextMenu(e), 0);\r\n\t\t\telse this.onContextMenu(e);\r\n\t\t});\r\n\r\n\t\tNeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());\r\n\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\t}\r\n\r\n\tonContextMenu(e) {\r\n\t\tlet member = NeatoLib.DOM.searchForParentElementByClassName(e.target, NeatoLib.getClass(\"member\")),\r\n\t\t\tdm = NeatoLib.DOM.searchForParentElementByClassName(e.target, \"private\") || NeatoLib.DOM.searchForParentElementByClassName(e.target, \"friends-row\"),\r\n\t\t\tmessageGroup = NeatoLib.DOM.searchForParentElementByClassName(e.target, NeatoLib.getClass(\"containerCozy\", \"container\")),\r\n\t\t\tuser = NeatoLib.ReactData.get(NeatoLib.ContextMenu.get()) ? NeatoLib.ReactData.get(NeatoLib.ContextMenu.get()).return.return.return.return.memoizedProps.user : null,\r\n\t\t\tguild = NeatoLib.ReactData.getProp(NeatoLib.ContextMenu.get(), \"guild\");\r\n\r\n\t\tif (e.target.localName != \"a\" && e.target.localName != \"img\" && e.target.localName != \"video\" && !member && !dm && !messageGroup && !e.target.className.includes(\"emojiItem\") && !user && !guild) return;\r\n\r\n\t\tlet saveLabel = \"Save To\",\r\n\t\t\turl = e.target.poster || e.target.style.backgroundImage.substring(e.target.style.backgroundImage.indexOf(`\"`) + 1, e.target.style.backgroundImage.lastIndexOf(`\"`)) || e.target.href || e.target.src,\r\n\t\t\tmenu = [];\r\n\r\n\t\tif (user) {\r\n\t\t\turl = user.getAvatarURL();\r\n\t\t\tif (url.includes(\"/a_\")) url = url.replace(\".png\", \".gif\");\r\n\t\t\r\n\t\t\tsaveLabel = \"Save Avatar To\";\r\n\t\t}\r\n\r\n\t\tif (guild) {\r\n\t\t\turl = guild.getIconURL();\r\n\r\n\t\t\tsaveLabel = \"Save Icon To\";\r\n\t\t}\r\n\r\n\t\tif (e.target.className.includes(\"guildIcon\")) saveLabel = \"Save Icon To\";\r\n\r\n\t\tif ((!url && !e.target.className.includes(\"emojiItem\")) || e.target.classList.contains(\"emote\") || url.includes(\"youtube.com/\")) return;\r\n\r\n\t\turl = url.split(\"?\")[0];\r\n\r\n\t\tif (saveLabel.includes(\"Avatar\") || saveLabel.includes(\"Icon\")) url += \"?size=2048\";\r\n\r\n\t\turl = url.replace(\".webp\", \".png\");\r\n\r\n\t\tlet fileName = url.substring(url.lastIndexOf(\"/\") + 1, url.length),\r\n\t\t\tfileExtension = url.substring(url.lastIndexOf(\".\"), url.length);\r\n\r\n\t\tif (e.target.classList.contains(\"emoji\")) {\r\n\t\t\tsaveLabel = \"Save Emoji To\";\r\n\t\t\tfileName = NeatoLib.ReactData.getProps(e.target).emojiName.replace(/[^A-Za-z]/g, \"\") + fileExtension;\r\n\t\t}\r\n\r\n\t\tif (e.target.className.includes(\"emojiItem\")) {\r\n\t\t\tsaveLabel = \"Save Emoji To\";\r\n\t\t\tfileName = (url = e.target.style.backgroundImage.split('\"')[1]).split(\"?\")[0].split(\"/\")[4];\r\n\t\t}\r\n\r\n\t\tlet date = new Date();\r\n\r\n\t\tif (this.settings.fileNameType == \"date\") fileName = `${date.toLocaleDateString().split(\"/\").join(\"-\")} ${date.getMinutes()}${date.getSeconds()}${date.getMilliseconds()}${fileExtension}`;\r\n\r\n\t\tif (this.settings.fileNameType == \"random\") fileName = `${Math.random().toString(36).substring(10)}${fileExtension}`;\r\n\r\n\t\tif (this.settings.fileNameType == \"original+random\") fileName = fileName.replace(fileExtension, \"\") + ` ${Math.random().toString(36).substring(10)}${fileExtension}`;\r\n\r\n\t\tlet optionsSubMenu = i => {\r\n\t\t\tlet r = [];\r\n\r\n\t\t\tr.push(NeatoLib.ContextMenu.createItem(\"Remove Folder\", e => {\r\n\t\t\t\tthis.data.folders.splice(i, 1);\r\n\t\t\t\tthis.saveData();\r\n\t\t\t\tNeatoLib.ContextMenu.close();\r\n\t\t\t}));\r\n\r\n\t\t\tr.push(NeatoLib.ContextMenu.createItem(\"Open Folder\", () => {\r\n\t\t\t\twindow.open(\"file:///\" + this.data.folders[i].path);\r\n\t\t\t}));\r\n\r\n\t\t\tr.push(NeatoLib.ContextMenu.createItem(\"Save File Here\", () => {\r\n\t\t\t\tNeatoLib.downloadFile(url, this.data.folders[i].path, fileName);\r\n\t\t\t}));\r\n\r\n\t\t\tr.push(NeatoLib.ContextMenu.createItem(\"Save File Here As\", () => {\r\n\t\t\t\tNeatoLib.ContextMenu.close();\r\n\r\n\t\t\t\tconsole.log(fileName);\r\n\r\n\t\t\t\tNeatoLib.UI.createTextPrompt(\"save-file-as\", \"Save File As...\", (filename, prompt) => {\r\n\t\t\t\t\tif (!filename) NeatoLib.showToast(\"File not saved! No filename specified!\", \"error\");\r\n\t\t\t\t\telse NeatoLib.downloadFile(url, this.data.folders[i].path, filename + \".\" + fileName.split(\".\")[fileName.split(\".\").length - 1]);\r\n\r\n\t\t\t\t\tprompt.close();\r\n\t\t\t\t}, fileName.split(\".\")[0]);\r\n\t\t\t}));\r\n\r\n\t\t\tr.push(NeatoLib.ContextMenu.createItem(\"Save and Open File\", () => {\r\n\t\t\t\tNeatoLib.downloadFile(url, this.data.folders[i].path, fileName, p => window.open(\"file:///\" + p));\r\n\t\t\t}));\r\n\r\n\t\t\treturn r;\r\n\t\t};\r\n\r\n\t\tlet sorted = this.data.folders;\r\n\r\n\t\tif (this.settings.sortMode == \"a-z\" || this.settings.sortMode == \"z-a\") sorted = sorted.sort((x, y) => {\r\n\t\t\tif (x.name.toLowerCase() < y.name.toLowerCase()) return -1;\r\n\t\t\tif (x.name.toLowerCase() > y.name.toLowerCase()) return 1;\r\n\t\t\treturn 0;\r\n\t\t});\r\n\r\n\t\tif (this.settings.sortMode == \"z-a\" || this.settings.sortMode == \"old-new\") sorted = sorted.reverse();\r\n\r\n\t\tif (this.settings.sortMode == \"custom\") sorted = sorted.sort((x, y) => {\r\n\t\t\tif (x.position > y.position) return -1;\r\n\t\t\tif (x.position < y.position) return 1;\r\n\t\t\treturn 0;\r\n\t\t});\r\n\r\n\t\tlet g = [];\r\n\t\tfor (let i = 0; i < this.data.folders.length; i++) {\r\n\t\t\tg.push(NeatoLib.ContextMenu.createSubMenu(this.data.folders[i].name, optionsSubMenu(i), {\r\n\t\t\t\tcallback: () => NeatoLib.downloadFile(url, this.data.folders[i].path, fileName)\r\n\t\t\t}));\r\n\t\t}\r\n\t\tmenu.push(NeatoLib.ContextMenu.createGroup(g));\r\n\r\n\t\tmenu.push(NeatoLib.ContextMenu.createGroup([\r\n\t\t\tNeatoLib.ContextMenu.createItem(\"Add Folder\", () => this.browseForFolder()),\r\n\t\t\tNeatoLib.ContextMenu.createItem(\"Browse\", () => NeatoLib.browseForFile(folder => NeatoLib.downloadFile(url, folder.path, fileName), {\r\n\t\t\t\tdirectory: true\r\n\t\t\t})),\r\n\t\t\tNeatoLib.ContextMenu.createItem(\"Plugin Settings\", () => {\r\n\t\t\t\tNeatoLib.Settings.showPluginSettings(this.getName());\r\n\t\t\t\tNeatoLib.ContextMenu.close();\r\n\t\t\t})\r\n\t\t]));\r\n\r\n\t\tif (NeatoLib.ContextMenu.get()) NeatoLib.ContextMenu.get().insertAdjacentElement(this.settings.dropdownOnTop ? \"afterbegin\" : \"beforeend\", NeatoLib.ContextMenu.createGroup([NeatoLib.ContextMenu.createSubMenu(saveLabel, menu)]));\r\n\t\telse NeatoLib.ContextMenu.create([NeatoLib.ContextMenu.createGroup([NeatoLib.ContextMenu.createSubMenu(saveLabel, menu)])], e);\r\n\t}\r\n\r\n\tbrowseForFolder(selected) {\r\n\t\tNeatoLib.browseForFile(folder => {\r\n\t\t\tif (this.data.folders.findIndex(f => f.path == folder.path) == -1) {\r\n\t\t\t\tthis.data.folders.push({\r\n\t\t\t\t\tpath: folder.path,\r\n\t\t\t\t\tname: folder.name,\r\n\t\t\t\t\tposition: this.data.folders.length\r\n\t\t\t\t});\r\n\r\n\t\t\t\tthis.saveData();\r\n\t\t\t}\r\n\r\n\t\t\tif (selected) selected();\r\n\t\t}, {\r\n\t\t\tdirectory: true\r\n\t\t});\r\n\t}\r\n\r\n\tstop() {\r\n\t\tdocument.removeEventListener(\"contextmenu\", this.contextMenuEvent);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "SelectedChannelNotifications.plugin.js",
    "content": "//META{\"name\":\"SelectedChannelNotifications\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/SelectedChannelNotifications.plugin.js\"}*//\r\n\r\nclass SelectedChannelNotifications {\r\n\t\r\n    getName() { return \"Selected Channel Notifications\"; }\r\n    getDescription() { return \"Plays a sound and displays a notification (both optional) when Discord is minimized and a message is received in the selected channel.\"; }\r\n    getVersion() { return \"0.1.6\"; }\r\n    getAuthor() { return \"Metalloriff\"; }\r\n\r\n    load() {}\r\n\r\n    start() {\r\n\t\tconst libLoadedEvent = () => {\r\n\t\t\ttry{ this.onLibLoaded(); }\r\n\t\t\tcatch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n\t\t};\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif (!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n\t\tif (typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\t}\r\n\r\n\tget settingFields() {\r\n\t\treturn {\r\n\t\t\tmessageSound: { label: \"Message notification sound\", type: \"string\" },\r\n\t\t\tplaySound: { label: \"Play sound on message\", type: \"bool\" },\r\n\t\t\tdisplayNotification: { label: \"Display notification on message\", type: \"bool\" }\r\n\t\t};\r\n\t}\r\n\r\n\tget defaultSettings() {\r\n\t\treturn {\r\n            messageSound : \"https://discordapp.com/assets/dd920c06a01e5bb8b09678581e29d56f.mp3\",\r\n            playSound : true,\r\n            displayNotification : true\r\n        };\r\n\t}\r\n\t\r\n\tgetSettingsPanel() {\r\n\t\treturn NeatoLib.Settings.createPanel(this);\r\n\t}\r\n\t\r\n\tsaveSettings() {\r\n\t\tNeatoLib.Settings.save(this);\r\n\t\tthis.audio.src = this.settings.messageSound;\r\n\t\tthis.audio.volume = this.volumeModule.getOutputVolume() / 200;\r\n\t}\r\n\t\r\n\tonLibLoaded() {\r\n\t\tNeatoLib.Updates.check(this);\r\n\r\n\t\tthis.channelModule = NeatoLib.Modules.get(\"getChannel\");\r\n\t\tthis.notificationSettingsModule = NeatoLib.Modules.get(\"getChannelMessageNotifications\");\r\n\t\tthis.volumeModule = NeatoLib.Modules.get(\"getOutputVolume\");\r\n\r\n\t\tNeatoLib.Settings.load(this);\r\n\r\n\t\tthis.audio = new Audio();\r\n\r\n\t\tthis.audio.src = this.settings.messageSound;\r\n\t\tthis.audio.volume = this.volumeModule.getOutputVolume() / 200;\r\n\r\n\t\tthis.notifications = [];\r\n\r\n\t\tthis.focused = true;\r\n\t\t\r\n\t\twindow.addEventListener(\"focus\", this.focus = () => {\r\n\t\t\tthis.focused = true;\r\n\t\t\tfor (let i = 0; i < this.notifications.length; i++) this.notifications[i].close();\r\n\t\t});\r\n\r\n\t\twindow.addEventListener(\"blur\", this.unfocus = () => {\r\n\t\t\tthis.focused = false;\r\n\t\t});\r\n\r\n\t\tNeatoLib.Events.attach(\"message\", this.msgRec = () => this.onMessageReceived());\r\n\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n    }\r\n\r\n    onMessageReceived() {\r\n\t\tif (!this.focused && NeatoLib.getLocalStatus() != \"dnd\") {\r\n\t\t\tconst messages = document.getElementsByClassName(NeatoLib.getClass(\"containerCozy\", \"container\")),\r\n\t\t\t\tlastGroup = NeatoLib.ReactData.getProps(messages[messages.length - 1]).messages,\r\n\t\t\t\tlastMsg = lastGroup[lastGroup.length - 1];\r\n\t\t\t\r\n\t\t\tif (lastMsg.author.id != NeatoLib.getLocalUser().id && !lastMsg.mentioned) {\r\n\t\t\t\tif (this.settings.displayNotification) {\r\n\t\t\t\t\tconst n = new Notification(\r\n\t\t\t\t\t\t(!lastMsg.nick ? lastMsg.author.username : lastMsg.nick) + \" - #\" + NeatoLib.getSelectedTextChannel().name, {\r\n\t\t\t\t\t\t\tsilent: true,\r\n\t\t\t\t\t\t\tbody: lastMsg.content,\r\n\t\t\t\t\t\t\ticon: lastMsg.author.getAvatarURL()\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t);\r\n\r\n\t\t\t\t\tconst i = this.notifications.push(n) - 1;\r\n\r\n\t\t\t\t\tn.onclose = () => this.notifications.splice(i, 1);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (this.settings.playSound) this.audio.play();\r\n\t\t\t}\r\n\t\t}\r\n    }\r\n\t\r\n    stop() {\r\n\t\twindow.removeEventListener(\"focus\", this.focus);\r\n\t\twindow.removeEventListener(\"blur\", this.unfocus);\r\n\r\n\t\tNeatoLib.Events.detach(\"message\", this.msgRec);\r\n    }\r\n\t\r\n}\r\n"
  },
  {
    "path": "SendBDEmotes.plugin.js",
    "content": "//META{\"name\":\"SendBDEmotes\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/SendBDEmotes.plugin.js\"}*//\r\n\r\nclass SendBDEmotes {\r\n\r\n  getName() { return \"Send BD Emotes\"; }\r\n  getDescription() { return \"Allows you to enclose Better Discord emotes in square brackets to send them as a higher resolution link that all users can see. Example: [forsenE]. You can also do [EmoteChannelName.EmoteName]. Example: [FrankerFaceZ.SeemsGood]. [EmoteName:size]. Example: [forsenE:1]. And [EmoteName_a] for animated emotes.\"; }\r\n  getVersion() { return \"1.6.12\"; }\r\n  getAuthor() { return \"Metalloriff\"; }\r\n\r\n\tload() {}\r\n\r\n\tstart() {\r\n\t\tconst libLoadedEvent = () => {\r\n\t\t\ttry{ this.onLibLoaded(); }\r\n\t\t\tcatch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n\t\t};\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif (!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n\t\tif (typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\t}\r\n\r\n\tget settingFields() {\r\n\t\treturn {\r\n\t\t\temoteSize: { label: \"Sent emote size\", type: \"radio\", choices: {\r\n\t\t\t\t1: { label: \"Small\", description: \"BetterDiscord Default\" },\r\n\t\t\t\t2: { label: \"Medium\" },\r\n\t\t\t\t4: { label: \"Large\" }\r\n\t\t\t}},\r\n\t\t\tsendAsLink: { label: \"Send emotes as links\", description: \"(faster)\", type: \"bool\" },\r\n\t\t\tdisplayPreview: { label: \"Display auto-complete\", type: \"bool\" },\r\n\t\t\tforceGif: { label: \"Send emotes as gif\", type: \"bool\" },\r\n\t\t\tpreviewLimit: { label: \"Maximum amount of emotes to display on auto-complete\", type: \"int\" }\r\n\t\t};\r\n\t}\r\n\r\n\tget defaultSettings() {\r\n\t\treturn {\r\n\t\t\temoteSize: 4,\r\n\t\t\tsendAsLink: false,\r\n\t\t\tdisplayPreview: true,\r\n\t\t\tforceGif: false,\r\n\t\t\tpreviewLimit: 25\r\n\t\t};\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\t\treturn NeatoLib.Settings.createPanel(this);\r\n\t}\r\n\r\n\tsaveSettings() {\r\n\t\tNeatoLib.Settings.save(this);\r\n\t}\r\n\r\n\tonLibLoaded() {\r\n\t\tNeatoLib.Updates.check(this);\r\n\r\n\t\tthis.hasPermission = NeatoLib.Modules.get([\"can\"]).can;\r\n\t\tthis.uploadFile = NeatoLib.Modules.findAllByPropertyName('upload')[1];\r\n\t\tthis.messageModule = NeatoLib.Modules.get([\"sendMessage\"]);\r\n\r\n\t\tthis.settings = NeatoLib.Settings.load(this);\r\n\r\n\t\tthis.keyDownEvent = e => this.onKeyDown(e);\r\n\t\tthis.keyUpEvent = e => this.onKeyUp(e);\r\n\r\n\t\tNeatoLib.Events.attach(\"switch\", this.switchEvent = () => this.switch());\r\n\r\n\t\tthis.getEmotes();\r\n\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\r\n\t\tthis.switch();\r\n\t}\r\n\r\n\tonKeyDown(e) {\r\n\t\tif (!e.target.value) {\r\n\t\t\tif (document.getElementById(\"sbde-autocomplete\")) document.getElementById(\"sbde-autocomplete\").remove();\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst words = e.target.value.split(\" \"), lastWord = words[words.length - 1];\r\n\r\n\t\tif (e.key == \"Enter\" && !e.shiftKey && lastWord.startsWith(\"[\") && lastWord.endsWith(\"]\")) {\r\n\t\t\tlet emoteName = lastWord.substring(1, lastWord.length - 1), size = this.settings.emoteSize, animated = false;\r\n\r\n\t\t\tif (emoteName.includes(\":\")) {\r\n\t\t\t\tconst trySize = parseInt(emoteName.substring(emoteName.indexOf(\":\") + 1, emoteName.length));\r\n\t\t\t\tif (!isNaN(trySize)) {\r\n\t\t\t\t\tsize = trySize;\r\n\t\t\t\t\temoteName = emoteName.substring(0, emoteName.indexOf(\":\"));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (emoteName.endsWith(\"_a\")) {\r\n\t\t\t\tanimated = true;\r\n\t\t\t\temoteName = emoteName.replace(\"_a\", \"\");\r\n\t\t\t}\r\n\r\n\t\t\tlet emote = window.bdEmotes.TwitchGlobal[emoteName] || window.bdEmotes.TwitchSubscriber[emoteName] || window.bdEmotes.BTTV[emoteName] || window.bdEmotes.FrankerFaceZ[emoteName] || window.bdEmotes.BTTV2[emoteName];\r\n\r\n\t\t\tif (emoteName.includes(\".\")) {\r\n\t\t\t\tconst sourceAndName = emoteName.split(\".\");\r\n\t\t\t\temote = window.bdEmotes[sourceAndName[0]][sourceAndName[1]];\r\n\t\t\t}\r\n\r\n\t\t\tif (emote) {\r\n\t\t\t\tconst message = e.target.value.split(lastWord).join(\"\");\r\n\r\n\t\t\t\tNeatoLib.Chatbox.setText(\"\");\r\n\r\n\t\t\t\tthis.trySend(message, emoteName, emote, size, animated);\r\n\t\t\t} else if (emoteName == \"RANDOM\") {\r\n\t\t\t\tconst randomEmote = this.emotes[this.emotes.length * Math.random() << 0], message = e.target.value.split(lastWord).join(\"\");\r\n\r\n\t\t\t\tNeatoLib.Chatbox.setText(\"\");\r\n\r\n\t\t\t\tthis.trySend(message, randomEmote.name, randomEmote.url, size, animated);\r\n\t\t\t}\r\n\r\n\t\t\tif (document.getElementById(\"sbde-autocomplete\")) document.getElementById(\"sbde-autocomplete\").remove();\r\n\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif (lastWord.endsWith(\"]\")) {\r\n\t\t\tif (document.getElementById(\"sbde-autocomplete\")) document.getElementById(\"sbde-autocomplete\").remove();\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n\tasync onKeyUp(e) {\r\n\t\tif (!this.settings.displayPreview) return;\r\n\r\n\t\tconst words = e.target.value.split(\" \"), lastWord = words[words.length - 1];\r\n\t\tlet autocomplete = document.getElementById(\"sbde-autocomplete\");\r\n\r\n\t\tif (lastWord.startsWith(\"[\")) {\r\n\t\t\tconst emoteName = lastWord.substring(1, lastWord.length);\r\n\r\n\t\t\tif (!autocomplete) {\r\n\t\t\t\te.target.parentElement.insertAdjacentHTML(\"beforeend\", `\r\n\t\t\t\t\t<div id=\"sbde-autocomplete\" class=\"autocomplete-1vrmpx autocomplete-i9yVHs\" style=\"width:inherit\">\r\n\t\t\t\t\t\t<div class=\"autocompleteInner-zh20B_\">\r\n\t\t\t\t\t\t\t<div class=\"autocompleteRowVertical-q1K4ky autocompleteRow-2OthDa\">\r\n\t\t\t\t\t\t\t\t<div class=\"selector-2IcQBU\">\r\n\t\t\t\t\t\t\t\t\t<div class=\"contentTitle-2tG_sM small-29zrCQ size12-3R0845 height16-2Lv3qA weightSemiBold-NJexzi\"></div>\r\n\t\t\t\t\t\t\t\t\t<span class=\"material-icons\" style=\"cursor:pointer;color:white\">looks_one</span>\r\n\t\t\t\t\t\t\t\t\t<span class=\"material-icons\" style=\"cursor:pointer;color:white\">looks_two</span>\r\n\t\t\t\t\t\t\t\t\t<span class=\"material-icons\" style=\"cursor:pointer;color:white\">looks_3</span>\r\n\t\t\t\t\t\t\t\t\t<span class=\"material-icons\" style=\"cursor:pointer;color:white\">loop</span>\r\n\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t<div id=\"sbde-autocomplete-list\" class=\"flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2NDFzi alignStretch-DpGPf3 noWrap-3jynv6 horizontalAutocompletes-x8hlrn scrollbarGhostHairline-1mSOM1 scrollbar-3dvm_9\" style=\"flex: 1 1 auto;\">\r\n\r\n\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t`);\r\n\r\n\t\t\t\tautocomplete = document.getElementById(\"sbde-autocomplete\");\r\n\r\n\t\t\t\tconst buttons = autocomplete.getElementsByClassName(\"material-icons\");\r\n\r\n\t\t\t\tconst sizeButton = (button, size) => {\r\n\t\t\t\t\tif (this.settings.emoteSize == size) button.style.color = NeatoLib.Colors.DiscordDefaults.blue;\r\n\t\t\t\t\telse button.style.color = \"white\";\r\n\r\n\t\t\t\t\tbutton.onclick = () => {\r\n\t\t\t\t\t\tthis.settings.emoteSize = size;\r\n\t\t\t\t\t\tthis.saveSettings();\r\n\t\t\t\t\t\tupdate();\r\n\t\t\t\t\t};\r\n\t\t\t\t};\r\n\r\n\t\t\t\tconst update = () => {\r\n\t\t\t\t\tfor (let i = 0; i < buttons.length; i++) {\r\n\t\t\t\t\t\tconst button = buttons[i];\r\n\t\t\t\t\t\tswitch (button.textContent) {\r\n\t\t\t\t\t\t\tcase \"looks_one\":\r\n\t\t\t\t\t\t\t\tsizeButton(button, 1);\r\n\r\n\t\t\t\t\t\t\t\tif (!button.tooltip) NeatoLib.Tooltip.attach(\"Small\", button);\r\n\t\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\t\tcase \"looks_two\":\r\n\t\t\t\t\t\t\t\tsizeButton(button, 2);\r\n\r\n\t\t\t\t\t\t\t\tif (!button.tooltip) NeatoLib.Tooltip.attach(\"Medium\", button);\r\n\t\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\t\tcase \"looks_3\":\r\n\t\t\t\t\t\t\t\tsizeButton(button, 4);\r\n\r\n\t\t\t\t\t\t\t\tif (!button.tooltip) NeatoLib.Tooltip.attach(\"Large\", button);\r\n\t\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\t\tcase \"loop\":\r\n\t\t\t\t\t\t\t\tif (this.settings.forceGif) button.style.color = NeatoLib.Colors.DiscordDefaults.blue;\r\n\t\t\t\t\t\t\t\telse button.style.color = \"white\";\r\n\r\n\t\t\t\t\t\t\t\tbutton.onclick = () => {\r\n\t\t\t\t\t\t\t\t\tthis.settings.forceGif = !this.settings.forceGif;\r\n\t\t\t\t\t\t\t\t\tthis.saveSettings();\r\n\t\t\t\t\t\t\t\t\tupdate();\r\n\t\t\t\t\t\t\t\t};\r\n\r\n\t\t\t\t\t\t\t\tif (!button.tooltip) NeatoLib.Tooltip.attach(\"Send as gif\", button);\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t};\r\n\r\n\t\t\t\tupdate();\r\n\t\t\t}\r\n\r\n\t\t\tconst list = document.getElementById(\"sbde-autocomplete-list\");\r\n\t\t\tlist.innerHTML = \"\";\r\n\r\n\t\t\tlet lim = 0;\r\n\t\t\tfor (let i = 0; i < this.emotes.length; i++) {\r\n\t\t\t\tconst emote = this.emotes[i];\r\n\r\n\t\t\t\tif (emote.name.startsWith(emoteName)) {\r\n\t\t\t\t\tif (lim >= this.settings.previewLimit) break;\r\n\r\n\t\t\t\t\tlist.insertAdjacentHTML(\"beforeend\", `\r\n\t\t\t\t\t\t<div class=\"horizontalAutocomplete-1DAQoM autocompleteRowHorizontal-32jwnH autocompleteRow-2OthDa\">\r\n\t\t\t\t\t\t\t<div class=\"selectorSelected-1_M1WV selector-2IcQBU selectable-3dP3y-\"><img src=\"${this.emotes[i].url}\" width=\"50\" height=\"50\"></div>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t`);\r\n\r\n\t\t\t\t\tconst images = list.getElementsByTagName(\"img\"), lastImage = images[images.length - 1];\r\n\r\n\t\t\t\t\tNeatoLib.Tooltip.attach(emote.name, lastImage);\r\n\r\n\t\t\t\t\tlastImage.addEventListener(\"click\", ev => {\r\n\t\t\t\t\t\tif (ev.shiftKey) this.trySend(\"\", emote.name, emote.url, this.settings.emoteSize);\r\n\t\t\t\t\t\telse {\r\n\t\t\t\t\t\t\tthis.trySend(e.target.value.split(lastWord).join(\"\"), emote.name, emote.url, this.settings.emoteSize);\r\n\r\n\t\t\t\t\t\t\tNeatoLib.Chatbox.setText(\"\");\r\n\r\n\t\t\t\t\t\t\tautocomplete.remove();\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\tlim++;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} else if (autocomplete) autocomplete.remove();\r\n\t}\r\n\r\n\tasync getEmotes() {\r\n\t\tconst channels = Object.keys(window.bdEmotes);\r\n\r\n\t\tthis.emotes = [];\r\n\r\n\t\tfor (let ec of channels) {\r\n\t\t\tconst channel = window.bdEmotes[ec];\r\n\r\n\t\t\tfor (let emote in channel) {\r\n\t\t\t\tthis.emotes.push({\r\n\t\t\t\t\tname: emote,\r\n\t\t\t\t\turl: channel[emote]\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthis.emotes.sort((x, y) => x.name.length - y.name.length);\r\n\t}\r\n\r\n\ttrySend(message, emoteName, emoteURL, size = 4, animated = false) {\r\n\t\tif (this.settings.forceGif) animated = true;\r\n\r\n\t\tconst i = emoteURL.lastIndexOf(\"1\"), url = emoteURL.substring(0, i) + size + emoteURL.substring(i + 1);\r\n\r\n\t\tif (this.settings.sendAsLink) this.messageModule.sendMessage(NeatoLib.getSelectedTextChannel().id, { content: message + \" \" + url });\r\n\t\telse NeatoLib.requestFile(url, emoteName + (animated ? \".gif\" : \".png\"), file => {\r\n\t\t\tif (file.size < 100) {\r\n\t\t\t\tif (size > 1) this.trySend(message, emoteName, emoteURL, size - 1, animated);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tthis.uploadFile(NeatoLib.getSelectedTextChannel().id, file, { content: message, tts: false });\r\n\t\t});\r\n\t}\r\n\r\n\tswitch () {\r\n\t\tif (!this.ready) return;\r\n\r\n\t\tif (!this.emotes || this.emotes.length < 700000) this.getEmotes();\r\n\r\n\t\tconst chatbox = NeatoLib.Chatbox.get();\r\n\r\n\t\tif (chatbox) {\r\n\t\t\tchatbox.addEventListener(\"keydown\", this.keyDownEvent);\r\n\t\t\tchatbox.addEventListener(\"keyup\", this.keyUpEvent);\r\n\t\t}\r\n\t}\r\n\r\n\tstop() {\r\n\t\tconst chatbox = NeatoLib.Chatbox.get();\r\n\r\n\t\tif (chatbox) {\r\n\t\t\tchatbox.removeEventListener(\"keydown\", this.keyDownEvent);\r\n\t\t\tchatbox.removeEventListener(\"keyup\", this.keyUpEvent);\r\n\t\t}\r\n\r\n\t\tNeatoLib.Events.detach(\"switch\", this.switchEvent);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "SendLinksDirectly.plugin.js",
    "content": "//META{\"name\":\"SendLinksDirectly\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/SendLinksDirectly.plugin.js\"}*//\r\n\r\nclass SendLinksDirectly {\r\n\r\n  getName() { return \"SendLinksDirectly\"; }\r\n  getDescription() { return `Allows you to enclose direct links in square brackets to upload them directly, instead of sending a link.\r\n  Usage: [link] or [link, filename.fileformat]\r\n  Example: [https://static-cdn.jtvnw.net/emoticons/v1/521050/4.0, forsenE.png]`; }\r\n  getVersion() { return \"1.1.4\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\tgetChanges() {\r\n\t\treturn {\r\n\t\t\t\"1.1.3\":\r\n\t\t\t`\r\n\t\t\t\tYou can now send files from your PC with files paths.\r\n\t\t\t`\r\n\t\t};\r\n\t}\r\n\r\n\tload() {}\r\n\r\n\tstart() {\r\n\t\tconst libLoadedEvent = () => {\r\n\t\t\ttry{ this.onLibLoaded(); }\r\n\t\t\tcatch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n\t\t};\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif (!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n\t\tif (typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\t}\r\n\r\n\tonLibLoaded() {\r\n\t\tNeatoLib.Updates.check(this);\r\n\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\r\n\t\tconst fs = require(\"fs\");\r\n\r\n\t\tthis.keyEvent = e => {\r\n\t\t\tconst chatbox = e.target,\r\n\t\t\t\tselectedChannel = NeatoLib.getSelectedTextChannel();\r\n\r\n\t\t\tif (selectedChannel == undefined) return;\r\n\r\n\t\t\tif (e.which == 13 && !e.shiftKey && chatbox.value.trim() != \"\") {\r\n\t\t\t\tlet search = chatbox.value.split(\"[\").join(\"][\").split(\"]\"),\r\n\t\t\t\t\tlinks = [],\r\n\t\t\t\t\tfiles = [],\r\n\t\t\t\t\tmessage = chatbox.value;\r\n\r\n\t\t\t\tfor (let i = 1; i < search.length - 1; i++) {\r\n\t\t\t\t\tsearch[i] = search[i].split(\"\\\\\").join(\"/\").split(\"\\\"\").join(\"\");\r\n\r\n\t\t\t\t\tif (search[i].startsWith(\"[http\")) {\r\n\t\t\t\t\t\tlinks.push(search[i].substring(1, search[i].length).split(\",\"));\r\n\t\t\t\t\t\tmessage = message.substring(0, message.indexOf(\"[\")) + message.substring(message.indexOf(\"]\") + 1, message.length);\r\n\t\t\t\t\t} else if (search[i].substring(1, search[i].length).match(/^[A-Z]:\\//)) {\r\n\t\t\t\t\t\tfiles.push(search[i].substring(1, search[i].length).split(\",\"));\r\n\t\t\t\t\t\tmessage = message.substring(0, message.indexOf(\"[\")) + message.substring(message.indexOf(\"]\") + 1, message.length);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tfor (let i = 0; i < links.length; i++) {\r\n\t\t\t\t\tconst filename = links[i][0].substring(links[i][0].lastIndexOf(\"/\") + 1, links[i][0].length).split(\"?\")[0];\r\n\r\n\t\t\t\t\tNeatoLib.requestFile(links[i][0], links[i].length > 1 ? links[i][1] : filename, file => {\r\n\t\t\t\t\t\tNeatoLib.Modules.find(m => m.upload && typeof m.upload === 'function').upload(selectedChannel.id, file, {\r\n\t\t\t\t\t\t\tcontent: i == 0 ? message : \"\",\r\n\t\t\t\t\t\t\ttts: false\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\r\n\t\t\t\tfor (let i = 0; i < files.length; i++) {\r\n\t\t\t\t\tconst filename = files[i][0].substring(files[i][0].lastIndexOf(\"/\") + 1, files[i][0].length);\r\n\r\n\t\t\t\t\tNeatoLib.Modules.find(m => m.upload && typeof m.upload === 'function').upload(selectedChannel.id, new File([fs.readFileSync(files[i][0])], files[i].length > 1 ? files[i][1] : filename), {\r\n\t\t\t\t\t\tcontent: i == 0 ? message : \"\",\r\n\t\t\t\t\t\ttts: false\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (links.length > 0 || files.length > 0) NeatoLib.Chatbox.setText(\"\");\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tthis.switchEvent = () => this.switch();\r\n\r\n\t\tthis.switch();\r\n\r\n\t\tMetalloriff.Changelog.compareVersions(this.getName(), this.getChanges());\r\n\r\n\t\tNeatoLib.Events.attach(\"switch\", this.switchEvent);\r\n\t}\r\n\r\n\tswitch () {\r\n\t\tconst chatbox = NeatoLib.Chatbox.get();\r\n\r\n\t\tif (chatbox == undefined) return;\r\n\r\n\t\tchatbox.addEventListener(\"keydown\", this.keyEvent);\r\n\t}\r\n\r\n\tstop() {\r\n\t\tif (NeatoLib.Chatbox.get()) NeatoLib.Chatbox.get().removeEventListener(\"keydown\", this.keyEvent);\r\n\t\tNeatoLib.Events.detach(\"switch\", this.switchEvent);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "ShareButton.plugin.js",
    "content": "//META{\"name\":\"ShareButton\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/ShareButton.plugin.js\"}*//\r\n\r\nclass ShareButton {\r\n\t\r\n    getName() { return \"Share Button\"; }\r\n    getDescription() { return \"Allows you to easily share images, videos, links and messages to other channels and servers via the context menu and message dropdown menu.\"; }\r\n    getVersion() { return \"0.2.9\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\tgetChanges() {\r\n\t\treturn {\r\n            \"0.1.2\" : \r\n            `\r\n                Halfed the size of the server items.\r\n                Added a submenu for pinned and recent channels in the context menu.\r\n                You can now share messages via the context menu.\r\n                Shared messages are now quoted with the user's name.\r\n                You can now share to direct messages.\r\n            `,\r\n            \"0.2.7\" :\r\n            `\r\n                Fixed the plugin, but the recent channels is still broken. I will try to fix it soon.\r\n            `,\r\n            \"0.2.8\" :\r\n            `\r\n                Fixed not showing guilds\r\n            `\r\n\t\t};\r\n\t}\r\n\r\n    load() {}\r\n\r\n\tstart() {\r\n\t\tconst libLoadedEvent = () => {\r\n\t\t\ttry{ this.onLibLoaded(); }\r\n\t\t\tcatch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n\t\t};\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif (!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n\t\tif (typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\t}\r\n    \r\n    getSettingsPanel() {\r\n        setTimeout(() => {\r\n            Metalloriff.Settings.pushChangelogElements(this);\r\n        }, 0);\r\n\r\n        return Metalloriff.Settings.Elements.pluginNameLabel(this.getName());\r\n    }\r\n\r\n    saveSettings() {\r\n        NeatoLib.Settings.save(this);\r\n    }\r\n\t\r\n\tonLibLoaded() {\r\n        NeatoLib.Updates.check(this);\r\n\r\n        this.settings = NeatoLib.Settings.load(this, {\r\n            displayUpdateNotes : true\r\n        });\r\n\r\n        Metalloriff.Changelog.compareVersions(this.getName(), this.getChanges());\r\n\r\n        this.guildModule = NeatoLib.Modules.get([\"getGuild\", \"getGuilds\"]);\r\n        this.sortedGuildModule = NeatoLib.Modules.get(\"getSortedGuilds\");\r\n        this.channelModule = NeatoLib.Modules.get([\"getChannel\", \"getChannels\"]);\r\n        this.dmModule = NeatoLib.Modules.get(\"getPrivateChannelIds\");\r\n        this.userModule = NeatoLib.Modules.get([\"getUser\", \"getUsers\"]);\r\n        this.transitionModule = NeatoLib.Modules.get(\"transitionTo\");\r\n        this.messageModule = NeatoLib.Modules.get(\"sendMessage\");\r\n\r\n        let data = NeatoLib.Data.load(\"ShareButton\", \"data\", { recentChannels : [], pinnedChannels : [] });\r\n\r\n        this.recentChannels = data.recentChannels || [];\r\n        this.pinnedChannels = data.pinnedChannels || [];\r\n\r\n        this.popoutObserver = new MutationObserver(m => {\r\n            if(m[0].addedNodes && m[0].addedNodes[0] instanceof Element && m[0].addedNodes[0].firstChild && m[0].addedNodes[0].firstChild.classList.contains(\"option-popout\")) {\r\n                let dropdown = e[0].addedNodes[0].firstChild, message = NeatoLib.ReactData.getProps(dropdown).message;\r\n                dropdown.insertAdjacentHTML(\"afterBegin\", `<div id=\"sb-share-popout\" class=\"btn-item\">Share</div>`);\r\n                document.getElementById(\"sb-share-popout\").addEventListener(\"click\", () => {\r\n                    this.openShareMenu(undefined, NeatoLib.Modules.get(\"message\").message.split(\" \").join(\"\"), `\"${message.content}\" - ${message.author.username}`);\r\n                    dropdown.style.display = \"none\";\r\n                });\r\n            }\r\n        });\r\n\r\n        setTimeout(() => {\r\n            if(document.getElementsByClassName(\"popouts-3dRSmE\")) this.popoutObserver.observe(document.getElementsByClassName(\"popouts-3dRSmE\")[0], { childList : true });\r\n        }, 5000);\r\n\r\n        this.contextEvent = e => {\r\n            if(!NeatoLib.ContextMenu.get()) setTimeout(() => this.onContextMenu(e), 0);\r\n            else this.onContextMenu(e);\r\n        };\r\n\r\n        document.addEventListener(\"contextmenu\", this.contextEvent);\r\n\r\n        NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());\r\n\r\n        NeatoLib.Events.onPluginLoaded(this);\r\n\r\n    }\r\n    \r\n    saveData() { NeatoLib.Data.save(\"ShareButton\", \"data\", { recentChannels : this.recentChannels, pinnedChannels : this.pinnedChannels }); }\r\n\r\n    onContextMenu(e) {\r\n        if(e.target.localName != \"img\" && e.target.localName != \"video\" && !e.target.className.includes(\"markup\")) return;\r\n\r\n        let choices = [], pinnedChannelsItem = [], recentChannelsItem = [];\r\n\r\n        let channelClick = (channel, ce) => {\r\n\r\n            let url = e.target.src;\r\n\r\n            if(!url) url = e.target.href;\r\n\r\n            url = url.split(\"?\")[0];\r\n\r\n            let msg = NeatoLib.ReactData.getProps(e.target).message;\r\n\r\n            if(!url) url = `\"${msg.content}\" - ${msg.author.username}`;\r\n\r\n            ce.currentTarget.dataset.guildId = channel.guild_id;\r\n            ce.currentTarget.dataset.channelId = channel.id;\r\n            ce.currentTarget.dataset.content = url;\r\n\r\n            this.sendMessage(ce);\r\n\r\n            ce.currentTarget.innerText = \"Sent!\";\r\n            ce.currentTarget.style.backgroundColor = \"#43b581\";\r\n            ce.currentTarget.style.cursor = \"default\";\r\n\r\n            ce.currentTarget.onclick = null;\r\n\r\n        };\r\n\r\n        for(let i = 0; i < this.pinnedChannels.length; i++) {\r\n            let channel = this.channelModule.getChannel(this.pinnedChannels[i]);\r\n\r\n            if(!channel) continue;\r\n\r\n            pinnedChannelsItem.push(NeatoLib.ContextMenu.createItem(\"#\" + channel.name, ce => channelClick(channel, ce), { hint : this.guildModule.getGuild(channel.guild_id).name }));\r\n        }\r\n\r\n        for(let i = 0; i < this.recentChannels.length; i++) {\r\n            let channel = this.channelModule.getChannel(this.recentChannels[i]);\r\n\r\n            if(!channel) continue;\r\n\r\n            recentChannelsItem.push(NeatoLib.ContextMenu.createItem(\"#\" + channel.name, ce => channelClick(channel, ce), { hint : this.guildModule.getGuild(channel.guild_id).name }));\r\n        }\r\n\r\n        let open = () => { this.openShareMenu(e); NeatoLib.ContextMenu.close(); };\r\n        \r\n        choices.push(NeatoLib.ContextMenu.createSubMenu(\"Pinned Channels\", pinnedChannelsItem));\r\n        choices.push(NeatoLib.ContextMenu.createSubMenu(\"Recent Channels\", recentChannelsItem));\r\n        choices.push(NeatoLib.ContextMenu.createItem(\"Open Share Menu\", open));\r\n\r\n        NeatoLib.ContextMenu.get().insertAdjacentElement(\"afterBegin\", NeatoLib.ContextMenu.createSubMenu(\"Share\", [NeatoLib.ContextMenu.createGroup(choices)], { callback : open }));\r\n    }\r\n\r\n    openShareMenu(e, definedName, definedData) {\r\n        if(document.getElementById(\"sb-menu\")) return document.getElementById(\"sb-menu\").remove();\r\n\r\n        let menu = NeatoLib.UI.createBasicScrollList(\"sb-menu\", \"Share\");\r\n\r\n        menu.window.insertAdjacentHTML(\"afterBegin\", `\r\n            <style>\r\n            #sb-menu {\r\n                z-index: 1000;\r\n                position: absolute;\r\n                left: 45%;\r\n                bottom: 8%;\r\n            }\r\n\r\n            .sb-label {\r\n                color: white;\r\n                font-size: 30px;\r\n                padding-top: 20px;\r\n                padding-left: 20px;\r\n            }\r\n\r\n            .sb-button {\r\n                border-radius: 10px;\r\n                background-color: rgba(0, 0, 0, 0.2);\r\n                cursor: pointer;\r\n                transition: background 0.3s;\r\n            }\r\n\r\n            .sb-button:hover {\r\n                background-color: rgba(255, 255, 255, 0.2);\r\n            }\r\n\r\n            .sb-button:active {\r\n                background-color: rgba(0, 0, 0, 0.3);\r\n            }\r\n\r\n            .sb-guilds {\r\n                max-height: 870px;\r\n            }\r\n            \r\n            .sb-channel-item-button {\r\n                text-align: center;\r\n                margin: 5px;\r\n            }\r\n\r\n            .sb-channel-item {\r\n                margin: auto;\r\n                width: 95%;\r\n                height: 40px;\r\n                line-height: 40px;\r\n                color: white;\r\n                margin-top: 18px;\r\n            }\r\n\r\n            .sb-server-item {\r\n                margin: 5px;\r\n                margin-top: 10px;\r\n                padding: 1px;\r\n            }\r\n\r\n            .sb-server-item-icon {\r\n                width: 25px;\r\n                height: 25px;\r\n                background-color: rgba(0, 0, 0, 0.2);\r\n                background-size: cover;\r\n                margin: 10px;\r\n                border-radius: 5px;\r\n            }\r\n\r\n            .sb-server-item-label {\r\n                color: white;\r\n                display: inline-block;\r\n                margin-left: 35px;\r\n                width: 600px;\r\n                font-size: 25px;\r\n                font-weight: 500;\r\n            }\r\n            </style>\r\n        `);\r\n        \r\n        let url, filename;\r\n\r\n        if(definedName && definedData) {\r\n            url = definedData;\r\n            filename = definedName;\r\n        } else {\r\n            url = e.target.src;\r\n\r\n            if(!url) {\r\n                url = e.target.href;\r\n                filename = url;\r\n            } else {\r\n                url = url.split(\"?\")[0];\r\n                filename = url.substring(url.lastIndexOf(\"/\") + 1, url.length);\r\n            }\r\n        }\r\n\r\n        let msg = NeatoLib.ReactData.getProps(e.target).message;\r\n\r\n        if(!url) url = `\"${msg.content}\" - ${msg.author.username}`;\r\n\r\n        if(filename) menu.window.getElementsByTagName(\"h2\")[0].innerText += filename;\r\n\r\n        let recentChannels, pinnedChannels;\r\n\r\n        let updateChannels = () => {\r\n            let ci = document.getElementsByClassName(\"sb-channel-item-button\");\r\n\r\n            for(let i = 0; i < ci.length; i++) {\r\n                ci[i].onclick = channelClickEvent;\r\n                ci[i].oncontextmenu = channelContextEvent;\r\n            }\r\n        },\r\n        \r\n        updateRecentChannels = () => {\r\n            if(recentChannels) {\r\n                recentChannels.remove();\r\n                recentChannels = null;\r\n            }\r\n\r\n            for(let i = 0; i < this.recentChannels.length; i++) {\r\n                let channel = this.channelModule.getChannel(this.recentChannels[i]), guild;\r\n                if (channel) guild = this.guildModule.getGuild(channel.guild_id);\r\n\r\n                if(!channel || !guild) continue;\r\n\r\n                if(!recentChannels) recentChannels = document.getElementById(\"sb-servers\").insertBefore(NeatoLib.DOM.createElement({\r\n                    id : \"sb-recent-channels\",\r\n                    innerHTML : `<div class=\"sb-label\">Recent Channels</div>`\r\n                }), null);\r\n\r\n                let ci = document.createElement(\"div\");\r\n                ci.className = \"sb-channel-item-button sb-button\";\r\n                ci.dataset.guildId = guild.id;\r\n                ci.dataset.channelId = channel.id;\r\n                ci.dataset.content = url;\r\n                ci.innerHTML = `<div class=\"sb-channel-item\">#${channel.name} - ${guild.name}</div>`;\r\n\r\n                recentChannels.appendChild(ci);\r\n            }\r\n            updateChannels()\r\n        },\r\n        \r\n        updatePinnedChannels = () => {\r\n            if(pinnedChannels) {\r\n                pinnedChannels.remove();\r\n                pinnedChannels = null;\r\n            }\r\n\r\n            for(let i = 0; i < this.pinnedChannels.length; i++) {\r\n                let channel = this.channelModule.getChannel(this.pinnedChannels[i]), guild;\r\n                if (channel) guild = this.guildModule.getGuild(channel.guild_id);\r\n\r\n                if(!channel || !guild) continue;\r\n\r\n                if(!pinnedChannels) {\r\n                    menu.scroller.insertAdjacentHTML(\"afterBegin\", `<div id=\"sb-pinned-channels\"><div class=\"sb-label\">Pinned Channels</div></div>`);\r\n                    pinnedChannels = document.getElementById(\"sb-pinned-channels\");\r\n                }\r\n\r\n                let ci = document.createElement(\"div\");\r\n                ci.className = \"sb-channel-item-button sb-button\";\r\n                ci.dataset.guildId = guild.id;\r\n                ci.dataset.channelId = channel.id;\r\n                ci.dataset.content = url;\r\n                ci.innerHTML = `<div class=\"sb-channel-item\">#${channel.name} - ${guild.name}</div>`;\r\n\r\n                pinnedChannels.appendChild(ci);\r\n            }\r\n            updateChannels();\r\n        };\r\n\r\n        let guilds = [], guildsParent, allChannels = Object.values(this.channelModule.getChannels()).sort((x, y) => x.position - y.position);\r\n        this.sortedGuildModule.getSortedGuilds().forEach(e => {\r\n            guilds.push(...e.guilds);\r\n        });\r\n\r\n        for(let i = 0; i < guilds.length; i++) {\r\n            if(!guildsParent) {\r\n                menu.scroller.insertAdjacentHTML(\"beforeEnd\", \r\n                `<div id=\"sb-servers\">\r\n                    <div class =\"sb-label\">Servers</div>\r\n                    <div data-server-id=\"DM\" data-opened=\"false\" class=\"sb-server-item sb-dm-item sb-button\">\r\n                        <div class=\"sb-server-item-icon\" style=\"background-image: url('/assets/89576a4bb71f927eb20e8aef987b499b.svg')\">\r\n                            <div class=\"sb-server-item-label\">Direct Messages</div>\r\n                        </div>\r\n                        <div class=\"sb-server-item-channels\"></div>\r\n                    </div>\r\n                </div>`);\r\n\r\n                guildsParent = document.getElementById(\"sb-servers\");\r\n                \r\n                document.getElementsByClassName(\"sb-dm-item\")[0].onclick = e => {\r\n                    if(e.target.classList.contains(\"sb-dm-subitem\")) return;\r\n\r\n                    let par = e.currentTarget.getElementsByClassName(\"sb-server-item-channels\")[0], dms = this.dmModule.getPrivateChannelIds();\r\n\r\n                    let dmClickEvent = e => {\r\n                        this.sendMessage(e);\r\n\r\n                        e.currentTarget.style.backgroundColor = \"#43b581\";\r\n                        e.currentTarget.style.cursor = \"default\";\r\n\r\n                        NeatoLib.showToast(\"Shared!\", \"success\");\r\n\r\n                        e.currentTarget.onclick = null;\r\n                        getEventListeners(e.currentTarget).click[0].remove();\r\n                    };\r\n\r\n                    if(par.children.length) {\r\n                        par.innerHTML = \"\";\r\n                        e.currentTarget.style.backgroundColor = \"\";\r\n                    } else {\r\n                        for(let di = 0; di < dms.length; di++) {\r\n                            let dm = this.channelModule.getChannel(dms[di]);\r\n\r\n                            if(dm.recipients.length > 1) {\r\n                                let item = document.createElement(\"div\");\r\n\r\n                                item.className = \"sb-server-item sb-dm-subitem sb-button\";\r\n\r\n                                item.style.margin = \"15px\";\r\n                                item.style.marginLeft = \"30px\";\r\n\r\n                                item.dataset.channelId = dms[di];\r\n                                item.dataset.content = url;\r\n\r\n                                item.innerHTML = `<div class=\"sb-server-item-icon\" style=\"height: auto;width:0px\"><div class=\"sb-server-item-label\" style=\"pointer-events:none;\">${Array.from(dm.recipients, uid => this.userModule.getUser(uid).username).join(\", \")}</div></div>`;\r\n\r\n                                item.addEventListener(\"click\", dmClickEvent);\r\n\r\n                                par.appendChild(item);\r\n                            } else {\r\n                                let user = this.userModule.getUser(dm.recipients[0]);\r\n\r\n                                if(!user) continue;\r\n\r\n                                let item = document.createElement(\"div\");\r\n\r\n                                item.className = \"sb-server-item sb-dm-subitem sb-button\";\r\n\r\n                                item.style.margin = \"15px\";\r\n                                item.style.marginLeft = \"30px\";\r\n\r\n                                item.dataset.channelId = dms[di];\r\n                                item.dataset.content = url;\r\n\r\n                                item.innerHTML = `<div class=\"sb-server-item-icon\" style=\"background-image: url('${user.getAvatarURL()}');\"><div class=\"sb-server-item-label\" style=\"pointer-events:none;\">${user.username}</div></div>`;\r\n\r\n                                item.addEventListener(\"click\", dmClickEvent);\r\n\r\n                                par.appendChild(item);\r\n                            }\r\n                        }\r\n                    }\r\n                };\r\n            }\r\n\r\n            guildsParent.insertAdjacentHTML(\"beforeEnd\", `<div data-server-id=\"${guilds[i].id}\" data-opened=\"false\" class=\"sb-server-item sb-button\"><div class=\"sb-server-item-icon\" style=\"background-image: url('${guilds[i].getIconURL()}');\"><div class=\"sb-server-item-label\">${guilds[i].name}</div></div><div class=\"sb-server-item-channels\"></div></div>`);\r\n        }\r\n        \r\n        let channelClickEvent = e => {\r\n            this.sendMessage(e);\r\n\r\n            e.currentTarget.getElementsByClassName(\"sb-channel-item\")[0].innerText = \"Shared!\";\r\n\r\n            e.currentTarget.style.backgroundColor = \"#43b581\";\r\n            e.currentTarget.style.cursor = \"default\";\r\n\r\n            NeatoLib.showToast(\"Shared!\", \"success\");\r\n\r\n            e.currentTarget.removeEventListener(\"click\", channelClickEvent);\r\n            e.currentTarget.removeEventListener(\"contextmenu\", channelContextEvent);\r\n\r\n            if(this.recentChannels.indexOf(e.currentTarget.dataset.channelId) != -1) this.recentChannels.splice(this.recentChannels.indexOf(e.currentTarget.dataset.channelId), 1);\r\n            this.recentChannels.push(e.currentTarget.dataset.channelId);\r\n            while(this.recentChannels.length >= 10) this.recentChannels.splice(0, 1);\r\n\r\n            updateRecentChannels();\r\n\r\n            this.saveData();\r\n        },\r\n\r\n        channelContextEvent = e => {\r\n            let items = [], cid = e.currentTarget.dataset.channelId;\r\n\r\n            if(this.pinnedChannels.includes(cid)) items.push(NeatoLib.ContextMenu.createItem(\"Unpin\", () => {\r\n                this.pinnedChannels.splice(this.pinnedChannels.indexOf(cid), 1);\r\n                updatePinnedChannels();\r\n                this.saveData();\r\n                NeatoLib.ContextMenu.close();\r\n            }));\r\n            else items.push(NeatoLib.ContextMenu.createItem(\"Pin\", () => {\r\n                this.pinnedChannels.push(cid);\r\n                updatePinnedChannels();\r\n                this.saveData();\r\n                NeatoLib.ContextMenu.close();\r\n            }));\r\n\r\n            NeatoLib.ContextMenu.create([NeatoLib.ContextMenu.createGroup(items)], e);\r\n        };\r\n\r\n        let serverItems = Array.from(document.getElementsByClassName(\"sb-server-item\")).filter(e => !e.classList.contains(\"sb-dm-item\")),\r\n        \r\n        serverItemClickEvent = e => {\r\n            if(e.target.classList.contains(\"sb-channel-item\")) return;\r\n\r\n            let targ = NeatoLib.DOM.searchForParentElementByClassName(e.target, \"sb-server-item\");\r\n\r\n            let par = targ.getElementsByClassName(\"sb-server-item-channels\")[0], guild = this.guildModule.getGuild(targ.dataset.serverId), categories = [];\r\n\r\n            if(par.children.length) {\r\n                par.innerHTML = \"\";\r\n                targ.style.backgroundColor = \"\";\r\n            } else {\r\n                for(let i = 0; i < allChannels.length; i++) {\r\n                    if(allChannels[i].guild_id == targ.dataset.serverId && allChannels[i].type == 0) {\r\n                        if(allChannels[i].parent_id && categories.indexOf(allChannels[i].parent_id) == -1) {\r\n                            par.insertAdjacentHTML(\"beforeEnd\", `<div id=\"sb-recent-channels\" style=\"text-align:center;\"><div class=\"sb-label\" style=\"padding-left:0px;\">${this.channelModule.getChannel(allChannels[i].parent_id).name}</div></div>`);\r\n                            \r\n                            categories.push(allChannels[i].parent_id);\r\n                        }\r\n\r\n                        if(!allChannels[i].parent_id && categories.indexOf(\"uncategorized\") == -1) {\r\n                            par.insertAdjacentHTML(\"beforeEnd\", `<div id=\"sb-recent-channels\" style=\"text-align:center;\"><div class=\"sb-label\" style=\"padding-left:0px;\">Uncategorized</div></div>`);\r\n\r\n                            categories.push(\"uncategorized\");\r\n                        }\r\n\r\n                        let item = document.createElement(\"div\");\r\n\r\n                        item.className = \"sb-channel-item-button sb-button\";\r\n\r\n                        item.dataset.guildId = guild.id;\r\n                        item.dataset.channelId = allChannels[i].id;\r\n                        item.dataset.content = url;\r\n\r\n                        item.innerHTML = `<div class=\"sb-channel-item\">#${allChannels[i].name}</div>`;\r\n\r\n                        par.appendChild(item);\r\n                    }\r\n                }\r\n                updateChannels();\r\n            }\r\n        };\r\n\r\n        for(let i = 0; i < serverItems.length; i++) serverItems[i].onclick = serverItemClickEvent;\r\n\r\n        updatePinnedChannels();\r\n        updateRecentChannels();\r\n    }\r\n\r\n    sendMessage(e) {\r\n        let lastServer = NeatoLib.getSelectedServer(), lastChannel = NeatoLib.getSelectedTextChannel(), lastScroll = document.getElementsByClassName(NeatoLib.getClass(\"messages\"))[0].scrollTop;\r\n\r\n        this.transitionModule.transitionTo(e.currentTarget.dataset.channelId, e.currentTarget.dataset.guildId);\r\n\r\n        this.messageModule.sendMessage(e.currentTarget.dataset.channelId, { content : e.currentTarget.dataset.content });\r\n\r\n        if(lastServer && lastChannel) this.transitionModule.transitionTo(lastChannel.id, lastServer.id);\r\n\r\n        document.getElementsByClassName(NeatoLib.getClass(\"messages\"))[0].scrollTop = lastScroll;\r\n    }\r\n\t\r\n    stop() {\r\n        document.removeEventListener(\"contextmenu\", this.contextEvent);\r\n\r\n        if(this.popoutObserver != undefined) this.popoutObserver.disconnect();\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "SuppressUserMentions.plugin.js",
    "content": "//META{\"name\":\"SuppressUserMentions\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/SuppressUserMentions.plugin.js\"}*//\r\n\r\nclass SuppressUserMentions {\r\n\r\n\tgetName() { return \"SuppressUserMentions\"; }\r\n\tgetDescription() { return \"Allows you to suppress mentions from specified users without blocking them.\"; }\r\n\tgetVersion() { return \"0.0.2\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\tgetChanges() {\r\n\t\treturn {\r\n\r\n\t\t};\r\n\t}\r\n\r\n\tload() {}\r\n\r\n\tstart() {\r\n\t\tconst libLoadedEvent = () => {\r\n\t\t\ttry{ this.onLibLoaded(); }\r\n\t\t\tcatch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n\t\t};\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif (!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n\t\tif (typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\t}\r\n\r\n\tget settingFields() {\r\n\t\treturn {\r\n\t\t\tsuppressedUsers: { title: \"Suppressed user IDs\", description: \"You can also add and remove suppressed users from the context menu\", type: \"string\", array: true }\r\n\t\t};\r\n\t}\r\n\r\n\tget defaultSettings() {\r\n\t\treturn {\r\n\t\t\tdisplayUpdateNotes: true,\r\n\t\t\tsuppressedUsers: [\"454465635972284428\"]\r\n\t\t};\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\t\treturn NeatoLib.Settings.createPanel(this);\r\n\t}\r\n\r\n\tsaveSettings() {\r\n\t\tNeatoLib.Settings.save(this);\r\n\t}\r\n\r\n\tonLibLoaded() {\r\n\t\tthis.settings = NeatoLib.Settings.load(this);\r\n\r\n\t\tNeatoLib.Updates.check(this);\r\n\r\n\t\t//if (this.settings.displayUpdateNotes) NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());\r\n\r\n\t\tthis.unpatch = NeatoLib.monkeyPatchInternal(NeatoLib.Modules.get(\"isMentioned\"), \"isMentioned\", e => {\r\n\t\t\tif (this.settings.suppressedUsers.includes(e.args[0].author.id)) return false;\r\n\t\t\telse return e.callDefault();\r\n\t\t});\r\n\r\n\t\tdocument.addEventListener(\"contextmenu\", this.contextEvent = e => this.onContextMenu(e));\r\n\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\t}\r\n\r\n\tonContextMenu(e) {\r\n\t\tif (!e.target.className.includes(\"username\") && !e.target.className.includes(\"large\")) return;\r\n\r\n\t\tconst contextMenu = NeatoLib.ContextMenu.get();\r\n\t\tif (!contextMenu) return;\r\n\r\n\t\tconst uid = NeatoLib.ReactData.get(contextMenu).return.return.return.return.memoizedProps.user.id;\r\n\r\n\t\tcontextMenu.insertAdjacentElement(\"afterBegin\", NeatoLib.ContextMenu.createGroup([\r\n\t\t\t!this.settings.suppressedUsers.includes(uid) ? NeatoLib.ContextMenu.createItem(\"Suppress User Mentions\", () => {\r\n\t\t\t\tthis.settings.suppressedUsers.push(uid);\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t\tNeatoLib.ContextMenu.close();\r\n\t\t\t}) :\r\n\t\t\tNeatoLib.ContextMenu.createItem(\"Unsuppress User Mentions\", () => {\r\n\t\t\t\tthis.settings.suppressedUsers.splice(this.settings.suppressedUsers.indexOf(uid), 1);\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t\tNeatoLib.ContextMenu.close();\r\n\t\t\t})\r\n\t\t]));\r\n\t}\r\n\r\n\tstop() {\r\n\t\tthis.unpatch();\r\n\t\tdocument.removeEventListener(\"contextmenu\", this.contextEvent);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "TheClapBestClapPluginClapEver.plugin.js",
    "content": "/**\r\n * @name TheClapBestClapPluginClapEver\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www.paypal.me/israelboone\r\n * @website https://metalloriff.github.io/toms-discord-stuff/\r\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/TheClapBestClapPluginClapEver.plugin.js\r\n */\r\n\r\nmodule.exports = (() =>\r\n{\r\n    const config =\r\n    {\r\n\t\tinfo:\r\n\t\t{\r\n\t\t\tname: \"TheClapBestClapPluginClapEver\",\r\n\t\t\tauthors:\r\n\t\t\t[\r\n\t\t\t\t{\r\n\t\t\t\t\tname: \"Metalloriff\",\r\n\t\t\t\t\tdiscord_id: \"264163473179672576\",\r\n\t\t\t\t\tgithub_username: \"metalloriff\",\r\n\t\t\t\t\ttwitter_username: \"Metalloriff\"\r\n\t\t\t\t}\r\n\t\t\t],\r\n\t\t\tversion: \"2.1.0\",\r\n\t\t\tdescription: \"Literally useless, toxic cancer. Write 'clapclap$' at the beginning of your message to separate each word with a clap emoji. Write 'clapclap( :your_emoji: )$' to separate each word with your own custom emoji. 'superclapclap$' or 'superclapclap( :emoji: )$' for every letter instead of every word. 'ra$' to replace every letter with a regional indicator emoji. 'reverse$' to reverse your message. 'b$' to replace every 'b' with the B emoji. 'woke$' to capitalize every other letter. 'owo$' if you have no will to live.\",\r\n\t\t\tgithub: \"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/TheClapBestClapPluginClapEver.plugin.js\",\r\n\t\t\tgithub_raw: \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/TheClapBestClapPluginClapEver.plugin.js\"\r\n\t\t},\r\n\t\tchangelog:\r\n\t\t[\r\n\t\t\t{\r\n\t\t\t\ttitle: \"2.1.0\",\r\n\t\t\t\ttype: \"added\",\r\n\t\t\t\titems:\r\n\t\t\t\t[\r\n\t\t\t\t\t\"Added 'woke$' option. This will capitalize every other letter in your message, showing that you are, in fact, the most intelligent and dominant person in chat.\"\r\n\t\t\t\t]\r\n\t\t\t}\r\n\t\t]\r\n    };\r\n\r\n    return (([Plugin, Api]) => {\r\n\r\n\t\tconst plugin = (Plugin, Api) =>\r\n\t\t{\r\n\t\t\tconst { DiscordModules, Patcher } = Api;\r\n\r\n\t\t\treturn class TheClapBestClapPluginClapEver extends Plugin\r\n\t\t\t{\r\n\t\t\t\tconstructor()\r\n\t\t\t\t{\r\n\t\t\t\t\tsuper();\r\n\t\t\t\t}\r\n\t\r\n\t\t\t\tonStart()\r\n\t\t\t\t{\r\n\t\t\t\t\tPatcher.after(DiscordModules.MessageActions, \"sendMessage\", (_, [, message]) =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconst content = message.content.toLowerCase();\r\n\t\t\t\t\t\tconst clapclap = (/^clapclap(\\S| )*\\$/g).exec(content) || (/^superclapclap(\\S| )*\\$/g).exec(content);\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tif (clapclap)\r\n\t\t\t\t\t\t{\t\r\n\t\t\t\t\t\t\tconst filler = clapclap[0].includes(\"(\") && clapclap[0].includes(\")\")\r\n\t\t\t\t\t\t\t\t? clapclap[0].substr(clapclap[0].indexOf(\"(\") + 1, clapclap[0].indexOf(\")\") - clapclap[0].indexOf(\"(\") - 1)\r\n\t\t\t\t\t\t\t\t: \" :clap: \";\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\tmessage.content = message.content.substr(clapclap[0].length, message.content.length)\r\n\t\t\t\t\t\t\t\t.split(clapclap[0].startsWith(\"super\") ? \"\" : \" \")\r\n\t\t\t\t\t\t\t\t.join(filler);\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\tmessage.content = filler + message.content + filler;\r\n\r\n\t\t\t\t\t\t\treturn;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tswitch (content.split(\"$\")[0])\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcase \"ra\":\r\n\t\t\t\t\t\t\t\tconst ra = (/^ra\\$/g).exec(content);\r\n\r\n\t\t\t\t\t\t\t\tmessage.content = message.content.toLowerCase().substr(ra[0].length, message.content.length)\r\n\t\t\t\t\t\t\t\t\t.split(\" \")\r\n\t\t\t\t\t\t\t\t\t.join(\"\\t\")\r\n\t\t\t\t\t\t\t\t\t.replace(/[A-Za-z]/g, x => ` :regional_indicator_${x.toLowerCase()}: `);\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\t\tcase \"reverse\":\r\n\t\t\t\t\t\t\t\tconst reverse = (/^reverse\\$/g).exec(content);\r\n\r\n\t\t\t\t\t\t\t\tmessage.content = message.content.substr(reverse[0].length, message.content.length)\r\n\t\t\t\t\t\t\t\t\t.split(\"\")\r\n\t\t\t\t\t\t\t\t\t.reverse()\r\n\t\t\t\t\t\t\t\t\t.join(\"\");\r\n\r\n\t\t\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\t\tcase \"owo\":\r\n\t\t\t\t\t\t\t\tconst owo = (/^owo\\$/g).exec(content);\r\n\r\n\t\t\t\t\t\t\t\tmessage.content = message.content.substr(owo[0].length, message.content.length)\r\n\t\t\t\t\t\t\t\t\t.replace(/r/g, \"w\")\r\n\t\t\t\t\t\t\t\t\t.replace(/R/g, \"W\")\r\n\t\t\t\t\t\t\t\t\t.replace(/l/g, \"w\")\r\n\t\t\t\t\t\t\t\t\t.replace(/L/g, \"W\")\r\n\t\t\t\t\t\t\t\t\t.replace(/ n/g, \" ny\")\r\n\t\t\t\t\t\t\t\t\t.replace(/ N/g, \" Ny\")\r\n\t\t\t\t\t\t\t\t\t.replace(/ove/g, \"uv\")\r\n\t\t\t\t\t\t\t\t\t.replace(/OVE/g, \"UV\")\r\n\t\t\t\t\t\t\t\t\t+ \" \" + [\"owo\", \"OwO\", \"uwu\", \"UwU\", \">w<\", \"^w^\", \"♥w♥\"][7 * Math.random() << 0];\r\n\r\n\t\t\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\t\tcase \"b\":\r\n\t\t\t\t\t\t\t\tconst b = (/^b\\$/g).exec(content);\r\n\r\n\t\t\t\t\t\t\t\tmessage.content = message.content.substr(b[0].length, message.content.length)\r\n\t\t\t\t\t\t\t\t\t.replace(/b/g, \":b:\");\r\n\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\tcase \"woke\":\r\n\t\t\t\t\t\t\t\tconst woke = (/^woke\\$/g).exec(content);\r\n\r\n\t\t\t\t\t\t\t\tmessage.content = message.content.substr(woke[0].length, message.content.length)\r\n\t\t\t\t\t\t\t\t\t.replace(/.{2}/gm, c => c[0].toUpperCase() + c[1].toLowerCase());\r\n\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\r\n\t\t\t\tonStop()\r\n\t\t\t\t{\r\n\t\t\t\t\tPatcher.unpatchAll();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\r\n        return plugin(Plugin, Api);\r\n    })(global.ZeresPluginLibrary.buildPlugin(config));\r\n})();\r\n"
  },
  {
    "path": "TransitioningBackgrounds.plugin.js",
    "content": "/**\r\n * @name TransitioningBackgrounds\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www.paypal.me/israelboone\r\n * @website https://metalloriff.github.io/toms-discord-stuff/\r\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/TransitioningBackgrounds.plugin.js\r\n */\r\n\r\n /* TODO\r\n\tAdd server-based images.\r\n\tSlow zoom option.\r\n */\r\n\r\nmodule.exports = (() =>\r\n{\r\n\tconst ImageSource =\r\n\t{\r\n\t\tFolder: 1\r\n\t};\r\n\r\n\tconst TransitionMethod =\r\n\t{\r\n\t\tFadeInOut: 1,\r\n\t\tSlideLeft: 2,\r\n\t\tSlideRight: 3,\r\n\t\tSlideUp: 4,\r\n\t\tSlideDown: 5,\r\n\t\tShrink: 6,\r\n\t\tRotateX: 7,\r\n\t\tRotateY: 8,\r\n\t\tZoomFade: 9\r\n\t};\r\n\r\n\tconst SortType =\r\n\t{\r\n\t\tShuffle: 1,\r\n\t\tAZ: 2\r\n\t};\r\n\r\n\tconst ImageSizeMode =\r\n\t{\r\n\t\tAuto: \"auto\",\r\n\t\tCover: \"cover\",\r\n\t\tContain: \"contain\",\r\n\t\tInitial: \"initial\",\r\n\t\tStretch: \"100% 100%\"\r\n\t};\r\n\r\n\tconst config =\r\n\t{\r\n\t\tinfo:\r\n\t\t{\r\n\t\t\tname: \"TransitioningBackgrounds\",\r\n\t\t\tauthors:\r\n\t\t\t[\r\n\t\t\t\t{\r\n\t\t\t\t\tname: \"Metalloriff\",\r\n\t\t\t\t\tdiscord_id: \"264163473179672576\",\r\n\t\t\t\t\tgithub_username: \"metalloriff\",\r\n\t\t\t\t\ttwitter_username: \"Metalloriff\"\r\n\t\t\t\t}\r\n\t\t\t],\r\n\t\t\tversion: \"2.2.3\",\r\n\t\t\tdescription: \"Allows you to set a list of background images, or pick a source, to transitioning between using various animations and sort modes.\",\r\n\t\t\tgithub: \"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/TransitioningBackgrounds.plugin.js\",\r\n\t\t\tgithub_raw: \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/TransitioningBackgrounds.plugin.js\"\r\n\t\t},\r\n\t\tdefaultConfig:\r\n\t\t[\r\n\t\t\t{\r\n\t\t\t\ttype: \"category\",\r\n\t\t\t\tname: \"General Settings\",\r\n\t\t\t\tid: \"general\",\r\n\t\t\t\tcollapsible: true,\r\n\t\t\t\tshown: false,\r\n\t\t\t\tsettings:\r\n\t\t\t\t[\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"slider\",\r\n\t\t\t\t\t\tname: \"Image Life (seconds)\",\r\n\t\t\t\t\t\tid: \"imageLife\",\r\n\t\t\t\t\t\tvalue: 30,\r\n\t\t\t\t\t\tmin: 0,\r\n\t\t\t\t\t\tmax: 601,\r\n\t\t\t\t\t\trenderValue: v =>\r\n\t\t\t\t\t\t\tv <= 600\r\n\t\t\t\t\t\t\t\t? Math.round(v) + \" seconds\"\r\n\t\t\t\t\t\t\t\t: \"on startup\"\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"dropdown\",\r\n\t\t\t\t\t\tname: \"Image Size Mode\",\r\n\t\t\t\t\t\tid: \"imageSizeMode\",\r\n\t\t\t\t\t\tvalue: ImageSizeMode.Cover,\r\n\t\t\t\t\t\toptions:\r\n\t\t\t\t\t\t[\r\n\t\t\t\t\t\t\t{ label: \"Auto\", value: ImageSizeMode.Auto },\r\n\t\t\t\t\t\t\t{ label: \"Cover\", value: ImageSizeMode.Cover },\r\n\t\t\t\t\t\t\t{ label: \"Contain\", value: ImageSizeMode.Contain },\r\n\t\t\t\t\t\t\t{ label: \"Initial\", value: ImageSizeMode.Initial },\r\n\t\t\t\t\t\t\t{ label: \"Stretch\", value: ImageSizeMode.Stretch }\r\n\t\t\t\t\t\t]\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"switch\",\r\n\t\t\t\t\t\tname: \"Force Transparency\",\r\n\t\t\t\t\t\tnote: \"For use without a theme.\",\r\n\t\t\t\t\t\tid: \"forceTransparency\",\r\n\t\t\t\t\t\tvalue: false\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"slider\",\r\n\t\t\t\t\t\tname: \"Background Brightness\",\r\n\t\t\t\t\t\tid: \"backgroundBrightness\",\r\n\t\t\t\t\t\tvalue: 0.5,\r\n\t\t\t\t\t\tmin: 0,\r\n\t\t\t\t\t\tmax: 1\r\n\t\t\t\t\t}\r\n\t\t\t\t]\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\ttype: \"category\",\r\n\t\t\t\tname: \"Image Source Settings\",\r\n\t\t\t\tid: \"imageSource\",\r\n\t\t\t\tcollapsible: true,\r\n\t\t\t\tshown: false,\r\n\t\t\t\tsettings:\r\n\t\t\t\t[\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"dropdown\",\r\n\t\t\t\t\t\tname: \"Image Source\",\r\n\t\t\t\t\t\tid: \"source\",\r\n\t\t\t\t\t\tvalue: ImageSource.Folder,\r\n\t\t\t\t\t\toptions: [\r\n\t\t\t\t\t\t\t{ label: \"Folder\", value: ImageSource.Folder }\r\n\t\t\t\t\t\t]\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"textbox\",\r\n\t\t\t\t\t\tname: \"Source Path\",\r\n\t\t\t\t\t\tid: \"folder\",\r\n\t\t\t\t\t\tvalue: \"\"\r\n\t\t\t\t\t}\r\n\t\t\t\t]\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\ttype: \"category\",\r\n\t\t\t\tname: \"Transition Settings\",\r\n\t\t\t\tid: \"transition\",\r\n\t\t\t\tcollapsible: true,\r\n\t\t\t\tshown: false,\r\n\t\t\t\tsettings:\r\n\t\t\t\t[\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"dropdown\",\r\n\t\t\t\t\t\tname: \"Transition Method\",\r\n\t\t\t\t\t\tid: \"method\",\r\n\t\t\t\t\t\tvalue: TransitionMethod.FadeInOut,\r\n\t\t\t\t\t\toptions:\r\n\t\t\t\t\t\t[\r\n\t\t\t\t\t\t\t{ label: \"Fade In/Out\", value: TransitionMethod.FadeInOut },\r\n\t\t\t\t\t\t\t{ label: \"Slide Left\", value: TransitionMethod.SlideLeft },\r\n\t\t\t\t\t\t\t{ label: \"Slide Right\", value: TransitionMethod.SlideRight },\r\n\t\t\t\t\t\t\t{ label: \"Slide Up\", value: TransitionMethod.SlideUp },\r\n\t\t\t\t\t\t\t{ label: \"Slide Down\", value: TransitionMethod.SlideDown },\r\n\t\t\t\t\t\t\t{ label: \"Shrink\", value: TransitionMethod.Shrink },\r\n\t\t\t\t\t\t\t{ label: \"Rotate X\", value: TransitionMethod.RotateX },\r\n\t\t\t\t\t\t\t{ label: \"Rotate Y\", value: TransitionMethod.RotateY },\r\n\t\t\t\t\t\t\t{ label: \"Zoom In And Fade Out\", value: TransitionMethod.ZoomFade }\r\n\t\t\t\t\t\t]\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"slider\",\r\n\t\t\t\t\t\tname: \"Transition Time (seconds)\",\r\n\t\t\t\t\t\tid: \"time\",\r\n\t\t\t\t\t\tvalue: 3,\r\n\t\t\t\t\t\tmin: 0,\r\n\t\t\t\t\t\tmax: 60,\r\n\t\t\t\t\t\trenderValue: v => Math.round(v) + \" seconds\"\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"dropdown\",\r\n\t\t\t\t\t\tname: \"Sort Method\",\r\n\t\t\t\t\t\tid: \"sort\",\r\n\t\t\t\t\t\tvalue: SortType.Shuffle,\r\n\t\t\t\t\t\toptions: [\r\n\t\t\t\t\t\t\t{ label: \"Shuffle On Start\", value: SortType.Shuffle },\r\n\t\t\t\t\t\t\t{ label: \"Alphabetically\", value: SortType.AZ }\r\n\t\t\t\t\t\t]\r\n\t\t\t\t\t}\r\n\t\t\t\t]\r\n\t\t\t}\r\n\t\t],\r\n\t\tchangelog:\r\n\t\t[\r\n\t\t\t{\r\n\t\t\t\ttitle: \"changes and bug fixes\",\r\n\t\t\t\ttype: \"fixed\",\r\n\t\t\t\titems:\r\n\t\t\t\t[\r\n\t\t\t\t\t\"Your settings are now based on your user ID.\"\r\n\t\t\t\t]\r\n\t\t\t}\r\n\t\t]\r\n\t};\r\n\r\n\treturn !global.ZeresPluginLibrary ? class\r\n\t{\r\n\t\tconstructor() { this._config = config; }\r\n\r\n\t\tgetName = () => config.info.name;\r\n\t\tgetAuthor = () => config.info.description;\r\n\t\tgetVersion = () => config.info.version;\r\n\r\n\t\tload()\r\n\t\t{\r\n\t\t\tBdApi.showConfirmationModal(\"Library Missing\", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {\r\n\t\t\t\tconfirmText: \"Download Now\",\r\n\t\t\t\tcancelText: \"Cancel\",\r\n\t\t\t\tonConfirm: () =>\r\n\t\t\t\t{\r\n\t\t\t\t\trequire(\"request\").get(\"https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js\", async (err, res, body) =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (err) return require(\"electron\").shell.openExternal(\"https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js\");\r\n\t\t\t\t\t\tawait new Promise(r => require(\"fs\").writeFile(require(\"path\").join(BdApi.Plugins.folder, \"0PluginLibrary.plugin.js\"), body, r));\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tstart() { }\r\n\t\tstop() { }\r\n\t} : (([Plugin, Api]) => {\r\n\r\n\t\tconst plugin = (Plugin, Api) =>\r\n\t\t{\r\n\t\t\tconst { WebpackModules, DiscordModules: { UserStore: { getCurrentUser } }, PluginUtilities } = Api;\r\n\r\n\t\t\tconst fs = require(\"fs\");\r\n\t\t\tconst path = require(\"path\");\r\n\t\t\tconst { Buffer } = require(\"buffer\");\r\n\r\n\t\t\treturn class TransitioningBackgrounds extends Plugin\r\n\t\t\t{\r\n\t\t\t\tconstructor()\r\n\t\t\t\t{\r\n\t\t\t\t\tsuper();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tasync showChangelog(footer)\r\n\t\t\t\t{\r\n\t\t\t\t\ttry { footer = (await WebpackModules.getByProps(\"getUser\", \"acceptAgreements\").getUser(\"264163473179672576\")).tag + \" | https://discord.gg/yNqzuJa\"; }\r\n\t\t\t\t\tfinally { super.showChangelog(footer); }\r\n\t\t\t\t}\r\n\r\n\t\t\t\tgetDataName = () => this.getName() + \".\" + getCurrentUser().id;\r\n\t\t\t\tloadSettings = s => PluginUtilities.loadSettings(this.getDataName(), PluginUtilities.loadSettings(this.getName(), s || this.defaultSettings));\r\n\t\t\t\tsaveSettings = s => PluginUtilities.saveSettings(this.getDataName(), this.settings || s);\r\n\r\n\t\t\t\tgetSettingsPanel() {\r\n\t\t\t\t\tconst panel = this.buildSettingsPanel();\r\n\r\n\t\t\t\t\tpanel.addListener(() => {\r\n\t\t\t\t\t\tthis.term();\r\n\t\t\t\t\t\tthis.init();\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\treturn panel.getElement();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcreateElement(type, options)\r\n\t\t\t\t{\r\n\t\t\t\t\tconst e = document.createElement(type);\r\n\t\r\n\t\t\t\t\tfor (let o in options)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\te[o] = options[o];\r\n\t\t\t\t\t}\r\n\t\r\n\t\t\t\t\treturn e;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tget CSS()\r\n\t\t\t\t{\r\n\t\t\t\t\treturn `\r\n\t\t\t\t\t\t.tb-image\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tdisplay: none;\r\n\t\t\t\t\t\t\tbackground-size: ${this.settings.general.imageSizeMode};\r\n\t\t\t\t\t\t\tbackground-position: center;\r\n\t\t\t\t\t\t\tbackground-repeat: no-repeat;\r\n\t\t\t\t\t\t\twidth: 100%;\r\n\t\t\t\t\t\t\theight: 100%;\r\n\t\t\t\t\t\t\tposition: absolute;\r\n\t\t\t\t\t\t\tfilter: brightness(${this.settings.general.backgroundBrightness});\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t.tb-active .tb-image\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tdisplay: block;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t.tb-transition-${TransitionMethod.FadeInOut} { transition: opacity ${this.settings.transition.time}s; opacity: 0 }\r\n\t\t\t\t\t\t.tb-transition-${TransitionMethod.SlideLeft} { transition: transform ${this.settings.transition.time}s; transform: translateX(-100%) }\r\n\t\t\t\t\t\t.tb-transition-${TransitionMethod.SlideRight} { transition: transform ${this.settings.transition.time}s; transform: translateX(100%) }\r\n\t\t\t\t\t\t.tb-transition-${TransitionMethod.SlideUp} { transition: transform ${this.settings.transition.time}s; transform: translateY(-100%) }\r\n\t\t\t\t\t\t.tb-transition-${TransitionMethod.SlideDown} { transition: transform ${this.settings.transition.time}s; transform: translateY(100%) }\r\n\t\t\t\t\t\t.tb-transition-${TransitionMethod.Shrink} { transition: transform ${this.settings.transition.time}s; transform: scale(0) }\r\n\t\t\t\t\t\t.tb-transition-${TransitionMethod.RotateX} { transition: transform ${this.settings.transition.time}s; transform: rotateX(180deg) }\r\n\t\t\t\t\t\t.tb-transition-${TransitionMethod.RotateY} { transition: transform ${this.settings.transition.time}s; transform: rotateY(180deg) }\r\n\t\t\t\t\t\t.tb-transition-${TransitionMethod.ZoomFade} { transition: transform ${this.settings.transition.time}s, opacity ${this.settings.transition.time}s; transform: scale(5); opacity: 1 }\r\n\t\t\t\t\t`;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tget ForceTransparencyCSS()\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!this.settings.general.forceTransparency)\r\n\t\t\t\t\t\treturn \"\";\r\n\r\n\t\t\t\t\tconst opacity = 0.3;\r\n\r\n\t\t\t\t\treturn `\r\n\t\t\t\t\t\t:root\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t--background-primary: rgba(0,0,0,${opacity});\r\n\t\t\t\t\t\t\t--background-secondary: rgba(0,0,0,${opacity});\r\n\t\t\t\t\t\t\t--background-secondary-alt: rgba(0,0,0,${opacity});\r\n\t\t\t\t\t\t\t--background-tertiary: rgba(0,0,0,${opacity});\r\n\t\t\t\t\t\t\t--background-accent: rgba(0,0,0,${opacity});\r\n\t\t\t\t\t\t\t--background-floating: rgba(0,0,0,${opacity});\r\n\r\n\t\t\t\t\t\t\t--background-modifier-hover: rgba(0,0,0,0.1);\r\n\t\t\t\t\t\t\t--background-modifier-selected: rgba(0,0,0,0.3);\r\n\t\t\t\t\t\t\t--channeltextarea-background: rgba(0,0,0,${opacity});\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t.theme-dark .container-1D34oG\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tbackground: var(--background-primary);\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t.typeWindows-1za-n7\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tbackground: black;\r\n\t\t\t\t\t\t\tmargin: 0;\r\n\t\t\t\t\t\t\tborder: 3px solid black;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t`;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tonStart = () => this.init();\r\n\t\t\t\tonStop = () => this.term();\r\n\t\r\n\t\t\t\tinit()\r\n\t\t\t\t{\r\n\t\t\t\t\tif (this.settings.forceTransparency != undefined)\r\n\t\t\t\t\t\tthis.settings = this.defaultSettings;\r\n\r\n\t\t\t\t\tPluginUtilities.addStyle(\"tb-css\", this.CSS);\r\n\t\t\t\t\tPluginUtilities.addStyle(\"tb-ft-css\", this.ForceTransparencyCSS);\r\n\r\n\t\t\t\t\tthis.el = this.createElement(\"div\", { id: \"tb-container\", className: \"tb-active\" });\r\n\t\t\t\t\tthis.image0 = this.createElement(\"div\", { id: \"tb-image-0\", className: \"tb-image\" });\r\n\t\t\t\t\tthis.image1 = this.createElement(\"div\", { id: \"tb-image-1\", className: \"tb-image\" });\r\n\r\n\t\t\t\t\tthis.el.appendChild(this.image0);\r\n\t\t\t\t\tthis.el.appendChild(this.image1);\r\n\r\n\t\t\t\t\tdocument.getElementsByTagName(\"html\")[0].prepend(this.el);\r\n\r\n\t\t\t\t\tthis.activeImage = this.image1;\r\n\t\t\t\t\tthis.inactiveImage = this.image0;\r\n\r\n\t\t\t\t\tthis.index = 0;\r\n\r\n\t\t\t\t\tthis.loadedImageNames = null;\r\n\r\n\t\t\t\t\tif (this.settings.general.imageLife <= 600)\r\n\t\t\t\t\t\tthis.loop = setInterval(() => this.main(), this.settings.general.imageLife * 1000);\r\n\t\t\t\t\tthis.main();\r\n\t\t\t\t\tthis.updateNextImage();\r\n\r\n\t\t\t\t\tthis.onVisibilityChanged = () =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconst container = document.getElementById(\"tb-container\");\r\n\t\t\t\t\t\tcontainer.classList[document.hidden ? \"remove\" : \"add\"](\"tb-active\");\r\n\t\t\t\t\t};\r\n\r\n\t\t\t\t\tdocument.addEventListener(\"visibilitychange\", this.onVisibilityChanged);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tset nextImageURL(url)\r\n\t\t\t\t{\r\n\t\t\t\t\tthis.inactiveImage.style.backgroundImage = `url(${url})`;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tshuffle(arr)\r\n\t\t\t\t{\r\n\t\t\t\t\tfor (let i = arr.length - 1; i > 0; i--)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tlet r = Math.floor(Math.random() * (i + 1));\r\n\t\t\t\t\t\tlet t = arr[i];\r\n\r\n\t\t\t\t\t\tarr[i] = arr[r];\r\n\t\t\t\t\t\tarr[r] = t;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tasync main()\r\n\t\t\t\t{\r\n\t\t\t\t\tthis.activeImage = this.activeImage == this.image0 ? this.image1 : this.image0;\r\n\t\t\t\t\tthis.inactiveImage = this.activeImage == this.image0 ? this.image1 : this.image0;\r\n\r\n\t\t\t\t\tthis.inactiveImage.classList.remove(\"tb-transition-\" + this.settings.transition.method);\r\n\t\t\t\t\tthis.activeImage.classList.add(\"tb-transition-\" + this.settings.transition.method);\r\n\r\n\t\t\t\t\tawait new Promise(r => setTimeout(r, this.settings.transition.time));\r\n\r\n\t\t\t\t\tthis.updateNextImage();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tupdateNextImage()\r\n\t\t\t\t{\r\n\t\t\t\t\tswitch (this.settings.imageSource.source)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcase ImageSource.Folder:\r\n\t\t\t\t\t\t\tif (this.loadedImageNames == null)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\ttry\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tthis.loadedImageNames = [];\r\n\r\n\t\t\t\t\t\t\t\t\tif (this.settings.imageSource.folder != null && this.settings.imageSource.folder.length > 0)\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tconst files = fs.readdirSync(this.settings.imageSource.folder, { withFileTypes: true });\r\n\r\n\t\t\t\t\t\t\t\t\t\tfor (let { name } of files)\r\n\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\tif (name.match(/(\\.jpg|\\.jpeg|\\.png|\\.gif|\\.webp)$/g) != null)\r\n\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\tthis.loadedImageNames.push(name);\r\n\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t\t\tswitch (this.settings.transition.sort)\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tcase SortType.Shuffle:\r\n\t\t\t\t\t\t\t\t\t\t\tthis.shuffle(this.loadedImageNames);\r\n\t\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t\t\tcase SortType.AZ:\r\n\t\t\t\t\t\t\t\t\t\t\tthis.loadedImageNames = this.loadedImageNames.sort()\r\n\t\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\tcatch (exc)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tconsole.error(exc);\r\n\t\t\t\t\t\t\t\t\tBdApi.showConfirmationModal(\"TransitioningBackgrounds: Error reading files from directory.\", exc.toString());\r\n\r\n\t\t\t\t\t\t\t\t\tthis.loadedImageNames = [];\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tif (this.loadedImageNames.length > 0)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tconst fp = path.join(this.settings.imageSource.folder, this.loadedImageNames[this.index]);\r\n\t\t\t\t\t\t\t\tfs.readFile(fp, (exc, data) =>\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tif (exc != null)\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tconsole.error(exc);\r\n\t\r\n\t\t\t\t\t\t\t\t\t\treturn;\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\r\n\t\t\t\t\t\t\t\t\tconst ext = path.extname(fp);\r\n\t\t\t\t\t\t\t\t\tconst b64 = Buffer.from(data, \"binary\").toString(\"base64\");\r\n\t\t\t\t\t\t\t\t\tconst src = `data:image/${ext.split(\".\")[1]};base64,${b64}`;\r\n\t\r\n\t\t\t\t\t\t\t\t\tthis.nextImageURL = src;\r\n\t\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tthis.index++;\r\n\r\n\t\t\t\t\t\t\tif (this.index >= this.loadedImageNames.length)\r\n\t\t\t\t\t\t\t\tthis.index = 0;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\r\n\t\t\t\tterm()\r\n\t\t\t\t{\r\n\t\t\t\t\tthis.el.remove();\r\n\r\n\t\t\t\t\tPluginUtilities.removeStyle(\"tb-css\");\r\n\t\t\t\t\tPluginUtilities.removeStyle(\"tb-ft-css\");\r\n\r\n\t\t\t\t\tdocument.removeEventListener(\"visibilitychange\", this.onVisibilityChanged);\r\n\r\n\t\t\t\t\tif (this.loop != null)\r\n\t\t\t\t\t\tclearTimeout(this.loop);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\treturn plugin(Plugin, Api);\r\n\t})(global.ZeresPluginLibrary.buildPlugin(config));\r\n})();\r\n"
  },
  {
    "path": "UnreadCountBadges.plugin.js",
    "content": "//META{\"name\":\"UnreadCountBadges\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/UnreadCountBadges.plugin.js\"}*//\r\n\r\nclass UnreadCountBadges {\r\n\t\r\n    getName() { return \"UnreadCountBadges\"; }\r\n    getDescription() { return \"Adds an unread count badge on unread servers and channels.\"; }\r\n    getVersion() { return \"0.2.7\"; }\r\n\tgetAuthor() { return \"Metalloriff\"; }\r\n\tgetChanges() {\r\n\t\treturn {\r\n            \"0.1.1\":\r\n            `\r\n                Redid the way the plugin counts unreads. The upsides of this, is that the counts will have no limit now, will be more accurate, and will not require the channel/server to be loaded. The downside is that it will only show unread messages since the plugin was started.\r\n                Fixed messages not being marked as read until you switch channels/servers.\r\n\t\t\t`,\r\n\t\t\t\"0.1.2\":\r\n\t\t\t`\r\n\t\t\t\tThe last update was a lie, it was far from more accurate. Fixed that.\r\n\t\t\t`,\r\n\t\t\t\"0.2.2\":\r\n\t\t\t`\r\n\t\t\t\tDiscord broke it, I fixed it, that is all.\r\n\t\t\t`,\r\n\t\t\t\"0.2.5\":\r\n\t\t\t`\r\n\t\t\t\tFixed channel unread styles.\r\n\t\t\t\tFixed muted channels never being read.\r\n\t\t\t\tFixed the muted channel opacity setting.\r\n\t\t\t`\r\n\t\t};\r\n\t}\r\n\r\n    load() {}\r\n\r\n    start() {\r\n\r\n        let libLoadedEvent = () => {\r\n            try{ this.onLibLoaded(); }\r\n            catch(err) { console.error(this.getName(), \"fatal error, plugin could not be started!\", err); try { this.stop(); } catch(err) { console.error(this.getName() + \".stop()\", err); } }\r\n        };\r\n\r\n\t\tlet lib = document.getElementById(\"NeatoBurritoLibrary\");\r\n\t\tif(!lib) {\r\n\t\t\tlib = document.createElement(\"script\");\r\n\t\t\tlib.id = \"NeatoBurritoLibrary\";\r\n\t\t\tlib.type = \"text/javascript\";\r\n\t\t\tlib.src = \"https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js\";\r\n\t\t\tdocument.head.appendChild(lib);\r\n\t\t}\r\n\t\tthis.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);\r\n        if(typeof window.NeatoLib !== \"undefined\") libLoadedEvent();\r\n\t\telse lib.addEventListener(\"load\", libLoadedEvent);\r\n\r\n\t}\r\n\r\n\tgetSettingsPanel() {\r\n\r\n\t\tsetTimeout(() => {\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createToggleGroup(\"ucb-toggles\", \"Toggles\", [\r\n                { title : \"Ignore muted servers\", value : \"ignoreMutedGuilds\", setValue : this.settings.ignoreMutedGuilds }\r\n            ], choice => {\r\n                this.settings[choice.value] = !this.settings[choice.value];\r\n                this.applySettings();\r\n                this.saveSettings();\r\n            }), this.getName());\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField(\"Badge color\", this.settings.badgeColor, e => {\r\n                this.settings.badgeColor = e.target.value;\r\n                this.applySettings();\r\n                this.saveSettings();\r\n            }), this.getName());\r\n\r\n            NeatoLib.Settings.pushHTML(`\r\n            <div style=\"margin-top: 10px;\">\r\n                <div class=\"containerDefault-1ZnADq ucb-ex\" style=\"width: 45%; display: inline-block;\">\r\n                    <div class=\"wrapperUnreadText-2zuiuD wrapper-KpKNwI\">\r\n                        <div class=\"unread-1Dp-OI\"></div>\r\n                        <div class=\"contentUnreadText-2vNnZc content-20Aix8\">\r\n                            <div class=\"marginReset-3RfdVe\" style=\"flex: 0 0 auto;\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" class=\"colorUnreadText-2t7XRb icon-sxakjD\"><path class=\"foreground-2W-aJk\" fill=\"currentColor\" d=\"M2.27333333,12 L2.74666667,9.33333333 L0.08,9.33333333 L0.313333333,8 L2.98,8 L3.68666667,4 L1.02,4 L1.25333333,2.66666667 L3.92,2.66666667 L4.39333333,0 L5.72666667,0 L5.25333333,2.66666667 L9.25333333,2.66666667 L9.72666667,0 L11.06,0 L10.5866667,2.66666667 L13.2533333,2.66666667 L13.02,4 L10.3533333,4 L9.64666667,8 L12.3133333,8 L12.08,9.33333333 L9.41333333,9.33333333 L8.94,12 L7.60666667,12 L8.08,9.33333333 L4.08,9.33333333 L3.60666667,12 L2.27333333,12 L2.27333333,12 Z M5.02,4 L4.31333333,8 L8.31333333,8 L9.02,4 L5.02,4 L5.02,4 Z\" transform=\"translate(1.333 2)\"></path></svg></div>\r\n                            <div class=\"nameUnreadText-DfkrI4 name-3M0b8v overflowEllipsis-jeThUf\" style=\"flex: 1 1 auto;\">example-channel</div>\r\n                            <div class=\"flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2NDFzi alignCenter-1dQNNs noWrap-3jynv6 marginReset-3RfdVe\" style=\"flex: 0 1 auto;\">\r\n                                <div class=\"iconSpacing-3JkGQO\">\r\n                                    <div class=\"wrapper-232cHJ unread-count-channel-badge\">5</div>\r\n                                </div>\r\n                            </div>\r\n                        </div>\r\n                    </div>\r\n                </div>\r\n                <div class=\"containerDefault-1ZnADq ucb-ex\" style=\"width: 45%; display: inline-block; float:right;\">\r\n                    <div class=\"wrapperMutedText-1YBpvv wrapper-KpKNwI\">\r\n                        <div class=\"contentMutedText-2y6aPQ content-20Aix8\">\r\n                            <div class=\"marginReset-3RfdVe\" style=\"flex: 0 0 auto;\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" class=\"colorMutedText-36M8WR icon-sxakjD\"><path class=\"foreground-2W-aJk\" fill=\"currentColor\" d=\"M2.27333333,12 L2.74666667,9.33333333 L0.08,9.33333333 L0.313333333,8 L2.98,8 L3.68666667,4 L1.02,4 L1.25333333,2.66666667 L3.92,2.66666667 L4.39333333,0 L5.72666667,0 L5.25333333,2.66666667 L9.25333333,2.66666667 L9.72666667,0 L11.06,0 L10.5866667,2.66666667 L13.2533333,2.66666667 L13.02,4 L10.3533333,4 L9.64666667,8 L12.3133333,8 L12.08,9.33333333 L9.41333333,9.33333333 L8.94,12 L7.60666667,12 L8.08,9.33333333 L4.08,9.33333333 L3.60666667,12 L2.27333333,12 L2.27333333,12 Z M5.02,4 L4.31333333,8 L8.31333333,8 L9.02,4 L5.02,4 L5.02,4 Z\" transform=\"translate(1.333 2)\"></path></svg></div>\r\n                            <div class=\"nameMutedText-3Vj4bM name-3M0b8v overflowEllipsis-jeThUf\" style=\"flex: 1 1 auto;\">muted-example-channel</div>\r\n                        <div class=\"flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2NDFzi alignCenter-1dQNNs noWrap-3jynv6 marginReset-3RfdVe\" style=\"flex: 0 1 auto;\">\r\n                            <div class=\"iconSpacing-3JkGQO\">\r\n                                <div class=\"wrapper-232cHJ unread-count-channel-badge\">69</div>\r\n                            </div>\r\n                        </div>\r\n                    </div>\r\n                </div>\r\n            </div>\r\n            </div>`, this.getName());\r\n\r\n            for(let example of document.getElementsByClassName(\"containerDefault-1ZnADq ucb-ex\")) {\r\n\r\n                const wrapper = example.getElementsByClassName(\"wrapper-KpKNwI\")[0],\r\n                content = example.getElementsByClassName(\"content-20Aix8\")[0],\r\n                text = example.getElementsByClassName(\"overflowEllipsis-jeThUf\")[0];\r\n\r\n                wrapper.originalClassName = wrapper.className;\r\n                content.originalClassName = content.className;\r\n                text.originalClassName = text.className;\r\n\r\n                example.addEventListener(\"mouseenter\", () => {\r\n                    wrapper.className = \"wrapperHoveredText-2geN_M wrapper-KpKNwI\";\r\n                    content.className = \"contentHoveredText-2D9B-x content-20Aix8\";\r\n                    text.className = \"nameHoveredText-1uO31y name-3M0b8v overflowEllipsis-jeThUf\";\r\n                });\r\n\r\n                example.addEventListener(\"mouseleave\", () => {\r\n                    wrapper.className = wrapper.originalClassName;\r\n                    content.className = content.originalClassName;\r\n                    text.className = text.originalClassName;\r\n                });\r\n\r\n            }\r\n\r\n            NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField(\"Muted channel badge opacity\", this.settings.mutedChannelBadgeOpacity, e => {\r\n\t\t\t\tif(isNaN(e.target.value)) return NeatoLib.showToast(\"Value must be a number\", \"error\");\r\n                this.settings.mutedChannelBadgeOpacity = e.target.value;\r\n                this.applySettings();\r\n                this.saveSettings();\r\n            }), this.getName());\r\n\t\t\t\r\n\t\t\tNeatoLib.Settings.pushChangelogElements(this);\r\n\r\n\t\t}, 0);\r\n\r\n\t\treturn NeatoLib.Settings.Elements.pluginNameLabel(this.getName());\r\n\t\t\r\n\t}\r\n\r\n\tsaveSettings() {\r\n\t\tNeatoLib.Settings.save(this);\r\n\t}\r\n\r\n\tonLibLoaded() {\r\n\t\t\r\n\t\tthis.settings = NeatoLib.Settings.load(this, {\r\n            displayUpdateNotes : true,\r\n            badgeColor : \"#7289da\",\r\n            mutedChannelBadgeOpacity : 0.3,\r\n            ignoreMutedGuilds : true\r\n\t\t});\r\n\t\t\r\n\t\tNeatoLib.Updates.check(this);\r\n\t\t\r\n        if(this.settings.displayUpdateNotes) NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());\r\n        \r\n        this.guildModule = NeatoLib.Modules.get([\"getGuild\", \"getGuilds\"]);\r\n        this.unreadModule = NeatoLib.Modules.get(\"getUnreadCount\");\r\n        this.channelModule = NeatoLib.Modules.get([\"getChannel\", \"getChannels\"]);\r\n        this.muteModule = NeatoLib.Modules.get(\"isMuted\");\r\n        this.scrollModule = NeatoLib.Modules.get(\"isAtBottom\");\r\n\r\n        this.badges = {};\r\n        this.channelBadges = {};\r\n        this.unreads = {};\r\n\r\n        this.checkForBottom = () => {\r\n            let guild = NeatoLib.getSelectedGuildId();\r\n            if(this.scrollModule.isAtBottom(guild)) {\r\n                this.updateBadges(guild);\r\n            }\r\n        };\r\n\r\n        this.applySettings();\r\n\r\n        this.switchEvent = () => {\r\n\r\n            for(let cid in this.channelBadges) {\r\n                this.channelBadges[cid].remove();\r\n                delete this.channelBadges[cid];\r\n            }\r\n\r\n            this.updateBadges();\r\n\r\n            let scroller = document.getElementsByClassName(\"messages scroller\")[0], guild = NeatoLib.getSelectedGuild();\r\n            if(scroller && guild) scroller.addEventListener(\"scroll\", this.checkForBottom);\r\n\r\n        };\r\n\r\n        this.checkForBottomUpdate = setInterval(this.checkForBottom, 500);\r\n\r\n        NeatoLib.Events.attach(\"switch\", this.switchEvent);\r\n\r\n        this.unpatchHandleMessage = NeatoLib.monkeyPatchInternal(NeatoLib.Modules.get(\"handleMessage\"), \"handleMessage\", e => {\r\n\r\n\t\t\ttry {\r\n\r\n\t\t\t\tconst data = e.args[0];\r\n\r\n\t\t\t\tif(data.type == \"MESSAGE_CREATE\" && data.message) {\r\n\r\n\t\t\t\t\tif(!this.unreads[data.message.guild_id]) this.unreads[data.message.guild_id] = { total : 0 };\r\n\r\n\t\t\t\t\tif(!this.muteModule.isGuildOrCategoryOrChannelMuted(data.message.guild_id, data.message.channel_id) || !this.settings.ignoreMutedGuilds) this.unreads[data.message.guild_id].total++;\r\n\r\n\t\t\t\t\tif(!this.unreads[data.message.guild_id][data.message.channel_id]) this.unreads[data.message.guild_id][data.message.channel_id] = 0;\r\n\r\n\t\t\t\t\tthis.unreads[data.message.guild_id][data.message.channel_id]++;\r\n\r\n\t\t\t\t\tthis.updateBadges(data.message.guild_id);\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t} catch(err) {\r\n\t\t\t\tconsole.error(err);\r\n\t\t\t} finally {\r\n\t\t\t\treturn e.callDefault();\r\n\t\t\t}\r\n\r\n        }, this.getName());\r\n\t\t\r\n\t\tNeatoLib.Events.onPluginLoaded(this);\r\n\r\n        this.updateBadges();\r\n\r\n    }\r\n    \r\n    applySettings() {\r\n        \r\n        if(this.styles) this.styles.destroy();\r\n\r\n        this.styles = NeatoLib.injectCSS(`\r\n            .${NeatoLib.getClass(\"unreadMentionsBar\", \"scroller\")} .${NeatoLib.getClass(\"lurkingGuild\", \"badge\")}.unread-count-badge {\r\n                bottom: 35px;\r\n                background-color: ${this.settings.badgeColor};\r\n            }\r\n            #app-mount .unread-count-channel-badge {\r\n                background-color: ${this.settings.badgeColor};\r\n            }\r\n            .${NeatoLib.Modules.get(\"wrapperMutedText\").wrapperMutedText.split(\" \").join(\".\")} .unread-count-channel-badge {\r\n                opacity: ${this.settings.mutedChannelBadgeOpacity};\r\n            }\r\n        `);\r\n\r\n    }\r\n\r\n    updateBadges(guild) {\r\n\r\n        const guilds = !guild ? this.guildModule.getGuilds() : undefined, selectedGuild = NeatoLib.getSelectedGuild();\r\n\r\n        const updateUnreadFor = id => {\r\n\r\n            if(!this.unreads[id]) return;\r\n\r\n            if(selectedGuild && selectedGuild.id == id) {\r\n\r\n\t\t\t\tconst classes = NeatoLib.Modules.get(\"wrapperDefaultText\"), channels = document.getElementsByClassName(classes.wrapper);\r\n\r\n                for(let i = 0; i < channels.length; i++) {\r\n\r\n                    const cid = NeatoLib.ReactData.getProp(channels[i].parentElement, \"channel.id\");\r\n\r\n                    if(!cid) continue;\r\n\r\n                    if(this.unreads[id][cid] > 0 && this.unreadModule.getUnreadCount(cid) == 0) {\r\n                        this.unreads[id].total -= this.unreads[id][cid];\r\n                        if(this.unreads[id].total < 0) this.unreads[id].total = 0;\r\n\t\t\t\t\t\tthis.unreads[id][cid] = 0;\r\n                    }\r\n\r\n                    if(this.unreads[id][cid] > 0) {\r\n\r\n                        if(this.channelBadges[cid]) this.channelBadges[cid].firstChild.innerText = this.unreads[id][cid];\r\n                        else this.channelBadges[cid] = channels[i].getElementsByClassName(classes.content)[0].lastChild.appendChild(this.createChannelBadge(this.unreads[id][cid]));\r\n\r\n                    } else if(this.channelBadges[cid]) {\r\n\r\n                        this.channelBadges[cid].remove();\r\n                        delete this.channelBadges[cid];\r\n                        \r\n                    }\r\n\r\n                }\r\n\r\n            }\r\n\r\n            if(this.unreads[id].total > 0) {\r\n\r\n                if(this.badges[id] != undefined) this.badges[id].innerText = this.unreads[id].total;\r\n                else this.badges[id] = NeatoLib.DOM.searchForParentElementByClassName(document.querySelector(\"[style*='\" + id + \"']\"), NeatoLib.getClass(\"lurkingGuild\", \"container\")).appendChild(this.createBadge(this.unreads[id].total));\r\n\r\n            } else if(this.badges[id]) {\r\n\r\n                this.badges[id].remove();\r\n                delete this.badges[id];\r\n\r\n                for(let cid in this.channelBadges) {\r\n                    this.channelBadges[cid].remove();\r\n                    delete this.channelBadges[cid];\r\n                }\r\n\r\n            }\r\n\r\n        };\r\n\r\n        if(guild) return updateUnreadFor(guild);\r\n\t\telse if(guilds) for(let id in guilds) updateUnreadFor(id);\r\n\r\n    }\r\n\r\n    createBadge(unreadCount) {\r\n\r\n        const badge = document.createElement(\"div\");\r\n\r\n        badge.className = \"wrapper-232cHJ \" + NeatoLib.getClass(\"lurkingGuild\", \"badge\") + \" unread-count-badge\";\r\n        badge.innerText = unreadCount;\r\n\r\n        return badge;\r\n\r\n    }\r\n\r\n    createChannelBadge(unreadCount) {\r\n\r\n        const badge = document.createElement(\"div\");\r\n\r\n\t\tbadge.className = NeatoLib.getClass(\"containerDragAfter\", \"iconSpacing\");\r\n\r\n        badge.innerHTML = `<div class=\"wrapper-232cHJ unread-count-channel-badge\">${unreadCount}</div>`;\r\n\r\n        return badge;\r\n\r\n    }\r\n\t\r\n    stop() {\r\n\r\n        const css = document.getElementById(\"uc-css\");\r\n        if(css) css.remove();\r\n\r\n        NeatoLib.Events.detach(\"switch\", this.switchEvent);\r\n\r\n        this.styles.destroy();\r\n\r\n        for(let id in this.badges) this.badges[id].remove();\r\n\t\tfor(let id in this.channelBadges) this.channelBadges[id].remove();\r\n\t\t\r\n\t\tthis.unpatchHandleMessage();\r\n\r\n        clearInterval(this.checkForBottomUpdate);\r\n\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "UserBirthdays.plugin.js",
    "content": "/**\n * @name UserBirthdays\n * @invite yNqzuJa\n * @authorLink https://github.com/metalloriff\n * @donate https://www.paypal.me/israelboone\n * @website https://metalloriff.github.io/toms-discord-stuff\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/UserBirthdays.plugin.js\n */\n/*@cc_on\n@if (@_jscript)\n\t\n\t// Offer to self-install for clueless users that try to run this directly.\n\tvar shell = WScript.CreateObject(\"WScript.Shell\");\n\tvar fs = new ActiveXObject(\"Scripting.FileSystemObject\");\n\tvar pathPlugins = shell.ExpandEnvironmentStrings(\"%APPDATA%\\BetterDiscord\\plugins\");\n\tvar pathSelf = WScript.ScriptFullName;\n\t// Put the user at ease by addressing them in the first person\n\tshell.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);\n\tif (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {\n\t\tshell.Popup(\"I'm in the correct folder already.\", 0, \"I'm already installed\", 0x40);\n\t} else if (!fs.FolderExists(pathPlugins)) {\n\t\tshell.Popup(\"I can't find the BetterDiscord plugins folder.\\nAre you sure it's even installed?\", 0, \"Can't install myself\", 0x10);\n\t} else if (shell.Popup(\"Should I copy myself to BetterDiscord's plugins folder for you?\", 0, \"Do you need some help?\", 0x34) === 6) {\n\t\tfs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);\n\t\t// Show the user where to put plugins in the future\n\t\tshell.Exec(\"explorer \" + pathPlugins);\n\t\tshell.Popup(\"I'm installed!\", 0, \"Successfully installed\", 0x40);\n\t}\n\tWScript.Quit();\n\n@else@*/\n\nmodule.exports = (() => {\n    const config = {\n        \"info\": {\n            \"name\": \"UserBirthdays\",\n            \"authors\": [{\n                \"name\": \"Metalloriff\",\n                \"discord_id\": \"264163473179672576\",\n                \"github_username\": \"Metalloriff\",\n                \"twitter_username\": \"Metalloriff\"\n            }],\n            \"version\": \"3.0.0\",\n            \"description\": \"Allows you to set birthdays for users and get notified when it's a user's birthday.\",\n            \"github\": \"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/UserBirthdays.plugin.js\",\n            \"github_raw\": \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/UserBirthdays.plugin.js\"\n        },\n        \"changelog\": [{\n            \"title\": \"Rewritten!\",\n            \"type\": \"improved\",\n            \"items\": [\"UserBirthdays fixed by Danielle#1788\"]\n        }]\n    };\n\n    return !global.ZeresPluginLibrary ? class {\n        constructor() {\n            this._config = config;\n        }\n        getName() {\n            return config.info.name;\n        }\n        getAuthor() {\n            return config.info.authors.map(a => a.name).join(\", \");\n        }\n        getDescription() {\n            return config.info.description;\n        }\n        getVersion() {\n            return config.info.version;\n        }\n        load() {\n            BdApi.showConfirmationModal(\"Library Missing\", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {\n                confirmText: \"Download Now\",\n                cancelText: \"Cancel\",\n                onConfirm: () => {\n                    require(\"request\").get(\"https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js\", async (error, response, body) => {\n                        if (error) return require(\"electron\").shell.openExternal(\"https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js\");\n                        await new Promise(r => require(\"fs\").writeFile(require(\"path\").join(BdApi.Plugins.folder, \"0PluginLibrary.plugin.js\"), body, r));\n                    });\n                }\n            });\n        }\n        start() {}\n        stop() {}\n    } : (([Plugin, Api]) => {\n        const plugin = (Plugin, Api) => {\n\n            const {\n                Toasts,\n                PluginUtilities,\n                DiscordModules\n            } = Api;\n\n            const {\n                UserStore\n            } = DiscordModules;\n\n            return class UserBirthdays extends Plugin {\n\n                onStart() {\n                    this.birthdays = PluginUtilities.loadData(this.getName(), \"birthdays\", {\n                        \"264163473179672576\": {\n                            day: \"5/20\",\n                            hadIn: \"\"\n                        }\n                    });\n\n                    const noteItem = (profile, uid) => {\n                        let el = document.createElement(\"div\");\n                        let tt = document.createElement(\"div\");\n                        let birthday = this.birthdays[uid];\n\n                        tt.className = this.c.tooltip.self;\n                        tt.style = \"opacity:0;position:absolute;top:15%;right:10%;transition:opacity 0.5s;animation: none;\"\n                        tt.innerHTML = `<div class=\"${this.c.tooltip.pointer}\"></div>Inproper date format!</div>`;\n\n                        el.appendChild(tt);\n\n                        el.innerHTML += `\n                    <div id=\"ub-field\" class=\"${this.c.note.title}\">Birthday</div>\n                    <div class=\"${profile ? this.c.profile.note : this.c.note.self}\">\n                        <textarea placeholder=\"Example: 4/20, April 20 or YYYY-MM-DD\" maxlength=\"50\" autocorrect=\"off\" class=\"${this.c.note.textArea}\" style=\"height:35px\">${birthday ? birthday.day : \"\"}</textarea>\n                    </div>`\n\n                        el.getElementsByTagName(\"textarea\")[0].addEventListener(\"input\", e => {\n                            let v = e.target.value;\n                            let d = new Date(v);\n                            let t = e.target.parentElement.parentElement.getElementsByClassName(this.c.tooltip.self)[0];\n\n                            if (v.length && d == \"Invalid Date\") {\n                                t.style.opacity = 1;\n                            } else {\n                                t.style.opacity = 0;\n                                this.setBirthday(uid, v);\n                            }\n                        });\n\n                        return el;\n                    };\n\n                    (this.o = new MutationObserver(m => {\n                        for (let i = 0; i < m.length; i++) {\n                            const p = m[i].addedNodes[0];\n\n                            if (p != null) {\n                                if (p.getElementsByClassName(this.c.userPopout)[0]) {\n                                    let uid = p.getElementsByClassName(this.c.userPopout)[0].dataset.userId;\n                                    if (uid) {\n                                        p.getElementsByClassName(this.c.note.self)[0].appendChild(noteItem(false, uid));\n                                    }\n                                }\n\n                                if (p.getElementsByClassName(this.c.profile.self)[0]) {\n                                    p.getElementsByClassName(this.c.profile.self)[0].appendChild(noteItem(true, p.getElementsByClassName(this.c.profile.self)[0].parentElement.parentElement.parentElement.dataset.userId));\n                                }\n                            }\n                        }\n                    })).observe(document.getElementsByClassName(this.c.layerContainer)[1], {\n                        childList: true\n                    });\n                    this.o.observe(document.getElementsByClassName(this.c.layerContainer)[1].previousSibling.previousElementSibling, {\n                        childList: true\n                    });\n                    this.o.observe(document.getElementsByClassName(this.c.popouts)[0], {\n                        childList: true\n                    });\n\n                    this.loop = setInterval(() => {\n                        const now = new Date();\n\n                        for (let uid in this.birthdays) {\n                            const _user = UserStore.getUser(uid),\n                                birthday = new Date(this.birthdays[uid].day);\n                            let user;\n                            if (_user) {\n                                user = {\n                                    avatar: _user.getAvatarURL(),\n                                    tag: _user.tag,\n                                    name: _user.username,\n                                    id: uid\n                                }\n                            } else if (uid == \"264163473179672576\") {\n                                user = {\n                                    avatar: \"https://cdn.discordapp.com/attachments/396895633732272139/707233064031486002/well_frickly_frack.png\",\n                                    tag: \"Metalloriff#2891\",\n                                    name: \"Metalloriff\",\n                                    id: uid\n                                }\n                            } else {\n                                user = {\n                                    avatar: \"/assets/f046e2247d730629309457e902d5c5b3.svg\",\n                                    tag: uid,\n                                    name: \"Unknown User\",\n                                    id: uid\n                                }\n                            }\n\n                            if (now.getMonth() == birthday.getMonth() && now.getDate() == birthday.getDate() &&\n                                (this.birthdays[uid].hadIn == \"\" || isNaN(this.birthdays[uid].hadIn) || now.getFullYear() != this.birthdays[uid].hadIn)) {\n                                this.createBirthdayItem(user);\n\n                                this.birthdays[uid].hadIn = now.getFullYear();\n                                PluginUtilities.saveData(this.getName(), \"birthdays\", this.birthdays);\n                            }\n                        }\n                    }, 60 * 1000)\n                }\n\n                onStop() {\n                    this.o.disconnect();\n                    clearInterval(this.loop);\n                }\n\n                get c() {\n                    return {\n                        app: \"app-1q1i1E\",\n                        layerContainer: \"layerContainer-yqaFcK\",\n                        userPopout: \"userPopout-xaxa6l\",\n                        popouts: \"popouts-2bnG9Z\",\n                        note: {\n                            self: \"note-1oo11U\",\n                            title: \"bodyTitle-1ySSKn base-1x0h_U size12-3cLvbJ\",\n                            textArea: \"textarea-2r0oV8 scrollbarGhostHairline-1mSOM1 scrollbar-3dvm_9\"\n                        },\n                        profile: {\n                            self: \"userInfoSection-q_35fn\",\n                            note: \"note-367eZJ\"\n                        },\n                        tooltip: {\n                            self: \"tooltip-2QfLtc tooltipTop-XDDSxx tooltipBlack-PPG47z tooltipDisablePointerEvents-3eaBGN\",\n                            pointer: \"tooltipPointer-3ZfirK\"\n                        }\n                    }\n                }\n\n                setBirthday(uid, day) {\n                    if (day) {\n                        this.birthdays[uid] = {\n                            day: day,\n                            hadIn: \"\"\n                        }\n                    } else {\n                        delete this.birthdays[uid];\n                    }\n\n                    PluginUtilities.saveData(this.getName(), \"birthdays\", this.birthdays);\n                    Toasts.success(\"Birthday Set!\")\n                }\n\n                createBirthdayItem(user) {\n                    let i = document.getElementById(\"ub-birthday-item-\" + user.id);\n\n                    if (!i) {\n                        i = document.createElement(\"div\");\n\n                        i.id = \"ub-birthday-item-\" + user.id;\n\n                        i.style = `\n                    background: white;\n                    border-radius: 10px;\n    \n                    width: 80%;\n                    height: 150px;\n                    margin-top: 40px;\n                `;\n\n                        i.innerHTML = `\n                    <div style=\"width: 130px; height: 130px; margin: 10px; border-radius: 10px; background: url(${user.avatar}) no-repeat; background-size: cover;\">\n                    <div style=\"position: relative; left: 105%; font-size: 35px; font-weight: bold;\">${user.tag}</div>\n                    <div style=\"position: relative; left: 105%; font-size: 35px; width: 1000px;\">It's ${user.name}'s birthday today!</div>\n                    </div>`;\n\n                        this.createBirthdayContainer().appendChild(i);\n                    }\n\n                    return i;\n                }\n\n                createBirthdayContainer() {\n                    let c = document.getElementById(\"ub-birthday-container\");\n\n                    if (!c) {\n                        c = document.createElement(\"div\");\n\n                        c.id = \"ub-birthday-container\";\n\n                        c.style = `\n\t\t\t\tbackground: rgba(0, 0, 0, 0.7);\n\t\t\t\toverflow: scroll;\n\n\t\t\t\tposition: absolute;\n\t\t\t\twidth: 100%;\n\t\t\t\theight: 100%;\n\t\t\t\tborder-top: 50px solid transparent;\n\t\t\t\tz-index: 1000;\n\n\t\t\t\topacity: 0;\n\t\t\t\ttransition: opacity 0.5s;\n\n\t\t\t\tdisplay: grid;\n\t\t\t\tjustify-items: center;\n\n\t\t\t\tgrid-template-columns: 1fr;\n\t\t\t\tgrid-auto-rows: 15%;\n\t\t\t`;\n\n                        c.addEventListener(\"click\", e => {\n                            e.currentTarget.style.opacity = 0;\n\n                            setTimeout(() => {\n                                c.remove();\n                            }, 600);\n                        });\n\n                        document.getElementsByClassName(this.c.app)[0].appendChild(c);\n\n                        setTimeout(() => {\n                            c.style.opacity = 1;\n                        }, 100);\n                    }\n\n                    return c;\n                }\n\n            };\n\n        };\n        return plugin(Plugin, Api);\n    })(global.ZeresPluginLibrary.buildPlugin(config));\n})();\n/*@end@*/\n"
  },
  {
    "path": "VCMuteSounds.plugin.js",
    "content": "//META{\"name\":\"VCMuteSounds\"}*//\r\n\r\nclass VCMuteSounds {\r\n\t\r\n\tconstructor(){\r\n\t\tthis.muteSound = new Audio();\r\n\t\tthis.unmuteSound = new Audio();\r\n\t\tthis.checkDelay = 100;\r\n\t\tthis.lastMuteCount = 0;\r\n\t\tthis.switched = false;\r\n\t\tthis.checkFunc;\r\n\t}\r\n\t\r\n    getName() { return \"Voice Chat Mute Sounds\"; }\r\n    getDescription() { return \"Enables the mute and unmute sound for all users in a voice call when the server/group is selected.\"; }\r\n    getVersion() { return \"0.0.3\"; }\r\n    getAuthor() { return \"Metalloriff\"; }\r\n\r\n    load() {}\r\n\t\r\n\tgetSettingsPanel(){\r\n\t\treturn \"Volume:<br><input id='vcms-vol' type='number' min='0' max='100' value='\" + (this.muteSound.volume * 100) + \"'><br><br>Mute Sound:<br><input id='vcms-msc' type='text' value='\" + this.muteSound.src + \"'><br><br>Unmute Sound:<br><input id='vcms-usc' type='text' value='\" + this.unmuteSound.src + \"'><br><br>Update Delay (ms):<br><input id='vcms-delay' type='number' min='10' max='1000' value='\" + this.checkDelay + \"'><br><br><button onclick='BdApi.getPlugin('${this.getName()}').resetSettings();'>Reset Settings</button><br><br><button onclick='BdApi.getPlugin('${this.getName()}').saveSettings();'>Save & Update</button>\"\r\n\t}\r\n\t\r\n\tsaveSettings(){\r\n\t\tvar msc = document.getElementById(\"vcms-msc\").value, usc = document.getElementById(\"vcms-usc\").value, vol = document.getElementById(\"vcms-vol\").value / 100, delay = document.getElementById(\"vcms-delay\").value;\r\n\t\tthis.muteSound.src = msc;\r\n\t\tthis.muteSound.volume = vol;\r\n\t\tthis.unmuteSound.src = usc;\r\n\t\tthis.unmuteSound.volume = vol;\r\n\t\tthis.checkDelay = delay;\r\n\t\tPluginUtilities.saveData(\"VCMuteSounds\", \"settings\", {volume : vol, muteSoundClip : msc, unmuteSoundClip : usc, checkDelay : delay});\r\n\t\tPluginUtilities.showToast(\"Settings saved!\");\r\n\t}\r\n\t\r\n\tresetSettings(){\r\n\t\tif(document.getElementById(\"vcms-vol\")){\r\n\t\t\tdocument.getElementById(\"vcms-vol\").value = 50;\r\n\t\t\tdocument.getElementById(\"vcms-msc\").value = \"https://discordapp.com/assets/e4d539271704b87764dc465b1a061abd.mp3\";\r\n\t\t\tdocument.getElementById(\"vcms-usc\").value = \"https://discordapp.com/assets/5a000a0d4dff083d12a1d4fc2c7cbf66.mp3\";\r\n\t\t\tdocument.getElementById(\"vcms-delay\").value = 100;\r\n\t\t\tPluginUtilities.showToast(\"Settings reset to defaults!\");\r\n\t\t\tthis.saveSettings();\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tthis.muteSound.volume = 0.5;\r\n\t\tthis.muteSound.src = \"https://discordapp.com/assets/e4d539271704b87764dc465b1a061abd.mp3\";\r\n\t\tthis.unmuteSound.volume = 0.5;\r\n\t\tthis.unmuteSound.src = \"https://discordapp.com/assets/5a000a0d4dff083d12a1d4fc2c7cbf66.mp3\";\r\n\t\tthis.checkDelay = 100;\r\n\t}\r\n\r\n    start() {\r\n\t\tvar libraryScript = document.getElementById('zeresLibraryScript');\r\n\t\tif (!libraryScript) {\r\n\t\t\tlibraryScript = document.createElement(\"script\");\r\n\t\t\tlibraryScript.setAttribute(\"type\", \"text/javascript\");\r\n\t\t\tlibraryScript.setAttribute(\"src\", \"https://rauenzi.github.io/BetterDiscordAddons/Plugins/PluginLibrary.js\");\r\n\t\t\tlibraryScript.setAttribute(\"id\", \"zeresLibraryScript\");\r\n\t\t\tdocument.head.appendChild(libraryScript);\r\n\t\t}\r\n\t\tif (typeof window.ZeresLibrary !== \"undefined\") this.initialize();\r\n\t\telse libraryScript.addEventListener(\"load\", () => { this.initialize(); });\r\n\t}\r\n\t\r\n\tinitialize(){\r\n\t\tPluginUtilities.checkForUpdate(this.getName(), this.getVersion(), \"https://github.com/Metalloriff/BetterDiscordPlugins/raw/master/VCMuteSounds.plugin.js\");\r\n\t\tthis.muteSound = new Audio();\r\n\t\tthis.unmuteSound = new Audio();\r\n\t\tthis.resetSettings();\r\n\t\tvar data = PluginUtilities.loadData(\"VCMuteSounds\", \"settings\", {volume : 0.5, muteSoundClip : \"https://discordapp.com/assets/e4d539271704b87764dc465b1a061abd.mp3\",\r\n\t\t\tunmuteSoundClip : \"https://discordapp.com/assets/5a000a0d4dff083d12a1d4fc2c7cbf66.mp3\", checkDelay : 100});\r\n\t\tthis.lastMuteCount = 0;\r\n\t\tthis.muteSound.src = data[\"muteSoundClip\"];\r\n\t\tthis.muteSound.volume = data[\"volume\"]\r\n\t\tthis.unmuteSound.src = data[\"unmuteSoundClip\"];\r\n\t\tthis.unmuteSound.volume = data[\"volume\"];\r\n\t\tthis.checkDelay = data[\"checkDelay\"];\r\n\t\tthis.check();\r\n\t}\r\n\t\r\n\tonSwitch(){\r\n\t\tthis.switched = true;\r\n\t}\r\n\t\r\n\tcheck(){\r\n\t\tvar selectedVoiceChannel = document.getElementsByClassName(\"wrapperSelectedVoice-1Q1ocJ wrapper-fDmxzK\")[0], muteCount = document.getElementsByClassName(\"callAvatarStatus-3y6S04\").length;\r\n\t\tif(selectedVoiceChannel != null || muteCount > 0){\r\n\t\t\tif(selectedVoiceChannel != null)\r\n\t\t\t\tmuteCount = selectedVoiceChannel.parentElement.getElementsByClassName(\"iconSpacing-3jB4W5\").length;\r\n\t\t\tif(this.switched == false){\r\n\t\t\t\tif(muteCount > this.lastMuteCount)\r\n\t\t\t\t\tthis.muteSound.play();\r\n\t\t\t\tif(muteCount < this.lastMuteCount)\r\n\t\t\t\t\tthis.unmuteSound.play();\r\n\t\t\t}\r\n\t\t}\r\n\t\tthis.lastMuteCount = muteCount;\r\n\t\tthis.switched = false;\r\n\t\tthis.checkFunc = setTimeout(e => { this.check(e); }, this.checkDelay);\r\n\t}\r\n\t\r\n    stop() {\r\n\t\tclearTimeout(this.checkFunc);\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "VideoExamples/README.md",
    "content": "\n"
  },
  {
    "path": "ViewGuildRelationships.plugin.js",
    "content": "/**\r\n * @name ViewGuildRelationships\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www.paypal.me/israelboone\r\n * @website https://metalloriff.github.io/toms-discord-stuff/\r\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/ViewGuildRelationships.plugin.js.plugin.js\r\n */\r\n/*@cc_on\r\n@if (@_jscript)\r\n    \r\n\t// Offer to self-install for clueless users that try to run this directly.\r\n\tvar shell = WScript.CreateObject(\"WScript.Shell\");\r\n\tvar fs = new ActiveXObject(\"Scripting.FileSystemObject\");\r\n\tvar pathPlugins = shell.ExpandEnvironmentStrings(\"%APPDATA%\\BetterDiscord\\plugins\");\r\n\tvar pathSelf = WScript.ScriptFullName;\r\n\t// Put the user at ease by addressing them in the first person\r\n\tshell.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);\r\n\tif (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {\r\n\t\tshell.Popup(\"I'm in the correct folder already.\", 0, \"I'm already installed\", 0x40);\r\n\t} else if (!fs.FolderExists(pathPlugins)) {\r\n\t\tshell.Popup(\"I can't find the BetterDiscord plugins folder.\\nAre you sure it's even installed?\", 0, \"Can't install myself\", 0x10);\r\n\t} else if (shell.Popup(\"Should I copy myself to BetterDiscord's plugins folder for you?\", 0, \"Do you need some help?\", 0x34) === 6) {\r\n\t\tfs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);\r\n\t\t// Show the user where to put plugins in the future\r\n\t\tshell.Exec(\"explorer \" + pathPlugins);\r\n\t\tshell.Popup(\"I'm installed!\", 0, \"Successfully installed\", 0x40);\r\n\t}\r\n\tWScript.Quit();\r\n\r\n@else@*/\r\n\r\nmodule.exports = (() => {\r\n\tconst config = {\r\n\t\tinfo: {\r\n\t\t\tname: 'View Guild Relationships',\r\n\t\t\tauthors: [\r\n\t\t\t\t{\r\n\t\t\t\t\tname: \"Metalloriff\",\r\n\t\t\t\t\tdiscord_id: \"264163473179672576\",\r\n\t\t\t\t\tgithub_username: \"metalloriff\",\r\n\t\t\t\t\ttwitter_username: \"Metalloriff\"\r\n\t\t\t\t},\r\n\t\t\t],\r\n\t\t\tversion: '0.0.1',\r\n\t\t\tdescription:\r\n\t\t\t\t'Adds a View Relationships button to the guild dropdown and context menu that opens a list of all friends, requested friends, and blocked users in the server.',\r\n\t\t\tgithub:\r\n\t\t\t\t'https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/ViewGuildRelationships.plugin.js',\r\n\t\t\tgithub_raw:\r\n\t\t\t\t'https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/ViewGuildRelationships.plugin.js',\r\n\t\t},\r\n\t\tchangelog: [\r\n\t\t\t{\r\n\t\t\t\ttitle: 'rewrite',\r\n\t\t\t\ttype: 'added',\r\n\t\t\t\titems: ['The plugin got rewritten.'],\r\n\t\t\t},\r\n\t\t],\r\n\t};\r\n\r\n\treturn !global.ZeresPluginLibrary\r\n\t\t? class {\r\n\t\t\tconstructor() {\r\n\t\t\t\tthis._config = config;\r\n\t\t\t}\r\n\t\t\tgetName() {\r\n\t\t\t\treturn config.info.name;\r\n\t\t\t}\r\n\t\t\tgetAuthor() {\r\n\t\t\t\treturn config.info.authors.map((a) => a.name).join(', ');\r\n\t\t\t}\r\n\t\t\tgetDescription() {\r\n\t\t\t\treturn config.info.description;\r\n\t\t\t}\r\n\t\t\tgetVersion() {\r\n\t\t\t\treturn config.info.version;\r\n\t\t\t}\r\n\t\t\tload() {\r\n\t\t\t\tBdApi.showConfirmationModal(\r\n\t\t\t\t\t'Library plugin is needed',\r\n\t\t\t\t\t[\r\n\t\t\t\t\t\t`The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`,\r\n\t\t\t\t\t],\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconfirmText: 'Download',\r\n\t\t\t\t\t\tcancelText: 'Cancel',\r\n\t\t\t\t\t\tonConfirm: () => {\r\n\t\t\t\t\t\t\trequire('request').get(\r\n\t\t\t\t\t\t\t\t'https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js',\r\n\t\t\t\t\t\t\t\tasync (error, response, body) => {\r\n\t\t\t\t\t\t\t\t\tif (error)\r\n\t\t\t\t\t\t\t\t\t\treturn require('electron').shell.openExternal(\r\n\t\t\t\t\t\t\t\t\t\t\t'https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js'\r\n\t\t\t\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t\t\t\tawait new Promise((r) =>\r\n\t\t\t\t\t\t\t\t\t\trequire('fs').writeFile(\r\n\t\t\t\t\t\t\t\t\t\t\trequire('path').join(\r\n\t\t\t\t\t\t\t\t\t\t\t\tBdApi.Plugins.folder,\r\n\t\t\t\t\t\t\t\t\t\t\t\t'0PluginLibrary.plugin.js'\r\n\t\t\t\t\t\t\t\t\t\t\t),\r\n\t\t\t\t\t\t\t\t\t\t\tbody,\r\n\t\t\t\t\t\t\t\t\t\t\tr\r\n\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t}\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t\tstart() { }\r\n\t\t\tstop() { }\r\n\t\t}\r\n\t\t: (([Plugin, Api]) => {\r\n\t\t\tconst plugin = (Plugin, Api) => {\r\n\t\t\t\tconst {\r\n\t\t\t\t\tWebpackModules,\r\n\t\t\t\t\tPluginUtilities,\r\n\t\t\t\t\tDiscordModules,\r\n\t\t\t\t\tReactComponents,\r\n\t\t\t\t\tPatcher,\r\n\t\t\t\t\tUtilities,\r\n\t\t\t\t\tDiscordModules: { React },\r\n\t\t\t\t} = Api;\r\n\t\t\t\tconst {\r\n\t\t\t\t\tlistRow,\r\n\t\t\t\t\tlistRowContent,\r\n\t\t\t\t\tlistName,\r\n\t\t\t\t} = WebpackModules.getByProps('header', 'botTag', 'listAvatar');\r\n\t\t\t\tconst Avatar = WebpackModules.getByProps('AnimatedAvatar').default;\r\n\t\t\t\tconst TabComponent = WebpackModules.getByDisplayName('TabBar');\r\n\t\t\t\tconst Modal = WebpackModules.getByProps('CloseButton');\r\n\t\t\t\tconst { MenuItem, MenuGroup } = WebpackModules.getModule(\r\n\t\t\t\t\t(m) => m.MenuRadioItem && !m.default\r\n\t\t\t\t);\r\n\t\t\t\tconst\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tdefault: ScrollWrapper\r\n\t\t\t\t\t} = WebpackModules.getByProps('ScrollerAuto');\r\n\t\t\t\tconst {\r\n\t\t\t\t\ttabBarContainer,\r\n\t\t\t\t\ttabBarItem,\r\n\t\t\t\t\ttabBar,\r\n\t\t\t\t} = WebpackModules.getByProps(\r\n\t\t\t\t\t'root',\r\n\t\t\t\t\t'topSectionStreaming',\r\n\t\t\t\t\t'topSectionSpotify'\r\n\t\t\t\t);\r\n\t\t\t\tconst { sizeMedium } = WebpackModules.getByProps(\r\n\t\t\t\t\t'responsiveWidthMobile',\r\n\t\t\t\t\t'modal',\r\n\t\t\t\t\t'sizeSmall'\r\n\t\t\t\t);\r\n\t\t\t\tconst Item = ({ user, guild, onClose }) => {\r\n\t\t\t\t\treturn React.createElement('div', {\r\n\t\t\t\t\t\tclassName: listRow,\r\n\t\t\t\t\t\tonClick: () => {\r\n\t\t\t\t\t\t\tWebpackModules.getByProps(\r\n\t\t\t\t\t\t\t\t'openPrivateChannel'\r\n\t\t\t\t\t\t\t).openPrivateChannel(user.id);\r\n\t\t\t\t\t\t\tonClose();\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t\tchildren: [\r\n\t\t\t\t\t\t\tReact.createElement(Avatar, {\r\n\t\t\t\t\t\t\t\tsrc: user.getAvatarURL(),\r\n\t\t\t\t\t\t\t\tisMobile: false,\r\n\t\t\t\t\t\t\t\tisTyping: false,\r\n\t\t\t\t\t\t\t\tsize: 'SIZE_40',\r\n\t\t\t\t\t\t\t}),\r\n\t\t\t\t\t\t\tReact.createElement('div', {\r\n\t\t\t\t\t\t\t\tclassName: listRowContent,\r\n\t\t\t\t\t\t\t\tstyle: {\r\n\t\t\t\t\t\t\t\t\tmarginLeft: '5px',\r\n\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\tchildren: [\r\n\t\t\t\t\t\t\t\t\tReact.createElement('div', {\r\n\t\t\t\t\t\t\t\t\t\tclassName: listName,\r\n\t\t\t\t\t\t\t\t\t\tchildren: user.username,\r\n\t\t\t\t\t\t\t\t\t}),\r\n\t\t\t\t\t\t\t\t],\r\n\t\t\t\t\t\t\t}),\r\n\t\t\t\t\t\t],\r\n\t\t\t\t\t});\r\n\t\t\t\t};\r\n\t\t\t\tconst Tabs = [\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tlabel: 'Blocked Users',\r\n\t\t\t\t\t\tid: 'BLOCKED_USERS',\r\n\t\t\t\t\t\telement: (props, onClose) => {\r\n\t\t\t\t\t\t\tvar blockedUsers = [];\r\n\t\t\t\t\t\t\tObject.entries(\r\n\t\t\t\t\t\t\t\tDiscordModules.RelationshipStore.getRelationships()\r\n\t\t\t\t\t\t\t).forEach(([key, value]) => {\r\n\t\t\t\t\t\t\t\tif (value == 2)\r\n\t\t\t\t\t\t\t\t\tblockedUsers.push(DiscordModules.UserStore.getUser(key));\r\n\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\tconst blocked = blockedUsers.filter(\r\n\t\t\t\t\t\t\t\t(e) =>\r\n\t\t\t\t\t\t\t\t\t!!DiscordModules.GuildMemberStore.getMember(props.id, e.id)\r\n\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t\treturn blocked.length == 0\r\n\t\t\t\t\t\t\t\t? React.createElement(\r\n\t\t\t\t\t\t\t\t\t'p',\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tstyle: {\r\n\t\t\t\t\t\t\t\t\t\t\tcolor: 'white',\r\n\t\t\t\t\t\t\t\t\t\t\tfontWeight: 'bold',\r\n\t\t\t\t\t\t\t\t\t\t\ttextAlign: 'center',\r\n\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t'No Mutual Blocked Users'\r\n\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t: blocked.map((e) =>\r\n\t\t\t\t\t\t\t\t\tReact.createElement(Item, {\r\n\t\t\t\t\t\t\t\t\t\tuser: e,\r\n\t\t\t\t\t\t\t\t\t\tguild: props,\r\n\t\t\t\t\t\t\t\t\t\tonClose,\r\n\t\t\t\t\t\t\t\t\t})\r\n\t\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tlabel: 'Incoming Friends',\r\n\t\t\t\t\t\tid: 'INCOMING_FRIENDS',\r\n\t\t\t\t\t\telement: (props, onClose) => {\r\n\t\t\t\t\t\t\tvar icomingFriends = [];\r\n\t\t\t\t\t\t\tObject.entries(\r\n\t\t\t\t\t\t\t\tDiscordModules.RelationshipStore.getRelationships()\r\n\t\t\t\t\t\t\t).forEach(([key, value]) => {\r\n\t\t\t\t\t\t\t\tif (value == 3)\r\n\t\t\t\t\t\t\t\t\ticomingFriends.push(DiscordModules.UserStore.getUser(key));\r\n\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\tconst inc = icomingFriends.filter(\r\n\t\t\t\t\t\t\t\t(e) =>\r\n\t\t\t\t\t\t\t\t\t!!DiscordModules.GuildMemberStore.getMember(props.id, e.id)\r\n\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t\treturn inc.length == 0\r\n\t\t\t\t\t\t\t\t? React.createElement(\r\n\t\t\t\t\t\t\t\t\t'p',\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tstyle: {\r\n\t\t\t\t\t\t\t\t\t\t\tcolor: 'white',\r\n\t\t\t\t\t\t\t\t\t\t\tfontWeight: 'bold',\r\n\t\t\t\t\t\t\t\t\t\t\ttextAlign: 'center',\r\n\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t'No Mutual Incoming Friends'\r\n\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t: inc.map((e) =>\r\n\t\t\t\t\t\t\t\t\tReact.createElement(Item, {\r\n\t\t\t\t\t\t\t\t\t\tuser: e,\r\n\t\t\t\t\t\t\t\t\t\tguild: props,\r\n\t\t\t\t\t\t\t\t\t\tonClose,\r\n\t\t\t\t\t\t\t\t\t})\r\n\t\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tlabel: 'Outgoing Friends',\r\n\t\t\t\t\t\tid: 'OUTGOING_FRIENDS',\r\n\t\t\t\t\t\telement: (props, onClose) => {\r\n\t\t\t\t\t\t\tvar outgoingFriends = [];\r\n\t\t\t\t\t\t\tObject.entries(\r\n\t\t\t\t\t\t\t\tDiscordModules.RelationshipStore.getRelationships()\r\n\t\t\t\t\t\t\t).forEach(([key, value]) => {\r\n\t\t\t\t\t\t\t\tif (value == 4)\r\n\t\t\t\t\t\t\t\t\toutgoingFriends.push(DiscordModules.UserStore.getUser(key));\r\n\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\tconst out = outgoingFriends.filter(\r\n\t\t\t\t\t\t\t\t(e) =>\r\n\t\t\t\t\t\t\t\t\t!!DiscordModules.GuildMemberStore.getMember(props.id, e.id)\r\n\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t\treturn out.length == 0\r\n\t\t\t\t\t\t\t\t? React.createElement(\r\n\t\t\t\t\t\t\t\t\t'p',\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tstyle: {\r\n\t\t\t\t\t\t\t\t\t\t\tcolor: 'white',\r\n\t\t\t\t\t\t\t\t\t\t\tfontWeight: 'bold',\r\n\t\t\t\t\t\t\t\t\t\t\ttextAlign: 'center',\r\n\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t'No Mutual Outgoing Friends'\r\n\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t: out.map((e) =>\r\n\t\t\t\t\t\t\t\t\tReact.createElement(Item, {\r\n\t\t\t\t\t\t\t\t\t\tuser: e,\r\n\t\t\t\t\t\t\t\t\t\tguild: props,\r\n\t\t\t\t\t\t\t\t\t\tonClose,\r\n\t\t\t\t\t\t\t\t\t})\r\n\t\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tlabel: 'Mutual Friends',\r\n\t\t\t\t\t\tid: 'MUTUAL_FRIENDS',\r\n\t\t\t\t\t\telement: (props, onClose) => {\r\n\t\t\t\t\t\t\tvar friends = [];\r\n\t\t\t\t\t\t\tObject.entries(\r\n\t\t\t\t\t\t\t\tDiscordModules.RelationshipStore.getRelationships()\r\n\t\t\t\t\t\t\t).forEach(([key, value]) => {\r\n\t\t\t\t\t\t\t\tif (value == 1)\r\n\t\t\t\t\t\t\t\t\tfriends.push(DiscordModules.UserStore.getUser(key));\r\n\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\tconst friend = friends.filter(\r\n\t\t\t\t\t\t\t\t(e) =>\r\n\t\t\t\t\t\t\t\t\t!!DiscordModules.GuildMemberStore.getMember(props.id, e.id)\r\n\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t\treturn friend.length == 0\r\n\t\t\t\t\t\t\t\t? React.createElement(\r\n\t\t\t\t\t\t\t\t\t'p',\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tstyle: {\r\n\t\t\t\t\t\t\t\t\t\t\tcolor: 'white',\r\n\t\t\t\t\t\t\t\t\t\t\tfontWeight: 'bold',\r\n\t\t\t\t\t\t\t\t\t\t\ttextAlign: 'center',\r\n\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t'No Mutual Friends'\r\n\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t: friend.map((e) =>\r\n\t\t\t\t\t\t\t\t\tReact.createElement(Item, {\r\n\t\t\t\t\t\t\t\t\t\tuser: e,\r\n\t\t\t\t\t\t\t\t\t\tguild: props,\r\n\t\t\t\t\t\t\t\t\t\tonClose,\r\n\t\t\t\t\t\t\t\t\t})\r\n\t\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t},\r\n\t\t\t\t];\r\n\t\t\t\tclass Menu extends React.Component {\r\n\t\t\t\t\tconstructor(props) {\r\n\t\t\t\t\t\tsuper(props);\r\n\t\t\t\t\t\tthis.state = { selected: Tabs[0].id };\r\n\t\t\t\t\t}\r\n\t\t\t\t\trender() {\r\n\t\t\t\t\t\treturn React.createElement(Modal, {\r\n\t\t\t\t\t\t\tclassName: [sizeMedium, 'modal'],\r\n\t\t\t\t\t\t\tchildren: [\r\n\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t'div',\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tclassName: tabBarContainer,\r\n\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\tReact.createElement(TabComponent.Header, {\r\n\t\t\t\t\t\t\t\t\t\tclassName: [TabComponent.Types.TOP, tabBar].join(' '),\r\n\t\t\t\t\t\t\t\t\t\tchildren: Tabs.map((e) =>\r\n\t\t\t\t\t\t\t\t\t\t\tObject.assign({}, { id: e.id, label: e.label })\r\n\t\t\t\t\t\t\t\t\t\t).map((e) =>\r\n\t\t\t\t\t\t\t\t\t\t\tReact.createElement(TabComponent.Item, {\r\n\t\t\t\t\t\t\t\t\t\t\t\tid: e.id,\r\n\t\t\t\t\t\t\t\t\t\t\t\tonClick: () => this.setTab(e.id),\r\n\t\t\t\t\t\t\t\t\t\t\t\tonItemSelect: () => this.setTab(e.id),\r\n\t\t\t\t\t\t\t\t\t\t\t\tchildren: e.label,\r\n\t\t\t\t\t\t\t\t\t\t\t\tclassName: tabBarItem,\r\n\t\t\t\t\t\t\t\t\t\t\t\tselectedItem: this.state.selected,\r\n\t\t\t\t\t\t\t\t\t\t\t})\r\n\t\t\t\t\t\t\t\t\t\t),\r\n\t\t\t\t\t\t\t\t\t})\r\n\t\t\t\t\t\t\t\t),\r\n\t\t\t\t\t\t\t\tReact.createElement('div', {\r\n\t\t\t\t\t\t\t\t\tstyle: {\r\n\t\t\t\t\t\t\t\t\t\tmarginTop: '10px',\r\n\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\tchildren: React.createElement(ScrollWrapper, {\r\n\t\t\t\t\t\t\t\t\t\tstyle: {\r\n\t\t\t\t\t\t\t\t\t\t\tmaxHeight: \"333px\",\r\n\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\tchildren: Tabs.find(\r\n\t\t\t\t\t\t\t\t\t\t\t(e) => e.id == this.state.selected\r\n\t\t\t\t\t\t\t\t\t\t).element(this.props.guild, this.props.onClose),\r\n\t\t\t\t\t\t\t\t\t}),\r\n\t\t\t\t\t\t\t\t}),\r\n\t\t\t\t\t\t\t],\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\t}\r\n\t\t\t\t\tsetTab(e) {\r\n\t\t\t\t\t\tthis.setState({ selected: e });\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\treturn class ViewGuildRelationships extends Plugin {\r\n\t\t\t\t\tconstructor() {\r\n\t\t\t\t\t\tsuper();\r\n\t\t\t\t\t}\r\n\t\t\t\t\tget css() {\r\n\t\t\t\t\t\treturn `.modal {\r\n\t\t\t\t\t\t\twidth: 630px;\r\n\t\t\t\t\t\t}`;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tonStart() {\r\n\t\t\t\t\t\tPluginUtilities.addStyle(config.info.name, this.css);\r\n\t\t\t\t\t\tconst GuildContextMenu = WebpackModules.getModule(\r\n\t\t\t\t\t\t\t(m) => m.default.displayName === 'GuildContextMenu'\r\n\t\t\t\t\t\t);\r\n\t\t\t\t\t\tif (GuildContextMenu)\r\n\t\t\t\t\t\t\tPatcher.after(\r\n\t\t\t\t\t\t\t\tGuildContextMenu,\r\n\t\t\t\t\t\t\t\t'default',\r\n\t\t\t\t\t\t\t\t(_, [props], ret) => {\r\n\t\t\t\t\t\t\t\t\tret.props.children.unshift(\r\n\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\tMenuGroup,\r\n\t\t\t\t\t\t\t\t\t\t\t{},\r\n\t\t\t\t\t\t\t\t\t\t\tReact.createElement(MenuItem, {\r\n\t\t\t\t\t\t\t\t\t\t\t\tid: 'view-guild-relationships',\r\n\t\t\t\t\t\t\t\t\t\t\t\taction: () => {\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tDiscordModules.ModalStack.push(Menu, {\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tguild: props.guild,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: 'View GuildRelationships',\r\n\t\t\t\t\t\t\t\t\t\t\t})\r\n\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tonStop() {\r\n\t\t\t\t\t\tPluginUtilities.removeStyle(config.info.name);\r\n\t\t\t\t\t\tPatcher.unpatchAll();\r\n\t\t\t\t\t}\r\n\t\t\t\t};\r\n\t\t\t};\r\n\t\t\treturn plugin(Plugin, Api);\r\n\t\t})(global.ZeresPluginLibrary.buildPlugin(config));\r\n})();\r\n"
  },
  {
    "path": "VoiceChatNotifications.plugin.js",
    "content": "/**\r\n * @name VoiceChatNotifications\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www.paypal.me/israelboone\r\n * @website https://metalloriff.github.io/toms-discord-stuff/\r\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/VoiceChatNotifications.plugin.js\r\n */\r\n\r\nmodule.exports = (() =>\r\n{\r\n\tconst config =\r\n\t{\r\n\t\tinfo:\r\n\t\t{\r\n\t\t\tname: \"VoiceChatNotifications\",\r\n\t\t\tauthors:\r\n\t\t\t[\r\n\t\t\t\t{\r\n\t\t\t\t\tname: \"Metalloriff\",\r\n\t\t\t\t\tdiscord_id: \"264163473179672576\",\r\n\t\t\t\t\tgithub_username: \"metalloriff\",\r\n\t\t\t\t\ttwitter_username: \"Metalloriff\"\r\n\t\t\t\t}\r\n\t\t\t],\r\n\t\t\tversion: \"3.0.1\",\r\n\t\t\tdescription: \"Displays notifications when users join/leave, mute/unmute, deafen/undeafen, or are moved in the voice channel you're in.\",\r\n\t\t\tgithub: \"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/VoiceChatNotifications.plugin.js\",\r\n\t\t\tgithub_raw: \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/VoiceChatNotifications.plugin.js\"\r\n\t\t},\r\n\t\tdefaultConfig:\r\n\t\t[\r\n\t\t\t{\r\n\t\t\t\ttype: \"category\",\r\n\t\t\t\tid: \"log\",\r\n\t\t\t\tname: \"notification settings\",\r\n\t\t\t\tcollapsible: true,\r\n\t\t\t\tshown: true,\r\n\t\t\t\tsettings:\r\n\t\t\t\t[\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"switch\",\r\n\t\t\t\t\t\tid: \"connections\",\r\n\t\t\t\t\t\tname: \"Show join/leave notifications\",\r\n\t\t\t\t\t\tvalue: true\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"switch\",\r\n\t\t\t\t\t\tid: \"selfMute\",\r\n\t\t\t\t\t\tname: \"Show mute/unmute notifications\",\r\n\t\t\t\t\t\tvalue: true\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"switch\",\r\n\t\t\t\t\t\tid: \"selfDeafen\",\r\n\t\t\t\t\t\tname: \"Show deafen/undeafen notifications\",\r\n\t\t\t\t\t\tvalue: true\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"switch\",\r\n\t\t\t\t\t\tid: \"moved\",\r\n\t\t\t\t\t\tname: \"Show user channel move notifications\",\r\n\t\t\t\t\t\tvalue: true\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"switch\",\r\n\t\t\t\t\t\tid: \"mute\",\r\n\t\t\t\t\t\tname: \"Show server mute/unmute notifications\",\r\n\t\t\t\t\t\tvalue: true\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"switch\",\r\n\t\t\t\t\t\tid: \"deafen\",\r\n\t\t\t\t\t\tname: \"Show server deafen/undeafen notifications\",\r\n\t\t\t\t\t\tvalue: true\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"switch\",\r\n\t\t\t\t\t\tid: \"selfVideo\",\r\n\t\t\t\t\t\tname: \"Show user video notifications\",\r\n\t\t\t\t\t\tvalue: true\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"switch\",\r\n\t\t\t\t\t\tid: \"selfStream\",\r\n\t\t\t\t\t\tname: \"Show user stream notifications\",\r\n\t\t\t\t\t\tvalue: true\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttype: \"switch\",\r\n\t\t\t\t\t\tid: \"supressInDnd\",\r\n\t\t\t\t\t\tname: \"Disable notifications while in do not disturb\",\r\n\t\t\t\t\t\tvalue: true\r\n\t\t\t\t\t}\r\n\t\t\t\t]\r\n\t\t\t}\r\n\t\t],\r\n\t\tchangelog:\r\n\t\t[\r\n\t\t\t{\r\n\t\t\t\ttype: \"fixed\",\r\n\t\t\t\ttitle: \"3.0.1 bug fix\",\r\n\t\t\t\titems:\r\n\t\t\t\t[\r\n\t\t\t\t\t\"Fixed the plugin\"\r\n\t\t\t\t]\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\ttype: \"fixed\",\r\n\t\t\t\ttitle: \"3.0 rewrite\",\r\n\t\t\t\titems:\r\n\t\t\t\t[\r\n\t\t\t\t\t\"This plugin has been rewritten, if you experience any new bugs, please contact me.\",\r\n\t\t\t\t\t\"Please note that all settings have been reset and you will have to reconfigure them.\"\r\n\t\t\t\t]\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\ttype: \"added\",\r\n\t\t\t\ttitle: \"new features\",\r\n\t\t\t\titems:\r\n\t\t\t\t[\r\n\t\t\t\t\t\"Added user video notifications.\",\r\n\t\t\t\t\t\"Added user stream notifications.\"\r\n\t\t\t\t]\r\n\t\t\t}\r\n\t\t]\r\n\t};\r\n\r\n\treturn !global.ZeresPluginLibrary ? class\r\n\t{\r\n\t\tconstructor() { this._config = config; }\r\n\r\n\t\tgetName = () => config.info.name;\r\n\t\tgetAuthor = () => config.info.description;\r\n\t\tgetVersion = () => config.info.version;\r\n\r\n\t\tload()\r\n\t\t{\r\n\t\t\tBdApi.showConfirmationModal(\"Library Missing\", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {\r\n\t\t\t\tconfirmText: \"Download Now\",\r\n\t\t\t\tcancelText: \"Cancel\",\r\n\t\t\t\tonConfirm: () =>\r\n\t\t\t\t{\r\n\t\t\t\t\trequire(\"request\").get(\"https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js\", async (err, res, body) =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (err) return require(\"electron\").shell.openExternal(\"https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js\");\r\n\t\t\t\t\t\tawait new Promise(r => require(\"fs\").writeFile(require(\"path\").join(BdApi.Plugins.folder, \"0PluginLibrary.plugin.js\"), body, r));\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tstart() { }\r\n\t\tstop() { }\r\n\t} : (([Plugin, Api]) => {\r\n\r\n\t\tconst plugin = (Plugin, Api) =>\r\n\t\t{\r\n\t\t\tconst { WebpackModules, DiscordModules } = Api;\r\n\t\t\tconst { UserStore, ChannelStore, SelectedChannelStore, UserStatusStore } = DiscordModules;\r\n\r\n\t\t\tconst { getVoiceStates } = WebpackModules.getByProps(\"getVoiceStates\");\r\n\r\n\t\t\treturn class VoiceChatNotifications extends Plugin\r\n\t\t\t{\r\n\t\t\t\tconstructor()\r\n\t\t\t\t{\r\n\t\t\t\t\tsuper();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tasync showChangelog(footer)\r\n\t\t\t\t{\r\n\t\t\t\t\ttry { footer = (await WebpackModules.getByProps(\"getUser\", \"acceptAgreements\").getUser(\"264163473179672576\")).tag + \" | https://discord.gg/yNqzuJa\"; }\r\n\t\t\t\t\tfinally { super.showChangelog(footer); }\r\n\t\t\t\t}\r\n\r\n\t\t\t\tgetSettingsPanel = () => this.buildSettingsPanel().getElement();\r\n\t\r\n\t\t\t\tonStart()\r\n\t\t\t\t{\r\n\t\t\t\t\tthis.loop = setInterval(() => this.main(), 500);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tmain()\r\n\t\t\t\t{\r\n\t\t\t\t\tconst vcid = SelectedChannelStore.getVoiceChannelId();\r\n\t\t\t\t\tconst vc = vcid == null ? null : ChannelStore.getChannel(vcid);\r\n\r\n\t\t\t\t\tif (vc == null)\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\t\tconst me = UserStore.getCurrentUser();\r\n\t\t\t\t\tconst states = getVoiceStates(vc.guild_id);\r\n\r\n\t\t\t\t\tif (UserStatusStore.getStatus(me.id) != \"dnd\" || !this.settings.log.supressInDnd)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfor (let state of Object.values(states))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tconst user = UserStore.getUser(state.userId);\r\n\t\t\t\t\t\t\tconst channel = ChannelStore.getChannel(state.channelId);\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\tif (state.userId == me.id || this.states == null || user == null || channel == null)\r\n\t\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\tconst o = { silent: true, icon: user.getAvatarURL() };\r\n\r\n\t\t\t\t\t\t\tif (this.states[state.userId] == null)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tif (this.settings.log.connections)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tnew Notification(`${user.username} joined ${channel.name == \"\" ? \"the call\" : channel.name}`, o);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tconst lastState = this.states[state.userId];\r\n\t\t\t\t\t\t\t\tconst diff = Object.keys(state).filter(k => state[k] != lastState[k]);\r\n\r\n\t\t\t\t\t\t\t\tif (this.settings.log.moves && lastState.channelId != state.channelId)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tnew Notification(`${user.username} moved to ${channel.name}`, o);\r\n\t\t\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t\tfor (let d of diff)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tconst v = state[d];\r\n\t\t\t\t\t\t\t\t\tlet m = null;\r\n\r\n\t\t\t\t\t\t\t\t\tswitch (d)\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tcase \"selfMute\":\r\n\t\t\t\t\t\t\t\t\t\t\tm = `${user.username} ${v ? \"muted\" : \"unmuted\"} themselves`;\r\n\t\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t\t\tcase \"selfDeafen\":\r\n\t\t\t\t\t\t\t\t\t\t\tm = `${user.username} ${v ? \"deafened\" : \"undeafened\"} themselves`;\r\n\t\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t\t\tcase \"mute\":\r\n\t\t\t\t\t\t\t\t\t\t\tm = `${user.username} was ${v ? \"muted\" : \"unmuted\"}`;\r\n\t\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t\t\tcase \"deafen\":\r\n\t\t\t\t\t\t\t\t\t\t\tm = `${user.username} was ${v ? \"deafened\" : \"undeafened\"}`;\r\n\t\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t\t\tcase \"selfVideo\":\r\n\t\t\t\t\t\t\t\t\t\t\tm = `${user.username} ${v ? \"enabled\" : \"disabled\"} their video`;\r\n\t\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t\t\tcase \"selfStream\":\r\n\t\t\t\t\t\t\t\t\t\t\tm = `${user.username} ${v ? \"started a\" : \"stopped their\"} stream`;\r\n\t\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t\t\tif (m != null && this.settings.log[d] == true)\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tnew Notification(m, o);\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tif (this.states != null)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tfor (let state of Object.values(this.states))\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tconst user = UserStore.getUser(state.userId);\r\n\t\t\t\t\t\t\t\tconst channel = ChannelStore.getChannel(state.channelId);\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\tif (state.userId == me.id || this.states == null || user == null || channel == null)\r\n\t\t\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\tconst o = { silent: true, icon: user.getAvatarURL() };\r\n\r\n\t\t\t\t\t\t\t\tif (states[state.userId] == null && this.settings.log.connections)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tnew Notification(`${user.username} disconnected from ${channel.name == \"\" ? \"the call\" : channel.name}`, o);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tthis.states = states;\r\n\t\t\t\t}\r\n\t\r\n\t\t\t\tonStop()\r\n\t\t\t\t{\r\n\t\t\t\t\tclearInterval(this.loop);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\treturn plugin(Plugin, Api);\r\n\t})(global.ZeresPluginLibrary.buildPlugin(config));\r\n})();\r\n"
  },
  {
    "path": "VoiceChatPanel.plugin.js",
    "content": "/**\r\n * @name VoiceChatPanel\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www.paypal.me/israelboone\r\n * @website https://metalloriff.github.io/toms-discord-stuff/\r\n * @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/VoiceChatPanel.plugin.js\r\n */\r\n\r\nmodule.exports = (() =>\r\n{\r\n\tconst config =\r\n\t{\r\n\t\tinfo:\r\n\t\t{\r\n\t\t\tname: \"VoiceChatPanel\",\r\n\t\t\tauthors:\r\n\t\t\t[\r\n\t\t\t\t{\r\n\t\t\t\t\tname: \"Metalloriff\",\r\n\t\t\t\t\tdiscord_id: \"264163473179672576\",\r\n\t\t\t\t\tgithub_username: \"metalloriff\",\r\n\t\t\t\t\ttwitter_username: \"Metalloriff\"\r\n\t\t\t\t}\r\n\t\t\t],\r\n\t\t\tversion: \"0.0.1\",\r\n\t\t\tdescription: \"Adds user voice db beside their name, and adds a history graph for user volumes.\",\r\n\t\t\tgithub: \"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/VoiceChatPanel.plugin.js\",\r\n\t\t\tgithub_raw: \"https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/VoiceChatPanel.plugin.js\"\r\n\t\t},\r\n\t\tdefaultConfig:\r\n\t\t[\r\n\t\t\t{\r\n\t\t\t\tid: \"hideSilentUsers\",\r\n\t\t\t\ttype: \"switch\",\r\n\t\t\t\tname: \"Hide Silent Users\",\r\n\t\t\t\tvalue: true\r\n\t\t\t}\r\n\t\t]\r\n\t};\r\n\r\n\treturn !global.ZeresPluginLibrary ? class\r\n\t{\r\n\t\tconstructor() { this._config = config; }\r\n\r\n\t\tgetName = () => config.info.name;\r\n\t\tgetAuthor = () => config.info.description;\r\n\t\tgetVersion = () => config.info.version;\r\n\r\n\t\tload()\r\n\t\t{\r\n\t\t\tBdApi.showConfirmationModal(\"Library Missing\", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {\r\n\t\t\t\tconfirmText: \"Download Now\",\r\n\t\t\t\tcancelText: \"Cancel\",\r\n\t\t\t\tonConfirm: () =>\r\n\t\t\t\t{\r\n\t\t\t\t\trequire(\"request\").get(\"https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js\", async (err, res, body) =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (err) return require(\"electron\").shell.openExternal(\"https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js\");\r\n\t\t\t\t\t\tawait new Promise(r => require(\"fs\").writeFile(require(\"path\").join(BdApi.Plugins.folder, \"0PluginLibrary.plugin.js\"), body, r));\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tstart() { }\r\n\t\tstop() { }\r\n\t} : (([Plugin, Api]) => {\r\n\r\n\t\tconst plugin = (Plugin, Api) =>\r\n\t\t{\r\n\t\t\tconst { WebpackModules, Patcher, DiscordModules, PluginUtilities } = Api;\r\n\t\t\tconst { React, UserStore, ChannelStore, SelectedChannelStore } = DiscordModules;\r\n\t\t\t\r\n\t\t\tconst MediaEngine = WebpackModules.getByProps(\"getMediaEngine\").getMediaEngine();\r\n\t\t\tconst { TimelineDataSeries, TimelineGraphView } = WebpackModules.getByProps(\"TimelineDataSeries\");\r\n\t\t\tconst ModalStack = WebpackModules.getByProps(\"openModal\");\r\n\t\t\t\r\n\t\t\tconst VoiceUser = WebpackModules.getByDisplayName(\"VoiceUser\");\r\n\t\t\tconst { default: ScrollWrapper } = WebpackModules.getByProps(\"ScrollerAuto\");\r\n\t\t\tconst { ModalRoot } = WebpackModules.getByProps(\"ModalRoot\");\r\n\t\t\tconst RTCDebugItem = WebpackModules.getByDisplayName(\"RTCDebugItem\");\r\n\t\t\tconst PanelButton = WebpackModules.getByDisplayName(\"PanelButton\");\r\n\t\t\tconst SwitchItem = WebpackModules.getByDisplayName(\"SwitchItem\");\r\n\r\n\t\t\tconst icon = e => React.createElement((WebpackModules.find(m => m.id != null && typeof m.keys == \"function\" && m.keys().includes(\"./Activity\"))(e) || {}).default);\r\n\t\t\tconst db = l => l.toLocaleString({}, { minimumFractionDigits: 1, maximumFractionDigits: 1 });\r\n\t\t\tconst getVoiceStates = () => WebpackModules.getByProps(\"getVoiceStatesForChannel\").getVoiceStatesForChannel(ChannelStore.getChannel(SelectedChannelStore.getVoiceChannelId()));\r\n\r\n\t\t\tlet components = [];\r\n\t\t\tlet timeline = {};\r\n\r\n\t\t\tclass Graph extends React.Component\r\n\t\t\t{\r\n\t\t\t\tconstructor()\r\n\t\t\t\t{\r\n\t\t\t\t\tsuper();\r\n\t\t\t\t\tthis.state = { dataSeries: new TimelineDataSeries() };\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcomponentDidMount()\r\n\t\t\t\t{\r\n\t\t\t\t\tthis.interval = setInterval(() => {\r\n\t\t\t\t\t\tif (timeline[this.props.user.id] != null)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tthis.setState({ dataSeries: timeline[this.props.user.id] });\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}, 500);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcomponentWillUnmount()\r\n\t\t\t\t{\r\n\t\t\t\t\tclearInterval(this.interval);\r\n\t\t\t\t}\r\n\r\n\t\t\t\thandleRenderGraph(e)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (e != null)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tthis.state.graphView = new TimelineGraphView(e);\r\n\t\t\t\t\t\tthis.state.graphView.backgroundColor = \"black\";\r\n\t\t\t\t\t\tthis.state.graphView.textColor = \"white\";\r\n\t\t\t\t\t\tthis.state.graphView.timeOptions = { timeStyle: \"short\" };\r\n\t\t\t\t\t\tthis.state.graphView.addDataSeries(this.state.dataSeries);\r\n\t\t\t\t\t\tthis.state.graphView.updateEndDate();\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\trender()\r\n\t\t\t\t{\r\n\t\t\t\t\treturn React.createElement(\r\n\t\t\t\t\t\t\"canvas\",\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tkey: \"canvas\",\r\n\t\t\t\t\t\t\theight: 100,\r\n\t\t\t\t\t\t\tstyle: { width: \"100%\" },\r\n\t\t\t\t\t\t\tref: e => this.handleRenderGraph(e)\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tclass Panel extends React.Component\r\n\t\t\t{\r\n\t\t\t\tconstructor()\r\n\t\t\t\t{\r\n\t\t\t\t\tsuper();\r\n\t\t\t\t\tthis.state = BdApi.getPlugin(\"VoiceChatPanel\").settings\r\n\t\t\t\t}\r\n\r\n\t\t\t\trender()\r\n\t\t\t\t{\r\n\t\t\t\t\treturn React.createElement(\r\n\t\t\t\t\t\t\"div\",\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tchildren:\r\n\t\t\t\t\t\t\t[\r\n\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\tScrollWrapper,\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tstyle: { maxHeight: 600 },\r\n\t\t\t\t\t\t\t\t\t\tchildren:\r\n\t\t\t\t\t\t\t\t\t\t[\r\n\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\"div\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tstyle: { margin: 10 }\r\n\t\t\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tSwitchItem,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tchildren: \"Hide Silent Users\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnote: \"\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonChange: e =>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst plugin = BdApi.getPlugin(\"VoiceChatPanel\");\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplugin.settings.hideSilentUsers = !plugin.settings.hideSilentUsers;\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplugin.saveSettings();\r\n\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tthis.setState(plugin.settings);\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvalue: this.state.hideSilentUsers\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t\t\t),\r\n\t\t\t\t\t\t\t\t\t\t\t...this.props.users.map(user =>\r\n\t\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"div\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstyle: { margin: \"5%\" },\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tchildren:\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRTCDebugItem,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tchildren: user.username,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trenderGraph: () =>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tGraph,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{ user }\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvalueRendered: \"timeline\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t\t))\r\n\t\t\t\t\t\t\t\t\t\t]\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t]\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tclass UserVolumeLabel extends React.Component\r\n\t\t\t{\r\n\t\t\t\tconstructor()\r\n\t\t\t\t{\r\n\t\t\t\t\tsuper();\r\n\t\t\t\t\tthis.state = { volume: 0 };\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcomponentDidMount()\r\n\t\t\t\t{\r\n\t\t\t\t\tcomponents.push(this);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcomponentWillUnmount()\r\n\t\t\t\t{\r\n\t\t\t\t\tcomponents.splice(components.indexOf(this));\r\n\t\t\t\t}\r\n\r\n\t\t\t\trender()\r\n\t\t\t\t{\r\n\t\t\t\t\treturn this.props.serverMute || this.props.mute\r\n\t\t\t\t\t\t? React.createElement(\"span\")\r\n\t\t\t\t\t\t: React.createElement(\r\n\t\t\t\t\t\t\t\"span\",\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tstyle:\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tfontSize: \"14px\",\r\n\t\t\t\t\t\t\t\t\tlineHeight: \"18px\",\r\n\t\t\t\t\t\t\t\t\tfontWeight: 500,\r\n\t\t\t\t\t\t\t\t\tcolor: this.state.volume > 0.5 ? \"red\" : \"white\",\r\n\t\t\t\t\t\t\t\t\twhiteSpace: \"nowrap\",\r\n\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\tchildren: db(this.state.volume)\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\treturn class VoiceChatPanel extends Plugin\r\n\t\t\t{\r\n\t\t\t\tconstructor()\r\n\t\t\t\t{\r\n\t\t\t\t\tsuper();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tasync showChangelog(footer)\r\n\t\t\t\t{\r\n\t\t\t\t\ttry { footer = (await WebpackModules.getByProps(\"getUser\", \"acceptAgreements\").getUser(\"264163473179672576\")).tag + \" | https://discord.gg/yNqzuJa\"; }\r\n\t\t\t\t\tfinally { super.showChangelog(footer); }\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tgetSettingsPanel = () => this.buildSettingsPanel().getElement();\r\n\t\r\n\t\t\t\tonStart()\r\n\t\t\t\t{\r\n\t\t\t\t\tthis.localUserId = UserStore.getCurrentUser().id;\r\n\r\n\t\t\t\t\tPluginUtilities.addStyle(this.getName(), `\r\n\t\t\t\t\t\t.draggable-1KoBzC { height: auto }\r\n\r\n\t\t\t\t\t\t.vcp-button\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tposition: absolute;\r\n\t\t\t\t\t\t\ttop: 7px;\r\n\t\t\t\t\t\t\tleft: 135px;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t`);\r\n\r\n\t\t\t\t\tPatcher.instead(VoiceUser.prototype, \"render\", ({ props }, _, e) =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (!props.speaking && this.settings.hideSilentUsers)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\treturn React.createElement(\"div\");\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tconst re = e(props);\r\n\r\n\t\t\t\t\t\tif (props.user.id != this.localUserId)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tre.props.children.props.children.splice(3, 0, React.createElement(UserVolumeLabel, props));\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\treturn re;\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\tPatcher.after(WebpackModules.getByDisplayName(\"RTCConnectionStatus\").prototype, \"render\", ({ props }, _, re) =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tre.props.children.push(\r\n\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\"div\",\r\n\t\t\t\t\t\t\t\t{ className: \"vcp-button\" },\r\n\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\tPanelButton,\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\ticon: () => icon(\"./TrendingUp.tsx\"),\r\n\t\t\t\t\t\t\t\t\t\tonClick: () =>\r\n\t\t\t\t\t\t\t\t\t\t\tModalStack.openModal(\r\n\t\t\t\t\t\t\t\t\t\t\t\tprops =>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tModalRoot,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t...props\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tReact.createElement(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tPanel,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t...props,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tusers: getVoiceStates().filter(user => user.userId != this.localUserId).map(s => UserStore.getUser(s.userId))\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t\t\t\t\t),\r\n\t\t\t\t\t\t\t\t\t\ttooltipText: \"Open Audio Graph\"\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t);\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\tthis.interval = setInterval(() =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tlet i = 0;\r\n\t\t\t\t\t\tMediaEngine.eachConnection(e =>\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (i == 0)\r\n\t\t\t\t\t\t\t\tthis.tick(e);\r\n\t\t\t\t\t\t\ti++;\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\t}, 500);\r\n\t\t\t\t}\r\n\r\n\t\t\t\ttick(e)\r\n\t\t\t\t{\r\n\t\t\t\t\te.getStats().then(stats =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconst volumeStates = {};\r\n\r\n\t\t\t\t\t\tfor (let uid in stats.rtp.inbound)\r\n\t\t\t\t\t\t\tif (uid != this.localUserId)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tvolumeStates[uid] = stats.rtp.inbound[uid][0].audioLevel;\r\n\r\n\t\t\t\t\t\t\t\tif (timeline[uid] == null)\r\n\t\t\t\t\t\t\t\t\ttimeline[uid] = new TimelineDataSeries();\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\ttimeline[uid].addPoint(Date.now(), volumeStates[uid]);\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tfor (let component of components)\r\n\t\t\t\t\t\t\tif (volumeStates[component.props.user.id] != null)\r\n\t\t\t\t\t\t\t\tcomponent.setState({ volume: volumeStates[component.props.user.id] });\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\r\n\t\t\t\tonStop()\r\n\t\t\t\t{\r\n\t\t\t\t\tclearInterval(this.interval);\r\n\t\t\t\t\tPatcher.unpatchAll();\r\n\t\t\t\t\tPluginUtilities.removeStyle(this.getName());\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\treturn plugin(Plugin, Api);\r\n\t})(global.ZeresPluginLibrary.buildPlugin(config));\r\n})();"
  }
]