Showing preview only (467K chars total). Download the full file or copy to clipboard to get everything.
Repository: Metalloriff/BetterDiscordPlugins
Branch: master
Commit: 8bc613c09900
Files: 73
Total size: 442.3 KB
Directory structure:
gitextract_pvv8lvhu/
├── Assets/
│ ├── AC/
│ │ └── Preview/
│ │ └── README.md
│ ├── AIC/
│ │ └── Preview/
│ │ └── README.md
│ ├── BDEA/
│ │ └── Preview/
│ │ └── README.md
│ ├── BES/
│ │ └── Preview/
│ │ └── README.md
│ ├── BPECC/
│ │ └── Preview/
│ │ └── README.md
│ ├── CJS/
│ │ └── Preview/
│ │ └── README.md
│ ├── DST/
│ │ └── Preview/
│ │ └── README.md
│ ├── FMC/
│ │ └── Preview/
│ │ └── README.md
│ ├── GC/
│ │ └── Preview/
│ │ └── README.md
│ ├── GFRA/
│ │ └── Preview/
│ │ └── README.md
│ ├── IB/
│ │ └── Preview/
│ │ └── README.md
│ ├── MA/
│ │ └── Preview/
│ │ └── README.md
│ ├── ML/
│ │ └── Preview/
│ │ └── README.md
│ ├── OLID/
│ │ └── Preview/
│ │ └── README.md
│ ├── PCC/
│ │ └── Preview/
│ │ └── README.md
│ ├── PPT/
│ │ └── Preview/
│ │ └── README.md
│ ├── RA/
│ │ └── Preview/
│ │ └── README.md
│ ├── SB/
│ │ └── Preview/
│ │ └── README.md
│ ├── SBDE/
│ │ └── Preview/
│ │ └── README.md
│ ├── SLD/
│ │ └── Preview/
│ │ └── README.md
│ ├── ST/
│ │ └── Preview/
│ │ └── README.md
│ ├── TB/
│ │ └── Preview/
│ │ └── README.md
│ ├── UCB/
│ │ └── Preview/
│ │ └── README.md
│ ├── VCN/
│ │ └── Preview/
│ │ └── README.md
│ └── VGR/
│ └── Preview/
│ └── README.md
├── AutoCorrect.plugin.js
├── AutoRefreshSettingsPanel.plugin.js
├── AvatarIconViewer.plugin.js
├── BDEmoteAutocomplete.plugin.js
├── BetterEmoteSizes.plugin.js
├── Bruh.plugin.js
├── CustomJs.plugin.js
├── CustomizableAvatarDPI.plugin.js
├── DetailedServerTooltips.plugin.js
├── DoubleClickVoiceChannels.plugin.js
├── FormattableMessageCopier.plugin.js
├── GuildAndFriendRemovalAlerts/
│ ├── GuildAndFriendRemovalAlerts.plugin.js
│ ├── README.md
│ └── src/
│ ├── components/
│ │ ├── item.jsx
│ │ ├── item.scss
│ │ └── settings.jsx
│ ├── index.js
│ ├── modules/
│ │ └── settings.js
│ ├── package.json
│ └── styles.scss
├── GuildAndFriendRemovalAlerts.plugin.js
├── GuildCounter.plugin.js
├── IdleGuildlistScroller.plugin.js
├── ImageBrowser.plugin.js
├── Lib/
│ └── NeatoBurritoLibrary.js
├── MentionAliases.plugin.js
├── NateUtilities.plugin.js
├── OpenLinksInDiscord.plugin.js
├── PinCollapsedChannels.plugin.js
├── PinPluginsAndThemes.plugin.js
├── PreventSpotifyAutoPause.plugin.js
├── README.md
├── ReactionImages.plugin.js
├── SaveTo.plugin.js
├── SelectedChannelNotifications.plugin.js
├── SendBDEmotes.plugin.js
├── SendLinksDirectly.plugin.js
├── ShareButton.plugin.js
├── SuppressUserMentions.plugin.js
├── TheClapBestClapPluginClapEver.plugin.js
├── TransitioningBackgrounds.plugin.js
├── UnreadCountBadges.plugin.js
├── UserBirthdays.plugin.js
├── VCMuteSounds.plugin.js
├── VideoExamples/
│ └── README.md
├── ViewGuildRelationships.plugin.js
├── VoiceChatNotifications.plugin.js
└── VoiceChatPanel.plugin.js
================================================
FILE CONTENTS
================================================
================================================
FILE: Assets/AC/Preview/README.md
================================================



================================================
FILE: Assets/AIC/Preview/README.md
================================================





================================================
FILE: Assets/BDEA/Preview/README.md
================================================


================================================
FILE: Assets/BES/Preview/README.md
================================================


================================================
FILE: Assets/BPECC/Preview/README.md
================================================


================================================
FILE: Assets/CJS/Preview/README.md
================================================


================================================
FILE: Assets/DST/Preview/README.md
================================================



================================================
FILE: Assets/FMC/Preview/README.md
================================================




================================================
FILE: Assets/GC/Preview/README.md
================================================

================================================
FILE: Assets/GFRA/Preview/README.md
================================================



================================================
FILE: Assets/IB/Preview/README.md
================================================

================================================
FILE: Assets/MA/Preview/README.md
================================================



================================================
FILE: Assets/ML/Preview/README.md
================================================




================================================
FILE: Assets/OLID/Preview/README.md
================================================

================================================
FILE: Assets/PCC/Preview/README.md
================================================



================================================
FILE: Assets/PPT/Preview/README.md
================================================



================================================
FILE: Assets/RA/Preview/README.md
================================================


================================================
FILE: Assets/SB/Preview/README.md
================================================




================================================
FILE: Assets/SBDE/Preview/README.md
================================================


================================================
FILE: Assets/SLD/Preview/README.md
================================================

================================================
FILE: Assets/ST/Preview/README.md
================================================






================================================
FILE: Assets/TB/Preview/README.md
================================================




================================================
FILE: Assets/UCB/Preview/README.md
================================================



================================================
FILE: Assets/VCN/Preview/README.md
================================================




================================================
FILE: Assets/VGR/Preview/README.md
================================================


================================================
FILE: AutoCorrect.plugin.js
================================================
/**
* @name AutoCorrect
* @invite yNqzuJa
* @authorLink https://github.com/Metalloriff
* @donate https://www.paypal.me/israelboone
* @website https://metalloriff.github.io/toms-discord-stuff/
* @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/AutoCorrect.plugin.js
*/
/* BUGS AND TODO
Implement DB's SpellChecker.
*/
module.exports = (() =>
{
const config =
{
info:
{
name: "AutoCorrect",
authors:
[
{
name: "Metalloriff",
discord_id: "264163473179672576",
github_username: "metalloriff",
twitter_username: "Metalloriff"
}
],
version: "2.0.1",
description: "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.",
github: "https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/AutoCorrect.plugin.js",
github_raw: "https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/AutoCorrect.plugin.js"
},
changelog:
[
{
title: "2.0 Rewrite",
type: "fixed",
items: [
"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.",
"Fixed a settings save/load issue.",
"Fixed spelling errors not auto replacing, I did big dumb."
]
}
]
};
return !global.ZeresPluginLibrary ? class
{
constructor() { this._config = config; }
getName = () => config.info.name;
getAuthor = () => config.info.description;
getVersion = () => config.info.version;
load()
{
BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {
confirmText: "Download Now",
cancelText: "Cancel",
onConfirm: () =>
{
require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (err, res, body) =>
{
if (err) return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js");
await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
});
}
});
}
start() { }
stop() { }
} : (([Plugin, Api]) => {
const plugin = (Plugin, Api) =>
{
const { WebpackModules, DiscordModules, ReactComponents, Patcher, PluginUtilities, Settings, Utilities } = Api;
const { React } = DiscordModules;
const process = require("process");
const SlateModule = WebpackModules.find(m => m.deserialize && !m.add);
const SpellChecker = WebpackModules.getByProps("isMisspelled");
const ContextMenu = WebpackModules.getByProps("MenuItem");
const Button = WebpackModules.find(m => m.Link && m.Link.displayName == "ButtonLink");
const getSelectiontext = WebpackModules.getByProps("getSelectionText").getSelectionText;
const formatToDict = s =>
{
if (s == null || s.toLowerCase == null)
return "";
const match = s.toLowerCase().match(/([a-z]|[0-9])/gi);
return match == null ? "" : match.join("");
};
let lastRender = new Date();
let lastError;
let lastLength;
let lastTextAreaEvent;
return class AutoCorrect extends Plugin
{
constructor()
{
super();
this.defaultSettings =
{
autoReplace: true,
punctuate: true,
capitalize: true,
bsUndo: true,
overrides:
[
{
key: "idk",
value: "I don't know"
},
{
key: "discord",
value: "shitcord"
}
],
dictionary: []
};
}
async showChangelog(footer)
{
try { footer = (await WebpackModules.getByProps("getUser", "acceptAgreements").getUser("264163473179672576")).tag + " | https://discord.gg/yNqzuJa"; }
finally { super.showChangelog(footer); }
}
loadSettings = () => this.settings = PluginUtilities.loadSettings(config.info.name, this.defaultSettings);
saveSettings = () => PluginUtilities.saveSettings(config.info.name, this.settings);
createSettingsSwitch(title, desc, setting)
{
return new Settings.Switch(title, desc, this.settings[setting], value =>
{
this.settings[setting] = value;
this.saveSettings();
}).getElement();
}
createSettingsOverrideItem(index)
{
const item = new Settings.SettingGroup("Override Item #" + (parseInt(index) + 1)).append(
new Settings.Textbox("Key", null, this.settings.overrides[index].key, key =>
{
this.settings.overrides[index].key = key;
this.saveSettings();
}).getElement(),
new Settings.Textbox("Value", null, this.settings.overrides[index].value, value =>
{
this.settings.overrides[index].value = value;
this.saveSettings();
}).getElement(),
new Settings.SettingField(null, null, null, Button,
{
children: "Remove Override",
onClick: () =>
{
this.settings.overrides.splice(index);
this.saveSettings();
document.getElementById("ac-oi-" + index).remove();
}
}).getElement()
).getElement();
item.id = "ac-oi-" + index;
return item;
}
getSettingsPanel()
{
this.loadSettings();
let overrideItems = [];
for (let i in this.settings.overrides)
overrideItems.push(this.createSettingsOverrideItem(i));
return new Settings.SettingPanel(null,
this.createSettingsSwitch("Auto Replace Spelling Errors", "Automatically replaces any spelling mistakes you make with the first available correction, if any.", "autoReplace"),
this.createSettingsSwitch("Auto Punctuate", "Automatically punctuates your messages when you send them.", "punctuate"),
this.createSettingsSwitch("Auto Capitalize", "Automatically capitlizes the first words after punctuations and at the start of your messages.", "capitalize"),
this.createSettingsSwitch("Backspace Undo Correction", "Hitting backspace after a correction will undo it to the errored word.", "bsUndo"),
new Settings.SettingGroup("Overrides").append(
...overrideItems,
new Settings.SettingField(null, null, null, Button,
{
children: "Add Override",
onClick: () =>
{
this.settings.overrides.push(
{
key: "ex",
value: "Example."
});
this.saveSettings();
const parent = document.querySelector("div.plugin-settings div.plugin-inputs");
parent.insertBefore(this.createSettingsOverrideItem(this.settings.overrides.length - 1), parent.lastChild);
}
}).getElement()
).getElement()
).getElement();
}
async onStart()
{
this.loadSettings();
const TextArea = await ReactComponents.getComponentByName("ChannelEditorContainer", "*");
const SlateTextAreaContextMenu = WebpackModules.find(m => m.default && m.default.displayName == "SlateTextAreaContextMenu");
SpellChecker.setLearnedWords(new Set(this.settings.dictionary));
Patcher.after(SpellChecker, "setLearnedWords", (_, [set]) =>
{
for (let word of set)
{
if (this.settings.dictionary.indexOf(formatToDict(word)) == -1)
{
this.learnWord(formatToDict(word));
}
}
});
Patcher.after(SlateTextAreaContextMenu, "default", (_, [], re) =>
{
if (getSelectiontext().length == 0)
return;
let index = -1;
for (let i = 0; i < this.settings.dictionary.length; i++)
if (this.settings.dictionary[i] == formatToDict(getSelectiontext()))
index = i;
if (index == -1)
return;
const spellCheckGroup = Utilities.getNestedProp(re, "props.children.1.props.children.props.children");
if (spellCheckGroup != null)
spellCheckGroup.unshift(
React.createElement(
ContextMenu.MenuItem,
{
label: "Remove from Dictionary",
id: "ac-removefromdict",
action: () => this.forgetWord(index)
}
)
);
else
console.warn("AutoCorrect: SpellCheckGroup nested prop could not be found!");
});
Patcher.instead(DiscordModules.MessageActions, "sendMessage", (_, args, sendMessage) =>
{
this.tryCorrect(args[1].content + " ", true).then(correction =>
{
args[1].content = correction;
sendMessage(...args);
lastLength = -1;
process.nextTick(() => this.setText(null, ""));
});
});
Patcher.after(TextArea.component.prototype, "render", e =>
{
const now = new Date();
if (now - lastRender > 10)
{
this.tryCorrect(e.props.textValue, false).then(correction =>
{
if (e.props.textValue != correction)
{
this.setText(e, correction);
}
});
}
lastRender = now;
});
}
async tryCorrect(textValue, sending)
{
const words = textValue.split(" ");
let lastWordIndex = words.length - 2;
for (let i = words.length - 1; i > -1; i--)
{
if (words[i].trim().length > 0)
{
lastWordIndex = i;
break;
}
}
if (words.join(" ").length < lastLength)
{
if (this.settings.bsUndo)
words[lastWordIndex] = lastError;
lastLength = words.join(" ").length;
return words.join(" ");
}
if (this.settings.autoReplace && (sending || textValue.endsWith(" ")) && words[lastWordIndex] != lastError)
{
let hasOverride = false;
for (let o of this.settings.overrides)
if (o.value.toLowerCase() == words[lastWordIndex].toLowerCase())
hasOverride = true;
const isMisspelled = !hasOverride && this.settings.dictionary.indexOf(formatToDict(words[lastWordIndex])) == -1 && await SpellChecker.isMisspelled(words[lastWordIndex]);
if (isMisspelled)
{
const corrections = await SpellChecker.getCorrections(words[lastWordIndex]);
if (corrections && corrections.length > 0)
{
words[lastWordIndex] = corrections[0];
lastError = words[lastWordIndex];
}
}
}
else
await Promise.resolve();
if (words[lastWordIndex] != null)
{
for (let override of this.settings.overrides)
{
if (override.key.toLowerCase() == words[lastWordIndex].toLowerCase()
&& override.value.toLowerCase() != words[lastWordIndex].toLowerCase() && words[lastWordIndex] != lastError)
{
lastError = words[lastWordIndex];
words[lastWordIndex] = override.value;
}
}
}
let wasPunctuated = false;
if (this.settings.capitalize && (sending || textValue.endsWith(" ")))
{
if (words[lastWordIndex] == "i")
words[lastWordIndex] = "I";
if (words[0].charAt(0) != words[0].charAt(0).toUpperCase() && !words[0].trim().startsWith("http"))
words[0] = words[0].charAt(0).toUpperCase() + words[0].slice(1);
for (let i = 0; i < words.length; i++)
{
if (wasPunctuated && words[i].trim().length > 1 && !words[i].trim().startsWith("http"))
{
words[i] = words[i].charAt(0).toUpperCase() + words[i].slice(1);
wasPunctuated = false;
}
if (words[i].trim().match(/[.!?\\-]$/g))
wasPunctuated = true;
}
}
if (this.settings.punctuate && (sending || textValue.endsWith(" ")))
{
for (let i = words.length - 1; i > -1; i--)
{
if (words[i].trim().match(/[.!?\\-]$/g))
{
i = -1;
continue;
}
if (words[i].trim().endsWith(">")
|| words[i].trim().match(/(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/g) || words[i].trim().startsWith("http"))
continue;
if (i > -1 && words[i].trim().length > 0)
{
words[i] += ".";
break;
}
}
}
return words.join(" ");
}
setText(_e, text)
{
const e = _e == null ? lastTextAreaEvent : _e;
if (e && e.ref.current)
{
e.ref.current.setValue(SlateModule.deserialize(text));
e.focus();
e.ref.current.editorRef.moveFocusToEndOfText();
e.ref.current.editorRef.moveToFocus();
lastTextAreaEvent = e;
}
}
learnWord(word)
{
if (word.length == 0)
return;
this.settings.dictionary.push(formatToDict(word));
this.saveSettings();
SpellChecker.setLearnedWords(new Set(this.settings.dictionary));
}
forgetWord(index)
{
if (index != -1)
{
this.settings.dictionary.splice(index);
this.saveSettings();
}
SpellChecker.setLearnedWords(new Set(this.settings.dictionary));
}
onStop()
{
Patcher.unpatchAll();
}
};
}
return plugin(Plugin, Api);
})(global.ZeresPluginLibrary.buildPlugin(config));
})();
================================================
FILE: AutoRefreshSettingsPanel.plugin.js
================================================
//META{"name":"AutoRefreshSettingsPanel"}*//
class AutoRefreshSettingsPanel {
getName() { return "AutoRefreshSettingsPanel"; }
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."; }
getVersion() { return "0.0.1"; }
getAuthor() { return "Metalloriff"; }
load() {}
start() {
this.refreshPluginSettings = () => {
let panel = document.getElementsByClassName("plugin-settings")[0];
if(panel) panel.innerHTML = BdApi.getPlugin(panel.id.substring(panel.id.lastIndexOf("-") + 1, panel.id.length)).getSettingsPanel();
};
window.addEventListener("focus", this.refreshPluginSettings);
}
stop() {
window.removeEventListener("focus", this.refreshPluginSettings);
}
}
================================================
FILE: AvatarIconViewer.plugin.js
================================================
/**
* @name AvatarIconViewer
* @invite yNqzuJa
* @authorLink https://discord.com/users/264163473179672576
* @donate https://www.paypal.me/israelboone
* @website https://kinzoku.one/
* @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/AvatarIconViewer.plugin.js
*/
module.exports = (() => {
const config =
{
info:
{
name: "AvatarIconViewer",
authors:
[
{
name: "Metalloriff",
discord_id: "264163473179672576",
github_username: "metalloriff",
twitter_username: "Metalloriff"
}
],
version: "2.0.2",
description: "Allows you to view server icons, user avatars, and emotes in fullscreen via the context menu, or copy the link to them.",
github: "https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/AvatarIconViewer.plugin.js",
github_raw: "https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/AvatarIconViewer.plugin.js"
}
};
return !global.ZeresPluginLibrary ? class {
constructor() { this._config = config; }
getName = () => config.info.name;
getAuthor = () => config.info.description;
getVersion = () => config.info.version;
load() {
BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {
confirmText: "Download Now",
cancelText: "Cancel",
onConfirm: () => {
require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (err, res, body) => {
if (err) return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js");
await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
});
}
});
}
start() { }
stop() { }
} : (([Plugin, Api]) => {
const plugin = (Plugin, Api) => {
const { DiscordModules, WebpackModules, Patcher } = Api;
const { React } = DiscordModules
const ModalStack = WebpackModules.getByProps("openModal", "hasModalOpen");
const { ModalRoot } = WebpackModules.getByProps("ModalRoot");
const { ModalSize } = WebpackModules.getByProps("ModalSize");
const ImageModal = WebpackModules.getByDisplayName("ImageModal");
const ContextMenu = WebpackModules.getByProps("MenuItem");
const MaskedLink = WebpackModules.getByDisplayName("MaskedLink");
const getChannelIconURL = WebpackModules.getByProps("getChannelIconURL").getChannelIconURL;
const copyToClipboard = require("electron").clipboard.writeText;
const formatURL = url =>
url == null || url.length == 0
? null
: (url.includes("/a_")
? url.replace(".webp", ".gif").replace(".png", ".gif")
: url).split("?")[0] + "?size=2048";
return class AvatarIconViewer extends Plugin {
constructor() {
super();
}
async onStart() {
// I have no idea why, nor do I have the energy to figure out why,
// but shitcord does not load the context menu modules until the context menu is opened.
// Therefore, this shitty workaround was made, which requires the user to
// open the context menu once before the plugin will work.
// UPDATE: It's less spaghetti, but still spaghetti. I'm sorry.
this.patched = [];
document.getElementById("app-mount").addEventListener("contextmenu", this.contextMenuListener = e => {
const modules = WebpackModules.findAll(
m => m.default && m.default.displayName && (
m.default.displayName.endsWith("UserContextMenu") || ~[
"GroupDMContextMenu",
"GuildContextMenu",
"MessageContextMenu"
].indexOf(m.default.displayName)
)
);
for (const m of modules) {
if (~this.patched.indexOf(m.default.displayName)) {
continue;
}
switch (m.default.displayName) {
default: {
Patcher.after(m, "default", (_, [props], re) => {
const type = props.user ? "Avatar" : props.channel && props.channel.type == 3 ? "Icon" : null;
const url = formatURL(type == "Avatar" ? props.user.getAvatarURL() : type == "Icon" ? getChannelIconURL(props.channel) : null);
if (type && url)
re.props.children.props.children.push(this.createContext(url, type));
});
} break;
case "GuildContextMenu": {
Patcher.after(m, "default", (_, [props], re) => {
const url = formatURL(props.guild.getIconURL());
if (url)
re.props.children.push(this.createContext(url, "Icon"));
});
} break;
case "MessageContextMenu": {
Patcher.after(m, "default", (_, [props], re) => {
if (props.target && props.target.src) {
re.props.children.push(this.createContext(props.target.src, "Emoji"));
}
});
} break;
}
this.patched.push(m.default.displayName);
}
});
}
createContext(url, type) {
const ClassModule = WebpackModules.getByProps("modal", "image");
return React.createElement(ContextMenu.MenuGroup,
{
children:
[
React.createElement(
ContextMenu.MenuItem,
{
label: "View " + type,
id: "aiv-view",
action: () =>
ModalStack.openModal(
props => (
React.createElement(
ModalRoot,
{
className: ClassModule.modal,
...props,
size: ModalSize.DYNAMIC
},
React.createElement(
ImageModal,
{
src: url,
placeholder: url,
original: url,
width: 2048,
height: 2048,
onClickUntrusted: e => e.openHref(),
renderLinkComponent: props => React.createElement(MaskedLink, props)
}
)
)
)
)
}
),
React.createElement(
ContextMenu.MenuItem,
{
label: "Copy " + type + " Link",
id: "aiv-copy",
action: () => copyToClipboard(url)
}
)
]
});
}
onStop() {
Patcher.unpatchAll();
document.getElementById("app-mount").removeEventListener("contextmenu", this.contextMenuListener);
}
}
};
return plugin(Plugin, Api);
})(global.ZeresPluginLibrary.buildPlugin(config));
})();
================================================
FILE: BDEmoteAutocomplete.plugin.js
================================================
//META{"name":"BDEmoteAutocomplete","website":"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/README.md","source":"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/BDEmoteAutocomplete.plugin.js"}*//
class BDEmoteAutocomplete {
getName() { return "BDEmoteAutocomplete"; }
getDescription() { return "Adds an auto-complete menu for BetterDiscord emotes."; }
getVersion() { return "1.0.4"; }
getAuthor() { return "Metalloriff"; }
getChanges() {
return {
};
}
load() {}
start() {
let libLoadedEvent = () => {
try{ this.onLibLoaded(); }
catch(err) { console.error(this.getName(), "fatal error, plugin could not be started!", err); }
};
let lib = document.getElementById("NeatoBurritoLibrary");
if(lib == undefined) {
lib = document.createElement("script");
lib.setAttribute("id", "NeatoBurritoLibrary");
lib.setAttribute("type", "text/javascript");
lib.setAttribute("src", "https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js");
document.head.appendChild(lib);
}
if(typeof window.NeatoLib !== "undefined") libLoadedEvent();
else lib.addEventListener("load", libLoadedEvent);
}
getSettingsPanel() {
setTimeout(() => {
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createToggleSwitch("Case-sensitive", this.settings.caseSensitive, () => {
this.settings.caseSensitive = !this.settings.caseSensitive;
this.saveSettings();
}), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createToggleGroup("bdea-enabled-channels", "Enabled emote channels", [
{ title : "TwitchGlobal", value : "TwitchGlobal", setValue : this.settings.enabledChannels.includes("TwitchGlobal") },
{ title : "TwitchSubscriber", value : "TwitchSubscriber", setValue : this.settings.enabledChannels.includes("TwitchSubscriber") },
{ title : "BTTV", value : "BTTV", setValue : this.settings.enabledChannels.includes("BTTV") },
{ title : "FrankerFaceZ", value : "FrankerFaceZ", setValue : this.settings.enabledChannels.includes("FrankerFaceZ") },
{ title : "BTTV2", value : "BTTV2", setValue : this.settings.enabledChannels.includes("BTTV2") }
], choice => {
if(this.settings.enabledChannels.includes(choice.value)) this.settings.enabledChannels.splice(this.settings.enabledChannels.indexOf(choice.value, 1));
else this.settings.enabledChannels.push(choice.value);
this.saveSettings();
this.getEmotes();
}), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createTextField("Prefix to display autocomplete", "text", this.settings.prefix, e => {
this.settings.prefix = e.target.value;
this.saveSettings();
}, { tooltip : "If you set this, it will be required before an emote to display the auto-complete menu." }), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createTextField("Auto-complete emote size", "number", this.settings.size, e => {
this.settings.size = e.target.value;
this.saveSettings();
}, { tooltip : "The size in pixels to display the auto-complete emotes." }), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createTextField("Auto-complete results limit", "number", this.settings.resultsCap, e => {
this.settings.resultsCap = e.target.value;
this.saveSettings();
}, { tooltip : "Maximum amount of results to display. The higher this is, the slower larger results will be." }), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createTextField("Auto-complete display delay (ms)", "number", this.settings.autocompleteDelay, e => {
this.settings.autocompleteDelay = e.target.value;
this.saveSettings();
}, { tooltip : "Delay in millseconds to display the auto-complete menu after pressing a key." }), this.getName());
NeatoLib.Settings.pushChangelogElements(this);
}, 0);
return NeatoLib.Settings.Elements.pluginNameLabel(this.getName());
}
saveSettings() {
NeatoLib.Settings.save(this);
}
updateSelected() {
let items = document.getElementById("bdea-autocomplete-list").getElementsByClassName("selector-2IcQBU selectable-3dP3y-");
for(let i = 0; i < items.length; i++) {
if(i == this.selectedIDX) items[i].classList.add("selectorSelected-1_M1WV");
else items[i].classList.remove("selectorSelected-1_M1WV");
}
}
onLibLoaded() {
NeatoLib.Updates.check(this);
this.settings = NeatoLib.Settings.load(this, {
displayUpdateNotes : true,
enabledChannels : ["TwitchGlobal", "TwitchSubscriber", "BTTV", "FrankerFaceZ", "BTTV2"],
prefix : "",
size : 16,
caseSensitive : true,
resultsCap : 15,
autocompleteDelay : 750
});
//if(this.settings.displayUpdateNotes) NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());
this.selectedIDX = 0;
this.results = [];
this.onGlobalKey = e => {
if(e.key == "Tab") {
let list = document.getElementById("bdea-autocomplete-list");
if(list) list.getElementsByClassName("autocompleteRowVertical-q1K4ky autocompleteRow-2OthDa")[this.selectedIDX].click();
}
};
this.onChatInput = (e, fromTimeout) => {
if(this.inputTimeout) clearTimeout(this.inputTimeout);
if(e.key.includes("Arrow")) {
if(e.key.includes("Up") && this.selectedIDX > 0) this.selectedIDX--;
else if(e.key.includes("Down") && this.selectedIDX < this.results.length - 1) this.selectedIDX++;
else return;
this.updateSelected();
return;
}
let chatbox = e.target, autocomplete = document.getElementById("bdea-autocomplete"), words = chatbox.value.split(" "), lastWord = words[words.length - 1];
if(!lastWord || lastWord.length < 4 || (this.settings.prefix && !lastWord.startsWith(this.settings.prefix))) {
if(autocomplete) autocomplete.outerHTML = "";
return;
}
if(this.settings.autocompleteDelay && !fromTimeout) {
this.inputTimeout = setTimeout(() => {
this.onChatInput(e, true);
}, this.settings.autocompleteDelay);
return;
}
if(this.settings.prefix) lastWord = lastWord.substring(this.settings.prefix.length, lastWord.length);
let emotes = [];
let lim = 0;
for(let i = 0; i < this.emotes.length; i++) {
if(lim >= this.settings.resultsCap) break;
if((this.settings.caseSensitive && !this.emotes[i].name.startsWith(lastWord)) || (!this.settings.caseSensitive && !this.emotes[i].name.toLowerCase().startsWith(lastWord.toLowerCase()))) continue;
emotes.push(this.emotes[i]);
lim++;
}
this.results = emotes;
if(emotes.length == 0) {
if(autocomplete) autocomplete.outerHTML = "";
return;
}
if(!autocomplete) {
chatbox.parentElement.insertAdjacentHTML("beforeend", `
<div id="bdea-autocomplete" class="autocomplete-1vrmpx autocomplete-i9yVHs">
<div class="autocompleteInner-zh20B_">
<div class="autocompleteRowVertical-q1K4ky autocompleteRow-2OthDa">
<div class="selector-2IcQBU">
<div class="contentTitle-2tG_sM small-29zrCQ size12-3R0845 height16-2Lv3qA weightSemiBold-NJexzi">Emotes matching <strong>${lastWord}</strong></div>
</div>
</div>
<div id="bdea-autocomplete-list">
</div>
</div>
</div>
`);
autocomplete = document.getElementById("bdea-autocomplete");
}
this.selectedIDX = 0;
let list = document.getElementById("bdea-autocomplete-list");
list.innerHTML = "";
for(let i = 0; i < emotes.length; i++) {
list.insertAdjacentHTML("beforeend", `
<div class="autocompleteRowVertical-q1K4ky autocompleteRow-2OthDa">
<div class="selector-2IcQBU selectable-3dP3y-">
<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}">
<div class="marginLeft8-1YseBe">${emotes[i].name}</div>
<div style="margin-left:auto;opacity:0.5;">${emotes[i].channel}</div>
</div>
</div>
</div>
`);
let items = document.getElementsByClassName("autocompleteRowVertical-q1K4ky autocompleteRow-2OthDa");
items[items.length - 1].addEventListener("click", e => {
words[words.length - 1] = emotes[this.selectedIDX].name + " ";
NeatoLib.Chatbox.setText(words.join(" "));
autocomplete.outerHTML = "";
});
items[items.length - 1].addEventListener("mouseover", e => {
this.selectedIDX = i;
this.updateSelected();
});
}
this.updateSelected();
};
this.getEmotes();
this.initialized = true;
document.addEventListener("keydown", this.onGlobalKey);
NeatoLib.Events.onPluginLoaded(this);
this.switch();
this.switchEvent = () => this.switch();
NeatoLib.Events.attach("switch", this.switchEvent);
}
switch() {
if(this.initialized != true) return;
if(this.emotes.length == 0) this.getEmotes();
let chatbox = NeatoLib.Chatbox.get();
if(chatbox) chatbox.addEventListener("keyup", this.onChatInput);
}
getEmotes() {
let emoteChannels = this.settings.enabledChannels, pushed = {};
this.emotes = [];
let blacklisted = {};
for(let i = 0; i < window.bemotes.length; i++) blacklisted[window.bemotes[i]] = true;
for(let ec of emoteChannels) {
let emoteChannel = window.bdEmotes[ec];
for(let emote in emoteChannel) {
if(emote.length > 2 && !pushed[emote] && !blacklisted[emote]) {
this.emotes.push({ name : emote, url : emoteChannel[emote], channel : ec });
pushed[emote] = true;
}
}
}
this.emotes.sort((a, b) => a.name.length - b.name.length);
}
stop() {
let chatbox = NeatoLib.Chatbox.get();
if(chatbox) chatbox.removeEventListener("keyup", this.onChatInput);
document.removeEventListener("keydown", this.onGlobalKey);
if(this.inputTimeout) clearTimeout(this.inputTimeout);
NeatoLib.Events.detach("switch", this.switchEvent);
}
}
================================================
FILE: BetterEmoteSizes.plugin.js
================================================
//META{"name":"BetterEmoteSizes","website":"https://metalloriff.github.io/toms-discord-stuff/","source":"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/BetterEmoteSizes.plugin.js"}*//
class BetterEmoteSizes {
getName() { return "Emote Zoom"; }
getDescription() { return "Increases the size of emojis, emotes, and reactions upon hovering over them and allows you to change their default sizes."; }
getVersion() { return "2.4.17"; }
getAuthor() { return "Metalloriff"; }
get settingFields() {
return {
alterSmall: { label: "Affect small emojis", type: "bool" },
smallSize: { label: "Default small emoji size (px)", type: "number" },
alterLarge: { label: "Affect large emojis", type: "bool" },
largeSize: { label: "Default large emoji size (px)", type: "number" },
alterBD: { label: "Affect small BetterDiscord emotes", type: "bool" },
bdSize: { label: "Default small BetterDiscord emote size (px)", type: "number" },
alterLargeBD: { label: "Affect large BetterDiscord emotes", type: "bool" },
largeBdSize: { label: "Default large BetterDiscord emote size (px)", type: "number" },
alterReactions: { label: "Affect reactions", type: "bool" },
reactionSize: { label: "Default reaction size (px)", type: "number" },
hoverSize: { label: "Emoji and BetterDiscord emote hover size multiplier", type: "number" },
reactionHoverSize: { label: "Reaction hover size multiplier", type: "number" },
transitionSpeed: { label: "Transition speed (seconds)", type: "number" },
delayAmount: { label: "Delay amount (seconds)", type: "number" },
equal: { label: "Small and large emote zoom to equal", type: "bool" }
};
}
get defaultSettings() {
return {
displayUpdateNotes: true,
alterSmall: true,
smallSize: 22,
alterLarge: true,
largeSize: 32,
alterBD: true,
bdSize: 28,
alterLargeBD: true,
largeBdSize: 32,
alterReactions: true,
reactionSize: 16,
hoverSize: 3,
transitionSpeed: 0.5,
delayAmount: 0,
reactionHoverSize: 2,
equal: false
};
}
getSettingsPanel() {
return NeatoLib.Settings.createPanel(this);
}
saveSettings() {
NeatoLib.Settings.save(this);
this.update();
}
load() {}
start() {
const libLoadedEvent = () => {
try{ this.onLibLoaded(); }
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); } }
};
let lib = document.getElementById("NeatoBurritoLibrary");
if (!lib) {
lib = document.createElement("script");
lib.id = "NeatoBurritoLibrary";
lib.type = "text/javascript";
lib.src = "https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js";
document.head.appendChild(lib);
}
this.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);
if (typeof window.NeatoLib !== "undefined") libLoadedEvent();
else lib.addEventListener("load", libLoadedEvent);
}
onLibLoaded() {
this.settings = NeatoLib.Settings.load(this);
NeatoLib.Updates.check(this, "https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/BetterEmoteSizes.plugin.js");
if (!NeatoLib.hasRequiredLibVersion(this, "0.7.19")) return;
this.update();
NeatoLib.Events.onPluginLoaded(this);
}
update() {
const markup = NeatoLib.getClass("markup"), markupRtl = NeatoLib.getClass("markupRtl"), messageGroup = NeatoLib.getClass("cozyMessage"), message = NeatoLib.getClass("message"), reaction = NeatoLib.getClass("reaction"), reactionMe = NeatoLib.getClass("reactionMe");
if (this.style) this.style.destroy();
this.style = NeatoLib.injectCSS(`.${messageGroup} { overflow: visible; }`);
if (this.settings.alterSmall) {
this.style.append(`
#app-mount .${markup} > .emoji:not(.jumboable),
#app-mount .${markupRtl} > .emoji:not(.jumboable) {
height: ${this.settings.smallSize}px;
width: auto;
transform: scale(1);
transition: transform ${this.settings.transitionSpeed}s;
transition-delay: 0s;
}
#app-mount .${markup} > .emoji:not(.jumboable):hover,
#app-mount .${markupRtl} > .emoji:not(.jumboable):hover {
transform: scale(${this.settings.equal ? ((this.settings.largeSize / this.settings.smallSize) * this.settings.hoverSize) : this.settings.hoverSize});
position: relative;
z-index: 1;
transition-delay: ${this.settings.delayAmount}s;
}
#app-mount .${messageGroup}:last-child .${message}:nth-last-child(2) .${markup} .emoji:not(.jumboable):hover,
#app-mount .${messageGroup}:last-child .${message}:nth-last-child(2) .${markupRtl} .emoji:not(.jumboable):hover {
transform: scale(${this.settings.equal ? ((this.settings.largeSize / this.settings.smallSize) * this.settings.hoverSize) : this.settings.hoverSize}) translateY(-35%);
}
`);
}
if (this.settings.alterLarge) {
this.style.append(`
#app-mount .${markup} > .emoji.jumboable,
#app-mount .${markupRtl} > .emoji.jumboable {
height: ${this.settings.largeSize}px;
width: auto;
transform: scale(1);
transition: transform ${this.settings.transitionSpeed}s;
transition-delay: 0s;
}
#app-mount .${markup} > .emoji.jumboable:hover,
#app-mount .${markupRtl} > .emoji.jumboable:hover {
transform: scale(${this.settings.hoverSize});
position: relative;
z-index: 1;
transition-delay: ${this.settings.delayAmount}s;
}
#app-mount .${messageGroup}:last-child .${message}:nth-last-child(2) .${markup} .emoji.jumboable:hover,
#app-mount .${messageGroup}:last-child .${message}:nth-last-child(2) .${markupRtl} .emoji.jumboable:hover {
transform: scale(${this.settings.hoverSize}) translateY(-35%);
}
`);
}
if (this.settings.alterBD) {
this.style.append(`
#app-mount .emote:not(.jumboable) {
height: ${this.settings.bdSize}px;
width: auto;
max-height: ${this.settings.bdSize}px !important;
transform: scale(1);
transition: transform ${this.settings.transitionSpeed}s;
transition-delay: 0s;
}
#app-mount .emote:not(.emoteshake):not(.emoteshake2):not(.emoteshake3):not(.jumboable):hover {
transform: scale(${this.settings.hoverSize});
position: relative;
z-index: 1;
transition-delay: ${this.settings.delayAmount}s;
}
#app-mount .${messageGroup}:last-child .${message}:nth-last-child(2) .emote:not(.emoteshake):not(.emoteshake2):not(.emoteshake3):not(.jumboable):hover {
transform: scale(${this.settings.hoverSize}) translateY(-35%);
}
`);
}
if (this.settings.alterLargeBD) {
this.style.append(`
#app-mount .emote.jumboable {
height: ${this.settings.largeBdSize}px;
width: auto;
max-height: ${this.settings.largeBdSize}px !important;
transform: scale(1);
transition: transform ${this.settings.transitionSpeed}s;
transition-delay: 0s;
}
#app-mount .emote.jumboable:not(.emoteshake):not(.emoteshake2):not(.emoteshake3):hover {
transform: scale(${this.settings.hoverSize});
position: relative;
z-index: 1;
transition-delay: ${this.settings.delayAmount}s;
}
#app-mount .${messageGroup}:last-child .${message}:nth-last-child(2) .emote.jumboable:not(.emoteshake2):not(.emoteshake3):hover {
transform: scale(${this.settings.hoverSize}) translateY(-35%);
}
`);
}
if (this.settings.alterReactions) {
this.style.append(`
#app-mount .${reaction} .emoji, .${reaction}.${reactionMe} .emoji {
height: ${this.settings.reactionSize}px;
width: auto;
}
#app-mount .${reaction} {
transition: transform ${this.settings.transitionSpeed}s;
transition-delay: 0s;
}
#app-mount .${reaction}:hover {
transform: scale(${this.settings.reactionHoverSize}) !important;
z-index: 1000;
transition-delay: ${this.settings.delayAmount}s;
}
`);
}
}
stop() {
if (this.style) this.style.destroy();
}
}
================================================
FILE: Bruh.plugin.js
================================================
/**
* @name Bruh
* @invite yNqzuJa
* @authorLink https://github.com/Metalloriff
* @donate https://www.paypal.me/israelboone
* @website https://metalloriff.github.io/toms-discord-stuff/
* @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/Bruh.plugin.js
*/
module.exports = (() => {
const config =
{
info: {
name: "Bruh",
authors: [{
name: "Metalloriff",
discord_id: "264163473179672576",
github_username: "metalloriff",
twitter_username: "Metalloriff"
}],
version: "0.0.2",
description: "bruh",
github: "https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/Bruh.plugin.js",
github_raw: "https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/Bruh.plugin.js"
},
defaultConfig: [{
id: "general",
name: "general settings",
type: "category",
collapsible: true,
shown: false,
settings: [{
id: "onlyCur",
name: "Current channel only",
note: "When this is enabled, the bruh sound effect will only play when a bruh is found in the selected channel.",
type: "switch",
value: true
}, {
id: "delay",
name: "Delay between each bruh (ms)",
note: "The amount of milliseconds to wait between each bruh when multiple bruhs are found within the same message.",
type: "slider",
value: 200,
min: 10,
max: 1000,
renderValue: v => Math.round(v) + "ms"
}]
}]
};
return !global.ZeresPluginLibrary ? class {
constructor() { this._config = config; }
getName = () => config.info.name;
getAuthor = () => config.info.description;
getVersion = () => config.info.version;
load() {
BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {
confirmText: "Download Now",
cancelText: "Cancel",
onConfirm: () => {
require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (err, res, body) => {
if (err) return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js");
await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
});
}
});
}
start() { }
stop() { }
} : (([Plugin, Api]) => {
const plugin = (Plugin, Api) => { try {
const {
DiscordModules: { Dispatcher, SelectedChannelStore }
} = Api;
const audio = new Audio();
return class Bruh extends Plugin {
constructor() {
super();
}
getSettingsPanel() {
return this.buildSettingsPanel().getElement();
}
onStart() {
Dispatcher.subscribe("MESSAGE_CREATE", this.messageEvent);
}
messageEvent = async ({ channelId, message, optimistic }) => {
if (this.settings.general.onlyCur && channelId != SelectedChannelStore.getChannelId())
return;
if (!optimistic) {
const count = (message.content.match(/bruh/gmi) || []).length;
for (let i = 0; i < count; i++) {
this.playBruh();
await new Promise(r => setTimeout(r, this.settings.general.delay));
}
}
};
playBruh() {
audio.src = "https://www.myinstants.com/media/sounds/movie_1.mp3";
audio.play();
}
onStop() {
Dispatcher.unsubscribe("MESSAGE_CREATE", this.messageEvent);
}
}
} catch (e) { console.error(e); }};
return plugin(Plugin, Api);
})(global.ZeresPluginLibrary.buildPlugin(config));
})();
================================================
FILE: CustomJs.plugin.js
================================================
//META{"name":"CustomJs","website":"https://metalloriff.github.io/toms-discord-stuff/","source":"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/CustomJs.plugin.js"}*//
class CustomJs {
getName() { return "CustomJs"; }
getDescription() { return "Allows you to specify a custom JavaScript file similar to custom CSS."; }
getVersion() { return "0.0.1"; }
getAuthor() { return "Metalloriff"; }
getChanges() {
return {
};
}
load() {}
start() {
let libLoadedEvent = () => {
try{ this.onLibLoaded(); }
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); } }
};
let lib = document.getElementById("NeatoBurritoLibrary");
if(!lib) {
lib = document.createElement("script");
lib.id = "NeatoBurritoLibrary";
lib.type = "text/javascript";
lib.src = "https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js";
document.head.appendChild(lib);
}
this.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);
if(typeof window.NeatoLib !== "undefined") libLoadedEvent();
else lib.addEventListener("load", libLoadedEvent);
}
getSettingsPanel() {
const fs = require("fs");
setTimeout(() => {
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField("Custom JS file path", this.settings.filepath, e => {
if(e.target.value && !fs.existsSync(e.target.value)) return NeatoLib.showToast("File does not exist", "error");
this.settings.filepath = e.target.value;
this.saveSettings();
}), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createButton("Browse", () => {
NeatoLib.browseForFile(filepath => {
if(!filepath) return NeatoLib.showToast("No file selected", "error");
this.settings.filepath = filepath;
this.saveSettings();
})
}), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createButton("Reload", () => this.reloadJs(), "float:right"), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.DOM.createElement({ innerHTML : `
Example #1
<pre><div style="background:#202225;display:block;padding:10px;margin-top:10px;border-radius:5px">
return new class {
start() {
console.log("start");
document.addEventListener("click", this.clickEvent = e => this.onClick(e));
}
onClick(e) {
console.log("clicked on", e.target);
}
stop() {
document.removeEventListener("click", this.clickEvent);
console.log("stop");
}
onSwitch() {
console.log(NeatoLib.getSelectedTextChannel());
}
}
</div></pre>
`, style : "color:white" }, { type : "p" }), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.DOM.createElement({ innerHTML : `
Example #2
<pre><div style="background:#202225;display:block;padding:10px;margin-top:10px;border-radius:5px">
return {
clickEvent : function(e) {
console.log("clicked on", e.target);
},
start : function() {
console.log("start");
document.addEventListener("click", this.clickEvent);
},
stop : function() {
document.removeEventListener("click", this.clickEvent);
console.log("stop");
},
onSwitch() {
console.log(NeatoLib.getSelectedTextChannel());
}
}
</div></pre>
`, style : "color:white" }, { type : "p" }), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.DOM.createElement({ innerHTML : `
Events and variables
<pre><div style="background:#202225;display:block;padding:10px;margin-top:10px;border-radius:5px">
start() - Called when the script is loaded. Use this for initialization.
stop() - Called before the script is stopped. Use this for de-initialization.
onSwitch() - Called when switching servers and channels.
loader - This plugin object. Example usage: loader.reloadJs()
</div></pre>
`, style : "color:white" }, { type : "p" }), this.getName());
NeatoLib.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());
NeatoLib.Settings.pushChangelogElements(this);
}, 0);
return NeatoLib.Settings.Elements.pluginNameLabel(this.getName());
}
saveSettings() {
this.rewatch();
NeatoLib.Settings.save(this);
}
onLibLoaded() {
this.settings = NeatoLib.Settings.load(this, {
displayUpdateNotes : true,
filepath : ""
});
NeatoLib.Updates.check(this);
//if(this.settings.displayUpdateNotes) NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());
NeatoLib.Events.onPluginLoaded(this);
this.reloadJs();
this.rewatch();
NeatoLib.Events.attach("switch", this.switchEvent = () => {
if(this.script && typeof this.script.onSwitch == "function") {
try { this.script.onSwitch(); }
catch(err) {
console.error(err);
NeatoLib.showToast("[Custom JS]: error in onSwitch(), check the console for more details.");
}
}
});
}
rewatch() {
const fs = require("fs");
if(this.watcher) this.watcher.close();
if(this.settings.filepath) this.watcher = fs.watch(this.settings.filepath, () => {
const current = fs.readFileSync(this.settings.filepath, "utf-8");
if(!current) return;
if(this.last != current) this.reloadJs();
});
}
reloadJs() {
if(this.script && typeof this.script.stop == "function") this.script.stop();
this.script = null;
const fs = require("fs");
if(!this.settings.filepath) return;
else if(!fs.existsSync(this.settings.filepath)) return NeatoLib.showToast("[Custom JS]: file not found", "error");
try {
const js = this.script = eval(`(function(){${this.last = fs.readFileSync(this.settings.filepath, "utf-8")}})();`);
js.loader = this;
try { if(typeof js.start == "function") js.start(); }
catch(err) {
console.error(err);
NeatoLib.showToast("[Custom JS]: error in start(), check the console for more details.");
}
} catch(err) {
console.error(err);
NeatoLib.showToast("[Custom JS]: error evaluating JS, " + err);
}
}
stop() {
if(this.script && typeof this.script.stop == "function") {
try { this.script.stop(); }
catch(err) {
console.error(err);
NeatoLib.showToast("[Custom JS]: error in stop(), check the console for more details.");
}
}
if(this.watcher) this.watcher.close();
NeatoLib.Events.detach("switch", this.switchEvent);
}
}
================================================
FILE: CustomizableAvatarDPI.plugin.js
================================================
//META{"name":"CustomizableAvatarDPI","website":"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/README.md","source":"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/CustomizableAvatarDPI.plugin.js"}*//
class CustomizableAvatarDPI {
getName() { return "Customizable Avatar DPI"; }
getDescription() { return "Allows you to change the DPI of user avatars, to reduce bluriness with themes that increase the size of them."; }
getVersion() { return "1.0.6"; }
getAuthor() { return "Metalloriff"; }
getChanges() {
return {
"1.0.6":
`
Just temporary fix until Metalloriff hopefully rewrites this plugin aroung christmas time
`
}
}
load() {}
start() {
let libLoadedEvent = () => {
try{ this.onLibLoaded(); }
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); } }
};
let lib = document.getElementById("NeatoBurritoLibrary");
if(!lib) {
lib = document.createElement("script");
lib.id = "NeatoBurritoLibrary";
lib.type = "text/javascript";
lib.src = "https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js";
document.head.appendChild(lib);
}
this.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);
if(typeof window.NeatoLib !== "undefined") libLoadedEvent();
else lib.addEventListener("load", libLoadedEvent);
}
saveSettings() {
NeatoLib.Settings.save(this);
}
getSettingsPanel() {
setTimeout(() => {
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField("Popout avatar size", this.settings.popoutAvatarSize, e => {
this.settings.popoutAvatarSize = e.target.value;
this.saveSettings();
}), this.getName(), { tooltip : "User popouts" });
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField("Other avatar size", this.settings.otherAvatarSize, e => {
this.settings.otherAvatarSize = e.target.value;
this.saveSettings();
}), this.getName(), { tooltip : "Everything else" });
NeatoLib.Settings.pushChangelogElements(this);
}, 0);
return NeatoLib.Settings.Elements.pluginNameLabel(this.getName());
}
onLibLoaded(){
NeatoLib.Updates.check(this);
this.settings = NeatoLib.Settings.load(this, {
popoutAvatarSize : 1024,
otherAvatarSize : 256,
displayUpdateNotes : true
});
this.appObserver = new MutationObserver(m => {
for(let i = 0; i < m.length; i++) {
if(!m[i].addedNodes.length) continue;
for(let a = 0; a < m[i].addedNodes.length; a++) {
let added = m[i].addedNodes[a];
if(!(added instanceof Element)) continue;
let imgs = added.getElementsByClassName(NeatoLib.getClass(["avatar", "mask", "pointer", "status"], "avatar"));
for(let img of imgs)
if(img) img.src = img.src.split("?size=")[0] + "?size=" + this.settings.otherAvatarSize;
let popouts = added.classList.contains(NeatoLib.getClass(["body", "footer", "popout", "title"], "popout")) ? [added] : added.getElementsByClassName(NeatoLib.getClass(["body", "footer", "popout", "title"], "popout"));
for(let popout of popouts){
let imgs = popout.getElementsByClassName(NeatoLib.getClass(["avatar", "mask", "pointer", "status"], "avatar"));
for(let img of imgs)
if(img) img.src = img.src.split("?size=")[0] + "?size=" + this.settings.popoutAvatarSize;
}
}
}
});
this.appObserver.observe(document.getElementById("app-mount"), { childList : true, subtree : true });
NeatoLib.Events.onPluginLoaded(this);
NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());
}
stop() {
this.appObserver.disconnect();
}
}
================================================
FILE: DetailedServerTooltips.plugin.js
================================================
//META{"name":"DetailedServerTooltips","website":"https://metalloriff.github.io/toms-discord-stuff/","source":"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/DetailedServerTooltips.plugin.js"}*//
class DetailedServerTooltips {
getName() { return "DetailedServerTooltips"; }
getDescription() { 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."; }
getVersion() { return "0.3.14"; }
getAuthor() { return "Metalloriff"; }
getChanges() {
return {
"0.1.1": `
Added a creation date field.
`,
"0.2.3": `
Fixed tooltip color not changing with some themes.
Fixed the tooltip arrow being offsetted wrong when the tooltip was prevented from going off-screen.
Tooltip guild icons are now full res.
`,
"0.3.4": `
Fixed update notes.
Fixed incompatibility with Zerebos' DoNotTrack plugin. (If you still have issues with tooltips sticking with it, please let me know. I barely tested it.)
Added a minimal mode setting.
`,
"0.3.5": `
Fixed tooltip getting stuck with ServerFolders
`,
"0.3.6": `
Fixed tooltips getting stuck when switching from dm to a server.
`,
"0.3.8": `
Fixed tooltips not showing for servers inside of folders with DevilBro's ServerFolders plugin.
`
};
}
load() {}
start() {
const libLoadedEvent = () => {
try{
if(window.pluginCookie["DoNotTrack"] == true) setTimeout(() => this.onLibLoaded(), 2000);
else this.onLibLoaded();
}
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); } }
};
let lib = document.getElementById("NeatoBurritoLibrary");
if (!lib) {
lib = document.createElement("script");
lib.id = "NeatoBurritoLibrary";
lib.type = "text/javascript";
lib.src = "https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js";
document.head.appendChild(lib);
}
this.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);
if (typeof window.NeatoLib !== "undefined") libLoadedEvent();
else lib.addEventListener("load", libLoadedEvent);
}
get settingFields() {
return {
tooltipColor: { label: "Tooltip color", type: "color" },
displayDelay: { label: "Tooltip display delay", description: "(ms)", type: "int" },
preview: { type: "custom", html: `
<div class="tooltip tooltip-right dst-tooltip" style="position: relative; margin-top: 20px;">
Kappa Stretch Server
<div class="dst-tooltip-icon" style="background-image: url(https://cdn.discordapp.com/attachments/392905457486004224/457784406313271296/KappaStretch.png);"></div>
<div id="dst-tooltip-owner-label" class="dst-tooltip-label">Owner: KappaStretch#0000</div>
<div class="dst-tooltip-label">Joined at: ${new Date().toLocaleDateString()}, ${new Date().toLocaleTimeString()} (0 days ago)</div>
<div id="dst-tooltip-member-count-label" class="dst-tooltip-label">400 members</div>
<div class="dst-tooltip-label">15 channels</div>
<div class="dst-tooltip-label">10 roles</div>
<div class="dst-tooltip-label">Region: us-central</div>
<div style="font-weight: bolder;" class="dst-tooltip-label"><div class="profileBadgePartner-SjK6L2 profileBadge-2BqF-Z" style="display: inline-block;"></div>PARTNERED SERVER</div>
</div>
` },
minimalMode: { label: "Minimal mode", type: "bool" },
minimalPreview: { type: "custom", html: `
<div class="tooltip tooltip-right dst-tooltip dst-min" style="position: relative; margin-top: 20px;">
Kappa Stretch Server
<div class="dst-tooltip-icon" style="background-image: url(https://cdn.discordapp.com/attachments/392905457486004224/457784406313271296/KappaStretch.png);"></div>
<div id="dst-tooltip-owner-label" class="dst-tooltip-label">Owner: KappaStretch#0000</div>
<div class="dst-tooltip-label">Joined at: ${new Date().toLocaleDateString()}, ${new Date().toLocaleTimeString()} (0 days ago)</div>
<div id="dst-tooltip-member-count-label" class="dst-tooltip-label">400 members</div>
<div class="dst-tooltip-label">15 channels</div>
<div class="dst-tooltip-label">10 roles</div>
<div class="dst-tooltip-label">Region: us-central</div>
<div style="font-weight: bolder;" class="dst-tooltip-label"><div class="profileBadgePartner-SjK6L2 profileBadge-2BqF-Z" style="display: inline-block;"></div>PARTNERED SERVER</div>
</div>
` }
};
}
get defaultSettings() {
return {
displayUpdateNotes: true,
tooltipColor: "#7289da",
displayDelay: 500,
minimalMode: false
};
}
getSettingsPanel() {
return NeatoLib.Settings.createPanel(this);
}
saveSettings() {
this.applyCSS();
NeatoLib.Settings.save(this);
}
applyCSS() {
if (this.style) this.style.destroy();
this.style = NeatoLib.injectCSS(`
.dst-tooltip {
width: 225px;
max-width: 225px;
text-align: center;
background-color: ${this.settings.tooltipColor} !important;
color: white;
}
.dst-tooltip:after {
border-right-color: ${this.settings.tooltipColor} !important;
top: 25px !important;
}
.dst-tooltip-icon {
width: 200px;
height: 200px;
background-size: cover;
border-radius: 5px;
margin-top: 5px;
flex: 1;
}
.dst-tooltip-label {
color: white;
margin-top: 10px;
font-size: 15px;
}
.dst-min .dst-tooltip-icon{display:none}
.dst-min .dst-tooltip-label{font-size:13px}
.dst-tooltip.dst-min{max-width:200px}
`);
}
onLibLoaded() {
if (!NeatoLib.hasRequiredLibVersion(this, "0.8.20")) return;
NeatoLib.Settings.load(this);
NeatoLib.Updates.check(this);
if (this.settings.displayUpdateNotes) NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());
this.guildModule = NeatoLib.Modules.get("getGuild");
this.userModule = NeatoLib.Modules.get("getUser");
this.memberModule = NeatoLib.Modules.get("getMembers");
this.channelModule = NeatoLib.Modules.get("getChannel");
this.memberCountModule = NeatoLib.Modules.get("getMemberCount");
this.localUser = NeatoLib.getLocalUser();
this.owners = {};
this.applyCSS();
let tooltip, timeout;
this.dragGuild = () => {
this.mouseLeaveGuild();
let tooltips = document.getElementsByClassName("dst-tooltip");
for (let i = 0; i < tooltips.length; i++) {
if (tooltips[i].updateLoop) clearInterval(tooltips[i].updateLoop);
tooltips[i].remove();
}
};
this.mouseEnterGuild = e => {
timeout = setTimeout(() => {
tooltip = this.tooltip(((e.target.parentElement.href || e.target.href).match(/\d+/) || [])[0], e.target);
if (!tooltip) return;
let tt = document.getElementsByClassName(NeatoLib.getClass("tooltip"))[0];
tt.appendChild(tooltip);
tt.getElementsByClassName(NeatoLib.getClass("tooltip", "tooltipPointer"))[0].style.borderTopColor = this.settings.tooltipColor;
let bottomPos = parseFloat(tooltip.style.top) + tooltip.offsetHeight;
if (bottomPos > window.innerHeight) {
tooltip.style.top = (parseFloat(tooltip.style.top) - (bottomPos - window.innerHeight)) + "px";
tooltip.insertAdjacentHTML("afterbegin", `<style>.dst-tooltip:after{top:calc(25px + ${parseFloat(tooltip.style.top) - (parseFloat(tooltip.style.top) - (bottomPos - window.innerHeight))}px) !important}</style>`);
}
var tooltipObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
var nodes = Array.from(mutation.removedNodes);
var ownMatch = nodes.indexOf(tooltip) > -1;
var directMatch = nodes.indexOf(e.target) > -1;
var parentMatch = nodes.some(parent => parent.contains(e.target));
if (ownMatch || directMatch || parentMatch) {
tooltipObserver.disconnect();
tooltip.remove();
}
});
});
}, this.settings.displayDelay);
};
this.mouseLeaveGuild = () => {
clearTimeout(timeout);
if (tooltip) tooltip.remove();
};
this.switchEvent = () => this.applyToGuilds();
this.guildObserver = new MutationObserver(this.switchEvent);
this.guildObserver.observe(document.getElementsByClassName(NeatoLib.getClass("unreadMentionsBar", "scroller"))[0], { childList: true, subtree: true });
NeatoLib.Events.attach("switch", this.switchEvent);
this.applyToGuilds();
NeatoLib.Events.onPluginLoaded(this);
}
applyToGuilds(detach) {
const guilds = document.getElementsByClassName(NeatoLib.getClass("acronym", "wrapper"));
for (let i = 0; i < guilds.length; i++) {
let reactEvents = NeatoLib.ReactData.getEvents(guilds[i]);
guilds[i].parentElement.removeEventListener("dragstart", this.dragGuild);
guilds[i].parentElement.removeEventListener("dragend", this.dragGuild);
guilds[i].removeEventListener("mouseenter", this.mouseEnterGuild);
guilds[i].removeEventListener("mouseleave", this.mouseLeaveGuild);
if (detach) continue;
guilds[i].parentElement.addEventListener("dragstart", this.dragGuild);
guilds[i].parentElement.addEventListener("dragend", this.dragGuild);
guilds[i].addEventListener("mouseenter", this.mouseEnterGuild);
guilds[i].addEventListener("mouseleave", this.mouseLeaveGuild);
}
}
tooltip(guildId, element) {
if (!guildId || !element || element.getBoundingClientRect().width == 0) return;
let tooltip = document.createElement("div"),
guild = this.guildModule.getGuild(guildId),
owner = this.userModule.getUser(guild.ownerId);
tooltip.className = NeatoLib.getClass("tooltip") + " " + NeatoLib.getClass("tooltip", "tooltipRight") + " dst-tooltip";
if (this.settings.minimalMode) tooltip.classList.add("dst-min");
tooltip.style.top = "0";
tooltip.style.left = "0";
tooltip.style.position = "fixed";
let creationDate = NeatoLib.getSnowflakeCreationDate(guild.id);
var imgURL = guild.getIconURL(guild && guild.icon && guild.icon.startsWith('a_') ? 'gif' : 'webp');
tooltip.innerHTML = `${this.escapeHtml(guild.name)}
<div class="dst-tooltip-icon" style="background-image: url(${imgURL}?size=2048);"></div>
<div id="dst-tooltip-owner-label" class="dst-tooltip-label">Owner: ${owner ? this.escapeHtml(owner.tag) : "unknown"}</div>
<div class="dst-tooltip-label">Created at: ${creationDate.toLocaleDateString()}, ${creationDate.toLocaleTimeString()} (${Math.round(Math.abs(creationDate.getTime() - new Date().getTime()) / 86400000)} days ago)</div>
${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>`}
<div id="dst-tooltip-member-count-label" class="dst-tooltip-label">${this.memberCountModule.getMemberCount(guildId)} members</div>
<div class="dst-tooltip-label">${Object.values(this.channelModule.getChannels()).filter(c => c.guild_id == guildId).length} channels</div>
<div class="dst-tooltip-label">${Object.keys(guild.roles).length} roles</div>
<div class="dst-tooltip-label">Region: ${guild.region}</div>`;
if(!guild.getIconURL()) tooltip.find(".dst-tooltip-icon").outerHTML = "";
if (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>`);
if(owner){
this.owners[guildId] = owner.tag;
}else{
NeatoLib.Modules.get("getAPIBaseURL").get(NeatoLib.Modules.get(["Permissions", "ActivityTypes", "StatusTypes"]).Endpoints.USER(guild.ownerId)).then(result => {
if(!result) return;
let res = JSON.parse(result.text);
this.owners[guildId] = res.username + "#" + res.discriminator;
});
}
let updateMemberCount = false;
if(this.memberCountModule.getMemberCount(guildId) < 500){
NeatoLib.Modules.get("requestMembers").requestMembers(guildId, "", 0);
updateMemberCount = true;
}
const self = setInterval(() => {
if (!Array.from(document.getElementsByClassName(NeatoLib.getClass("tooltip"))).includes(tooltip)) return clearInterval(self);
document.getElementById("dst-tooltip-owner-label").innerHTML = "Owner: " + (this.escapeHtml(this.owners[guildId] || "unknown"));
if(updateMemberCount) document.getElementById("dst-tooltip-member-count-label").innerText = this.memberModule.getMembers(guildId).length + " members";
}, 500);
tooltip.updateLoop = self;
return tooltip;
}
stop() {
let tooltips = document.getElementsByClassName("dst-tooltip");
for (let i = 0; i < tooltips.length; i++) {
if (tooltips[i].updateLoop) clearInterval(tooltips[i].updateLoop);
tooltips[i].remove();
}
this.applyToGuilds(true);
if (this.style) this.style.destroy();
NeatoLib.Events.detach("switch", this.switchEvent);
this.guildObserver.disconnect();
}
escapeHtml(txt){
return txt.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
}
================================================
FILE: DoubleClickVoiceChannels.plugin.js
================================================
/**
* @name DoubleClickVoiceChannels
* @invite yNqzuJa
* @authorLink https://github.com/Metalloriff
* @donate https://www.paypal.me/israelboone
* @website https://metalloriff.github.io/toms-discord-stuff/
* @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/DoubleClickVoiceChannels.plugin.js
*/
module.exports = (() => {
const config =
{
info:
{
name: "DoubleClickVoiceChannels",
authors:
[
{
name: "Metalloriff",
discord_id: "264163473179672576",
github_username: "metalloriff",
twitter_username: "Metalloriff"
}
],
version: "2.0.4",
description: "Requires you to double click voice channels to join them.",
github: "https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/DoubleClickVoiceChannels.plugin.js",
github_raw: "https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/DoubleClickVoiceChannels.plugin.js"
},
changelog:
[
{
title: "Patched",
type: "fixed",
items: ["Fixed again. Thanks cmd430 for the help!"]
}
]
};
return !global.ZeresPluginLibrary ? class {
constructor() { this._config = config; }
getName = () => config.info.name;
getAuthor = () => config.info.description;
getVersion = () => config.info.version;
load() {
BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {
confirmText: "Download Now",
cancelText: "Cancel",
onConfirm: () => {
require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (err, res, body) => {
if (err) return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js");
await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
});
}
});
}
start() { }
stop() { }
} : (([Plugin, Api]) => {
const plugin = (Plugin, Api) => {
const { WebpackModules, Patcher, Utilities, ReactComponents } = Api;
return class DoubleClickVoiceChannels extends Plugin {
constructor() {
super();
}
async showChangelog(footer) {
try { footer = (await WebpackModules.getByProps("getUser", "acceptAgreements").getUser("264163473179672576")).tag + " | https://discord.gg/yNqzuJa"; }
finally { super.showChangelog(footer); }
}
async onStart() {
const { component: ChannelItem } = await ReactComponents.getComponentByName("VoiceChannel", "*");
if (ChannelItem) {
Patcher.after(ChannelItem.prototype, "render", (r, _, el) => {
const children_type0 = Utilities.getNestedProp(el, "props.children.props.children.0.props.children.props.children");
const children_type1 = Utilities.getNestedProp(el, "props.children.0.props.children.props.children");
if (children_type0 || children_type1) {
const handleClick = (children) => {
const c = children();
const handler = c.props.children;
c.props.children = () => {
const h = handler({});
if (!h.props.connected) {
// for whatever reason, onDoubleClick stopped working, so here's a dumb workaround
const onClick = h.props.onClick;
let t = performance.now() - 200;
h.props.onClick = () => {
if (performance.now() - t < 200)
onClick();
t = performance.now();
};
}
return h;
};
return c;
};
if (children_type0) {
el.props.children.props.children[0].props.children.props.children = () => {
return handleClick(children_type0);
};
}
if (children_type1) {
el.props.children[0].props.children.props.children = () => {
return handleClick(children_type1);
};
};
}
else {
console.warn("DoubleClickVoiceChannel: Failed to get nested props!");
}
});
}
}
onStop() {
Patcher.unpatchAll();
}
}
};
return plugin(Plugin, Api);
})(global.ZeresPluginLibrary.buildPlugin(config));
})();
================================================
FILE: FormattableMessageCopier.plugin.js
================================================
//META{"name":"FormattableMessageCopier"}*//
class FormattableMessageCopier {
getSettingsPanel(){
let updateExample = () => {
let hints = document.getElementsByClassName("plugin-settings")[0].getElementsByTagName("p");
for(let i = 0; i < hints.length; i++) hints[i].style.color = `rgb(${this.settings.selectionColor})`;
let time = new Date().toLocaleTimeString("en-us"),
example = document.getElementById("mc-example"),
exampleMessages1 = [], exampleMessages2 = [];
for(let msg of ["Hello.", "These are some messages.", "This is a third message, with a reaction."]) {
exampleMessages1.push(this.settings.messageFormat
.split("$time").join(time)
.split("$messagetext").join(msg));
exampleMessages1.push(this.settings.reactionFormat
.split("$emoji").join("👌")
.split("$count").join("4") + this.settings.reactionFormat
.split("$emoji").join("💯")
.split("$count").join("20"));
exampleMessages2.push(this.settings.messageFormat
.split("$time").join(time)
.split("$messagetext").join("This message has an image attached to it."));
exampleMessages2.push(this.settings.attachmentFormat
.split("$filename").join("LUUL.jpg")
.split("$fileurl").join("https://i.imgur.com/cxWch9R.jpg"));
example.innerText = (this.settings.headerFormat
.split("$channel").join("#general")
.split("$selectionstarttime").join(time)
.split("$selectionendtime").join(time)
.split("$selectiondate").join(new Date().toLocaleDateString("en-us"))
+ "\n" + this.settings.groupFormat
.split("$time").join(time)
.split("$username").join("Metalloriff")
.split("$usertag").join("Metalloriff#2891")
.split("$jumplink").join("https://discordapp.com/channels/serverid/channelid/messageid")
.split("$message").join(exampleMessages1.join("\n"))
+ "\n" + this.settings.groupFormat
.split("$time").join(time)
.split("$username").join("Some Kid Named Nate")
.split("$usertag").join("Some Kid Named Nate#0000")
.split("$jumplink").join("https://discordapp.com/channels/serverid/channelid/messageid")
.split("$message").join(exampleMessages2.join("\n")))
.split("$newline").join("\n")
.split("$tab").join(" ");
}
};
setTimeout(() => {
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField("Header format", this.settings.headerFormat, e => {
this.settings.headerFormat = e.target.value;
updateExample();
this.saveSettings();
}), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createHint(`
Header variables:\n\n
$channel - The name of the selected channel\n
$selectionstarttime - The timestamp of the first selected message\n
$selectionendtime - The timestamp of the last selected message\n
$selectiondate - The date of the selected messages\n
$newline - New line\n
$tab - Tab
`), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField("Message group format", this.settings.groupFormat, e => {
this.settings.groupFormat = e.target.value;
updateExample();
this.saveSettings();
}), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createHint(`
$time - Message group timestamp\n
$username - Message group sender's username\n
$usertag - Message group sender's username and discriminator\n
$message - Formatted message\n
$newline - New line\n
$tab - Tab
`), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField("Message format", this.settings.messageFormat, e => {
this.settings.messageFormat = e.target.value;
updateExample();
this.saveSettings();
}), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createHint(`
$time - Message timestamp\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
$messagetext - Message text\n
$newline - New line\n
$tab - Tab
`), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField("Attachment format", this.settings.attachmentFormat, e => {
this.settings.attachmentFormat = e.target.value;
updateExample();
this.saveSettings();
}), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createHint(`
$filename - Name of the uploaded file\n
$fileurl - URL of the uploaded file\n
$newline - New line\n
$tab - Tab
`), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField("Reaction format", this.settings.reactionFormat, e => {
this.settings.reactionFormat = e.target.value;
updateExample();
this.saveSettings();
}), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createHint(`
$emoji - Reaction's emoji\n
$count - Reaction count\n
$newline - New line\n
$tab - Tab
`), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField("Selection color (R, G, B)", this.settings.selectionColor, e => {
this.settings.selectionColor = e.target.value;
updateExample();
this.saveSettings();
}), this.getName());
NeatoLib.Settings.pushHTML(`
<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">
<div class="comment">
<div class="message">
<div class="body">
<div class="message-text">
<div class="markup">...</div>
</div>
</div>
</div>
</div>
</div>
`, this.getName());
let fields = document.getElementsByClassName("plugin-settings")[0].getElementsByClassName("input-2YozMi");
updateExample();
}, 0);
return NeatoLib.Settings.Elements.pluginNameLabel(this.getName());
}
saveSettings(){
NeatoLib.Settings.save(this);
}
getName() { return "Formattable Message Copier"; }
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."; }
getVersion() { return "0.1.4"; }
getAuthor() { return "Metalloriff"; }
load() {}
start() {
let libLoadedEvent = () => {
try{ this.onLibLoaded(); }
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); } }
};
let lib = document.getElementById("NeatoBurritoLibrary");
if(lib == undefined) {
lib = document.createElement("script");
lib.setAttribute("id", "NeatoBurritoLibrary");
lib.setAttribute("type", "text/javascript");
lib.setAttribute("src", "https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js");
document.head.appendChild(lib);
}
if(typeof window.NeatoLib !== "undefined") libLoadedEvent();
else lib.addEventListener("load", libLoadedEvent);
}
onLibLoaded(){
this.settings = NeatoLib.Settings.load(this, {
headerFormat : "messages in $channel since $selectionstarttime, until $selectionendtime, on $selectiondate$newline$newline",
groupFormat : "[$time] $username:$newline$newline$message$newline$newline",
messageFormat : "$tab$messagetext",
attachmentFormat : "[$filename]: <$fileurl>",
reactionFormat : " [ $emoji : $count ] ",
selectionColor : "114, 137, 218"
});
NeatoLib.Updates.check(this);
NeatoLib.Events.onPluginLoaded(this);
this.userModule = NeatoLib.Modules.get("getUser");
this.channelModule = NeatoLib.Modules.get("getChannel");
this.selection = [];
this.applyCSS();
this.clickEvent = e => this.onClick(e);
document.addEventListener("dblclick", this.clickEvent);
document.addEventListener("click", this.clickEvent);
}
applyCSS(){
if(this.styles) this.styles.destroy();
this.styles = NeatoLib.injectCSS(`
.mc-message-selected {
margin-left: 0px !important;
margin-right: 0px !important;
padding-left: 20px !important;
padding-right: 6px !important;
border: 2px solid rgb(${this.settings.selectionColor}) !important;
background-color: rgba(${this.settings.selectionColor}, 0.5) !important;
}
#mc-copy-button {
background-color: rgb(${this.settings.selectionColor}) !important;
}
`);
}
onClick(e){
if(!e.target.classList || (!e.target.classList.contains("message-group") && e.target.className != "old-h2")) return;
let messageGroup;
if(e.target.className.includes("message-group")) messageGroup = e.target;
else messageGroup = $(e.target).parents(".message-group")[0];
if(messageGroup) {
if(e.type == "dblclick" || (e.type == "click" && e.ctrlKey)){
messageGroup.classList.add("mc-message-selected");
this.selection.push(messageGroup);
}
if(e.type == "click" && !e.ctrlKey && !e.shiftKey) this.clearSelection();
if(e.type == "click" && e.shiftKey && this.selection.length > 0 && messageGroup != this.selection[0]) {
if(document.selection) document.selection.empty();
else if(window.getSelection) window.getSelection().removeAllRanges();
let messages = document.getElementsByClassName("message-group"), selecting = false, newSelection = [];
let select = (i, reverse) => {
if(messages[i] == (reverse ? messageGroup : this.selection[0])) selecting = true;
if(selecting) {
newSelection.push(messages[i]);
messages[i].classList.add("mc-message-selected");
}
if(messages[i] == (reverse ? this.selection[0] : messageGroup)) selecting = false;
};
if(this.selection.length > 1) {
for(let i = 0; i < this.selection.length; i++) this.selection[i].classList.remove("mc-message-selected");
this.selection.splice(1, this.selection.length);
}
for(let i = 0; i < messages.length; i++) select(i);
if(selecting) {
selecting = false;
newSelection = [];
for(let i = 0; i < messages.length; i++) {
messages[i].classList.remove("mc-message-selected");
select(i, true);
}
}
this.selection = newSelection;
}
}
if(document.getElementById("mc-copy-button")) document.getElementById("mc-copy-button").remove();
if(this.selection.length > 0){
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>`);
document.getElementById("mc-copy-button").addEventListener("click", () => this.copySelection());
}
}
clearSelection(){
for(let i = 0; i < this.selection.length; i++) this.selection[i].classList.remove("mc-message-selected");
this.selection = [];
if(document.getElementById("mc-copy-button")) document.getElementById("mc-copy-button").remove();
}
copySelection(){
let formatted = [],
firstMessage = NeatoLib.ReactData.getProps(this.selection[0]),
lastGroup = NeatoLib.ReactData.getProps(this.selection[this.selection.length - 1]).messages,
lastMessageTimestamp = lastGroup[lastGroup.length - 1].timestamp,
channelName = firstMessage.channel.name;
if(this.settings.headerFormat != "" && this.selection.length > 1){
formatted.push(this.settings.headerFormat
.split("$channel").join(channelName == "" ? "DM" : ("#" + channelName))
.split("$selectionstarttime").join(firstMessage.messages[0].timestamp.toDate().toLocaleTimeString("en-us"))
.split("$selectionendtime").join(lastMessageTimestamp.toDate().toLocaleTimeString("en-us"))
.split("$selectiondate").join(lastMessageTimestamp.toDate().toLocaleDateString("en-us"))
.split("$newline").join("\n")
.split("$tab").join("\t")
);
}
for(let i = 0; i < this.selection.length; i++) {
let props = NeatoLib.ReactData.getProps(this.selection[i]),
time = props.messages[0].timestamp.toDate().toLocaleTimeString("en-us"),
messages = [];
for(let pi = 0; pi < props.messages.length; pi++) {
let message = props.messages[pi];
for(let mi = 0; mi < message.mentions.length; mi++) message.content = message.content.replace("<@" + message.mentions[i] + ">", "`@" + this.userModule.getUser(message.mentions[i]).tag + "`");
let content = this.settings.messageFormat
.split("$time").join(message.timestamp.toDate().toLocaleTimeString("en-us"))
.split("$newline").join("\n")
.split("$tab").join("\t")
.split("$messagetext").join(message.content);
if(content != "") messages.push(content);
let reactions = [];
for(let ri = 0; ri < message.reactions.length; ri++) {
reactions.push(this.settings.reactionFormat
.split("$emoji").join(message.reactions[ri].emoji.name)
.split("$count").join(message.reactions[ri].count)
.split("$newline").join("\n")
.split("$tab").join("\t")
);
}
if(reactions.length > 0) messages.push(reactions.join(""));
for(let ai = 0; ai < message.attachments.length; ai++) {
messages.push(this.settings.attachmentFormat
.split("$filename").join(message.attachments[ai].filename)
.split("$fileurl").join(message.attachments[ai].url)
.split("$newline").join("\n")
.split("$tab").join("\t")
);
}
}
formatted.push(this.settings.groupFormat
.split("$time").join(time)
.split("$username").join(props.messages[0].author.username)
.split("$usertag").join(props.messages[0].author.tag)
.split("$jumplink").join(props.channel.guild_id ? `<https://discordapp.com/channels/${props.channel.guild_id}/${props.channel.id}/${props.messages[0].id}>` : "")
.split("$message").join(messages.join("\n"))
.split("$newline").join("\n")
.split("$tab").join("\t")
);
}
this.clearSelection();
formatted = formatted.join("\n");
NeatoLib.Modules.get("copy").copy(formatted);
NeatoLib.showToast(formatted.length + " characters copied!", null, { color : `rgb(${this.settings.selectionColor})` });
}
stop() {
document.removeEventListener("dblclick", this.clickEvent);
document.removeEventListener("click", this.clickEvent);
this.styles.destroy();
}
}
================================================
FILE: GuildAndFriendRemovalAlerts/GuildAndFriendRemovalAlerts.plugin.js
================================================
/**
* @name GuildAndFriendRemovalAlerts
* @version 3.2.1
* @description Displays alerts when you are kicked/banned from a server, a server is deleted, and when a friend removes you.
* @author Metalloriff
* @source https://github.com/Metalloriff/BetterDiscordPlugins/GuildAndFriendRemovalAlerts
* @updateUrl https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/GuildAndFriendRemovalAlerts/GuildAndFriendRemovalAlerts.plugin.js
* @website https://metalloriff.github.io/toms-discord-stuff/#/
* @donate https://paypal.me/israelboone
* @invite yNqzuJa
*/
/*@cc_on
@if (@_jscript)
// Offer to self-install for clueless users that try to run this directly.
var shell = WScript.CreateObject("WScript.Shell");
var fs = new ActiveXObject("Scripting.FileSystemObject");
var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\BetterDiscord\plugins");
var pathSelf = WScript.ScriptFullName;
// Put the user at ease by addressing them in the first person
shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40);
} else if (!fs.FolderExists(pathPlugins)) {
shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
} else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
// Show the user where to put plugins in the future
shell.Exec("explorer " + pathPlugins);
shell.Popup("I'm installed!", 0, "Successfully installed", 0x40);
}
WScript.Quit();
@else@*/
/* Generated Code */
const config = {
"info": {
"name": "GuildAndFriendRemovalAlerts",
"version": "3.2.1",
"description": "Displays alerts when you are kicked/banned from a server, a server is deleted, and when a friend removes you.",
"authors": [{
"name": "Metalloriff",
"discord_id": "264163473179672576",
"github_username": "Metalloriff"
}],
"github": "https://github.com/Metalloriff/BetterDiscordPlugins/GuildAndFriendRemovalAlerts",
"github_raw": "https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/GuildAndFriendRemovalAlerts/GuildAndFriendRemovalAlerts.plugin.js",
"website": "https://metalloriff.github.io/toms-discord-stuff/#/",
"donate": "https://paypal.me/israelboone",
"invite": "yNqzuJa"
},
"changelog": [{
"title": "3.0 rewrite",
"type": "fixed",
"items": [
"This plugin has been rewritten. Functionality is simliar, and settings and data should still be valid.",
"If you experience any bugs, please contact me."
]
}],
"build": {
"zlibrary": true,
"copy": true,
"production": false,
"scssHash": false,
"alias": {
"components": "components/index.js"
},
"release": {
"source": true,
"readme": true,
"public": true,
"contributors": null
}
}
};
function buildPlugin([BasePlugin, PluginApi]) {
const module = {
exports: {}
};
(() => {
"use strict";
class StyleLoader {
static styles = "";
static element = null;
static append(module, css) {
this.styles += `/* ${module} */\n${css}`;
}
static inject(name = config.info.name) {
if (this.element) this.element.remove();
this.element = document.head.appendChild(Object.assign(document.createElement("style"), {
id: name,
textContent: this.styles
}));
}
static remove() {
if (this.element) {
this.element.remove();
this.element = null;
}
}
}
function ___createMemoize___(instance, name, value) {
value = value();
Object.defineProperty(instance, name, {
value,
configurable: true
});
return value;
};
const Modules = {
get 'react-spring'() {
return ___createMemoize___(this, 'react-spring', () => BdApi.findModuleByProps('useSpring'))
},
'@discord/utils': {
get 'joinClassNames'() {
return ___createMemoize___(this, 'joinClassNames', () => BdApi.findModule(e => e.toString().indexOf('return e.join(" ")') > 200))
},
get 'useForceUpdate'() {
return ___createMemoize___(this, 'useForceUpdate', () => BdApi.findModuleByProps('useForceUpdate')?.useForceUpdate)
},
get 'Logger'() {
return ___createMemoize___(this, 'Logger', () => BdApi.findModuleByProps('setLogFn')?.default)
},
get 'Navigation'() {
return ___createMemoize___(this, 'Navigation', () => BdApi.findModuleByProps('replaceWith', 'currentRouteIsPeekView'))
}
},
'@discord/components': {
get 'Tooltip'() {
return ___createMemoize___(this, 'Tooltip', () => BdApi.findModuleByDisplayName('Tooltip'))
},
get 'TooltipContainer'() {
return ___createMemoize___(this, 'TooltipContainer', () => BdApi.findModuleByProps('TooltipContainer')?.TooltipContainer)
},
get 'TextInput'() {
return ___createMemoize___(this, 'TextInput', () => BdApi.findModuleByDisplayName('TextInput'))
},
get 'SlideIn'() {
return ___createMemoize___(this, 'SlideIn', () => BdApi.findModuleByDisplayName('SlideIn'))
},
get 'SettingsNotice'() {
return ___createMemoize___(this, 'SettingsNotice', () => BdApi.findModuleByDisplayName('SettingsNotice'))
},
get 'TransitionGroup'() {
return ___createMemoize___(this, 'TransitionGroup', () => BdApi.findModuleByDisplayName('TransitionGroup'))
},
get 'Button'() {
return ___createMemoize___(this, 'Button', () => BdApi.findModuleByProps('DropdownSizes'))
},
get 'Popout'() {
return ___createMemoize___(this, 'Popout', () => BdApi.findModuleByDisplayName('Popout'))
},
get 'Flex'() {
return ___createMemoize___(this, 'Flex', () => BdApi.findModuleByDisplayName('Flex'))
},
get 'Text'() {
return ___createMemoize___(this, 'Text', () => BdApi.findModuleByDisplayName('Text'))
},
get 'Card'() {
return ___createMemoize___(this, 'Card', () => BdApi.findModuleByDisplayName('Card'))
}
},
'@discord/modules': {
get 'Dispatcher'() {
return ___createMemoize___(this, 'Dispatcher', () => BdApi.findModuleByProps('dispatch', 'subscribe'))
},
get 'ComponentDispatcher'() {
return ___createMemoize___(this, 'ComponentDispatcher', () => BdApi.findModuleByProps('ComponentDispatch')?.ComponentDispatch)
},
get 'EmojiUtils'() {
return ___createMemoize___(this, 'EmojiUtils', () => BdApi.findModuleByProps('uploadEmoji'))
},
get 'PermissionUtils'() {
return ___createMemoize___(this, 'PermissionUtils', () => BdApi.findModuleByProps('computePermissions', 'canManageUser'))
},
get 'DMUtils'() {
return ___createMemoize___(this, 'DMUtils', () => BdApi.findModuleByProps('openPrivateChannel'))
}
},
'@discord/stores': {
get 'Messages'() {
return ___createMemoize___(this, 'Messages', () => BdApi.findModuleByProps('getMessage', 'getMessages'))
},
get 'Channels'() {
return ___createMemoize___(this, 'Channels', () => BdApi.findModuleByProps('getChannel', 'getDMFromUserId'))
},
get 'Guilds'() {
return ___createMemoize___(this, 'Guilds', () => BdApi.findModuleByProps('getGuild'))
},
get 'SelectedGuilds'() {
return ___createMemoize___(this, 'SelectedGuilds', () => BdApi.findModuleByProps('getGuildId', 'getLastSelectedGuildId'))
},
get 'SelectedChannels'() {
return ___createMemoize___(this, 'SelectedChannels', () => BdApi.findModuleByProps('getChannelId', 'getLastSelectedChannelId'))
},
get 'Info'() {
return ___createMemoize___(this, 'Info', () => BdApi.findModuleByProps('getSessionId'))
},
get 'Status'() {
return ___createMemoize___(this, 'Status', () => BdApi.findModuleByProps('getStatus', 'getActivities', 'getState'))
},
get 'Users'() {
return ___createMemoize___(this, 'Users', () => BdApi.findModuleByProps('getUser', 'getCurrentUser'))
},
get 'SettingsStore'() {
return ___createMemoize___(this, 'SettingsStore', () => BdApi.findModuleByProps('afkTimeout', 'status'))
},
get 'UserProfile'() {
return ___createMemoize___(this, 'UserProfile', () => BdApi.findModuleByProps('getUserProfile'))
},
get 'Members'() {
return ___createMemoize___(this, 'Members', () => BdApi.findModuleByProps('getMember'))
},
get 'Activities'() {
return ___createMemoize___(this, 'Activities', () => BdApi.findModuleByProps('getActivities'))
},
get 'Games'() {
return ___createMemoize___(this, 'Games', () => BdApi.findModuleByProps('getGame', 'games'))
},
get 'Auth'() {
return ___createMemoize___(this, 'Auth', () => BdApi.findModuleByProps('getId', 'isGuest'))
},
get 'TypingUsers'() {
return ___createMemoize___(this, 'TypingUsers', () => BdApi.findModuleByProps('isTyping'))
}
},
'@discord/actions': {
get 'ProfileActions'() {
return ___createMemoize___(this, 'ProfileActions', () => BdApi.findModuleByProps('fetchProfile'))
},
get 'GuildActions'() {
return ___createMemoize___(this, 'GuildActions', () => BdApi.findModuleByProps('requestMembersById'))
}
},
get '@discord/i18n'() {
return ___createMemoize___(this, '@discord/i18n', () => BdApi.findModule(m => m.Messages?.CLOSE && typeof(m.getLocale) === 'function'))
},
get '@discord/constants'() {
return ___createMemoize___(this, '@discord/constants', () => BdApi.findModuleByProps('API_HOST'))
},
get '@discord/contextmenu'() {
return ___createMemoize___(this, '@discord/contextmenu', () => {
const ctx = Object.assign({}, BdApi.findModuleByProps('openContextMenu'), BdApi.findModuleByProps('MenuItem'));
ctx.Menu = ctx.default;
return ctx;
})
},
get '@discord/forms'() {
return ___createMemoize___(this, '@discord/forms', () => BdApi.findModuleByProps('FormItem'))
},
get '@discord/scrollbars'() {
return ___createMemoize___(this, '@discord/scrollbars', () => BdApi.findModuleByProps('ScrollerAuto'))
},
get '@discord/native'() {
return ___createMemoize___(this, '@discord/native', () => BdApi.findModuleByProps('requireModule'))
},
get '@discord/flux'() {
return ___createMemoize___(this, '@discord/flux', () => Object.assign({}, BdApi.findModuleByProps('useStateFromStores').default, BdApi.findModuleByProps('useStateFromStores')))
},
get '@discord/modal'() {
return ___createMemoize___(this, '@discord/modal', () => Object.assign({}, BdApi.findModuleByProps('ModalRoot'), BdApi.findModuleByProps('openModal', 'closeAllModals')))
},
get '@discord/connections'() {
return ___createMemoize___(this, '@discord/connections', () => BdApi.findModuleByProps('get', 'isSupported', 'map'))
},
get '@discord/sanitize'() {
return ___createMemoize___(this, '@discord/sanitize', () => BdApi.findModuleByProps('stringify', 'parse', 'encode'))
},
get '@discord/icons'() {
return ___createMemoize___(this, '@discord/icons', () => BdApi.findAllModules(m => m.displayName && ~m.toString().indexOf('currentColor')).reduce((icons, icon) => (icons[icon.displayName] = icon, icons), {}))
},
'@discord/classes': {
get 'Timestamp'() {
return ___createMemoize___(this, 'Timestamp', () => BdApi.findModuleByPrototypes('toDate', 'month'))
},
get 'Message'() {
return ___createMemoize___(this, 'Message', () => BdApi.findModuleByPrototypes('getReaction', 'isSystemDM'))
},
get 'User'() {
return ___createMemoize___(this, 'User', () => BdApi.findModuleByPrototypes('tag'))
},
get 'Channel'() {
return ___createMemoize___(this, 'Channel', () => BdApi.findModuleByPrototypes('isOwner', 'isCategory'))
}
}
};
var __webpack_modules__ = {
86: (module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.d(__webpack_exports__, {
Z: () => __WEBPACK_DEFAULT_EXPORT__
});
var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(246);
var _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__);
var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i) {
return i[1];
}));
___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}", ""]);
___CSS_LOADER_EXPORT___.locals = {
item: "GuildAndFriendRemovalAlerts-item-item",
image: "GuildAndFriendRemovalAlerts-item-image",
inner: "GuildAndFriendRemovalAlerts-item-inner",
title: "GuildAndFriendRemovalAlerts-item-title",
description: "GuildAndFriendRemovalAlerts-item-description"
};
StyleLoader.append(module.id, ___CSS_LOADER_EXPORT___.toString());
const __WEBPACK_DEFAULT_EXPORT__ = Object.assign(___CSS_LOADER_EXPORT___, ___CSS_LOADER_EXPORT___.locals);
},
377: (module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.d(__webpack_exports__, {
Z: () => __WEBPACK_DEFAULT_EXPORT__
});
var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(246);
var _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__);
var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()((function(i) {
return i[1];
}));
___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}", ""]);
___CSS_LOADER_EXPORT___.locals = {
modal: "GuildAndFriendRemovalAlerts-styles-modal",
itemContainer: "GuildAndFriendRemovalAlerts-styles-itemContainer",
title: "GuildAndFriendRemovalAlerts-styles-title",
nothingHere: "GuildAndFriendRemovalAlerts-styles-nothingHere",
clearButton: "GuildAndFriendRemovalAlerts-styles-clearButton",
floatRight: "GuildAndFriendRemovalAlerts-styles-floatRight"
};
StyleLoader.append(module.id, ___CSS_LOADER_EXPORT___.toString());
const __WEBPACK_DEFAULT_EXPORT__ = Object.assign(___CSS_LOADER_EXPORT___, ___CSS_LOADER_EXPORT___.locals);
},
234: (__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
default: () => GuildAndFriendRemovalAlerts
});
const external_BasePlugin_namespaceObject = BasePlugin;
var external_BasePlugin_default = __webpack_require__.n(external_BasePlugin_namespaceObject);
const external_PluginApi_namespaceObject = PluginApi;
var external_BdApi_React_ = __webpack_require__(832);
var external_BdApi_React_default = __webpack_require__.n(external_BdApi_React_);
var React = __webpack_require__(832);
function _extends() {
_extends = Object.assign || function(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source)
if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
}
return target;
};
return _extends.apply(this, arguments);
}
const createUpdateWrapper = (Component, valueProp = "value", changeProp = "onChange", valueIndex = 0) => props => {
const [value, setValue] = React.useState(props[valueProp]);
return React.createElement(Component, _extends({}, props, {
[valueProp]: value,
[changeProp]: (...args) => {
const value = args[valueIndex];
if ("function" === typeof props[changeProp]) props[changeProp](value);
setValue(value);
}
}));
};
const hooks_createUpdateWrapper = createUpdateWrapper;
const package_namespaceObject = JSON.parse('{"um":{"u2":"GuildAndFriendRemovalAlerts"}}');
function _defineProperty(obj, key, value) {
if (key in obj) Object.defineProperty(obj, key, {
value,
enumerable: true,
configurable: true,
writable: true
});
else obj[key] = value;
return obj;
}
class Settings {}
_defineProperty(Settings, "settings", external_PluginApi_namespaceObject.PluginUtilities.loadSettings(package_namespaceObject.um.u2, {}));
_defineProperty(Settings, "get", ((key, defaultValue) => Settings.settings[key] ?? defaultValue));
_defineProperty(Settings, "set", ((key, value) => {
Settings.settings[key] = value;
Settings.save();
}));
_defineProperty(Settings, "save", (() => external_PluginApi_namespaceObject.PluginUtilities.saveSettings(package_namespaceObject.um.u2, Settings.settings)));
const Switch = hooks_createUpdateWrapper(external_PluginApi_namespaceObject.WebpackModules.getByDisplayName("SwitchItem"));
function SettingsPanel() {
return external_BdApi_React_default().createElement("div", {
className: "gafSettingsPanel"
}, external_BdApi_React_default().createElement(Switch, {
note: "Whether or not to automatically show the modal when a guild/friend is removed.",
value: Settings.get("showModal", true),
onChange: value => Settings.set("showModal", value)
}, "Auto Show Modal"), external_BdApi_React_default().createElement(Switch, {
note: "Whether or not to show desktop notifications when a guild/friend is removed.",
value: Settings.get("showDeskNotifs", false),
onChange: value => Settings.set("showDeskNotifs", value)
}, "Show Desktop Notifications"));
}
const stores_namespaceObject = Modules["@discord/stores"];
var stores_default = __webpack_require__.n(stores_namespaceObject);
const modal_namespaceObject = Modules["@discord/modal"];
const external_StyleLoader_namespaceObject = StyleLoader;
var external_StyleLoader_default = __webpack_require__.n(external_StyleLoader_namespaceObject);
var styles = __webpack_require__(377);
var item = __webpack_require__(86);
const utils_namespaceObject = Modules["@discord/utils"];
const PrivateModule = external_PluginApi_namespaceObject.WebpackModules.getByProps("openPrivateChannel");
function Item({
title,
description,
icon,
clickId,
closeModal
}) {
return external_BdApi_React_default().createElement("div", {
className: (0, utils_namespaceObject.joinClassNames)(item.Z.item, item.Z.userItem),
onClick: () => {
PrivateModule.openPrivateChannel(clickId);
closeModal();
}
}, external_BdApi_React_default().createElement("img", {
className: item.Z.image,
src: icon || "/assets/485a854d5171c8dc98088041626e6fea.png",
alt: "image"
}), external_BdApi_React_default().createElement("div", {
className: item.Z.inner
}, external_BdApi_React_default().createElement("div", {
className: item.Z.title
}, title), description?.length ? external_BdApi_React_default().createElement("div", {
className: item.Z.description
}, description) : null));
}
const contextmenu_namespaceObject = Modules["@discord/contextmenu"];
var contextmenu_default = __webpack_require__.n(contextmenu_namespaceObject);
const constants_namespaceObject = Modules["@discord/constants"];
const modules_namespaceObject = Modules["@discord/modules"];
var GuildAndFriendRemovalAlerts_React = __webpack_require__(832);
function GuildAndFriendRemovalAlerts_extends() {
GuildAndFriendRemovalAlerts_extends = Object.assign || function(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source)
if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
}
return target;
};
return GuildAndFriendRemovalAlerts_extends.apply(this, arguments);
}
function GuildAndFriendRemovalAlerts_defineProperty(obj, key, value) {
if (key in obj) Object.defineProperty(obj, key, {
value,
enumerable: true,
configurable: true,
writable: true
});
else obj[key] = value;
return obj;
}
const {
getFriendIDs
} = external_PluginApi_namespaceObject.WebpackModules.getByProps("getFriendIDs");
const HomeButton = external_PluginApi_namespaceObject.WebpackModules.getByProps("HomeButton");
const events = ["GUILD_CREATE", "GUILD_DELETE", "GUILD_UPDATE", "RELATIONSHIP_ADD", "RELATIONSHIP_REMOVE", "RELATIONSHIP_UPDATE", "FRIEND_REQUEST_ACCEPTED"];
class GuildAndFriendRemovalAlerts extends(external_BasePlugin_default()) {
constructor(...args) {
super(...args);
GuildAndFriendRemovalAlerts_defineProperty(this, "history", {
guilds: Settings.get("removedGuildHistory", []),
friends: Settings.get("removedFriendHistory", []),
update: () => {
Settings.set("removedGuildHistory", this.history.guilds);
Settings.set("removedFriendHistory", this.history.friends);
},
clear: () => {
Object.assign(this.history, {
guilds: [],
friends: []
});
this.history.update();
}
});
GuildAndFriendRemovalAlerts_defineProperty(this, "snapshots", {
guilds: Settings.get("guildsSnapshot", []),
friends: Settings.get("friendsSnapshot", []),
update: ({
guilds,
friends
}) => {
Settings.set("guildsSnapshot", this.snapshots.guilds = guilds);
Settings.set("friendsSnapshot", this.snapshots.friends = friends);
}
});
GuildAndFriendRemovalAlerts_defineProperty(this, "getSettingsPanel", (() => GuildAndFriendRemovalAlerts_React.createElement(SettingsPanel, null)));
GuildAndFriendRemovalAlerts_defineProperty(this, "main", (() => {
console.log("main()");
const guilds = Object.keys(stores_default().Guilds.getGuilds()).map((guildId => this.serializeGuild(guildId)));
const friends = getFriendIDs().map((uid => this.serializeUser(uid)));
const removedGuilds = this.snapshots.guilds.filter((snapshot => !guilds.some((guild => snapshot.id === guild.id))));
const removedFriends = this.snapshots.friends.filter((snapshot => !friends.some((friend => snapshot.id === friend.id))));
if (removedGuilds.length || removedFriends.length) {
if (Settings.get("showModal", true)) this.openModal(removedGuilds, removedFriends);
removedGuilds.forEach((guild => this.history.guilds.unshift(guild)));
removedFriends.forEach((friend => this.history.friends.unshift(friend)));
if (Settings.get("showDeskNotifs", false)) {
removedGuilds.forEach((guild => new Notification(guild.name, {
silent: true,
body: "Server removed",
icon: guild.iconUrl
})));
removedFriends.forEach((friend => new Notification(friend.name, {
silent: true,
body: "Friend removed",
icon: friend.avatarUrl
})));
}
}
if (guilds.length !== this.snapshots.guilds.length || friends.length !== this.snapshots.friends.length) {
this.history.update();
this.snapshots.update({
guilds,
friends
});
console.log("update");
}
}));
}
onStart() {
const PatchedHomeButton = ({
originalType,
...props
}) => {
const returnValue = Reflect.apply(originalType, this, [props]);
try {
returnValue.props.onContextMenu = e => {
(0, contextmenu_namespaceObject.openContextMenu)(e, (() => GuildAndFriendRemovalAlerts_React.createElement(contextmenu_default().default, {
navId: package_namespaceObject.um.u2,
onClose: contextmenu_namespaceObject.closeContextMenu
}, GuildAndFriendRemovalAlerts_React.createElement(contextmenu_namespaceObject.MenuItem, {
label: "View GFR Logs",
action: () => this.openModal(this.history.guilds, this.history.friends, true),
id: package_namespaceObject.um.u2 + "-logs"
}), GuildAndFriendRemovalAlerts_React.createElement(contextmenu_namespaceObject.MenuItem, {
label: "View All Guilds and Friends",
action: () => this.openModal(),
id: package_namespaceObject.um.u2 + "-view-all"
}))));
};
} catch (error) {
external_PluginApi_namespaceObject.Logger.error("Error in DefaultHomeButton patch:", error);
}
return returnValue;
};
external_PluginApi_namespaceObject.Patcher.after(HomeButton, "HomeButton", ((_, __, component) => {
const originalType = component.type;
component.type = PatchedHomeButton;
Object.assign(component.props, {
originalType
});
}));
external_StyleLoader_default().inject();
events.forEach((eventType => modules_namespaceObject.Dispatcher.subscribe(eventType, this.main)));
this.main();
}
serializeGuild(guildId) {
const serialized = {
id: guildId,
invalid: true,
name: "Unknown Guild",
iconUrl: "/assets/1531b79c2f2927945582023e1edaaa11.png"
};
try {
const guild = stores_default().Guilds.getGuild(guildId);
if (guild) Object.assign(serialized, {
invalid: false,
name: guild.name,
ownerId: guild.ownerId,
iconUrl: "function" === typeof guild.getIconURL ? guild.getIconURL("webp") : serialized.iconUrl
});
} finally {}
return serialized;
}
serializeUser(userId) {
const serialized = {
id: userId,
invalid: true,
tag: "Unknown User",
avatarURL: "/assets/1cbd08c76f8af6dddce02c5138971129.png"
};
try {
const user = stores_default().Users.getUser(userId);
if (user) Object.assign(serialized, {
invalid: false,
tag: user.tag,
avatarUrl: "function" === typeof user.getAvatarURL ? user.getAvatarURL("webp") : serialized.avatarUrl
});
} finally {}
return serialized;
}
openModal(guilds, friends, showClearButton = false) {
if (!guilds && !friends) {
guilds = this.snapshots.guilds;
friends = this.snapshots.friends;
}
const clearLogs = () => BdApi.showConfirmationModal("Are you sure?", "Do you really want to clear the logs?\nThis action cannot be undone.", {
danger: true,
onConfirm: () => {
this.history.clear();
(0, modal_namespaceObject.closeModal)(modalId);
},
confirmText: "Clear"
});
const modalId = (0, modal_namespaceObject.openModal)((props => GuildAndFriendRemovalAlerts_React.createElement(modal_namespaceObject.ModalRoot, GuildAndFriendRemovalAlerts_extends({}, props, {
size: "large",
className: styles.Z.modal
}), GuildAndFriendRemovalAlerts_React.createElement(modal_namespaceObject.ModalHeader, null, "Guild And Friend Removal Alerts ", GuildAndFriendRemovalAlerts_React.createElement(modal_namespaceObject.ModalCloseButton, {
className: styles.Z.floatRight,
onClick: props.onClose
})), GuildAndFriendRemovalAlerts_React.createElement(modal_namespaceObject.ModalContent, props, guilds?.length || friends?.length ? GuildAndFriendRemovalAlerts_React.createElement(GuildAndFriendRemovalAlerts_React.Fragment, null, showClearButton ? GuildAndFriendRemovalAlerts_React.createElement("div", {
className: styles.Z.clearButton,
onClick: clearLogs
}, "Clear Logs") : null, guilds?.length ? GuildAndFriendRemovalAlerts_React.createElement("div", {
className: styles.Z.itemContainer
}, GuildAndFriendRemovalAlerts_React.createElement("div", {
className: styles.Z.title
}, "Guilds - ", GuildAndFriendRemovalAlerts_React.createElement("span", {
style: {
color: "var(--control-brand-foreground-new)"
}
}, guilds.length)), GuildAndFriendRemovalAlerts_React.createElement("div", {
className: styles.Z.items
}, guilds.map(((guild, i) => GuildAndFriendRemovalAlerts_React.createElement(Item, {
key: i,
title: guild.name,
description: "Owner ID - " + guild.ownerId,
icon: guild.iconUrl,
clickId: guild.ownerId,
closeModal: props.onClose
}))))) : null, friends?.length ? GuildAndFriendRemovalAlerts_React.createElement("div", {
className: styles.Z.itemContainer
}, GuildAndFriendRemovalAlerts_React.createElement("div", {
className: styles.Z.title
}, "Friends - ", GuildAndFriendRemovalAlerts_React.createElement("span", {
style: {
color: "var(--control-brand-foreground-new)"
}
}, friends.length)), GuildAndFriendRemovalAlerts_React.createElement("div", {
className: styles.Z.items
}, friends.map(((friend, i) => GuildAndFriendRemovalAlerts_React.createElement(Item, {
key: i,
title: friend.tag,
icon: friend.avatarUrl,
clickId: friend.id,
closeModal: props.onClose
}))))) : null) : GuildAndFriendRemovalAlerts_React.createElement("div", {
className: styles.Z.nothingHere
}, "No logs to show.")))));
}
onStop() {
external_PluginApi_namespaceObject.Patcher.unpatchAll();
external_StyleLoader_default().remove();
events.forEach((eventType => modules_namespaceObject.Dispatcher.unsubscribe(eventType, this.main)));
}
}
},
246: module => {
module.exports = function(cssWithMappingToString) {
var list = [];
list.toString = function() {
return this.map((function(item) {
var content = cssWithMappingToString(item);
if (item[2]) return "@media ".concat(item[2], " {").concat(content, "}");
return content;
})).join("");
};
list.i = function(modules, mediaQuery, dedupe) {
if ("string" === typeof modules) modules = [
[null, modules, ""]
];
var alreadyImportedModules = {};
if (dedupe)
for (var i = 0; i < this.length; i++) {
var id = this[i][0];
if (null != id) alreadyImportedModules[id] = true;
}
for (var _i = 0; _i < modules.length; _i++) {
var item = [].concat(modules[_i]);
if (dedupe && alreadyImportedModules[item[0]]) continue;
if (mediaQuery)
if (!item[2]) item[2] = mediaQuery;
else item[2] = "".concat(mediaQuery, " and ").concat(item[2]);
list.push(item);
}
};
return list;
};
},
832: module => {
module.exports = BdApi.React;
}
};
var __webpack_module_cache__ = {};
function __webpack_require__(moduleId) {
var cachedModule = __webpack_module_cache__[moduleId];
if (void 0 !== cachedModule) return cachedModule.exports;
var module = __webpack_module_cache__[moduleId] = {
id: moduleId,
exports: {}
};
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
(() => {
__webpack_require__.n = module => {
var getter = module && module.__esModule ? () => module["default"] : () => module;
__webpack_require__.d(getter, {
a: getter
});
return getter;
};
})();
(() => {
__webpack_require__.d = (exports, definition) => {
for (var key in definition)
if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, {
enumerable: true,
get: definition[key]
});
};
})();
(() => {
__webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
})();
(() => {
__webpack_require__.r = exports => {
if ("undefined" !== typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports, Symbol.toStringTag, {
value: "Module"
});
Object.defineProperty(exports, "__esModule", {
value: true
});
};
})();
var __webpack_exports__ = __webpack_require__(234);
module.exports.LibraryPluginHack = __webpack_exports__;
})();
const PluginExports = module.exports.LibraryPluginHack;
return PluginExports?.__esModule ? PluginExports.default : PluginExports;
}
module.exports = window.hasOwnProperty("ZeresPluginLibrary") ?
buildPlugin(window.ZeresPluginLibrary.buildPlugin(config)) :
class {
getName() {
return config.info.name;
}
getAuthor() {
return config.info.authors.map(a => a.name).join(", ");
}
getDescription() {
return `${config.info.description}. __**ZeresPluginLibrary was not found! This plugin will not work!**__`;
}
getVersion() {
return config.info.version;
}
load() {
BdApi.showConfirmationModal(
"Library plugin is needed",
[`The library plugin needed for ${config.info.name} is missing. Please click Download to install it.`], {
confirmText: "Download",
cancelText: "Cancel",
onConfirm: () => {
require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (error, response, body) => {
if (error) return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js");
await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
});
}
}
);
}
start() {}
stop() {}
};
/*@end@*/
================================================
FILE: GuildAndFriendRemovalAlerts/README.md
================================================
# GuildAndFriendRemovalAlerts
> Displays alerts when you are kicked/banned from a server, a server is deleted, and when a friend removes you.
<hr/>
<br/>
<span>Made with <img src="https://discord.com/assets/0483f2b648dcc986d01385062052ae1c.svg" width="15" /> by <a href="https://github.com/Kyza/bdbuilder">BDBuilder</a></span>
================================================
FILE: GuildAndFriendRemovalAlerts/src/components/item.jsx
================================================
import React from "react";
import styles from "./item.scss";
import { joinClassNames } from "@discord/utils";
import { WebpackModules } from "@zlibrary";
const PrivateModule = WebpackModules.getByProps("openPrivateChannel");
export default function Item({ title, description, icon, clickId, closeModal }) {
const handleClick = () => {
PrivateModule.openPrivateChannel(clickId);
closeModal();
};
return (
<div className={joinClassNames(styles.item, styles.userItem)} onClick={handleClick}>
<img className={styles.image} src={icon || "/assets/485a854d5171c8dc98088041626e6fea.png"} alt="image"/>
<div className={styles.inner}>
<div className={styles.title}>{ title }</div>
{ description?.length ? <div className={styles.description}>{ description }</div> : null }
</div>
</div>
);
}
================================================
FILE: GuildAndFriendRemovalAlerts/src/components/item.scss
================================================
.item {
display: flex;
margin: 5px;
padding: 10px;
cursor: pointer;
border-radius: 5px;
&:hover {
background-color: var(--background-modifier-hover);
}
.image {
width: 60px;
height: 60px;
border-radius: 5px;
}
.inner {
display: flex;
flex-direction: column;
padding: 5px;
margin-left: 10px;
.title {
margin: auto 0;
max-width: 400px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-weight: bolder;
font-size: 1.1rem;
}
.description {
margin: auto 0;
}
}
&.userItem {
}
&.guildItem {
}
}
================================================
FILE: GuildAndFriendRemovalAlerts/src/components/settings.jsx
================================================
import React from "react";
import createUpdateWrapper from "common/hooks/createUpdateWrapper";
import { WebpackModules } from "@zlibrary";
import Settings from "../modules/settings";
const Switch = createUpdateWrapper(WebpackModules.getByDisplayName("SwitchItem"));
export default function SettingsPanel() {
return (
<div className="gafSettingsPanel">
<Switch note="Whether or not to automatically show the modal when a guild/friend is removed."
value={Settings.get("showModal", true)}
onChange={value => Settings.set("showModal", value)}>Auto Show Modal</Switch>
<Switch note="Whether or not to show desktop notifications when a guild/friend is removed."
value={Settings.get("showDeskNotifs", false)}
onChange={value => Settings.set("showDeskNotifs", value)}>Show Desktop Notifications</Switch>
</div>
);
}
================================================
FILE: GuildAndFriendRemovalAlerts/src/index.js
================================================
import BasePlugin from "@zlibrary/plugin";
import { Logger, Patcher, PluginUtilities, ReactComponents, WebpackModules } from "@zlibrary";
import SettingsPanel from "./components/settings";
import Settings from "./modules/settings";
import Stores from "@discord/stores";
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal, closeModal } from "@discord/modal";
import stylesheet from "styles";
import styles from "./styles.scss";
import Item from "./components/item";
import ContextMenu, { closeContextMenu, MenuItem, openContextMenu } from "@discord/contextmenu";
import pkg from "./package.json";
import { ActionTypes } from "@discord/constants";
import { Dispatcher } from "@discord/modules";
const { getFriendIDs } = WebpackModules.getByProps("getFriendIDs");
const HomeButton = WebpackModules.getByProps("HomeButton");
const events = [ ActionTypes.GUILD_CREATE, ActionTypes.GUILD_DELETE, ActionTypes.GUILD_UPDATE, ActionTypes.RELATIONSHIP_ADD,
ActionTypes.RELATIONSHIP_REMOVE, ActionTypes.RELATIONSHIP_UPDATE, ActionTypes.FRIEND_REQUEST_ACCEPTED ];
export default class GuildAndFriendRemovalAlerts extends BasePlugin {
history = {
guilds: Settings.get("removedGuildHistory", []),
friends: Settings.get("removedFriendHistory", []),
update: () => {
Settings.set("removedGuildHistory", this.history.guilds);
Settings.set("removedFriendHistory", this.history.friends);
},
clear: () => {
Object.assign(this.history, {
guilds: [],
friends: []
});
this.history.update();
}
};
snapshots = {
guilds: Settings.get("guildsSnapshot", []),
friends: Settings.get("friendsSnapshot", []),
update: ({ guilds, friends }) => {
Settings.set("guildsSnapshot", this.snapshots.guilds = guilds);
Settings.set("friendsSnapshot", this.snapshots.friends = friends);
}
};
getSettingsPanel = () => <SettingsPanel/>;
onStart() {
const PatchedHomeButton = ({originalType, ...props}) => {
const returnValue = Reflect.apply(originalType, this, [props]);
try {
returnValue.props.onContextMenu = e => {
openContextMenu(e, () => (
<ContextMenu.default navId={pkg.info.name} onClose={closeContextMenu}>
<MenuItem label="View GFR Logs" action={() => this.openModal(this.history.guilds, this.history.friends, true)} id={pkg.info.name + "-logs"}/>
<MenuItem label="View All Guilds and Friends" action={() => this.openModal()} id={pkg.info.name + "-view-all"}/>
</ContextMenu.default>
));
};
} catch (error) {
Logger.error("Error in DefaultHomeButton patch:", error);
}
return returnValue;
}
Patcher.after(HomeButton, "HomeButton", (_, __, component) => {
const originalType = component.type;
component.type = PatchedHomeButton;
Object.assign(component.props, {originalType: originalType});
});
stylesheet.inject();
events.forEach(eventType => Dispatcher.subscribe(eventType, this.main));
this.main();
}
serializeGuild(guildId) {
const serialized = { id: guildId, invalid: true, name: "Unknown Guild", iconUrl: "/assets/1531b79c2f2927945582023e1edaaa11.png" };
try {
const guild = Stores.Guilds.getGuild(guildId);
if (guild) {
Object.assign(serialized, {
invalid: false,
name: guild.name,
ownerId: guild.ownerId,
iconUrl: typeof(guild.getIconURL) === "function" ? guild.getIconURL("webp") : serialized.iconUrl
});
}
} finally { }
return serialized;
}
serializeUser(userId) {
const serialized = { id: userId, invalid: true, tag: "Unknown User", avatarURL: "/assets/1cbd08c76f8af6dddce02c5138971129.png" };
try {
const user = Stores.Users.getUser(userId);
if (user) {
Object.assign(serialized, {
invalid: false,
tag: user.tag,
avatarUrl: typeof(user.getAvatarURL) === "function" ? user.getAvatarURL("webp") : serialized.avatarUrl
});
}
} finally { }
return serialized;
}
main = () => {
console.log("main()");
const guilds = Object.keys(Stores.Guilds.getGuilds()).map(guildId => this.serializeGuild(guildId));
const friends = getFriendIDs().map(uid => this.serializeUser(uid));
const removedGuilds = this.snapshots.guilds.filter(snapshot => !guilds.some(guild => snapshot.id === guild.id));
const removedFriends = this.snapshots.friends.filter(snapshot => !friends.some(friend => snapshot.id === friend.id));
if (removedGuilds.length || removedFriends.length) {
if (Settings.get("showModal", true))
this.openModal(removedGuilds, removedFriends);
removedGuilds.forEach(guild => this.history.guilds.unshift(guild));
removedFriends.forEach(friend => this.history.friends.unshift(friend));
if (Settings.get("showDeskNotifs", false)) {
removedGuilds.forEach(guild =>
new Notification(guild.name, {
silent: true,
body: "Server removed",
icon: guild.iconUrl
}));
removedFriends.forEach(friend =>
new Notification(friend.name, {
silent: true,
body: "Friend removed",
icon: friend.avatarUrl
}));
}
}
if (guilds.length !== this.snapshots.guilds.length || friends.length !== this.snapshots.friends.length) {
this.history.update();
this.snapshots.update({ guilds, friends });
console.log("update");
}
};
openModal(guilds, friends, showClearButton = false) {
if (!guilds && !friends) {
guilds = this.snapshots.guilds;
friends = this.snapshots.friends;
}
const clearLogs = () =>
BdApi.showConfirmationModal("Are you sure?", "Do you really want to clear the logs?\nThis action cannot be undone.", {
danger: true,
onConfirm: () => {
this.history.clear();
closeModal(modalId);
},
confirmText: "Clear"
});
const modalId = openModal(props => (
<ModalRoot {...props} size="large" className={styles.modal}>
<ModalHeader>Guild And Friend Removal Alerts <ModalCloseButton className={styles.floatRight} onClick={props.onClose}/></ModalHeader>
<ModalContent {...props}>
{ guilds?.length || friends?.length ? (
<React.Fragment>
{ showClearButton ? (
<div className={styles.clearButton} onClick={clearLogs}>
Clear Logs
</div>
) : null }
{ guilds?.length ? (
<div className={styles.itemContainer}>
<div className={styles.title}>Guilds - <span style={{ color: "var(--control-brand-foreground-new)" }}>{guilds.length}</span></div>
<div className={styles.items}>
{ guilds.map((guild, i) =>
<Item key={i} title={guild.name} description={"Owner ID - " + guild.ownerId} icon={guild.iconUrl} clickId={guild.ownerId} closeModal={props.onClose}/>) }
</div>
</div>
) : null }
{ friends?.length ? (
<div className={styles.itemContainer}>
<div className={styles.title}>Friends - <span style={{ color: "var(--control-brand-foreground-new)" }}>{friends.length}</span></div>
<div className={styles.items}>
{ friends.map((friend, i) =>
<Item key={i} title={friend.tag} icon={friend.avatarUrl} clickId={friend.id} closeModal={props.onClose}/>) }
</div>
</div>
) : null }
</React.Fragment>
) : (
<div className={styles.nothingHere}>
No logs to show.
</div>
) }
</ModalContent>
</ModalRoot>
));
}
onStop() {
Patcher.unpatchAll();
stylesheet.remove();
events.forEach(eventType => Dispatcher.unsubscribe(eventType, this.main));
}
}
================================================
FILE: GuildAndFriendRemovalAlerts/src/modules/settings.js
================================================
import pkg from "../package.json";
import { PluginUtilities } from "@zlibrary";
export default class Settings {
static settings = PluginUtilities.loadSettings(pkg.info.name, {});
static get = (key, defaultValue) => Settings.settings[key] ?? defaultValue;
static set = (key, value) => {
Settings.settings[key] = value;
Settings.save();
};
static save = () => PluginUtilities.saveSettings(pkg.info.name, Settings.settings);
}
================================================
FILE: GuildAndFriendRemovalAlerts/src/package.json
================================================
{
"info": {
"name": "GuildAndFriendRemovalAlerts",
"version": "3.0.0",
"description": "Displays alerts when you are kicked/banned from a server, a server is deleted, and when a friend removes you.",
"authors": [
{
"name": "Metalloriff",
"discord_id": "264163473179672576",
"github_username": "Metalloriff"
}
],
"github": "https://github.com/Metalloriff/BetterDiscordPlugins/GuildAndFriendRemovalAlerts",
"github_raw": "https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/GuildAndFriendRemovalAlerts/GuildAndFriendRemovalAlerts.plugin.js",
"website": "https://metalloriff.github.io/toms-discord-stuff/#/",
"donate": "https://paypal.me/israelboone",
"invite": "yNqzuJa"
},
"changelog": [{
"title": "3.0 rewrite",
"type": "fixed",
"items": [
"This plugin has been rewritten. Functionality is simliar, and settings and data should still be valid.",
"If you experience any bugs, please contact me."
]
}],
"build": {
"zlibrary": true,
"copy": true,
"production": false,
"scssHash": false,
"alias": {
"components": "components/index.js"
},
"release": {
"source": true,
"readme": true,
"public": true,
"contributors": null
}
}
}
================================================
FILE: GuildAndFriendRemovalAlerts/src/styles.scss
================================================
.modal {
color: white;
.itemContainer {
.title {
margin: 20px;
font-size: 1.2rem;
font-weight: bolder;
}
.items {
}
}
.nothingHere {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
font-size: 2rem;
font-weight: bolder;
opacity: 0.5;
}
.clearButton {
margin-top: 20px;
background-color: rgba(237, 66, 69, 0.1);
color: #ff6666;
border-radius: 5px;
padding: 15px 10px;
text-align: center;
cursor: pointer;
}
}
.floatRight {
margin-left: auto;
}
================================================
FILE: GuildAndFriendRemovalAlerts.plugin.js
================================================
/**
* @name GuildAndFriendRemovalAlerts
* @invite yNqzuJa
* @authorLink https://github.com/Metalloriff
* @donate https://www.paypal.me/israelboone
* @website https://metalloriff.github.io/toms-discord-stuff/
* @source https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/GuildAndFriendRemovalAlerts.plugin.js
*/
const name = "GuildAndFriendRemovalAlerts";
const newUrl = "https://github.com/Metalloriff/BetterDiscordPlugins/tree/master/" + name;
const rawUrl = `https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/${name}/${name}.plugin.js`;
module.exports = (() => {
const config = {
info: {
name: "0 GuildAndFriendRemovalAlerts OUTDATED",
authors: [{
name: "Metalloriff",
discord_id: "264163473179672576",
github_username: "metalloriff",
twitter_username: "Metalloriff"
}],
version: "999.999.999",
description: `Please delete this plugin and download the new one at ${newUrl}.`
}
};
return class {
getName = () => config.info.name;
getAuthor = () => config.info.authors[0].name;
getDescription = () => config.info.description;
getVersion = () => config.info.version;
load() {
BdApi.showConfirmationModal("Outdated Plugin", `This version of ${name} is outdated, consider installing the newest one by clicking the "Update Now" button.`, {
onConfirm: () => {
const https = require("https");
const fs = require("fs");
const path = require("path");
https.get(rawUrl, res => {
const chunks = [];
res.on("data", chunk => chunks.push(chunk));
res.on("end", () => {
try {
fs.writeFileSync(path.resolve(BdApi.Plugins.folder, path.basename(rawUrl)), chunks.join(""), "utf8");
} catch (error) {
console.error(name, error);
BdApi.showToast("Failed to download new update - check the console for details", { type: "error" });
}
});
});
}
});
}
start() { }
stop() { }
};
})();
================================================
FILE: GuildCounter.plugin.js
================================================
//META{"name":"GuildCounter","website":"https://metalloriff.github.io/toms-discord-stuff/","source":"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/GuildCounter.plugin.js"}*//
class GuildCounter {
getName() { return "Guild Counter"; }
getDescription() { return "Displays a guild counter below the online friend counter."; }
getVersion() { return "1.0.4"; }
getAuthor() { return "Metalloriff"; }
load() {}
start() {
let libLoadedEvent = () => {
try{ this.onLibLoaded(); }
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); } }
};
let lib = document.getElementById("NeatoBurritoLibrary");
if(!lib) {
lib = document.createElement("script");
lib.id = "NeatoBurritoLibrary";
lib.type = "text/javascript";
lib.src = "https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js";
document.head.appendChild(lib);
}
this.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);
if(typeof window.NeatoLib !== "undefined") libLoadedEvent();
else lib.addEventListener("load", libLoadedEvent);
}
onLibLoaded(){
NeatoLib.Updates.check(this);
(this.guildsScroller = document.getElementsByClassName(NeatoLib.getClass("unreadMentionsBar", "scroller"))[0]).addEventListener("DOMNodeInserted", this.count = () => {
if (typeof event !== "undefined" && event.relatedNode.id === "gc-counter") return;
let existing = document.getElementById("gc-counter"), count = Object.keys(NeatoLib.Modules.get("getGuilds").getGuilds()).length;
if(existing) existing.innerText = count + " guilds";
else 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" }));
});
this.count();
NeatoLib.Events.onPluginLoaded(this);
}
stop() {
this.guildsScroller.removeEventListener("DOMNodeInserted", this.count);
document.getElementById("gc-counter").remove();
}
}
================================================
FILE: IdleGuildlistScroller.plugin.js
================================================
//META{"name":"IdleGuildlistScroller"}*//
class IdleGuildlistScroller {
getName() { return "Idle Guildlist Scroller"; }
getDescription() { return "Automatically scrolls to the top of the guilds list after the specified amount of time that your mouse isn't over it."; }
getVersion() { return "0.1.1"; }
getAuthor() { return "Metalloriff"; }
load() {}
getSettingsPanel(){
return "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>";
}
static saveSettings(){
IdleGuildlistScroller.delay = document.getElementById("igs-delay").value;
IdleGuildlistScroller.includeChannels = document.getElementById("igs-includeChannels").checked;
PluginUtilities.saveData("IdleGuildlistScroller", "settings", {delay : IdleGuildlistScroller.delay, includeChannels : IdleGuildlistScroller.includeChannels});
PluginUtilities.showToast("Settings saved! You will need to switch channels for them to take effect.");
}
static resetSettings(){
IdleGuildlistScroller.delay = 1000;
IdleGuildlistScroller.includeChannels = true;
PluginUtilities.showToast("Settings reset! You will still have to save and update.");
}
start() {
var libraryScript = document.getElementById('zeresLibraryScript');
if (!libraryScript) {
libraryScript = document.createElement("script");
libraryScript.setAttribute("type", "text/javascript");
libraryScript.setAttribute("src", "https://rauenzi.github.io/BetterDiscordAddons/Plugins/PluginLibrary.js");
libraryScript.setAttribute("id", "zeresLibraryScript");
document.head.appendChild(libraryScript);
}
if (typeof window.ZeresLibrary !== "undefined") this.initialize();
else libraryScript.addEventListener("load", () => { this.initialize(); });
}
initialize(){
PluginUtilities.checkForUpdate(this.getName(), this.getVersion(), "https://github.com/Metalloriff/BetterDiscordPlugins/raw/master/IdleGuildlistScroller.plugin.js");
IdleGuildlistScroller.resetSettings();
var data = PluginUtilities.loadData("IdleGuildlistScroller", "settings", {delay : 1000, includeChannels : true});
IdleGuildlistScroller.delay = data["delay"];
IdleGuildlistScroller.includeChannels = data["includeChannels"];
this.guildsList.addEventListener("mouseenter", this.onHover, false);
this.guildsList.addEventListener("mouseleave", this.offHover, false);
this.onSwitch();
}
onSwitch(){
if(IdleGuildlistScroller.includeChannels && this.channelList){
this.channelList.addEventListener("mouseenter", this.onHover, false);
this.channelList.addEventListener("mouseleave", this.offHover, false);
}else if(this.channelList){
this.channelList.removeEventListener("mouseenter", this.onHover);
this.channelList.removeEventListener("mouseleave", this.offHover);
}
}
onHover(){
if(IdleGuildlistScroller.sttFunc)
clearTimeout(IdleGuildlistScroller.sttFunc);
}
offHover(){
IdleGuildlistScroller.sttFunc = setTimeout(function(){
$(document.getElementsByClassName("guilds scroller")[0]).animate({scrollTop : 0}, "fast");
}, IdleGuildlistScroller.delay);
}
stop() {
this.guildsList.removeEventListener("mouseenter", this.onHover);
this.guildsList.removeEventListener("mouseleave", this.offHover);
if(this.channelList){
this.channelList.removeEventListener("mouseenter", this.onHover);
this.channelList.removeEventListener("mouseleave", this.offHover);
}
}
get guildsList(){
return document.getElementsByClassName("guilds-wrapper")[0];
}
get channelList(){
return document.getElementsByClassName("scroller-fzNley scroller-NXV0-d")[0];
}
}
================================================
FILE: ImageBrowser.plugin.js
================================================
//META{"name":"ImageBrowser","website":"https://metalloriff.github.io/toms-discord-stuff/","source":"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/ImageBrowser.plugin.js"}*//
class ImageBrowser {
getName() { return "ImageBrowser"; }
getDescription() { return "Displays a next and previous button on image popouts for browsing through images in a channel."; }
getVersion() { return "0.1.1"; }
getAuthor() { return "Metalloriff"; }
getChanges() {
return {
"0.1.1":
`
You can now use the arrow keys to navigate through images.
`
};
}
load() {}
start() {
let libLoadedEvent = () => {
try{ this.onLibLoaded(); }
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); } }
};
let lib = document.getElementById("NeatoBurritoLibrary");
if(!lib) {
lib = document.createElement("script");
lib.id = "NeatoBurritoLibrary";
lib.type = "text/javascript";
lib.src = "https://rawgit.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js";
document.head.appendChild(lib);
}
this.forceLoadTimeout = setTimeout(libLoadedEvent, 30000);
if(typeof window.NeatoLib !== "undefined") libLoadedEvent();
else lib.addEventListener("load", libLoadedEvent);
}
getSettingsPanel() {
setTimeout(() => {
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createNewTextField("Zoom speed", this.settings.zoomSpeed, e => {
if(isNaN(e.target.value)) return NeatoLib.showToast("Value must be a number", "error");
this.settings.zoomSpeed = e.target.value;
this.saveSettings();
}), this.getName());
NeatoLib.Settings.pushElement(NeatoLib.Settings.Elements.createToggleSwitch("Display image loading filter", this.settings.loadingFilter, () => {
this.settings.loadingFilter = !this.settings.loadingFilter;
this.saveSettings();
}), this.getName(), { hint : "Grayscale, blurred, slightly darker, when an image is loading" });
NeatoLib.Settings.pushChangelogElements(this);
}, 0);
return NeatoLib.Settings.Elements.pluginNameLabel(this.getName());
}
saveSettings() {
NeatoLib.Settings.save(this);
}
onLibLoaded() {
this.settings = NeatoLib.Settings.load(this, {
displayUpdateNotes : true,
zoomSpeed : 0.2,
loadingFilter : true
});
NeatoLib.Updates.check(this);
const classes = NeatoLib.getClasses(["imageWrapper"], false);
this.style = NeatoLib.injectCSS(`
.ib-arrow {
position: fixed;
top: calc(50% - 100px);
width: 200px;
height: 200px;
filter: invert(50%);
animation: ib-arrow-fade-in 1s;
transition: filter 0.3s;
z-index: 10000;
}
.ib-arrow:hover {
filter: invert(100%);
}
@keyframes ib-arrow-fade-in {
0%{opacity:0}
100%{opacity:1}
}
`);
if(this.settings.displayUpdateNotes) NeatoLib.Changelog.compareVersions(this.getName(), this.getChanges());
let images = [], selectedImage = -1, zoomLevel = 1;
document.addEventListener("keydown", this.keyDownEvent = e => {
if(e.key == "ArrowRight" && document.getElementById("ib-next-arrow")) document.getElementById("ib-next-arrow").click();
else if(e.key == "ArrowLeft" && document.getElementById("ib-prev-arrow")) document.getElementById("ib-prev-arrow").click();
});
const selectImage = (wrapper, idx, initial) => {
selectedImage = idx;
if(!initial) images = Array.filter(document.getElementsByClassName("chat")[0].getElementsByTagName("img"), e => e.parentElement.className.includes("imageWrapper"));
const image = wrapper.lastChild;
image.style = "";
image.addEventListener("mousewheel", e => {
if(e.wheelDelta > 0) zoomLevel += this.settings.zoomSpeed;
else if(zoomLevel > 1) zoomLevel -= this.settings.zoomSpeed;
else {
clicked = false;
image.style.top = 0;
image.style.left = 0;
image.style.transform = "";
return;
}
if(zoomLevel > 1) {
image.draggable = false;
image.style.transform = `scale(${zoomLevel})`;
} else image.draggable = true;
});
let clicked = false, origPos;
image.addEventListener("mousedown", function(e) {
origPos = { x : e.clientX, y : e.clientY, top : parseInt(image.style.top), left : parseInt(image.style.left) };
clicked = true;
});
image.addEventListener("mouseup", function() { clicked = false; });
image.style.top = 0;
image.style.left = 0;
image.addEventListener("mousemove", function(e) {
if(!(clicked && zoomLevel > 1)) return;
image.style.top = (origPos.top - (origPos.y - e.clientY)) + "px";
image.style.left = (origPos.left - (origPos.x - e.clientX)) + "px";
});
let prevArrow = document.getElementById("ib-prev-arrow"), nextArrow = document.getElementById("ib-next-arrow");
if(images[idx - 1]) {
if(!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" }));
} else if(prevArrow) prevArrow.remove();
if(images[idx + 1]) {
if(!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" }));
} else if(nextArrow) nextArrow.remove();
image.src = images[idx].src;
if(this.settings.loadingFilter) image.style.filter = "grayscale(100%) brightness(0.8) blur(3px)";
images[idx].scrollIntoViewIfNeeded();
image.setAttribute("width", "");
image.setAttribute("height", "");
image.style.width = "";
image.style.height = "";
const resize = function() {
image.width *= 3;
const tryBegin = performance.now();
while((image.width > window.innerWidth * 0.65 || image.height > window.innerHeight * 0.65) && performance.now() - tryBegin < 500) image.width *= 0.9;
document.getElementById("app-mount").lastChild.lastChild.firstChild.style.width = image.width + "px";
document.getElementById("app-mount").lastChild.lastChild.firstChild.style.height = image.height + "px";
wrapper.style.width = image.width + "px";
wrapper.style.height = image.height + "px";
};
image.onload = function() {
resize();
image.src = images[idx].src.split("?")[0];
image.onload = e => {
resize();
image.style.transition = "all 0.1s";
e.target.style.filter = "";
};
};
if(image.complete) image.onload();
};
this.mutationObserver = new MutationObserver(async function(m) {
if(m[1] && m[1].addedNodes.length) {
images = Array.filter(document.getElementsByClassName("chat")[0].getElementsByTagName("img"), e => e.parentElement.className.includes("imageWrapper"));
const wrapper = m[1].addedNodes[0].getElementsByClassName(classes.imageWrapper)[0];
if(!wrapper) return;
const tryBegin = performance.now();
while(!wrapper.lastChild.src && performance.now() - tryBegin < 3000) await NeatoLib.Thread.sleep();
selectImage(wrapper, images.findIndex(i => i.src && i.src.split("?")[0] == wrapper.getElementsByTagName("img")[0].src.split("?")[0]), true);
}
});
this.mutationObserver.observe(document.getElementById("app-mount").lastChild, { childList : true });
NeatoLib.Events.onPluginLoaded(this);
}
stop() {
this.mutationObserver.disconnect();
this.style.destroy();
document.removeEventListener("keydown", this.keyDownEvent);
}
}
================================================
FILE: Lib/NeatoBurritoLibrary.js
================================================
var NeatoLib = {
version: "0.9.29",
parseVersion: function(version) {
let numbers = Array.from(version.split("."), n => parseInt(n)),
major = numbers[0],
minor = numbers[1],
patch = numbers[2];
return {
major: major,
minor: minor,
patch: patch,
compareTo: otherVersion => {
if (patch > otherVersion.patch || minor > otherVersion.minor || major > otherVersion.major) return "newer";
if (patch < otherVersion.patch || minor < otherVersion.minor || major < otherVersion.major) return "older";
return "equal";
}
};
},
hasRequiredLibVersion: function(plugin, requiredVersion) {
if (NeatoLib.parseVersion(NeatoLib.version).compareTo(NeatoLib.parseVersion(requiredVersion)) == "older") {
if (plugin.ready) plugin.ready = false;
const updateLibrary = () => {
const vm = require("vm");
fetch("https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js", { cache: "no-cache" }).then(r => r.text()).then(data => {
let lib = new vm.Script(data, {
filename: "NeatoBurritoLibrary.js",
displayErrors: true
});
new Promise(exec => exec(lib.runInThisContext())).then(() => {
NeatoLib.showToast(`[${plugin.getName()}]: Library updated!`, "success");
setTimeout(() => plugin.start(), 1000);
});
});
};
NeatoLib.showToast(`[${plugin.getName()}]: Library update required! Click this notification to update it.`, "error", {
timeout: 30000,
onClick: updateLibrary,
destroyOnClick: true
});
return false;
}
return true;
},
forceLibUpdate: function() {
const vm = require("vm");
fetch("https://raw.githubusercontent.com/Metalloriff/BetterDiscordPlugins/master/Lib/NeatoBurritoLibrary.js", { cache: "no-cache" }).then(r => r.text()).then(data => {
const lib = new vm.Script(data, { filename: "NeatoBurritoLibrary.js", displayErrors: true });
new Promise(e => e(lib.runInThisContext())).then(() => NeatoLib.showToast("Lib updated!", "success"));
});
},
Changelog: {
compareVersions: function(name, changes) {
var spacelessName = name.split(" ").join(""),
updateData = NeatoLib.Data.load("MetalloriffUpdateData", spacelessName, {}),
unreadChanges = [],
thisUpdateData = updateData[spacelessName],
first = false;
if (thisUpdateData != undefined) {
if (thisUpdateData.readChanges == undefined) thisUpdateData.readChanges = [];
for (var i in changes) {
if (!thisUpdateData.readChanges.includes(i)) {
unreadChanges.push(i);
thisUpdateData.readChanges.push(i);
}
}
} else {
updateData[spacelessName] = {
readChanges: Object.keys(changes)
};
first = true;
}
if (unreadChanges.length > 0 || first) {
NeatoLib.Changelog.createChangeWindow(name, unreadChanges, changes, updateData);
}
},
createChangeWindow: function(name, changes, allChanges, newUpdateData) {
let changeKeys = Object.keys(allChanges);
if (changeKeys.length == 0) {
NeatoLib.showToast("There are no updates notes for this plugin yet!", "error");
return;
}
let spacelessName = name.split(" ").join("");
if (document.getElementById(spacelessName + "-changelog")) document.getElementById(spacelessName + "-changelog").remove();
document.getElementsByClassName(NeatoLib.getClass("app"))[0].insertAdjacentHTML("beforeend", `
<div id="${spacelessName}-changelog">
<style>
.metalloriff-update-item {
padding: 10px;
}
.metalloriff-update-label {
color: white;
font-size: 35px;
}
.metalloriff-update-note {
color: white;
font-size: 25px;
opacity: 0.75;
line-height: 25px;
}
.metalloriff-changelog-backdrop {
opacity: 0.85;
background-color: black;
z-index: 1000;
position: fixed;
contain: strict;
bottom: 0;
left: 0;
top: 0;
right: 0;
}
.metalloriff-changelog-scroller {
width: 800px;
min-height: 800px;
max-height: 800px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
overflow-y: scroll;
background: #2f3136;
border-radius: 5px;
z-index: 10000;
}
#${spacelessName}-changelog *::-webkit-scrollbar {
max-width: 10px;
}
#${spacelessName}-changelog *::-webkit-scrollbar-track-piece {
background: transparent;
border: none;
border-radius: 5px;
}
#${spacelessName}-changelog *:hover::-webkit-scrollbar-track-piece {
background: #2F3136;
border-radius: 5px;
}
#${spacelessName}-changelog *::-webkit-scrollbar-thumb {
background: #1E2124;
border: none;
border-radius: 5px;
}
#${spacelessName}-changelog *::-webkit-scrollbar-button {
display: none;
}
.metalloriff-changelog-label {
flex: 1 1 auto;
text-align: center;
color: white;
padding-top: 10px;
font-size: 20px;
}
</style>
<div class="metalloriff-changelog-backdrop"></div>
<div class="metalloriff-changelog-scroller">
<div class="metalloriff-changelog-label">
<h2>${name} Update Notes</h2>
<br>
</div>
<div id="${spacelessName}-changelog-scroller"></div>
</div>
</div>
</div>
`);
document.getElementById(spacelessName + "-changelog").getElementsByClassName("metalloriff-changelog-backdrop")[0].addEventListener("click", () => {
if (newUpdateData != undefined) NeatoLib.Data.save("MetalloriffUpdateData", spacelessName, newUpdateData);
document.getElementById(spacelessName + "-changelog").remove();
});
let scroller = document.getElementById(spacelessName + "-changelog-scroller");
if (changes.length == 0) changes = changeKeys;
for (let i = 0; i < changes.length; i++) {
scroller.insertAdjacentHTML("afterbegin", `
<div class="metalloriff-update-item">
<p class="metalloriff-update-label">` + changes[i] + `</p><p class="metalloriff-update-note">` +
allChanges[changes[i]].split("\n").join("<br><br>") +
`</p>
</div>
`);
}
}
},
Settings: {
createPanel: function(plugin) {
setTimeout(() => {
this.create(plugin);
this.pushChangelogElements(plugin);
});
return this.Elements.pluginNameLabel(plugin.getName());
},
create: function(plugin) {
const panel = document.getElementById(`plugin-settings-${plugin.getName()}`), fields = plugin.settingFields;
panel.classList.add("neato-settings");
panel.insertAdjacentHTML("beforeEnd", `
<style>
.neato-setting-label {
color: white;
line-height: 24px;
margin-left: 5px;
}
:not(.neato-setting-array-items) > .neato-setting {
margin-top: 20px;
}
.neato-setting-array-items > .neato-setting {
margin-top: 10px;
}
.neato-textfield {
display: block;
color: white;
background-color: rgba(0, 0, 0, 0.2);
border: none;
border-radius: 5px;
height: 40px;
padding: 10px;
width: 100%;
}
.neato-setting-array .neato-textfield {
display: inline;
transition: width 0.3s;
}
.neato-setting-array .neato-setting:hover .neato-textfield {
width: calc(100% - 45px);
}
.neato-setting-array .neato-array-remove-button {
width: 0;
color: white;
float: right;
cursor: pointer;
font-size: 40px;
transition: width 0.3s;
}
.neato-setting-array .neato-setting:hover .neato-array-remove-button {
width: 40px;
}
.neato-setting-array-buttons {
margin-top: 10px;
text-align: center;
}
.neato-radio-button {
display: flex;
background: rgba(0, 0, 0, 0.2);
padding: 10px;
border-radius: 5px;
cursor: pointer;
position: relative;
margin: 10px;
transition: all 0.3s;
user-select: none;
}
.neato-radio-button .nrb-box {
width: 24px;
height: 24px;
border: none;
background: rgba(0, 0, 0, 0.5);
border-radius: 100%;
margin-right: 5px;
}
.neato-radio-button.nrb-selected {
background: #7289da;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
}
.neato-radio-button.nrb-selected .nrb-box {
background: white;
}
.neato-radio-button * {
cursor: pointer;
}
.neato-settings [data-description]::after {
content: attr(data-description);
margin-left: 10px;
opacity: 0.5;
}
</style>
`);
const createLabel = (title, description, tooltip) => {
const element = document.createElement("label");
element.className = "neato-setting-label";
element.textContent = title;
if(description) element.dataset.description = description || "";
if(tooltip) NeatoLib.Tooltip.attach(tooltip, element);
return element;
};
const createToggleSwitch = (title, description, tooltip, value, callback) => { //<input id="1" class="checkboxEnabled-CtinEn checkbox-2tyjJg da-checkboxEnabled da-checkbox" type="checkbox" tabindex="-1">
const element = document.createElement("div");
element.className = "neato-setting";
if (title) element.appendChild(createLabel(title, description, tooltip));
const tswitch = document.createElement("div");
tswitch.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(" ");
tswitch.style.float = "right";
const tcheckbox = document.createElement("input");
tcheckbox.type = "checkbox";
tcheckbox.className = [NeatoLib.getClass("checkboxEnabled"), NeatoLib.getClass("checkboxEnabled", "checkbox")].join(" ");
tswitch.appendChild(tcheckbox);
tswitch.addEventListener("click", e => {
value = !value;
if (value == true) tswitch.className = tswitch.className.replace(NeatoLib.getClass("valueUnchecked"), NeatoLib.getClass("valueChecked"));
else tswitch.className = tswitch.className.replace(NeatoLib.getClass("valueChecked"), NeatoLib.getClass("valueUnchecked"));
callback(value, e);
});
element.appendChild(tswitch);
return element;
};
const createTextField = (title, description, tooltip, value, type, callback) => {
const element = document.createElement("div");
element.className = "neato-setting";
if (title) element.appendChild(createLabel(title, description, tooltip));
const input = document.createElement("input");
input.className = "neato-textfield";
input.type = "text";
input.value = value || "";
let last = input.value;
input.addEventListener("focusout", e => {
switch (type) {
case "number": case "float":
if (!input.value) {
callback(input.value = last = 0, e);
break;
}
if (isNaN(input.value)) {
NeatoLib.showToast("Value must be a number!", "error");
input.value = last;
break;
}
callback(last = parseFloat(input.value), e);
break;
case "whole number": case "int": case "integer":
if (!input.value) {
callback(input.value = last = 0, e);
break;
}
if (isNaN(input.value)) {
NeatoLib.showToast("Value must be a number!", "error");
input.value = last;
break;
}
callback(last = parseInt(input.value), e);
break;
default: callback(input.value, e);
}
});
element.appendChild(input);
return element;
};
const createRadioGroup = (title, description, tooltip, value, choices, callback) => {
const element = document.createElement("div");
element.className = "neato-setting";
if (title) element.appendChild(createLabel(title, description, tooltip));
const list = document.createElement("div");
const update = () => {
for (let choice of element.getElementsByClassName("neato-radio-button")) {
if (choice.dataset.key == value) choice.classList.add("nrb-selected");
else choice.classList.remove("nrb-selected");
}
};
const createRadioButton = choice => {
const c = document.createElement("div");
c.className = "neato-radio-button";
c.dataset.key = choice;
if (value == choice) c.classList.add("nrb-selected");
const t = document.createElement("span");
t.className = "nrb-box";
c.appendChild(t);
c.appendChild(createLabel(choices[choice].label, choices[choice].description, choices[choice].tooltip));
c.addEventListener("click", e => {
callback(value = choice, e);
update();
});
return c;
};
for (let choice in choices) element.appendChild(createRadioButton(choice));
update();
return element;
};
if (!plugin.settings) plugin.settings = plugin.defaultSettings;
for (let setting in fields) {
const field = fields[setting];
try {
let repop, array, insertDeleteButton;
if (field.array || field.list) {
const list = document.createElement("div");
list.className = "neato-setting neato-setting-array";
list.appendChild(createLabel(field.label, field.description, field.tooltip));
array = document.createElement("div");
array.className = "neato-setting-array-items";
list.appendChild(array);
if (field.array) {
const addButton = document.createElement("button"), clearButton = document.createElement("button");
addButton.className = clearButton.className = [NeatoLib.getClass("button"), NeatoLib.getClass("lookFilled"), NeatoLib.getClass("colorBrand"), NeatoLib.getClass("sizeMedium"), NeatoLib.getClass("grow")].join(" ");
addButton.style = clearButton.style = "display:inline;margin:0 10px";
addButton.textContent = "Add";
addButton.addEventListener("click", e => {
plugin.settings[setting].push("");
array.innerHTML = "";
repop();
if (typeof field.callback == "function") field.callback({ type: "add", event: e, array: array, panel: panel });
plugin.saveSettings();
});
clearButton.textContent = "Clear";
clearButton.addEventListener("click", e => {
plugin.settings[setting] = [];
array.innerHTML = "";
repop();
if (typeof field.callback == "function") field.callback({ type: "clear", event: e, array: array, panel: panel });
plugin.saveSettings();
});
insertDeleteButton = i => {
const button = document.createElement("span");
button.className = "material-icons neato-array-remove-button";
button.textContent = "close";
button.addEventListener("click", e => {
plugin.settings[setting].splice(i, 1);
array.innerHTML = "";
repop();
if (typeof field.callback == "function") field.callback({ type: "remove", event: e, array: array, panel: panel });
plugin.saveSettings();
});
NeatoLib.Tooltip.attach("Remove", button, { delay : 250 });
array.children[i].appendChild(button);
};
const buttons = document.createElement("div");
buttons.className = "neato-setting-array-buttons";
buttons.appendChild(addButton);
buttons.appendChild(clearButton);
list.appendChild(buttons);
}
panel.appendChild(list);
}
const set = (nv, e) => {
if (typeof field.callback == "function") field.callback({ type: "change", oldValue: plugin.settings[setting], newValue: nv, event: e, panel: panel });
plugin.settings[setting] = nv;
plugin.saveSettings();
};
switch (field.type) {
case "bool": case "boolean":
if (array) {
(repop = () => {
for (let i in plugin.settings[setting]) {
const element = createToggleSwitch(null, null, null, plugin.settings[setting][i], (nv, e) => {
if (typeof field.callback == "function") field.callback({ type: "change", oldValue: plugin.settings[setting][i], newValue: nv, event: e, panel: panel });
plugin.settings[setting][i] = nv;
plugin.saveSettings();
});
if (field.events)
for (let event in field.events)
if (event == "start" || event == "init") field.events[i](element);
else element.addEventListener(event, field.events[i]);
array.appendChild(element);
if (field.array) insertDeleteButton(i);
}
})();
} else {
const element = createToggleSwitch(field.label, field.description, field.tooltip, plugin.settings[setting], set);
if (field.events)
for (let event in field.events)
if (event == "start" || event == "init") field.events[i](element);
else element.addEventListener(event, field.events[i]);
panel.appendChild(element);
}
break;
case "custom":
if (field.element) panel.appendChild(field.element);
if (field.html) panel.insertAdjacentHTML("beforeEnd", field.html);
if (field.createElement) field.createElement(panel);
if (field.events)
for (let event in field.events)
if (event == "start" || event == "init") field.events[i](panel.lastChild);
else panel.lastChild.addEventListener(event, field.events[i]);
break;
case "radio": case "radio group":
const element = createRadioGroup(field.label, field.description, field.tooltip, plugin.settings[setting], field.choices, set);
if (field.events)
for (let event in field.events)
if (event == "start" || event == "init") field.events[i](element);
else element.addEventListener(event, field.events[i]);
panel.appendChild(element);
break;
default:
if (array) {
(repop = () => {
for (let i in plugin.settings[setting]) {
const element = createTextField(null, null, null, plugin.settings[setting][i], field.type, (nv, e) => {
if (typeof field.callback == "function") field.callback({ type: "change", oldValue: plugin.settings[i], newValue: nv, event: e, panel: panel });
plugin.settings[setting][i] = nv;
plugin.saveSettings();
});
if (field.events)
for (let event in field.events)
if (event == "start" || event == "init") field.events[i](element);
else element.addEventListener(event, field.events[i]);
array.appendChild(element);
if (field.array) insertDeleteButton(i);
}
})();
} else {
const element = createTextField(field.label, field.description, field.tooltip, plugin.settings[setting], field.type, set);
if (field.events)
for (let event in field.events)
if (event == "start" || event == "init") field.events[i](element);
else element.addEventListener(event, field.events[i]);
panel.appendChild(element);
}
}
} catch(err) { console.error(err); }
}
},
Styles: {
textField: `color: white; background-color: rgba(0, 0, 0, 0.2); border: none; border-radius: 5px; height: 40px; padding: 10px; width: 100%;`
},
Elements: {
pluginNameLabel: function(name, authorName = "Metalloriff") {
return `
<style>
#bd-settingspane-container *::-webkit-scrollbar {
max-width: 10px;
}
#bd-settingspane-container *::-webkit-scrollbar-track-piece {
background: transparent;
border: none;
border-radius: 5px;
}
#bd-settingspane-container *:hover::-webkit-scrollbar-track-piece {
background: #2F3136;
border-radius: 5px;
}
#bd-settingspane-container *::-webkit-scrollbar-thumb {
background: #1E2124;
border: none;
border-radius: 5px;
}
#bd-settingspane-container *::-webkit-scrollbar-button {
display: none;
}
</style>
<h style="color: white;font-size: 30px;font-weight: bold;">${name.replace(/([A-Z])/g, ' $1').trim()} by ${authorName}</h>`;
},
createRadioGroup: function(id, label, choices, selectedChoice, callback, description = "") {
let element = document.createElement("div");
element.style.paddingTop = "20px";
element.innerHTML = `
<h5 style="color:white;padding-bottom:10px;">${label}</h5>
<h5 style="Color:white;padding-bottom:10px;opacity:0.5;">${description}<h5>
<div id="${id}" style="color:white;"></div>`;
for (let i = 0; i < choices.length; i++) {
if (choices[i].description == undefined) choices[i].description = "";
let choiceButton = document.createElement("div");
choiceButton.setAttribute("id", `${id}-${i}`);
choiceButton.setAttribute("data-value", choices[i].value);
choiceButton.setAttribute("data-index", i);
choiceButton.setAttribute("class", "metalloriff-checkbox-item");
choiceButton.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;`);
choiceButton.innerHTML =
`<label>
<div style="width:24px;height:24px;border:3px solid white;border-radius:100%;"></div>
</label>
<div style="margin: 0 8px;color:white;">
<div style="display:inline;padding-right:10px;line-height:24px;">${choices[i].title}</div>
<div style="display:inline;line-height:24px;opacity:0.5;">${choices[i].description}</div>
</div>`;
element.insertAdjacentElement("beforeend", choiceButton);
if (selectedChoice != undefined && choices[i].value == selectedChoice) choiceButton.querySelector(`label > div`).style.backgroundColor = "white";
choiceButton.addEventListener("click", e => {
let i = e.currentTarget.getAttribute("data-index");
let checkboxes = e.currentTarget.parentElement.querySelectorAll(`.metalloriff-checkbox-item > label > div`);
for (let ii = 0; ii < checkboxes.length; ii++) checkboxes[ii].style.backgroundColor = "";
element.querySelector(`#${id}-${i} > label > div`).style.backgroundColor = "white";
callback(choiceButton, choices[i]);
});
}
return element;
},
createToggleGroup: function(id, label, choices, callback, description = "") {
let element = document.createElement("div");
element.style.paddingTop = "10px";
element.insertAdjacentHTML("beforeend", `
<h5 style="color:white;padding-bottom:5px;">${label}</h5>
<h5 style="Color:white;padding-bottom:5px;opacity:0.5;">${description}<h5>
<div id="${id}" style="color:white;"></div>
`);
for (let i = 0; i < choices.length; i++) {
let choiceButton = NeatoLib.Settings.Elements.createToggleSwitch(choices[i].title, choices[i].setValue, e => {
callback(choices[i], e);
});
choiceButton.setAttribute("id", `${id}-${i}`);
choiceButton.setAttribute("data-value", choices[i].value);
choiceButton.setAttribute("data-index", i);
element.insertAdjacentElement("beforeend", choiceButton);
}
return element;
},
createTextField: function(label, type, value, callback, options = {}) {
let element = document.createElement("div");
element.style.paddingTop = options.spacing || "20px";
element.insertAdjacentHTML("beforeend", `
<p style="color:white;font-size:20px;">${label}</p>
<input value="${value}" type="${type}" class="inputDefault-_djjkz input-cIJ7To size16-14cGz5">
`);
if (options.tooltip) NeatoLib.Tooltip.attach(options.tooltip, element, {
side: "left"
});
element.querySelector("input").addEventListener(options.callbackType || "focusout", e => callback(e));
return element;
},
createNewTextField: function(label, value, callback, options = {}) {
let element = document.createElement("div");
element.style.paddingTop = options.spacing || "20px";
element.insertAdjacentHTML("beforeend", `
<style>
.neato-text-field-p {
color: white;
font-size: 20px;
}
</style>
<p class="neato-text-field-p">${label}</p>
<p class="neato-text-field-p" style="opacity:0.5;font-size:17px;">${options.description || ""}</p>
<input value="${value}" type="${options.type || "text"}" style="${NeatoLib.Settings.Styles.textField}">
`);
element.querySelector("input").addEventListener(options.callbackType || "focusout", e => callback(e));
return element;
},
createHint: function(text, options = {}) {
let element = document.createElement("p");
element.style.color = options.color || "white";
element.style.fontSize = options.fontSize || "17px";
element.innerText = text;
return element;
},
createButton: function(label, callback, style = "", attributes = {}) {
let element = document.createElement("button");
element.setAttribute("style", `display:inline-block;${style}`);
element.setAttribute("class", [NeatoLib.getClass("button"), NeatoLib.getClass("lookFilled"), NeatoLib.getClass("colorBrand"), NeatoLib.getClass("sizeMedium"), NeatoLib.getClass("grow")].join(" "));
for (let key in attributes) element.setAttribute(key, attributes[key]);
element.innerText = label;
element.addEventListener("click", e => callback(e));
return element;
},
createToggleSwitch: function(label, value, callback, spacing = "20px") {
var element = document.createElement("div");
element.style.paddingTop = spacing;
element.innerHTML =
`<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;">
<h3 class="titleDefault-a8-ZSr title-31JmR4 marginReset-236NPn weightMedium-2iZe9B size16-14cGz5 height24-3XzeJx flexChild-faoVW3" style="flex: 1 1 auto;">${label}</h3>
<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;">
<input class="checkboxEnabled-CtinEn checkbox-2tyjJg" type="checkbox">
</div>
</div>`;
element.querySelector("input").addEventListener("click", e => {
var b = e.currentTarget.parentElement;
if (b.classList.contains("valueChecked-m-4IJZ")) {
b.classList.add("valueUnchecked-2lU_20");
b.classList.remove("valueChecked-m-4IJZ");
} else {
b.classList.add("valueChecked-m-4IJZ");
b.classList.remove("valueUnchecked-2lU_20");
}
callback(e);
});
return element;
},
createLabel: function(title, spacing = "20px", style = "") {
return `<div style="color:white;margin: ${spacing} 0px;${style}">${title}</div>`;
},
createGroup: function(title, options = {}) {
let element = document.createElement("div");
element.setAttribute("style", `color:white;margin:${options.spacing || "20px"};${options.style || ""}`);
element.insertAdjacentHTML("beforeend", `<div style="margin: ${options.spacing || "20px"} 0px;">${title}</div><div></div>`);
return element;
},
createKeybindInput: function(title, value, callback, options = {}) {
let element = document.createElement("div"),
v = value.primaryKey || "",
oldValue = value;
if (value.modifiers && value.modifiers[0]) v = (value.modifiers.join(" + ") || "") + " + " + (value.primaryKey || "");
if (options.global) v = value;
element.insertAdjacentHTML("beforeend", `
<style>
#app-mount .card-FDVird.active-nvdKfC:before, .card-FDVird:before {
opacity: 0.5 !important;
}
</style>
<div class="row-2okwlC">
<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">
<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;">
<div class="item-rJ_Cmt flexChild-faoVW3" style="flex: 1 1 50%;">
<h5 class="h5-18_1nd title-3sZWYQ size12-3R0845 height16-2Lv3qA weightSemiBold-NJexzi defaultMarginh5-2mL-bP marginBottom8-AtZOdT">${title}</h5>
<div class="container-CpszHS container-1nZlH6 hasValue-3pdcdm">
<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;">
<div class="flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2NDFzi alignStretch-DpGPf3 noWrap-3jynv6"
style="flex: 0 1 auto; margin: 0px;">
<button type="button" class="button-34kXw5 button-3tQuzi button-38aScr lookGhost-2Fn_0- colorGrey-2DXtkV sizeMin-1mJd1x grow-q77ONN nbl-keybind-button">
<div class="contents-18-Yxp nbl-keybind-button">
<span class="text-2sI5Sd nbl-keybind-button">Edit Keybind</span>
<span class="editIcon-13gaox nbl-keybind-button">
</span>
</div>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="description-3_Ncsb formText-3fs7AJ keybindMessage-20JT9A flexChild-faoVW3 modeDefault-3a2Ph1 primary-jw0I4K" style="flex: 1 1 auto;">${options.description || ""}</div>
</div>
</div>
`);
let isRecording = false,
primaryKey = "",
modifiers = [],
globalKeys = [];
let keyEvent = e => {
e.preventDefault();
if (options.global) {
let key = e.key;
if (key.length == 1) key = key.toUpperCase();
if (globalKeys.indexOf(key) == -1) globalKeys.push(key);
if (globalKeys[0] == "") globalKeys.splice(0, 1);
input.value = globalKeys.join(" + ");
if (e.location == 0 && globalKeys.length > 1) button.click();
else input.value += " + ...";
} else {
if (e.location == 0) primaryKey = e.code;
else if (modifiers.indexOf(e.code) == -1) modifiers.push(e.code);
if (primaryKey && modifiers[0]) {
input.value = `${modifiers.join(" + ")} + ${primaryKey}`;
button.click();
} else if (primaryKey) input.value = primaryKey;
else if (modifiers[0]) input.value = modifiers.join(" + ") + " + ...";
else input.value = "";
input.value = input.value.replace("Key", "");
}
};
let keyUpEvent = e => {
e.preventDefault();
if (options.global) {
let key = e.key;
if (key.length == 1) key = key.toUpperCase();
if (globalKeys.indexOf(key) != -1) globalKeys.splice(globalKeys.indexOf(key), 1);
if (globalKeys[0] == "") globalKeys.splice(0, 1);
input.value = globalKeys.join(" + ");
} else {
if (e.location == 0) primaryKey = undefined;
else if (modifiers.indexOf(e.code) != -1) modifiers.splice(modifiers.indexOf(e.code), 1);
if (primaryKey && modifiers[0]) input.value = `${modifiers.join(" + ")} + ${primaryKey}`;
else if (primaryKey) input.value = primaryKey;
else if (modifiers[0]) input.value = modifiers.join(" + ") + " + ...";
else input.value = "";
input.value = input.value.replace("Key", "");
}
};
let toggleRecording = () => {
isRecording = !isRecording;
if (isRecording) {
if (options.global) NeatoLib.Keybinds.unregisterGlobal(oldValue);
document.addEventListener("keydown", keyEvent);
document.addEventListener("keyup", keyUpEvent);
document.addEventListener("click", documentClick);
container.classList.add("recording-1H2dS7");
label.innerText = "Save Keybind";
} else {
oldValue = globalKeys.join(" + ");
if (options.global) callback(oldValue);
else callback({
primaryKey: primaryKey,
modifiers: modifiers
});
primaryKey = undefined;
modifiers = [];
globalKeys = [];
document.removeEventListener("keydown", keyEvent);
documen
gitextract_pvv8lvhu/ ├── Assets/ │ ├── AC/ │ │ └── Preview/ │ │ └── README.md │ ├── AIC/ │ │ └── Preview/ │ │ └── README.md │ ├── BDEA/ │ │ └── Preview/ │ │ └── README.md │ ├── BES/ │ │ └── Preview/ │ │ └── README.md │ ├── BPECC/ │ │ └── Preview/ │ │ └── README.md │ ├── CJS/ │ │ └── Preview/ │ │ └── README.md │ ├── DST/ │ │ └── Preview/ │ │ └── README.md │ ├── FMC/ │ │ └── Preview/ │ │ └── README.md │ ├── GC/ │ │ └── Preview/ │ │ └── README.md │ ├── GFRA/ │ │ └── Preview/ │ │ └── README.md │ ├── IB/ │ │ └── Preview/ │ │ └── README.md │ ├── MA/ │ │ └── Preview/ │ │ └── README.md │ ├── ML/ │ │ └── Preview/ │ │ └── README.md │ ├── OLID/ │ │ └── Preview/ │ │ └── README.md │ ├── PCC/ │ │ └── Preview/ │ │ └── README.md │ ├── PPT/ │ │ └── Preview/ │ │ └── README.md │ ├── RA/ │ │ └── Preview/ │ │ └── README.md │ ├── SB/ │ │ └── Preview/ │ │ └── README.md │ ├── SBDE/ │ │ └── Preview/ │ │ └── README.md │ ├── SLD/ │ │ └── Preview/ │ │ └── README.md │ ├── ST/ │ │ └── Preview/ │ │ └── README.md │ ├── TB/ │ │ └── Preview/ │ │ └── README.md │ ├── UCB/ │ │ └── Preview/ │ │ └── README.md │ ├── VCN/ │ │ └── Preview/ │ │ └── README.md │ └── VGR/ │ └── Preview/ │ └── README.md ├── AutoCorrect.plugin.js ├── AutoRefreshSettingsPanel.plugin.js ├── AvatarIconViewer.plugin.js ├── BDEmoteAutocomplete.plugin.js ├── BetterEmoteSizes.plugin.js ├── Bruh.plugin.js ├── CustomJs.plugin.js ├── CustomizableAvatarDPI.plugin.js ├── DetailedServerTooltips.plugin.js ├── DoubleClickVoiceChannels.plugin.js ├── FormattableMessageCopier.plugin.js ├── GuildAndFriendRemovalAlerts/ │ ├── GuildAndFriendRemovalAlerts.plugin.js │ ├── README.md │ └── src/ │ ├── components/ │ │ ├── item.jsx │ │ ├── item.scss │ │ └── settings.jsx │ ├── index.js │ ├── modules/ │ │ └── settings.js │ ├── package.json │ └── styles.scss ├── GuildAndFriendRemovalAlerts.plugin.js ├── GuildCounter.plugin.js ├── IdleGuildlistScroller.plugin.js ├── ImageBrowser.plugin.js ├── Lib/ │ └── NeatoBurritoLibrary.js ├── MentionAliases.plugin.js ├── NateUtilities.plugin.js ├── OpenLinksInDiscord.plugin.js ├── PinCollapsedChannels.plugin.js ├── PinPluginsAndThemes.plugin.js ├── PreventSpotifyAutoPause.plugin.js ├── README.md ├── ReactionImages.plugin.js ├── SaveTo.plugin.js ├── SelectedChannelNotifications.plugin.js ├── SendBDEmotes.plugin.js ├── SendLinksDirectly.plugin.js ├── ShareButton.plugin.js ├── SuppressUserMentions.plugin.js ├── TheClapBestClapPluginClapEver.plugin.js ├── TransitioningBackgrounds.plugin.js ├── UnreadCountBadges.plugin.js ├── UserBirthdays.plugin.js ├── VCMuteSounds.plugin.js ├── VideoExamples/ │ └── README.md ├── ViewGuildRelationships.plugin.js ├── VoiceChatNotifications.plugin.js └── VoiceChatPanel.plugin.js
SYMBOL INDEX (490 symbols across 41 files)
FILE: AutoCorrect.plugin.js
method constructor (line 51) | constructor() { this._config = config; }
method load (line 57) | load()
method start (line 73) | start() { }
method stop (line 74) | stop() { }
method constructor (line 104) | constructor()
method showChangelog (line 129) | async showChangelog(footer)
method createSettingsSwitch (line 138) | createSettingsSwitch(title, desc, setting)
method createSettingsOverrideItem (line 147) | createSettingsOverrideItem(index)
method getSettingsPanel (line 177) | getSettingsPanel()
method onStart (line 212) | async onStart()
method tryCorrect (line 292) | async tryCorrect(textValue, sending)
method setText (line 398) | setText(_e, text)
method learnWord (line 414) | learnWord(word)
method forgetWord (line 425) | forgetWord(index)
method onStop (line 436) | onStop()
FILE: AutoRefreshSettingsPanel.plugin.js
class AutoRefreshSettingsPanel (line 3) | class AutoRefreshSettingsPanel {
method getName (line 5) | getName() { return "AutoRefreshSettingsPanel"; }
method getDescription (line 6) | getDescription() { return "Automatically refreshes the BetterDiscord p...
method getVersion (line 7) | getVersion() { return "0.0.1"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method load (line 10) | load() {}
method start (line 12) | start() {
method stop (line 23) | stop() {
FILE: AvatarIconViewer.plugin.js
method constructor (line 33) | constructor() { this._config = config; }
method load (line 39) | load() {
method start (line 52) | start() { }
method stop (line 53) | stop() { }
method constructor (line 78) | constructor() {
method onStart (line 82) | async onStart() {
method createContext (line 142) | createContext(url, type) {
method onStop (line 193) | onStop() {
FILE: BDEmoteAutocomplete.plugin.js
class BDEmoteAutocomplete (line 3) | class BDEmoteAutocomplete {
method getName (line 5) | getName() { return "BDEmoteAutocomplete"; }
method getDescription (line 6) | getDescription() { return "Adds an auto-complete menu for BetterDiscor...
method getVersion (line 7) | getVersion() { return "1.0.4"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method getChanges (line 9) | getChanges() {
method load (line 15) | load() {}
method start (line 17) | start() {
method getSettingsPanel (line 37) | getSettingsPanel() {
method saveSettings (line 87) | saveSettings() {
method updateSelected (line 91) | updateSelected() {
method onLibLoaded (line 102) | onLibLoaded() {
method switch (line 253) | switch() {
method getEmotes (line 265) | getEmotes() {
method stop (line 289) | stop() {
FILE: BetterEmoteSizes.plugin.js
class BetterEmoteSizes (line 3) | class BetterEmoteSizes {
method getName (line 5) | getName() { return "Emote Zoom"; }
method getDescription (line 6) | getDescription() { return "Increases the size of emojis, emotes, and r...
method getVersion (line 7) | getVersion() { return "2.4.17"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method settingFields (line 10) | get settingFields() {
method defaultSettings (line 30) | get defaultSettings() {
method getSettingsPanel (line 51) | getSettingsPanel() {
method saveSettings (line 55) | saveSettings() {
method load (line 60) | load() {}
method start (line 62) | start() {
method onLibLoaded (line 82) | onLibLoaded() {
method update (line 94) | update() {
method stop (line 211) | stop() {
FILE: Bruh.plugin.js
method constructor (line 52) | constructor() { this._config = config; }
method load (line 58) | load() {
method start (line 71) | start() { }
method stop (line 72) | stop() { }
method constructor (line 83) | constructor() {
method getSettingsPanel (line 87) | getSettingsPanel() {
method onStart (line 91) | onStart() {
method playBruh (line 110) | playBruh() {
method onStop (line 115) | onStop() {
FILE: CustomJs.plugin.js
class CustomJs (line 3) | class CustomJs {
method getName (line 5) | getName() { return "CustomJs"; }
method getDescription (line 6) | getDescription() { return "Allows you to specify a custom JavaScript f...
method getVersion (line 7) | getVersion() { return "0.0.1"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method getChanges (line 9) | getChanges() {
method load (line 15) | load() {}
method start (line 17) | start() {
method getSettingsPanel (line 38) | getSettingsPanel() {
method saveSettings (line 137) | saveSettings() {
method onLibLoaded (line 142) | onLibLoaded() {
method rewatch (line 170) | rewatch() {
method reloadJs (line 184) | reloadJs() {
method stop (line 213) | stop() {
FILE: CustomizableAvatarDPI.plugin.js
class CustomizableAvatarDPI (line 3) | class CustomizableAvatarDPI {
method getName (line 5) | getName() { return "Customizable Avatar DPI"; }
method getDescription (line 6) | getDescription() { return "Allows you to change the DPI of user avatar...
method getVersion (line 7) | getVersion() { return "1.0.6"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method getChanges (line 9) | getChanges() {
method load (line 18) | load() {}
method start (line 20) | start() {
method saveSettings (line 41) | saveSettings() {
method getSettingsPanel (line 45) | getSettingsPanel() {
method onLibLoaded (line 67) | onLibLoaded(){
method stop (line 114) | stop() {
FILE: DetailedServerTooltips.plugin.js
class DetailedServerTooltips (line 3) | class DetailedServerTooltips {
method getName (line 5) | getName() { return "DetailedServerTooltips"; }
method getDescription (line 6) | getDescription() { return "Displays a more detailed tooltip for server...
method getVersion (line 7) | getVersion() { return "0.3.14"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method getChanges (line 9) | getChanges() {
method load (line 36) | load() {}
method start (line 38) | start() {
method settingFields (line 61) | get settingFields() {
method defaultSettings (line 95) | get defaultSettings() {
method getSettingsPanel (line 104) | getSettingsPanel() {
method saveSettings (line 108) | saveSettings() {
method applyCSS (line 113) | applyCSS() {
method onLibLoaded (line 151) | onLibLoaded() {
method applyToGuilds (line 229) | applyToGuilds(detach) {
method tooltip (line 249) | tooltip(guildId, element) {
method stop (line 308) | stop() {
method escapeHtml (line 325) | escapeHtml(txt){
FILE: DoubleClickVoiceChannels.plugin.js
method constructor (line 41) | constructor() { this._config = config; }
method load (line 47) | load() {
method start (line 60) | start() { }
method stop (line 61) | stop() { }
method constructor (line 68) | constructor() {
method showChangelog (line 72) | async showChangelog(footer) {
method onStart (line 77) | async onStart() {
method onStop (line 131) | onStop() {
FILE: FormattableMessageCopier.plugin.js
class FormattableMessageCopier (line 3) | class FormattableMessageCopier {
method getSettingsPanel (line 5) | getSettingsPanel(){
method saveSettings (line 164) | saveSettings(){
method getName (line 168) | getName() { return "Formattable Message Copier"; }
method getDescription (line 169) | getDescription() { return "Allows you to select messages in a chat to ...
method getVersion (line 170) | getVersion() { return "0.1.4"; }
method getAuthor (line 171) | getAuthor() { return "Metalloriff"; }
method load (line 173) | load() {}
method start (line 175) | start() {
method onLibLoaded (line 195) | onLibLoaded(){
method applyCSS (line 223) | applyCSS(){
method onClick (line 246) | onClick(e){
method clearSelection (line 311) | clearSelection(){
method copySelection (line 317) | copySelection(){
method stop (line 401) | stop() {
FILE: GuildAndFriendRemovalAlerts.plugin.js
method load (line 35) | load() {
method start (line 59) | start() { }
method stop (line 60) | stop() { }
FILE: GuildAndFriendRemovalAlerts/GuildAndFriendRemovalAlerts.plugin.js
function buildPlugin (line 75) | function buildPlugin([BasePlugin, PluginApi]) {
method getName (line 748) | getName() {
method getAuthor (line 751) | getAuthor() {
method getDescription (line 754) | getDescription() {
method getVersion (line 757) | getVersion() {
method load (line 760) | load() {
method start (line 775) | start() {}
method stop (line 776) | stop() {}
FILE: GuildAndFriendRemovalAlerts/src/components/item.jsx
function Item (line 8) | function Item({ title, description, icon, clickId, closeModal }) {
FILE: GuildAndFriendRemovalAlerts/src/components/settings.jsx
function SettingsPanel (line 8) | function SettingsPanel() {
FILE: GuildAndFriendRemovalAlerts/src/index.js
class GuildAndFriendRemovalAlerts (line 20) | class GuildAndFriendRemovalAlerts extends BasePlugin {
method onStart (line 49) | onStart() {
method serializeGuild (line 82) | serializeGuild(guildId) {
method serializeUser (line 101) | serializeUser(userId) {
method openModal (line 160) | openModal(guilds, friends, showClearButton = false) {
method onStop (line 221) | onStop() {
FILE: GuildAndFriendRemovalAlerts/src/modules/settings.js
class Settings (line 4) | class Settings {
FILE: GuildCounter.plugin.js
class GuildCounter (line 3) | class GuildCounter {
method getName (line 5) | getName() { return "Guild Counter"; }
method getDescription (line 6) | getDescription() { return "Displays a guild counter below the online f...
method getVersion (line 7) | getVersion() { return "1.0.4"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method load (line 10) | load() {}
method start (line 12) | start() {
method onLibLoaded (line 33) | onLibLoaded(){
method stop (line 52) | stop() {
FILE: IdleGuildlistScroller.plugin.js
class IdleGuildlistScroller (line 3) | class IdleGuildlistScroller {
method getName (line 5) | getName() { return "Idle Guildlist Scroller"; }
method getDescription (line 6) | getDescription() { return "Automatically scrolls to the top of the gui...
method getVersion (line 7) | getVersion() { return "0.1.1"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method load (line 10) | load() {}
method getSettingsPanel (line 12) | getSettingsPanel(){
method saveSettings (line 16) | static saveSettings(){
method resetSettings (line 23) | static resetSettings(){
method start (line 29) | start() {
method initialize (line 42) | initialize(){
method onSwitch (line 53) | onSwitch(){
method onHover (line 63) | onHover(){
method offHover (line 68) | offHover(){
method stop (line 74) | stop() {
method guildsList (line 83) | get guildsList(){
method channelList (line 87) | get channelList(){
FILE: ImageBrowser.plugin.js
class ImageBrowser (line 3) | class ImageBrowser {
method getName (line 5) | getName() { return "ImageBrowser"; }
method getDescription (line 6) | getDescription() { return "Displays a next and previous button on imag...
method getVersion (line 7) | getVersion() { return "0.1.1"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method getChanges (line 9) | getChanges() {
method load (line 18) | load() {}
method start (line 20) | start() {
method getSettingsPanel (line 41) | getSettingsPanel() {
method saveSettings (line 64) | saveSettings() {
method onLibLoaded (line 68) | onLibLoaded() {
method stop (line 231) | stop() {
FILE: MentionAliases.plugin.js
method constructor (line 61) | constructor() { this._config = config; }
method load (line 67) | load() {
method start (line 80) | start() { }
method stop (line 81) | stop() { }
class Popout (line 178) | class Popout extends React.Component {
method renderItem (line 179) | renderItem(item) {
method render (line 196) | render() {
method constructor (line 236) | constructor() {
method showChangelog (line 244) | async showChangelog(footer) {
method onStart (line 249) | onStart() {
method patchAutoComplete (line 260) | patchAutoComplete() {
method patchMessageHeader (line 284) | patchMessageHeader() {
method patchUserContextMenu (line 304) | patchUserContextMenu() {
method patchMentions (line 357) | patchMentions() {
method patchTextAreaContainer (line 394) | patchTextAreaContainer() {
method openAliasModal (line 460) | openAliasModal(user) {
method onStop (line 540) | onStop() {
FILE: NateUtilities.plugin.js
class NateUtilities (line 3) | class NateUtilities {
method getName (line 5) | getName() { return "NateUtilities"; }
method getDescription (line 6) | getDescription() { return "For all of your ear and brain saving needs!...
method getVersion (line 7) | getVersion() { return "0.0.2"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method getChanges (line 9) | getChanges() {
method load (line 15) | load() {}
method start (line 17) | start() {
method getSettingsPanel (line 37) | getSettingsPanel() {
method saveSettings (line 76) | saveSettings() {
method onLibLoaded (line 80) | onLibLoaded() {
method onSwitch (line 103) | onSwitch() {
method registerKeybinds (line 112) | registerKeybinds() {
method unregisterKeybinds (line 168) | unregisterKeybinds() {
method stop (line 174) | stop() {
FILE: OpenLinksInDiscord.plugin.js
class OpenLinksInDiscord (line 3) | class OpenLinksInDiscord {
method getName (line 5) | getName() { return "OpenLinksInDiscord"; }
method getDescription (line 6) | getDescription() { return "Opens links in a new window in Discord, ins...
method getVersion (line 7) | getVersion() { return "1.1.4"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method load (line 10) | load() {}
method start (line 12) | start() {
method getSettingsPanel (line 33) | getSettingsPanel() {
method onLibLoaded (line 60) | onLibLoaded() {
method onClickLink (line 91) | onClickLink(e) {
method stop (line 103) | stop() {
FILE: PinCollapsedChannels.plugin.js
class PinCollapsedChannels (line 3) | class PinCollapsedChannels {
method getName (line 5) | getName() { return "PinCollapsedChannels"; }
method getDescription (line 6) | getDescription() { return "Allows you to pin channels on collapsed cat...
method getVersion (line 7) | getVersion() { return "0.0.2"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method getChanges (line 9) | getChanges() {
method load (line 15) | load() {}
method start (line 17) | start() {
method settingFields (line 37) | get settingFields() {
method defaultSettings (line 43) | get defaultSettings() {
method getSettingsPanel (line 50) | getSettingsPanel() {
method saveSettings (line 54) | saveSettings() {
method onLibLoaded (line 58) | onLibLoaded() {
method onContextMenu (line 79) | onContextMenu(e) {
method stop (line 104) | stop() {
FILE: PinPluginsAndThemes.plugin.js
class PinPluginsAndThemes (line 3) | class PinPluginsAndThemes {
method getName (line 5) | getName() { return "PinPluginsAndThemes"; }
method getDescription (line 6) | getDescription() { return "Allows you to pin plugins and themes via th...
method getVersion (line 7) | getVersion() { return "1.0.2"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method getChanges (line 9) | getChanges() {
method load (line 21) | load() {}
method start (line 23) | start() {
method getSettingsPanel (line 43) | getSettingsPanel() {
method applyStyles (line 62) | applyStyles() {
method saveSettings (line 87) | saveSettings() {
method onLibLoaded (line 92) | onLibLoaded() {
method stop (line 181) | stop() {
FILE: PreventSpotifyAutoPause.plugin.js
method getName (line 50) | getName() { return config.info.name; }
method getAuthor (line 51) | getAuthor() { return config.info.authors.map(x => x.name).join(", "); }
method getDescription (line 52) | getDescription() { return config.info.description; }
method getVersion (line 53) | getVersion() { return config.info.version; }
method load (line 55) | load() {
method start (line 79) | start() {}
method stop (line 80) | stop() {}
method onStart (line 84) | onStart() {
method onStop (line 88) | onStop() {
FILE: ReactionImages.plugin.js
method constructor (line 61) | constructor() { this._config = config; }
method load (line 67) | load() {
method start (line 80) | start() { }
method stop (line 81) | stop() { }
class AutocompleteItem (line 103) | class AutocompleteItem extends React.Component {
method render (line 104) | render() {
method constructor (line 135) | constructor() {
method showChangelog (line 143) | async showChangelog(footer) {
method getSettingsPanel (line 148) | getSettingsPanel() {
method init (line 159) | async init() {
method fetchAllFolders (line 163) | async fetchAllFolders() {
method getFolders (line 203) | getFolders() {
method term (line 207) | term() {
method onStart (line 214) | async onStart() {
method onStop (line 303) | onStop() {
FILE: SaveTo.plugin.js
class SaveTo (line 3) | class SaveTo {
method getName (line 5) | getName() { return "Save To"; }
method getDescription (line 6) | getDescription() { return "Allows you to save images, videos, files, s...
method getVersion (line 7) | getVersion() { return "0.7.9"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method getChanges (line 9) | getChanges() {
method saveSettings (line 53) | saveSettings() {
method saveData (line 57) | saveData() {
method getSettingsPanel (line 61) | getSettingsPanel() {
method load (line 245) | load() {}
method start (line 247) | start() {
method onLibLoaded (line 267) | onLibLoaded() {
method onContextMenu (line 309) | onContextMenu(e) {
method browseForFolder (line 442) | browseForFolder(selected) {
method stop (line 460) | stop() {
FILE: SelectedChannelNotifications.plugin.js
class SelectedChannelNotifications (line 3) | class SelectedChannelNotifications {
method getName (line 5) | getName() { return "Selected Channel Notifications"; }
method getDescription (line 6) | getDescription() { return "Plays a sound and displays a notification (...
method getVersion (line 7) | getVersion() { return "0.1.6"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method load (line 10) | load() {}
method start (line 12) | start() {
method settingFields (line 32) | get settingFields() {
method defaultSettings (line 40) | get defaultSettings() {
method getSettingsPanel (line 48) | getSettingsPanel() {
method saveSettings (line 52) | saveSettings() {
method onLibLoaded (line 58) | onLibLoaded() {
method onMessageReceived (line 90) | onMessageReceived() {
method stop (line 116) | stop() {
FILE: SendBDEmotes.plugin.js
class SendBDEmotes (line 3) | class SendBDEmotes {
method getName (line 5) | getName() { return "Send BD Emotes"; }
method getDescription (line 6) | getDescription() { return "Allows you to enclose Better Discord emotes...
method getVersion (line 7) | getVersion() { return "1.6.12"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method load (line 10) | load() {}
method start (line 12) | start() {
method settingFields (line 32) | get settingFields() {
method defaultSettings (line 46) | get defaultSettings() {
method getSettingsPanel (line 56) | getSettingsPanel() {
method saveSettings (line 60) | saveSettings() {
method onLibLoaded (line 64) | onLibLoaded() {
method onKeyDown (line 85) | onKeyDown(e) {
method onKeyUp (line 141) | async onKeyUp(e) {
method getEmotes (line 263) | async getEmotes() {
method trySend (line 282) | trySend(message, emoteName, emoteURL, size = 4, animated = false) {
method switch (line 298) | switch () {
method stop (line 311) | stop() {
FILE: SendLinksDirectly.plugin.js
class SendLinksDirectly (line 3) | class SendLinksDirectly {
method getName (line 5) | getName() { return "SendLinksDirectly"; }
method getDescription (line 6) | getDescription() { return `Allows you to enclose direct links in squar...
method getVersion (line 9) | getVersion() { return "1.1.4"; }
method getAuthor (line 10) | getAuthor() { return "Metalloriff"; }
method getChanges (line 11) | getChanges() {
method load (line 20) | load() {}
method start (line 22) | start() {
method onLibLoaded (line 42) | onLibLoaded() {
method switch (line 106) | switch () {
method stop (line 114) | stop() {
FILE: ShareButton.plugin.js
class ShareButton (line 3) | class ShareButton {
method getName (line 5) | getName() { return "Share Button"; }
method getDescription (line 6) | getDescription() { return "Allows you to easily share images, videos, ...
method getVersion (line 7) | getVersion() { return "0.2.9"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method getChanges (line 9) | getChanges() {
method load (line 30) | load() {}
method start (line 32) | start() {
method getSettingsPanel (line 52) | getSettingsPanel() {
method saveSettings (line 60) | saveSettings() {
method onLibLoaded (line 64) | onLibLoaded() {
method saveData (line 114) | saveData() { NeatoLib.Data.save("ShareButton", "data", { recentChannel...
method onContextMenu (line 116) | onContextMenu(e) {
method openShareMenu (line 172) | openShareMenu(e, definedName, definedData) {
method sendMessage (line 524) | sendMessage(e) {
method stop (line 536) | stop() {
FILE: SuppressUserMentions.plugin.js
class SuppressUserMentions (line 3) | class SuppressUserMentions {
method getName (line 5) | getName() { return "SuppressUserMentions"; }
method getDescription (line 6) | getDescription() { return "Allows you to suppress mentions from specif...
method getVersion (line 7) | getVersion() { return "0.0.2"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method getChanges (line 9) | getChanges() {
method load (line 15) | load() {}
method start (line 17) | start() {
method settingFields (line 37) | get settingFields() {
method defaultSettings (line 43) | get defaultSettings() {
method getSettingsPanel (line 50) | getSettingsPanel() {
method saveSettings (line 54) | saveSettings() {
method onLibLoaded (line 58) | onLibLoaded() {
method onContextMenu (line 75) | onContextMenu(e) {
method stop (line 97) | stop() {
FILE: TheClapBestClapPluginClapEver.plugin.js
method constructor (line 52) | constructor()
method onStart (line 57) | onStart()
method onStop (line 136) | onStop()
FILE: TransitioningBackgrounds.plugin.js
method constructor (line 210) | constructor() { this._config = config; }
method load (line 216) | load()
method start (line 232) | start() { }
method stop (line 233) | stop() { }
method constructor (line 246) | constructor()
method showChangelog (line 251) | async showChangelog(footer)
method getSettingsPanel (line 261) | getSettingsPanel() {
method createElement (line 272) | createElement(type, options)
method CSS (line 284) | get CSS()
method ForceTransparencyCSS (line 316) | get ForceTransparencyCSS()
method init (line 356) | init()
method nextImageURL (line 394) | set nextImageURL(url)
method shuffle (line 399) | shuffle(arr)
method main (line 411) | async main()
method updateNextImage (line 424) | updateNextImage()
method term (line 495) | term()
FILE: UnreadCountBadges.plugin.js
class UnreadCountBadges (line 3) | class UnreadCountBadges {
method getName (line 5) | getName() { return "UnreadCountBadges"; }
method getDescription (line 6) | getDescription() { return "Adds an unread count badge on unread server...
method getVersion (line 7) | getVersion() { return "0.2.7"; }
method getAuthor (line 8) | getAuthor() { return "Metalloriff"; }
method getChanges (line 9) | getChanges() {
method load (line 33) | load() {}
method start (line 35) | start() {
method getSettingsPanel (line 56) | getSettingsPanel() {
method saveSettings (line 144) | saveSettings() {
method onLibLoaded (line 148) | onLibLoaded() {
method applySettings (line 232) | applySettings() {
method updateBadges (line 251) | updateBadges(guild) {
method createBadge (line 315) | createBadge(unreadCount) {
method createChannelBadge (line 326) | createChannelBadge(unreadCount) {
method stop (line 338) | stop() {
FILE: UserBirthdays.plugin.js
method constructor (line 56) | constructor() {
method getName (line 59) | getName() {
method getAuthor (line 62) | getAuthor() {
method getDescription (line 65) | getDescription() {
method getVersion (line 68) | getVersion() {
method load (line 71) | load() {
method start (line 83) | start() {}
method stop (line 84) | stop() {}
method onStart (line 100) | onStart() {
method onStop (line 209) | onStop() {
method c (line 214) | get c() {
method setBirthday (line 236) | setBirthday(uid, day) {
method createBirthdayItem (line 250) | createBirthdayItem(user) {
method createBirthdayContainer (line 279) | createBirthdayContainer() {
FILE: VCMuteSounds.plugin.js
class VCMuteSounds (line 3) | class VCMuteSounds {
method constructor (line 5) | constructor(){
method getName (line 14) | getName() { return "Voice Chat Mute Sounds"; }
method getDescription (line 15) | getDescription() { return "Enables the mute and unmute sound for all u...
method getVersion (line 16) | getVersion() { return "0.0.3"; }
method getAuthor (line 17) | getAuthor() { return "Metalloriff"; }
method load (line 19) | load() {}
method getSettingsPanel (line 21) | getSettingsPanel(){
method saveSettings (line 25) | saveSettings(){
method resetSettings (line 36) | resetSettings(){
method start (line 53) | start() {
method initialize (line 66) | initialize(){
method onSwitch (line 82) | onSwitch(){
method check (line 86) | check(){
method stop (line 103) | stop() {
FILE: ViewGuildRelationships.plugin.js
method constructor (line 64) | constructor() {
method getName (line 67) | getName() {
method getAuthor (line 70) | getAuthor() {
method getDescription (line 73) | getDescription() {
method getVersion (line 76) | getVersion() {
method load (line 79) | load() {
method start (line 112) | start() { }
method stop (line 113) | stop() { }
class Menu (line 332) | class Menu extends React.Component {
method constructor (line 333) | constructor(props) {
method render (line 337) | render() {
method setTab (line 378) | setTab(e) {
method constructor (line 383) | constructor() {
method css (line 386) | get css() {
method onStart (line 391) | onStart() {
method onStop (line 419) | onStop() {
FILE: VoiceChatNotifications.plugin.js
method constructor (line 131) | constructor() { this._config = config; }
method load (line 137) | load()
method start (line 153) | start() { }
method stop (line 154) | stop() { }
method constructor (line 166) | constructor()
method showChangelog (line 171) | async showChangelog(footer)
method onStart (line 179) | onStart()
method main (line 184) | main()
method onStop (line 283) | onStop()
FILE: VoiceChatPanel.plugin.js
method constructor (line 44) | constructor() { this._config = config; }
method load (line 50) | load()
method start (line 66) | start() { }
method stop (line 67) | stop() { }
class Graph (line 93) | class Graph extends React.Component
method constructor (line 95) | constructor()
method componentDidMount (line 101) | componentDidMount()
method componentWillUnmount (line 111) | componentWillUnmount()
method handleRenderGraph (line 116) | handleRenderGraph(e)
method render (line 129) | render()
class Panel (line 143) | class Panel extends React.Component
method constructor (line 145) | constructor()
method render (line 151) | render()
class UserVolumeLabel (line 217) | class UserVolumeLabel extends React.Component
method constructor (line 219) | constructor()
method componentDidMount (line 225) | componentDidMount()
method componentWillUnmount (line 230) | componentWillUnmount()
method render (line 235) | render()
method constructor (line 258) | constructor()
method showChangelog (line 263) | async showChangelog(footer)
method onStart (line 271) | onStart()
method tick (line 349) | tick(e)
method onStop (line 372) | onStop()
Condensed preview — 73 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (528K chars).
[
{
"path": "Assets/AC/Preview/README.md",
"chars": 83,
"preview": "\n\n\n"
},
{
"path": "Assets/AIC/Preview/README.md",
"chars": 110,
"preview": "\n\n\n\n\n"
},
{
"path": "Assets/BDEA/Preview/README.md",
"chars": 48,
"preview": "\n\n"
},
{
"path": "Assets/BES/Preview/README.md",
"chars": 44,
"preview": "\n\n"
},
{
"path": "Assets/BPECC/Preview/README.md",
"chars": 44,
"preview": "\n\n"
},
{
"path": "Assets/CJS/Preview/README.md",
"chars": 44,
"preview": "\n\n"
},
{
"path": "Assets/DST/Preview/README.md",
"chars": 73,
"preview": "\n\n\n"
},
{
"path": "Assets/FMC/Preview/README.md",
"chars": 102,
"preview": "\n\n\n\n"
},
{
"path": "Assets/GC/Preview/README.md",
"chars": 22,
"preview": "\n"
},
{
"path": "Assets/GFRA/Preview/README.md",
"chars": 66,
"preview": "\n\n\n"
},
{
"path": "Assets/IB/Preview/README.md",
"chars": 26,
"preview": "\n"
},
{
"path": "Assets/MA/Preview/README.md",
"chars": 66,
"preview": "\n\n\n"
},
{
"path": "Assets/ML/Preview/README.md",
"chars": 108,
"preview": "\n\n\n\n"
},
{
"path": "Assets/OLID/Preview/README.md",
"chars": 26,
"preview": "\n"
},
{
"path": "Assets/PCC/Preview/README.md",
"chars": 66,
"preview": "\n\n\n"
},
{
"path": "Assets/PPT/Preview/README.md",
"chars": 66,
"preview": "\n\n\n"
},
{
"path": "Assets/RA/Preview/README.md",
"chars": 55,
"preview": "\n\n"
},
{
"path": "Assets/SB/Preview/README.md",
"chars": 88,
"preview": "\n\n\n\n"
},
{
"path": "Assets/SBDE/Preview/README.md",
"chars": 44,
"preview": "\n\n"
},
{
"path": "Assets/SLD/Preview/README.md",
"chars": 26,
"preview": "\n"
},
{
"path": "Assets/ST/Preview/README.md",
"chars": 154,
"preview": "\n\n\n\n\n\n\n\n"
},
{
"path": "Assets/UCB/Preview/README.md",
"chars": 73,
"preview": "\n\n\n"
},
{
"path": "Assets/VCN/Preview/README.md",
"chars": 94,
"preview": "\n\n\n\n"
},
{
"path": "Assets/VGR/Preview/README.md",
"chars": 44,
"preview": "\n\n"
},
{
"path": "AutoCorrect.plugin.js",
"chars": 13612,
"preview": "/**\r\n * @name AutoCorrect\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www.pay"
},
{
"path": "AutoRefreshSettingsPanel.plugin.js",
"chars": 895,
"preview": "//META{\"name\":\"AutoRefreshSettingsPanel\"}*//\r\n\r\nclass AutoRefreshSettingsPanel {\r\n\t\r\n getName() { return \"AutoRefresh"
},
{
"path": "AvatarIconViewer.plugin.js",
"chars": 6859,
"preview": "/**\r\n * @name AvatarIconViewer\r\n * @invite yNqzuJa\r\n * @authorLink https://discord.com/users/264163473179672576\r\n * @don"
},
{
"path": "BDEmoteAutocomplete.plugin.js",
"chars": 12181,
"preview": "//META{\"name\":\"BDEmoteAutocomplete\",\"website\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/README.md"
},
{
"path": "BetterEmoteSizes.plugin.js",
"chars": 8181,
"preview": "//META{\"name\":\"BetterEmoteSizes\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github."
},
{
"path": "Bruh.plugin.js",
"chars": 3747,
"preview": "/**\r\n * @name Bruh\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www.paypal.me/"
},
{
"path": "CustomJs.plugin.js",
"chars": 6870,
"preview": "//META{\"name\":\"CustomJs\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Meta"
},
{
"path": "CustomizableAvatarDPI.plugin.js",
"chars": 3877,
"preview": "//META{\"name\":\"CustomizableAvatarDPI\",\"website\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/README."
},
{
"path": "DetailedServerTooltips.plugin.js",
"chars": 13701,
"preview": "//META{\"name\":\"DetailedServerTooltips\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://g"
},
{
"path": "DoubleClickVoiceChannels.plugin.js",
"chars": 4484,
"preview": "/**\r\n * @name DoubleClickVoiceChannels\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate ht"
},
{
"path": "FormattableMessageCopier.plugin.js",
"chars": 18147,
"preview": "//META{\"name\":\"FormattableMessageCopier\"}*//\r\n\r\nclass FormattableMessageCopier {\r\n\t\r\n\tgetSettingsPanel(){\r\n \r"
},
{
"path": "GuildAndFriendRemovalAlerts/GuildAndFriendRemovalAlerts.plugin.js",
"chars": 35264,
"preview": "/**\n * @name GuildAndFriendRemovalAlerts\n * @version 3.2.1\n * @description Displays alerts when you are kicked/banned fr"
},
{
"path": "GuildAndFriendRemovalAlerts/README.md",
"chars": 328,
"preview": "# GuildAndFriendRemovalAlerts\n\n> Displays alerts when you are kicked/banned from a server, a server is deleted, and when"
},
{
"path": "GuildAndFriendRemovalAlerts/src/components/item.jsx",
"chars": 926,
"preview": "import React from \"react\";\nimport styles from \"./item.scss\";\nimport { joinClassNames } from \"@discord/utils\";\nimport { "
},
{
"path": "GuildAndFriendRemovalAlerts/src/components/item.scss",
"chars": 683,
"preview": ".item {\n display: flex;\n margin: 5px;\n padding: 10px;\n cursor: pointer;\n border-radius: 5px;\n \n &:hover {\n ba"
},
{
"path": "GuildAndFriendRemovalAlerts/src/components/settings.jsx",
"chars": 952,
"preview": "import React from \"react\";\nimport createUpdateWrapper from \"common/hooks/createUpdateWrapper\";\nimport { WebpackModules "
},
{
"path": "GuildAndFriendRemovalAlerts/src/index.js",
"chars": 7546,
"preview": "import BasePlugin from \"@zlibrary/plugin\";\nimport { Logger, Patcher, PluginUtilities, ReactComponents, WebpackModules } "
},
{
"path": "GuildAndFriendRemovalAlerts/src/modules/settings.js",
"chars": 471,
"preview": "import pkg from \"../package.json\";\nimport { PluginUtilities } from \"@zlibrary\";\n\nexport default class Settings {\n st"
},
{
"path": "GuildAndFriendRemovalAlerts/src/package.json",
"chars": 1231,
"preview": "{\n\t\"info\": {\n\t\t\"name\": \"GuildAndFriendRemovalAlerts\",\n\t\t\"version\": \"3.0.0\",\n\t\t\"description\": \"Displays alerts when you a"
},
{
"path": "GuildAndFriendRemovalAlerts/src/styles.scss",
"chars": 611,
"preview": ".modal {\n color: white;\n \n .itemContainer {\n .title {\n margin: 20px;\n font-size: 1.2rem;\n font-wei"
},
{
"path": "GuildAndFriendRemovalAlerts.plugin.js",
"chars": 2547,
"preview": "/**\r\n * @name GuildAndFriendRemovalAlerts\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate"
},
{
"path": "GuildCounter.plugin.js",
"chars": 2383,
"preview": "//META{\"name\":\"GuildCounter\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/"
},
{
"path": "IdleGuildlistScroller.plugin.js",
"chars": 4072,
"preview": "//META{\"name\":\"IdleGuildlistScroller\"}*//\r\n\r\nclass IdleGuildlistScroller {\r\n\t\r\n getName() { return \"Idle Guildlist Sc"
},
{
"path": "ImageBrowser.plugin.js",
"chars": 8165,
"preview": "//META{\"name\":\"ImageBrowser\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/"
},
{
"path": "Lib/NeatoBurritoLibrary.js",
"chars": 94159,
"preview": "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.spli"
},
{
"path": "MentionAliases.plugin.js",
"chars": 16032,
"preview": "/**\r\n * @name MentionAliases\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www."
},
{
"path": "NateUtilities.plugin.js",
"chars": 6486,
"preview": "//META{\"name\":\"NateUtilities\"}*//\r\n\r\nclass NateUtilities {\r\n\t\r\n getName() { return \"NateUtilities\"; }\r\n getDescrip"
},
{
"path": "OpenLinksInDiscord.plugin.js",
"chars": 3550,
"preview": "//META{\"name\":\"OpenLinksInDiscord\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://githu"
},
{
"path": "PinCollapsedChannels.plugin.js",
"chars": 3678,
"preview": "//META{\"name\":\"PinCollapsedChannels\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://git"
},
{
"path": "PinPluginsAndThemes.plugin.js",
"chars": 7122,
"preview": "//META{\"name\":\"PinPluginsAndThemes\",\"website\":\"https://github.com/Metalloriff/BetterDiscordPlugins/blob/master/README.md"
},
{
"path": "PreventSpotifyAutoPause.plugin.js",
"chars": 5087,
"preview": "//META{\"name\":\"PreventSpotifyAutoPause\",\"displayName\":\"PreventSpotifyAutoPause\",\"website\":\"https://metalloriff.github.io"
},
{
"path": "README.md",
"chars": 749,
"preview": "# BetterDiscordPlugins\n\n## NOTE: Due to loss of interest in BetterDiscord development, I'm pursuing other projects and n"
},
{
"path": "ReactionImages.plugin.js",
"chars": 10320,
"preview": "/**\r\n * @name ReactionImages\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www."
},
{
"path": "SaveTo.plugin.js",
"chars": 16652,
"preview": "//META{\"name\":\"SaveTo\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/Metall"
},
{
"path": "SelectedChannelNotifications.plugin.js",
"chars": 4163,
"preview": "//META{\"name\":\"SelectedChannelNotifications\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"htt"
},
{
"path": "SendBDEmotes.plugin.js",
"chars": 10799,
"preview": "//META{\"name\":\"SendBDEmotes\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/"
},
{
"path": "SendLinksDirectly.plugin.js",
"chars": 4215,
"preview": "//META{\"name\":\"SendLinksDirectly\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github"
},
{
"path": "ShareButton.plugin.js",
"chars": 22759,
"preview": "//META{\"name\":\"ShareButton\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github.com/M"
},
{
"path": "SuppressUserMentions.plugin.js",
"chars": 3365,
"preview": "//META{\"name\":\"SuppressUserMentions\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://git"
},
{
"path": "TheClapBestClapPluginClapEver.plugin.js",
"chars": 4622,
"preview": "/**\r\n * @name TheClapBestClapPluginClapEver\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @dona"
},
{
"path": "TransitioningBackgrounds.plugin.js",
"chars": 14427,
"preview": "/**\r\n * @name TransitioningBackgrounds\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate ht"
},
{
"path": "UnreadCountBadges.plugin.js",
"chars": 15525,
"preview": "//META{\"name\":\"UnreadCountBadges\",\"website\":\"https://metalloriff.github.io/toms-discord-stuff/\",\"source\":\"https://github"
},
{
"path": "UserBirthdays.plugin.js",
"chars": 14114,
"preview": "/**\n * @name UserBirthdays\n * @invite yNqzuJa\n * @authorLink https://github.com/metalloriff\n * @donate https://www.paypa"
},
{
"path": "VCMuteSounds.plugin.js",
"chars": 4829,
"preview": "//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.un"
},
{
"path": "VideoExamples/README.md",
"chars": 1,
"preview": "\n"
},
{
"path": "ViewGuildRelationships.plugin.js",
"chars": 12371,
"preview": "/**\r\n * @name ViewGuildRelationships\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate http"
},
{
"path": "VoiceChatNotifications.plugin.js",
"chars": 8091,
"preview": "/**\r\n * @name VoiceChatNotifications\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate http"
},
{
"path": "VoiceChatPanel.plugin.js",
"chars": 10367,
"preview": "/**\r\n * @name VoiceChatPanel\r\n * @invite yNqzuJa\r\n * @authorLink https://github.com/Metalloriff\r\n * @donate https://www."
}
]
About this extraction
This page contains the full source code of the Metalloriff/BetterDiscordPlugins GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 73 files (442.3 KB), approximately 115.7k tokens, and a symbol index with 490 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.