Upload files to 'Plugins'

This commit is contained in:
Dollar3795 2021-03-05 08:15:16 +00:00
parent af8fbe66e4
commit b0ec95966a
5 changed files with 8100 additions and 0 deletions

View File

@ -0,0 +1,603 @@
//META{"name":"InAppNotifications","source":"https://github.com/1Lighty/BetterDiscordPlugins/blob/master/Plugins/InAppNotifications/InAppNotifications.plugin.js","website":"https://1lighty.github.io/BetterDiscordStuff/?plugin=InAppNotifications","authorId":"239513071272329217","invite":"NYvWdN5","donate":"https://paypal.me/lighty13"}*//
/*@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.\nJust go to settings, plugins and enable me.', 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!\nJust go to settings, plugins and enable me!', 0, 'Successfully installed', 0x40);
}
WScript.Quit();
@else@*/
/*
* Copyright © 2019-2020, _Lighty_
* All rights reserved.
* Code may not be redistributed, modified or otherwise taken without explicit permission.
*/
module.exports = (() => {
/* Setup */
const config = {
main: 'index.js',
info: {
name: 'InAppNotifications',
authors: [
{
name: 'Lighty',
discord_id: '239513071272329217',
github_username: 'LightyPon',
twitter_username: ''
}
],
version: '1.0.10',
description: 'Show a notification in Discord when someone sends a message, just like on mobile.',
github: 'https://github.com/1Lighty',
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/InAppNotifications/InAppNotifications.plugin.js'
},
defaultConfig: [
{
name: 'Ignore DND mode',
id: 'dndIgnore',
type: 'switch',
value: true
},
{
name: 'Bar color',
id: 'color',
type: 'color',
value: '#4a90e2',
options: {
defaultColor: '#4a90e2'
}
},
{
name: 'Set bar color to top role color',
id: 'roleColor',
type: 'switch',
value: true
},
{
name: 'Calculate timeout by number of words',
note: 'Long text will stay for longer',
id: 'wpmTimeout',
type: 'switch',
value: true
},
{
name: 'Words per minute',
id: 'wordsPerMinute',
type: 'slider',
value: 300,
min: 50,
max: 900,
markers: Array.from(Array(18), (_, i) => (i + 1) * 50),
stickToMarkers: true
},
{
type: 'note'
}
],
changelog: [
{
title: 'RIP BBD on Canary',
type: 'fixed',
items: ['Implemented fixes that allow patches to work properly on canary using Powercord.']
}
]
};
/* Build */
const buildPlugin = ([Plugin, Api]) => {
const { ContextMenu, EmulatedTooltip, Toasts, Settings, Popouts, Modals, Utilities, WebpackModules, Filters, DiscordModules, ColorConverter, DOMTools, DiscordClasses, DiscordSelectors, ReactTools, ReactComponents, DiscordAPI, Logger, PluginUpdater, PluginUtilities, DiscordClassModules, Structs } = Api;
const { React, ModalStack, ContextMenuActions, ContextMenuItem, ContextMenuItemsGroup, ReactDOM, GuildStore, UserStore, DiscordConstants, Dispatcher, GuildMemberStore, GuildActions, SwitchRow, EmojiUtils, RadioGroup, Permissions, FlexChild, PopoutOpener, Textbox, RelationshipStore, WindowInfo, UserSettingsStore, NavigationUtils, UserNameResolver, SelectedChannelStore } = DiscordModules;
const Patcher = XenoLib.createSmartPatcher(Api.Patcher);
const ChannelStore = WebpackModules.getByProps('getChannel', 'getDMFromUserId');
const LurkerStore = WebpackModules.getByProps('isLurking');
const MuteStore = WebpackModules.getByProps('allowNoMessages');
const isMentionedUtils = WebpackModules.getByProps('isRawMessageMentioned');
const ParserModule = WebpackModules.getByProps('astParserFor', 'parse');
const MessageClasses = WebpackModules.getByProps('username', 'messageContent');
const MarkupClassname = XenoLib.getClass('markup');
const Messages = (WebpackModules.getByProps('Messages') || {}).Messages;
const SysMessageUtils = WebpackModules.getByProps('getSystemMessageUserJoin', 'stringify');
const MessageParseUtils = (WebpackModules.getByProps('parseAndRebuild', 'default') || {}).default;
const CUser = WebpackModules.getByPrototypes('getAvatarSource', 'isLocalBot');
const TextElement = WebpackModules.getByDisplayName('Text');
class ExtraText extends Settings.SettingField {
constructor(name, note) {
super(name, note, null, TextElement, {
children: 'To change the position or backdrop background color of the notifications, check XenoLib settings.'
});
}
}
const currentChannel = _ => {
const channel = ChannelStore.getChannel(SelectedChannelStore.getChannelId());
return channel ? Structs.Channel.from(channel) : null;
}
return class InAppNotifications extends Plugin {
constructor() {
super();
XenoLib.changeName(__filename, 'InAppNotifications');
const oOnStart = this.onStart.bind(this);
this.onStart = () => {
try {
oOnStart();
} catch (e) {
Logger.stacktrace('Failed to start!', e);
PluginUpdater.checkForUpdate(this.name, this.version, this._config.info.github_raw);
XenoLib.Notifications.error(`[**${this.name}**] Failed to start! Please update it, press CTRL + R, or ${GuildStore.getGuild(XenoLib.supportServerId) ? 'go to <#639665366380838924>' : '[join my support server](https://discord.gg/NYvWdN5)'} for further assistance.`, { timeout: 0 });
try {
this.onStop();
} catch (e) { }
}
};
const oMESSAGE_CREATE = this.MESSAGE_CREATE.bind(this);
this.MESSAGE_CREATE = e => {
try {
oMESSAGE_CREATE(e);
} catch (e) {
this.errorCount++;
if (this.errorCount >= 10) {
Logger.stacktrace('Error in MESSAGE_CREATE dispatch handler', e);
PluginUpdater.checkForUpdate(this.name, this.version, this._config.info.github_raw);
XenoLib.Notifications.error(`[**${this.name}**] Plugin is throwing errors and is in a broken state, please update it or ${GuildStore.getGuild(XenoLib.supportServerId) ? 'go to <#639665366380838924>' : '[join my support server](https://discord.gg/NYvWdN5)'} for further assistance.`, { timeout: 0 });
try {
this.onStop();
} catch (e) { }
}
}
};
try {
WebpackModules.getByProps('openModal', 'hasModalOpen').closeModal(`${this.name}_DEP_MODAL`);
} catch (e) { }
}
onStart() {
try {
/* do not, under any circumstances, let this kill the plugin */
const CUSTOM_RULES = XenoLib._.cloneDeep(WebpackModules.getByProps('RULES').RULES);
for (let rule of Object.keys(CUSTOM_RULES)) CUSTOM_RULES[rule].raw = null;
for (let rule of ['paragraph', 'text', 'codeBlock', 'emoji', 'inlineCode']) CUSTOM_RULES[rule].raw = e => e.content;
for (let rule of ['autolink', 'br', 'link', 'newline', 'url']) delete CUSTOM_RULES[rule];
for (let rule of ['blockQuote', 'channel', 'em', 'mention', 'roleMention', 's', 'spoiler', 'strong', 'u']) CUSTOM_RULES[rule].raw = (e, t, n) => t(e.content, n);
CUSTOM_RULES.customEmoji.raw = e => e.name;
const astTools = WebpackModules.getByProps('flattenAst');
const SimpleMarkdown = WebpackModules.getByProps('parserFor', 'outputFor');
const parser = SimpleMarkdown.parserFor(CUSTOM_RULES);
const render = SimpleMarkdown.htmlFor(SimpleMarkdown.ruleOutput(CUSTOM_RULES, 'raw'));
this._timeUnparser = (e = '', r = true, a = {}) => render(astTools.constrainAst(astTools.flattenAst(parser(e, Object.assign({ inline: r }, a)))));
} catch (err) {
Logger.stacktrace('Failed to create custom unparser', err);
this._timeUnparser = null;
}
this.errorCount = 0;
Dispatcher.subscribe('MESSAGE_CREATE', this.MESSAGE_CREATE);
this.patchAll();
PluginUtilities.addStyle(
this.short + '-CSS',
`
.IAN-message {
padding-left: 40px;
position: relative;
min-height: 36px;
pointer-events: none;
}
.IAN-message .IAN-avatar {
left: -2px;
pointer-events: none;
width: 32px;
height: 32px;
top: 0;
position: absolute;
border-radius: 50%;
}
.IAN-message .${MessageClasses.username.split(' ')[0]} {
font-size: 0.9rem;
line-height: unset;
}
.IAN-message .${MarkupClassname.split(' ')[0]} {
line-height: unset;
}
.IAN-message .${MarkupClassname.split(' ')[0]} {
max-height: calc(100vh - 150px);
}
.IAN-message .${MarkupClassname.split(' ')[0]}, .IAN-message .${MessageClasses.username.split(' ')[0]} {
overflow: hidden
}
`
);
}
onStop() {
Dispatcher.unsubscribe('MESSAGE_CREATE', this.MESSAGE_CREATE);
Patcher.unpatchAll();
PluginUtilities.removeStyle(this.short + '-CSS');
}
/* zlib uses reference to defaultSettings instead of a cloned object, which sets settings as default settings, messing everything up */
loadSettings(defaultSettings) {
return PluginUtilities.loadSettings(this.name, Utilities.deepclone(this.defaultSettings ? this.defaultSettings : defaultSettings));
}
buildSetting(data) {
if (data.type === 'color') {
const setting = new XenoLib.Settings.ColorPicker(data.name, data.note, data.value, data.onChange, data.options);
if (data.id) setting.id = data.id;
return setting;
} else if (data.type === 'note') {
return new ExtraText('', '');
}
return super.buildSetting(data);
}
_shouldNotify(iAuthor, iChannel) {
if (iChannel.isManaged()) return false;
const guildId = iChannel.getGuildId();
if (guildId && LurkerStore.isLurking(guildId)) return false;
if (iAuthor.id === DiscordAPI.currentUser.id || RelationshipStore.isBlocked(iAuthor.id)) return false;
if (!this.settings.dndIgnore && UserSettingsStore.status === DiscordConstants.StatusTypes.DND) return false;
if (MuteStore.allowNoMessages(iChannel)) return false;
return true;
}
shouldNotify(message, iChannel, iAuthor) {
if (!DiscordAPI.currentUser || !iChannel || !iAuthor) return false;
/* dunno what the func name is as this is copied from discord, so I named it _shouldNotify */
if (!this._shouldNotify(iAuthor, iChannel)) return false;
if (currentChannel() && currentChannel().id === iChannel.id) return false;
/* channel has notif settings set to all messages */
if (MuteStore.allowAllMessages(iChannel)) return true;
const everyoneSuppressed = MuteStore.isSuppressEveryoneEnabled(iChannel.guild_id);
const rolesSuppressed = MuteStore.isSuppressRolesEnabled(iChannel.guild_id);
/* only if mentioned, but only if settings allow */
return isMentionedUtils.isRawMessageMentioned(message, DiscordAPI.currentUser.id, everyoneSuppressed, rolesSuppressed);
}
getChannelName(iChannel, iAuthor) {
switch (iChannel.type) {
case DiscordConstants.ChannelTypes.GROUP_DM:
if ('' !== iChannel.name) return iChannel.name;
const recipients = iChannel.recipients.map(e => (e === iAuthor.id ? iAuthor : UserStore.getUser(e))).filter(e => e);
return recipients.length > 0 ? recipients.map(e => e.username).join(', ') : Messages.UNNAMED;
case DiscordConstants.ChannelTypes.GUILD_ANNOUNCEMENT:
case DiscordConstants.ChannelTypes.GUILD_TEXT:
return '#' + iChannel.name;
default:
return iChannel.name;
}
}
getActivity(e, t, n, r) {
switch (e.type) {
case DiscordConstants.ChannelTypes.GUILD_ANNOUNCEMENT:
case DiscordConstants.ChannelTypes.GUILD_TEXT:
return t;
case DiscordConstants.ChannelTypes.GROUP_DM:
return n;
case DiscordConstants.ChannelTypes.DM:
default:
return r;
}
}
makeTextChatNotification(iChannel, message, iAuthor) {
let author = UserNameResolver.getName(iChannel.guild_id, iChannel.id, iAuthor);
let channel = author;
switch (iChannel.type) {
case DiscordConstants.ChannelTypes.GUILD_ANNOUNCEMENT:
case DiscordConstants.ChannelTypes.GUILD_TEXT:
const iGuild = GuildStore.getGuild(iChannel.guild_id);
if (message.type === DiscordConstants.MessageTypes.DEFAULT || iGuild) channel += ` (${this.getChannelName(iChannel)}, ${iGuild.name})`;
break;
case DiscordConstants.ChannelTypes.GROUP_DM:
const newChannel = this.getChannelName(iChannel, iAuthor);
if (!iChannel.isManaged() || !iAuthor.bot || channel !== newChannel) channel += ` (${newChannel})`;
}
let d = message.content;
if (message.activity && message.application) {
const targetMessage = message.activity.type === DiscordConstants.ActivityActionTypes.JOIN ? this.getActivity(iChannel, Messages.NOTIFICATION_MESSAGE_CREATE_GUILD_ACTIVITY_JOIN, Messages.NOTIFICATION_MESSAGE_CREATE_GROUP_DM_ACTIVITY_JOIN, Messages.NOTIFICATION_MESSAGE_CREATE_DM_ACTIVITY_JOIN) : this.getActivity(iChannel, Messages.NOTIFICATION_MESSAGE_CREATE_GUILD_ACTIVITY_SPECTATE, Messages.NOTIFICATION_MESSAGE_CREATE_GROUP_DM_ACTIVITY_SPECTATE, Messages.NOTIFICATION_MESSAGE_CREATE_DM_ACTIVITY_SPECTATE);
d = targetMessage.format({ user: author, game: message.application.name });
} else if (message.activity && message.activity.type === DiscordConstants.ActivityActionTypes.LISTEN) {
const targetMessage = this.getActivity(iChannel, Messages.NOTIFICATION_MESSAGE_CREATE_GUILD_ACTIVITY_LISTEN, Messages.NOTIFICATION_MESSAGE_CREATE_GROUP_DM_ACTIVITY_LISTEN, Messages.NOTIFICATION_MESSAGE_CREATE_DM_ACTIVITY_LISTEN);
d = targetMessage.format({ user: author });
} else if (message.type !== DiscordConstants.MessageTypes.DEFAULT) {
const content = SysMessageUtils.stringify(message);
if (!content) return null;
d = MessageParseUtils.unparse(content, iChannel.id, true);
}
if (!d.length && message.attachments.length) d = Messages.NOTIFICATION_BODY_ATTACHMENT.format({ filename: message.attachments[0].filename });
if (!d.length && message.embeds.length) {
const embed = message.embeds[0];
if (embed.description) d = embed.title ? embed.title + ': ' + embed.description : embed.description;
else if (embed.title) d = embed.title;
else if (embed.fields) {
const field = embed.fields[0];
d = field.name + ': ' + field.value;
}
}
return {
icon: iAuthor.getAvatarURL(),
title: channel,
content: d
};
}
MESSAGE_CREATE({ channelId, message }) {
const iChannel = ChannelStore.getChannel(channelId);
let iAuthor = UserStore.getUser(message.author.id);
if (!iAuthor) {
iAuthor = new CUser(message.author);
UserStore.getUsers()[message.author.id] = iAuthor;
}
if (!iChannel || !iAuthor) return;
if (!this.shouldNotify(message, iChannel, iAuthor)) return;
const notif = this.makeTextChatNotification(iChannel, message, iAuthor);
if (!notif) return; /* wah */
const member = GuildMemberStore.getMember(iChannel.guild_id, iAuthor.id);
this.showNotification(notif, iChannel, this.settings.roleColor && member && member.colorString);
}
calculateTime(text) {
let words = 0;
if (this._timeUnparser) {
try {
text = this._timeUnparser(text);
} catch (err) {
Logger.stacktrace(`Failed to unparse text ${text}`, err);
this._timeUnparser = null;
}
}
/* https://github.com/ngryman/reading-time */
function ansiWordBound(c) {
return ' ' === c || '\n' === c || '\r' === c || '\t' === c;
}
for (var i = 0; i < text.length;) {
for (; i < text.length && !ansiWordBound(text[i]); i++);
words++;
for (; i < text.length && ansiWordBound(text[i]); i++);
}
return (words / this.settings.wordsPerMinute) * 60 * 1000;
}
showNotification(notif, iChannel, color) {
const timeout = this.settings.wpmTimeout ? Math.min(this.calculateTime(notif.title) + this.calculateTime(notif.content), 60000) : 0;
const notificationId = XenoLib.Notifications.show(
React.createElement(
'div',
{
className: 'IAN-message'
},
React.createElement('img', {
className: 'IAN-avatar',
src: notif.icon
}),
React.createElement(
'span',
{
className: MessageClasses.username
},
notif.title
),
React.createElement('div', { className: XenoLib.joinClassNames(MarkupClassname, MessageClasses.messageContent) }, ParserModule.parse(notif.content, true, { channelId: iChannel.id }))
),
{
timeout: Math.max(5000, timeout),
onClick: () => {
NavigationUtils.transitionTo(`/channels/${iChannel.guild_id || '@me'}/${iChannel.id}`);
XenoLib.Notifications.remove(notificationId);
},
color: color || this.settings.color
}
);
}
/* PATCHES */
patchAll() {
Utilities.suppressErrors(this.patchShouldNotify.bind(this), 'shouldNotify patch')();
}
patchShouldNotify() {
Patcher.after(WebpackModules.getByProps('shouldDisplayNotifications'), 'shouldDisplayNotifications', () => (WindowInfo.isFocused() ? false : undefined));
}
/* PATCHES */
showChangelog(footer) {
XenoLib.showChangelog(`${this.name} has been updated!`, this.version, this._config.changelog);
}
getSettingsPanel() {
return this.buildSettingsPanel().getElement();
}
get [Symbol.toStringTag]() {
return 'Plugin';
}
get name() {
return config.info.name;
}
get short() {
let string = '';
for (let i = 0, len = config.info.name.length; i < len; i++) {
const char = config.info.name[i];
if (char === char.toUpperCase()) string += char;
}
return string;
}
get author() {
return config.info.authors.map(author => author.name).join(', ');
}
get version() {
return config.info.version;
}
get description() {
return config.info.description;
}
};
};
/* Finalize */
let ZeresPluginLibraryOutdated = false;
let XenoLibOutdated = false;
try {
const i = (i, n) => ((i = i.split('.').map(i => parseInt(i))), (n = n.split('.').map(i => parseInt(i))), !!(n[0] > i[0]) || !!(n[0] == i[0] && n[1] > i[1]) || !!(n[0] == i[0] && n[1] == i[1] && n[2] > i[2])),
n = (n, e) => n && n._config && n._config.info && n._config.info.version && i(n._config.info.version, e),
e = BdApi.Plugins.get('ZeresPluginLibrary'),
o = BdApi.Plugins.get('XenoLib');
n(e, '1.2.27') && (ZeresPluginLibraryOutdated = !0), n(o, '1.3.32') && (XenoLibOutdated = !0);
} catch (i) {
console.error('Error checking if libraries are out of date', i);
}
return !global.ZeresPluginLibrary || !global.XenoLib || ZeresPluginLibraryOutdated || XenoLibOutdated
? class {
constructor() {
this._XL_PLUGIN = true;
this.start = this.load = this.handleMissingLib;
}
getName() {
return this.name.replace(/\s+/g, '');
}
getAuthor() {
return this.author;
}
getVersion() {
return this.version;
}
getDescription() {
return this.description + ' You are missing libraries for this plugin, please enable the plugin and click Download Now.';
}
start() { }
stop() { }
handleMissingLib() {
const a = BdApi.findModuleByProps('openModal', 'hasModalOpen');
if (a && a.hasModalOpen(`${this.name}_DEP_MODAL`)) return;
const b = !global.XenoLib,
c = !global.ZeresPluginLibrary,
d = (b && c) || ((b || c) && (XenoLibOutdated || ZeresPluginLibraryOutdated)),
e = (() => {
let a = '';
return b || c ? (a += `Missing${XenoLibOutdated || ZeresPluginLibraryOutdated ? ' and outdated' : ''} `) : (XenoLibOutdated || ZeresPluginLibraryOutdated) && (a += `Outdated `), (a += `${d ? 'Libraries' : 'Library'} `), a;
})(),
f = (() => {
let a = `The ${d ? 'libraries' : 'library'} `;
return b || XenoLibOutdated ? ((a += 'XenoLib '), (c || ZeresPluginLibraryOutdated) && (a += 'and ZeresPluginLibrary ')) : (c || ZeresPluginLibraryOutdated) && (a += 'ZeresPluginLibrary '), (a += `required for ${this.name} ${d ? 'are' : 'is'} ${b || c ? 'missing' : ''}${XenoLibOutdated || ZeresPluginLibraryOutdated ? (b || c ? ' and/or outdated' : 'outdated') : ''}.`), a;
})(),
g = BdApi.findModuleByDisplayName('Text'),
h = BdApi.findModuleByDisplayName('ConfirmModal'),
i = () => BdApi.alert(e, BdApi.React.createElement('span', {}, BdApi.React.createElement('div', {}, f), `Due to a slight mishap however, you'll have to download the libraries yourself. This is not intentional, something went wrong, errors are in console.`, c || ZeresPluginLibraryOutdated ? BdApi.React.createElement('div', {}, BdApi.React.createElement('a', { href: 'https://betterdiscord.net/ghdl?id=2252', target: '_blank' }, 'Click here to download ZeresPluginLibrary')) : null, b || XenoLibOutdated ? BdApi.React.createElement('div', {}, BdApi.React.createElement('a', { href: 'https://betterdiscord.net/ghdl?id=3169', target: '_blank' }, 'Click here to download XenoLib')) : null));
if (!a || !h || !g) return console.error(`Missing components:${(a ? '' : ' ModalStack') + (h ? '' : ' ConfirmationModalComponent') + (g ? '' : 'TextElement')}`), i();
class j extends BdApi.React.PureComponent {
constructor(a) {
super(a), (this.state = { hasError: !1 }), (this.componentDidCatch = a => (console.error(`Error in ${this.props.label}, screenshot or copy paste the error above to Lighty for help.`), this.setState({ hasError: !0 }), 'function' == typeof this.props.onError && this.props.onError(a))), (this.render = () => (this.state.hasError ? null : this.props.children));
}
}
let k = !1,
l = !1;
const m = a.openModal(
b => {
if (l) return null;
try {
return BdApi.React.createElement(
j,
{ label: 'missing dependency modal', onError: () => (a.closeModal(m), i()) },
BdApi.React.createElement(
h,
Object.assign(
{
header: e,
children: BdApi.React.createElement(g, { size: g.Sizes.SIZE_16, children: [`${f} Please click Download Now to download ${d ? 'them' : 'it'}.`] }),
red: !1,
confirmText: 'Download Now',
cancelText: 'Cancel',
onCancel: b.onClose,
onConfirm: () => {
if (k) return;
k = !0;
const b = require('request'),
c = require('fs'),
d = require('path'),
e = BdApi.Plugins && BdApi.Plugins.folder ? BdApi.Plugins.folder : window.ContentManager.pluginsFolder,
f = () => {
(global.XenoLib && !XenoLibOutdated) ||
b('https://gitdab.com/Dollar3795/LightcordPlugins/raw/branch/master/Plugins/1XenoLib.plugin.js', (b, f, g) => {
try {
if (b || 200 !== f.statusCode) return a.closeModal(m), i();
c.writeFile(d.join(e, '1XenoLib.plugin.js'), g, () => { });
} catch (b) {
console.error('Fatal error downloading XenoLib', b), a.closeModal(m), i();
}
});
};
!global.ZeresPluginLibrary || ZeresPluginLibraryOutdated
? b('https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js', (b, g, h) => {
try {
if (b || 200 !== g.statusCode) return a.closeModal(m), i();
c.writeFile(d.join(e, '0PluginLibrary.plugin.js'), h, () => { }), f();
} catch (b) {
console.error('Fatal error downloading ZeresPluginLibrary', b), a.closeModal(m), i();
}
})
: f();
}
},
b,
{ onClose: () => { } }
)
)
);
} catch (b) {
return console.error('There has been an error constructing the modal', b), (l = !0), a.closeModal(m), i(), null;
}
},
{ modalKey: `${this.name}_DEP_MODAL` }
);
}
get [Symbol.toStringTag]() {
return 'Plugin';
}
get name() {
return config.info.name;
}
get short() {
let string = '';
for (let i = 0, len = config.info.name.length; i < len; i++) {
const char = config.info.name[i];
if (char === char.toUpperCase()) string += char;
}
return string;
}
get author() {
return config.info.authors.map(author => author.name).join(', ');
}
get version() {
return config.info.version;
}
get description() {
return config.info.description;
}
}
: buildPlugin(global.ZeresPluginLibrary.buildPlugin(config));
})();
/*@end@*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,534 @@
//META{"name":"MultiUploads","source":"https://github.com/1Lighty/BetterDiscordPlugins/blob/master/Plugins/MultiUploads/MultiUploads.plugin.js","website":"https://1lighty.github.io/BetterDiscordStuff/?plugin=MultiUploads","authorId":"239513071272329217","invite":"NYvWdN5","donate":"https://paypal.me/lighty13"}*//
/*@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.\nJust go to settings, plugins and enable me.', 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!\nJust go to settings, plugins and enable me!', 0, 'Successfully installed', 0x40);
}
WScript.Quit();
@else@*/
/*
* Copyright © 2019-2020, _Lighty_
* All rights reserved.
* Code may not be redistributed, modified or otherwise taken without explicit permission.
*/
module.exports = (() => {
/* Setup */
const config = {
main: 'index.js',
info: {
name: 'MultiUploads',
authors: [
{
name: 'Lighty',
discord_id: '239513071272329217',
github_username: 'LightyPon',
twitter_username: ''
}
],
version: '1.1.4',
description: 'Multiple uploads send in a single message, like on mobile. Hold shift while pressing the upload button to only upload one. Adds ability to paste multiple times.',
github: 'https://github.com/1Lighty',
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/MultiUploads/MultiUploads.plugin.js'
},
changelog: [
{
title: 'RIP BBD on Canary',
type: 'fixed',
items: ['More canary fixes.']
}
]
};
/* Build */
const buildPlugin = ([Plugin, Api]) => {
const { Utilities, WebpackModules, DiscordModules, ReactComponents, Logger, PluginUtilities } = Api;
const { ChannelStore, DiscordConstants, Dispatcher, Permissions } = DiscordModules;
const rendererFunctionClass = (() => {
try {
const topContext = require('electron').webFrame.top.context;
if (topContext === window) return null;
return topContext.Function
} catch {
return null;
}
})();
const originalFunctionClass = Function;
function createSmartPatcher(patcher) {
const createPatcher = patcher => {
return (moduleToPatch, functionName, callback, options = {}) => {
try {
var origDef = moduleToPatch[functionName];
} catch (_) {
return Logger.error(`Failed to patch ${functionName}`);
}
if (rendererFunctionClass && origDef && !(origDef instanceof originalFunctionClass) && origDef instanceof rendererFunctionClass) window.Function = rendererFunctionClass;
const unpatches = [];
try {
unpatches.push(patcher(moduleToPatch, functionName, callback, options) || DiscordConstants.NOOP);
} catch (err) {
throw err;
} finally {
if (rendererFunctionClass) window.Function = originalFunctionClass;
}
try {
if (origDef && origDef.__isBDFDBpatched && moduleToPatch.BDFDBpatch && typeof moduleToPatch.BDFDBpatch[functionName].originalMethod === 'function') {
/* do NOT patch a patch by ZLIb, that'd be bad and cause double items in context menus */
if ((Utilities.getNestedProp(ZeresPluginLibrary, 'Patcher.patches') || []).findIndex(e => e.module === moduleToPatch) !== -1 && moduleToPatch.BDFDBpatch[functionName].originalMethod.__originalFunction) return;
unpatches.push(patcher(moduleToPatch.BDFDBpatch[functionName], 'originalMethod', callback, options));
}
} catch (err) {
Logger.stacktrace('Failed to patch BDFDB patches', err);
}
return function unpatch() {
unpatches.forEach(e => e());
};
};
};
return Object.assign({}, patcher, {
before: createPatcher(patcher.before),
instead: createPatcher(patcher.instead),
after: createPatcher(patcher.after)
});
};
const Patcher = createSmartPatcher(Api.Patcher);
const _ = WebpackModules.getByProps('bindAll', 'debounce');
const Upload = (WebpackModules.getByProps('Upload') || {}).Upload;
const MessageDraftUtils = WebpackModules.getByProps('saveDraft');
const FileUtils = WebpackModules.getByProps('anyFileTooLarge');
const GenericUploaderBase = WebpackModules.find(e => e.prototype && e.prototype.upload && e.prototype.cancel && !e.__proto__.prototype.cancel);
const MessageFileUploader = WebpackModules.find(e => e.prototype && e.prototype.upload && e.__proto__ === GenericUploaderBase);
const UploadUtils = WebpackModules.getByProps('upload', 'instantBatchUpload', 'cancel');
return class MultiUploads extends Plugin {
constructor() {
super();
try {
WebpackModules.getByProps('openModal', 'hasModalOpen').closeModal(`${this.name}_DEP_MODAL`);
} catch (e) { }
_.bindAll(this, ['UPLOAD_MODAL_POP_FILE', 'UPLOAD_MODAL_PUSH_FILES', 'UPLOAD_MODAL_CLEAR_ALL_FILES']);
}
onStart() {
this.promises = { cancelled: false };
this.patchAll();
this.uploads = [];
if (!Upload) throw 'Upload class could not be found';
Dispatcher.subscribe('UPLOAD_MODAL_POP_FILE', this.UPLOAD_MODAL_POP_FILE);
Dispatcher.subscribe('UPLOAD_MODAL_PUSH_FILES', this.UPLOAD_MODAL_PUSH_FILES);
Dispatcher.subscribe('UPLOAD_MODAL_CLEAR_ALL_FILES', this.UPLOAD_MODAL_CLEAR_ALL_FILES);
}
onStop() {
this.promises.cancelled = true;
Patcher.unpatchAll();
Dispatcher.unsubscribe('UPLOAD_MODAL_POP_FILE', this.UPLOAD_MODAL_POP_FILE);
Dispatcher.unsubscribe('UPLOAD_MODAL_PUSH_FILES', this.UPLOAD_MODAL_PUSH_FILES);
Dispatcher.unsubscribe('UPLOAD_MODAL_CLEAR_ALL_FILES', this.UPLOAD_MODAL_CLEAR_ALL_FILES);
}
// Replicate UploadModalStore so we keep track of our own instead
UPLOAD_MODAL_POP_FILE() {
this.uploads.shift();
}
UPLOAD_MODAL_PUSH_FILES({ files, channelId }) {
for (const file of files) this.uploads.push(new Upload(file, channelId));
}
UPLOAD_MODAL_CLEAR_ALL_FILES() {
this.uploads = [];
}
/* PATCHES */
patchAll() {
this.patchMessageFileUploader();
this.patchUploadModal(this.promises);
this.patchInstantBatchUpload();
}
patchMessageFileUploader() {
const superagent = WebpackModules.getByProps('getXHR');
// Reverse engineered, override it entirely with our own implementation
Patcher.instead(MessageFileUploader.prototype, 'upload', (_this, [file, message = {}]) => {
const noSpoilerMessage = Object.assign({}, message);
// hasSpoiler has no use here, only used to check if images should be spoilered
delete noSpoilerMessage.hasSpoiler;
// make a fale foče if it's multi upload, and set the name to represent how many files we're uploading
const fakeFile = Array.isArray(file) ? Object.assign({}, file[0], { name: `Uploading ${file.length} files...` }) : file;
// call super.upload(fakeFile, noSpoilerImage);
// since no access to super, this is the next best thing
GenericUploaderBase.prototype.upload.call(_this, fakeFile, noSpoilerMessage);
const req = superagent.post(_this._url);
// if it's multiple files, attach them, each having a different field name
// this was reversed by snooping mobile uploads, apparently each file is its own field
// each field being file(file index) so file0 file1 file2 file3 file4 etc, with file being the default single upload
if (Array.isArray(file)) {
const numMap = {};
file.forEach((e, idx) => {
let name = e.name;
// ensure no other file has the same name, otherwise we'll be in a world of pain
if (file.find((e_, idx_) => e_.name === name && idx_ < idx)) {
if (!numMap[name]) numMap[name] = 0;
numMap[name]++;
const split = name.split('.');
// no extention, just append the number
if (split.length === 1) name = `${numMap[name]}`;
else {
// extract everything before the extension, add number, then the extension
const beforeExt = split.slice(0, -1);
const ext = split.slice(-1);
name = `${beforeExt.join('.')}${numMap[name]}.${ext}`;
}
}
// attach, with its own unique file field
req.attach('file' + idx, e, (message.hasSpoiler ? 'SPOILER_' : '') + name);
});
}
else req.attach('file', file, (message.hasSpoiler ? 'SPOILER_' : '') + file.name);
// added on replies update, dunno why? throws error if it's not here
req.field('payload_json', JSON.stringify(noSpoilerMessage));
// attach all other fields, sometimes value is a non valid type though
_.each(noSpoilerMessage, (value, key) => {
if (!value) return;
req.field(key, value);
});
req.then(e => {
if (e.ok) _this._handleComplete()
else _this._handleError(e.body && e.body.code)
}, _ => _this._handleError());
const { xhr } = req;
if (xhr.upload) xhr.upload.onprogress = (...props) => _this._handleXHRProgress(...props);
xhr.addEventListener('progress', _this._handleXHRProgress, false);
_this._handleStart(_ => req.abort());
})
}
async patchUploadModal(promiseState) {
const Upload = await ReactComponents.getComponentByName('Upload', `.${WebpackModules.getByProps('uploadModal').uploadModal.split(' ')[0]}`);
if (promiseState.cancelled) return;
const ParseUtils = WebpackModules.getByProps('parsePreprocessor');
const WYSIWYGSerializeDeserialize = WebpackModules.getByProps('serialize', 'deserialize');
const UploadModalUtils = WebpackModules.getByProps('popFirstFile');
// our own function, just to check if we can send multiple messages at once
Patcher.instead(Upload.component.prototype, 'canSendBulk', _this => {
const { channel } = _this.props;
if (!channel.rateLimitPerUser) return true;
return Permissions.can(DiscordConstants.Permissions.MANAGE_CHANNELS, channel) || Permissions.can(DiscordConstants.Permissions.MANAGE_MESSAGES, channel);
});
// override this function in particular as it's easy to patch and is run after all checks are validated
// which are @everyone, slowmode, whatnot
Patcher.instead(Upload.component.prototype, 'submitUpload', (_this, [valid, content, upload, channel, hasSpoiler], orig) => {
if (!valid || upload === null || !_this.props.hasAdditionalUploads || _this.__MU_onlySingle) return orig(valid, content, upload, channel, hasSpoiler);
let parsed = ParseUtils.parse(channel, content);
MessageDraftUtils.saveDraft(channel.id, '');
_this.setState({
textFocused: false,
textValue: '',
richValue: WYSIWYGSerializeDeserialize.deserialize('')
});
const canSendAll = _this.canSendBulk();
// fetch group of files we can send in 1 message
let files = this.getNextMessageGroup(channel.id);
// upload them after a set timeout
const startUpload = e => setTimeout(_ => (UploadUtils.upload(channel.id, files, parsed, hasSpoiler), e && e()), 125);
if (canSendAll) {
// if we can send all of them, store the array locally because it'll be cleared at the end of this function
const uploads = [...this.uploads];
const start = _ => startUpload(_ => {
// clear parsed, so we don't send same text multiple times
parsed = { content: '', invalidEmojis: [], tts: false, validNonShortcutEmojis: [] };
// remove previously sent files
uploads.splice(0, files.length);
if (!uploads.length) return;
// fetch next group of files we can send in 1 message
files = this.getNextMessageGroup(channel.id, uploads);
start();
});
start();
} else startUpload();
// replica of the showNextFile function with only slight differences
if (!canSendAll && files.length !== this.uploads.length) {
_this.setState({
transitioning: true
});
setTimeout((function () {
// clear multiple times
for (let i = 0; i < files.length; i++) UploadModalUtils.popFirstFile();
}
), 100);
_this._transitionTimeout = setTimeout((() => {
return _this.setState({
transitioning: false
})
}
), 200);
} else {
// clear all instead of one
UploadModalUtils.clearAll();
_this.props.onClose();
}
});
Patcher.instead(Upload.component.prototype, '_confirm', (_this, [e]) => {
_this.__MU_onlySingle = e ? e.shiftKey : false;
return _this._olConfirm();
});
const patchId = _.uniqueId('MultiUploads');
Patcher.before(Upload.component.prototype, 'renderFooter', _this => {
if (_this.handleSubmit.__MU_patched === patchId) return;
if (!_this._olConfirm) _this._olConfirm = _this.confirm;
_this.confirm = _this._confirm.bind(_this);
_this.confirm.__MU_patched = patchId;
});
Patcher.after(Upload.component.prototype, 'renderFooter', (_this, _, ret) => {
if (!_this.props.hasAdditionalUploads) return;
const buttons = Utilities.findInReactTree(ret, e => Array.isArray(e) && e.find(e => e && e.props && e.props.onClick === _this.cancel));
const uploadButtonProps = Utilities.findInReactTree(buttons, e => e && typeof e.children === 'function');
if (!uploadButtonProps) return;
const oChildren = uploadButtonProps.children;
uploadButtonProps.children = e => {
try {
const ret = oChildren(e);
if (_this.props.hasAdditionalUploads) {
const uploadButton = Utilities.findInReactTree(ret, e => e && e.onClick === _this.confirm);
const group = this.getNextMessageGroup(_this.props.channel.id);
const { props } = uploadButton.children;
if (group.length === this.uploads.length || _this.canSendBulk()) props.children = 'Upload All';
else if (group.length > 1) props.children = `Upload ${group.length}`;
}
return ret;
} catch (err) {
Logger.stacktrace('Failed patching Upload button', err);
try {
return oChildren(e);
} catch (err) {
Logger.stacktrace('Failed calling original Upload button func', err);
return null;
}
}
}
});
const promptToUpload = WebpackModules.getByString('.Messages.UPLOAD_AREA_TOO_LARGE_TITLE');
Patcher.after(Upload.component.prototype, 'render', (_this, _, ret) => {
const channelTextEditorContainer = Utilities.findInReactTree(ret, e => Utilities.getNestedProp(e, 'type.type.render.displayName') === 'ChannelTextAreaContainer');
if (!channelTextEditorContainer) return;
channelTextEditorContainer.props.promptToUpload = promptToUpload;
})
Upload.forceUpdateAll();
}
patchInstantBatchUpload() {
Patcher.instead(UploadUtils, 'instantBatchUpload', (_, [channelId, files], orig) => {
if (files.length === 1) return orig(channelId, files);
let fileGroup = this.getNextMessageGroup(channelId, files);
const startUpload = e => setTimeout(_ => (UploadUtils.upload(channelId, fileGroup), e && e()), 125);
const uploads = [...files];
const start = _ => startUpload(_ => {
uploads.splice(0, fileGroup.length);
if (!uploads.length) return;
fileGroup = this.getNextMessageGroup(channelId, uploads);
start();
});
start();
})
}
getNextMessageGroup(channelId, uploads = this.uploads) {
const { guild_id } = ChannelStore.getChannel(channelId);
const maxSize = FileUtils.maxFileSize(guild_id);
if (!uploads.length) return [];
const isUpload = !!uploads[0].file;
const retAccum = [isUpload ? uploads[0].file : uploads[0]];
let sizeAccum = isUpload ? uploads[0].file.size : uploads[0].size;
for (let i = 1, len = uploads.length; i < len; i++) {
const { file } = isUpload ? uploads[i] : { file: uploads[i] };
if (sizeAccum + file.size > maxSize) break;
retAccum.push(file);
sizeAccum += file.size
if (retAccum.length >= 10) break;
}
return retAccum;
}
/* PATCHES */
get [Symbol.toStringTag]() {
return 'Plugin';
}
get name() {
return config.info.name;
}
get short() {
let string = '';
for (let i = 0, len = config.info.name.length; i < len; i++) {
const char = config.info.name[i];
if (char === char.toUpperCase()) string += char;
}
return string;
}
get author() {
return config.info.authors.map(author => author.name).join(', ');
}
get version() {
return config.info.version;
}
get description() {
return config.info.description;
}
};
};
/* Finalize */
let ZeresPluginLibraryOutdated = false;
try {
const a = (c, a) => ((c = c.split('.').map(b => parseInt(b))), (a = a.split('.').map(b => parseInt(b))), !!(a[0] > c[0])) || !!(a[0] == c[0] && a[1] > c[1]) || !!(a[0] == c[0] && a[1] == c[1] && a[2] > c[2]),
b = BdApi.Plugins.get('ZeresPluginLibrary');
((b, c) => b && b._config && b._config.info && b._config.info.version && a(b._config.info.version, c))(b, '1.2.27') && (ZeresPluginLibraryOutdated = !0);
} catch (e) {
console.error('Error checking if ZeresPluginLibrary is out of date', e);
}
return !global.ZeresPluginLibrary || ZeresPluginLibraryOutdated || window.BDModules
? class {
constructor() {
this._config = config;
this.start = this.load = this.handleMissingLib;
}
getName() {
return this.name.replace(/\s+/g, '');
}
getAuthor() {
return this.author;
}
getVersion() {
return this.version;
}
getDescription() {
return this.description + window.BDModules ? '' : 'You are missing ZeresPluginLibrary for this plugin, please enable the plugin and click Download Now.';
}
start() { }
stop() { }
handleMissingLib() {
if (window.BDModules) return;
const a = BdApi.findModuleByProps('openModal', 'hasModalOpen');
if (a && a.hasModalOpen(`${this.name}_DEP_MODAL`)) return;
const b = !global.ZeresPluginLibrary,
c = ZeresPluginLibraryOutdated ? 'Outdated Library' : 'Missing Library',
d = `The Library ZeresPluginLibrary required for ${this.name} is ${ZeresPluginLibraryOutdated ? 'outdated' : 'missing'}.`,
e = BdApi.findModuleByDisplayName('Text'),
f = BdApi.findModuleByDisplayName('ConfirmModal'),
g = () => BdApi.alert(c, BdApi.React.createElement('span', {}, BdApi.React.createElement('div', {}, d), `Due to a slight mishap however, you'll have to download the libraries yourself. This is not intentional, something went wrong, errors are in console.`, b || ZeresPluginLibraryOutdated ? BdApi.React.createElement('div', {}, BdApi.React.createElement('a', { href: 'https://betterdiscord.net/ghdl?id=2252', target: '_blank' }, 'Click here to download ZeresPluginLibrary')) : null));
if (!a || !f || !e) return console.error(`Missing components:${(a ? '' : ' ModalStack') + (f ? '' : ' ConfirmationModalComponent') + (e ? '' : 'TextElement')}`), g();
class h extends BdApi.React.PureComponent {
constructor(a) {
super(a), (this.state = { hasError: !1 });
}
componentDidCatch(a) {
console.error(`Error in ${this.props.label}, screenshot or copy paste the error above to Lighty for help.`), this.setState({ hasError: !0 }), 'function' == typeof this.props.onError && this.props.onError(a);
}
render() {
return this.state.hasError ? null : this.props.children;
}
}
let i = !1,
j = !1;
const k = a.openModal(
b => {
if (j) return null;
try {
return BdApi.React.createElement(
h,
{
label: 'missing dependency modal',
onError: () => {
a.closeModal(k), g();
}
},
BdApi.React.createElement(
f,
Object.assign(
{
header: c,
children: BdApi.React.createElement(e, { size: e.Sizes.SIZE_16, children: [`${d} Please click Download Now to download it.`] }),
red: !1,
confirmText: 'Download Now',
cancelText: 'Cancel',
onCancel: b.onClose,
onConfirm: () => {
if (i) return;
i = !0;
const b = require('request'),
c = require('fs'),
d = require('path');
b('https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js', (b, e, f) => {
try {
if (b || 200 !== e.statusCode) return a.closeModal(k), g();
c.writeFile(d.join(BdApi.Plugins && BdApi.Plugins.folder ? BdApi.Plugins.folder : window.ContentManager.pluginsFolder, '0PluginLibrary.plugin.js'), f, () => { });
} catch (b) {
console.error('Fatal error downloading ZeresPluginLibrary', b), a.closeModal(k), g();
}
});
}
},
b,
{ onClose: () => { } }
)
)
);
} catch (b) {
return console.error('There has been an error constructing the modal', b), (j = !0), a.closeModal(k), g(), null;
}
},
{ modalKey: `${this.name}_DEP_MODAL` }
);
}
get [Symbol.toStringTag]() {
return 'Plugin';
}
get name() {
return config.info.name;
}
get short() {
let string = '';
for (let i = 0, len = config.info.name.length; i < len; i++) {
const char = config.info.name[i];
if (char === char.toUpperCase()) string += char;
}
return string;
}
get author() {
return config.info.authors.map(author => author.name).join(', ');
}
get version() {
return config.info.version;
}
get description() {
return config.info.description;
}
}
: buildPlugin(global.ZeresPluginLibrary.buildPlugin(config));
})();
/*@end@*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,624 @@
//META{"name":"UnreadBadgesRedux","source":"https://github.com/1Lighty/BetterDiscordPlugins/blob/master/Plugins/UnreadBadgesRedux/","website":"https://1lighty.github.io/BetterDiscordStuff/?plugin=UnreadBadgesRedux","authorId":"239513071272329217","invite":"NYvWdN5","donate":"https://paypal.me/lighty13"}*//
/*@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.\nJust go to settings, plugins and enable me.', 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!\nJust go to settings, plugins and enable me!', 0, 'Successfully installed', 0x40);
}
WScript.Quit();
@else@*/
/*
* Copyright © 2019-2020, _Lighty_
* All rights reserved.
* Code may not be redistributed, modified or otherwise taken without explicit permission.
*/
module.exports = (() => {
/* Setup */
const config = {
main: 'index.js',
info: {
name: 'UnreadBadgesRedux',
authors: [
{
name: 'Lighty',
discord_id: '239513071272329217',
github_username: 'LightyPon',
twitter_username: ''
}
],
version: '1.0.12',
description: 'Adds a number badge to server icons and channels.',
github: 'https://github.com/1Lighty',
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/UnreadBadgesRedux/UnreadBadgesRedux.plugin.js'
},
changelog: [
{
title: 'RIP BBD on Canary',
type: 'fixed',
items: ['Implemented fixes that allow patches to work properly on canary using Powercord.']
}
],
defaultConfig: [
{
type: 'category',
id: 'misc',
name: 'Display settings',
collapsible: true,
shown: true,
settings: [
{
name: 'Display badge on folders',
id: 'folders',
type: 'switch',
value: true
},
{
name: 'Ignore muted servers in folders unread badge count',
id: 'noMutedGuildsInFolderCount',
type: 'switch',
value: true
},
{
name: 'Ignore muted channels in servers in folders unread badge count',
id: 'noMutedChannelsInGuildsInFolderCount',
type: 'switch',
value: true
},
{
name: "Don't display badge on expanded folders",
id: 'expandedFolders',
type: 'switch',
value: true
},
{
name: 'Display badge on servers',
id: 'guilds',
type: 'switch',
value: true
},
{
name: 'Display badge on muted servers',
id: 'mutedGuilds',
type: 'switch',
value: true
},
{
name: 'Ignore muted channels in server unread badge count',
id: 'noMutedInGuildCount',
type: 'switch',
value: true
},
{
name: 'Display badge on channels',
id: 'channels',
type: 'switch',
value: true
},
{
name: 'Display badge on muted channels',
id: 'mutedChannels',
type: 'switch',
value: true
},
{
name: 'Display badge on left side on channels',
note: "In case you want the settings button to stay where it always is. This however doesn't move it before the NSFW tag if you use the BetterNsfwTag plugin",
id: 'channelsDisplayOnLeft',
type: 'switch',
value: false
},
{
name: 'Background color',
id: 'backgroundColor',
type: 'color',
value: '#7289da',
options: {
defaultColor: '#7289da'
}
},
{
name: 'Text color',
id: 'textColor',
type: 'color',
value: '#ffffff',
options: {
defaultColor: '#ffffff'
}
},
{
name: 'Muted channel badge darkness',
id: 'mutedChannelBadgeDarkness',
type: 'slider',
value: 0.25,
min: 0,
max: 1,
equidistant: true,
options: {
equidistant: true
}
}
]
}
]
};
/* Build */
const buildPlugin = ([Plugin, Api], BasePlugin) => {
const { Settings, Utilities, WebpackModules, DiscordModules, ColorConverter, ReactComponents, PluginUtilities, Logger, ReactTools, ModalStack } = Api;
const { React, ChannelStore } = DiscordModules;
const Patcher = XenoLib.createSmartPatcher(Api.Patcher);
const ReactSpring = WebpackModules.getByProps('useTransition');
const BadgesModule = WebpackModules.getByProps('NumberBadge');
const StoresModule = WebpackModules.getByProps('useStateFromStores');
/* discord won't let me access it, so I remade it :( */
class BadgeContainer extends React.PureComponent {
componentDidMount() {
this.forceUpdate();
}
componentWillAppear(e) {
e();
}
componentWillEnter(e) {
e();
}
componentWillLeave(e) {
this.timeoutId = setTimeout(e, 300);
}
componentWillUnmount() {
clearTimeout(this.timeoutId);
}
render() {
return React.createElement(
ReactSpring.animated.div,
{
className: this.props.className,
style: this.props.animatedStyle
},
this.props.children
);
}
}
const UnreadStore = WebpackModules.getByProps('getUnreadCount');
const MuteModule = WebpackModules.getByProps('isMuted');
const AltChannelStore = WebpackModules.find(m => m.getChannels && m.getChannels.length === 1);
const getUnreadCount = (guildId, includeMuted) => {
const channels = AltChannelStore.getChannels(guildId);
let count = 0;
for (const { channel } of channels.SELECTABLE) {
/* isChannelMuted is SLOW! */
if (includeMuted || (!MuteModule.isChannelMuted(channel.guild_id, channel.id) && (!channel.parent_id || !MuteModule.isChannelMuted(channel.guild_id, channel.parent_id)))) count += UnreadStore.getUnreadCount(channel.id);
}
return count;
};
class Slider extends Settings.SettingField {
/* ripped out of ZeresPluginLibrary, because it does thingsin a way I DISLIKE!
but otherwise full credits to Zerebos
https://github.com/rauenzi/BDPluginLibrary/blob/master/src/ui/settings/types/slider.js
*/
constructor(name, note, min, max, value, onChange, options = {}) {
const props = {
onChange: _ => _,
defaultValue: value,
disabled: options.disabled ? true : false,
minValue: min,
maxValue: max,
handleSize: 10,
initialValue: value /* added this */
};
if (options.fillStyles) props.fillStyles = options.fillStyles;
if (options.markers) props.markers = options.markers;
if (options.stickToMarkers) props.stickToMarkers = options.stickToMarkers;
if (typeof options.equidistant != 'undefined') props.equidistant = options.equidistant;
super(name, note, onChange, DiscordModules.Slider, Object.assign(props, { onValueChange: v => this.onChange(v) }));
}
}
return class UnreadBadgesRedux extends BasePlugin(Plugin) {
constructor() {
super();
XenoLib.changeName(__filename, 'UnreadBadgesRedux');
const oOnStart = this.onStart.bind(this);
this.onStart = () => {
try {
oOnStart();
} catch (e) {
Logger.stacktrace('Failed to start!', e);
PluginUpdater.checkForUpdate(this.name, this.version, this._config.info.github_raw);
XenoLib.Notifications.error(`[**${this.name}**] Failed to start! Please update it, press CTRL + R, or ${GuildStore.getGuild(XenoLib.supportServerId) ? 'go to <#639665366380838924>' : '[join my support server](https://discord.gg/NYvWdN5)'} for further assistance.`, { timeout: 0 });
try {
this.onStop();
} catch (e) { }
}
};
try {
WebpackModules.getByProps('openModal', 'hasModalOpen').closeModal(`${this.name}_DEP_MODAL`);
} catch (e) { }
}
onStart() {
this.promises = { state: { cancelled: false } };
this.patchedModules = [];
this.patchAll();
PluginUtilities.addStyle(
this.short + '-CSS',
`
.unread-badge {
right: unset;
}
`
);
}
onStop() {
this.promises.state.cancelled = true;
Patcher.unpatchAll();
PluginUtilities.removeStyle(this.short + '-CSS');
this.forceUpdateAll();
}
buildSetting(data) {
if (data.type === 'color') {
const setting = new XenoLib.Settings.ColorPicker(data.name, data.note, data.value, data.onChange, data.options);
if (data.id) setting.id = data.id;
return setting;
} else if (data.type === 'slider') {
const options = {};
const { name, note, value, onChange, min, max } = data;
if (typeof data.markers !== 'undefined') options.markers = data.markers;
if (typeof data.stickToMarkers !== 'undefined') options.stickToMarkers = data.stickToMarkers;
const setting = new Slider(name, note, min, max, value, onChange, options);
if (data.id) setting.id = data.id;
return setting;
}
return super.buildSetting(data);
}
saveSettings(_, setting, value) {
super.saveSettings(_, setting, value);
this.forceUpdateAll();
}
forceUpdateAll() {
this.patchedModules.forEach(e => e());
}
/* PATCHES */
patchAll() {
Utilities.suppressErrors(this.patchBlobMask.bind(this), 'BlobMask patch')(this.promises.state);
Utilities.suppressErrors(this.patchGuildIcon.bind(this), 'GuildIcon patch')(this.promises.state);
Utilities.suppressErrors(this.patchChannelItem.bind(this), 'ChannelItem patch')(this.promises.state);
Utilities.suppressErrors(this.patchConnectedGuild.bind(this), 'ConnectedGuild patch')(this.promises.state);
Utilities.suppressErrors(this.patchGuildFolder.bind(this), 'GuildFolder patch')(this.promises.state);
}
async patchChannelItem(promiseState) {
const TextChannel = await ReactComponents.getComponentByName('TextChannel', `.${XenoLib.getSingleClass('mentionsBadge containerDefault')}`);
if (promiseState.cancelled) return;
const settings = this.settings;
const MentionsBadgeClassname = XenoLib.getClass('iconVisibility mentionsBadge');
function UnreadBadge(e) {
const unreadCount = StoresModule.useStateFromStores([UnreadStore], () => {
if ((e.muted && !settings.misc.mutedChannels) || !settings.misc.channels) return 0;
const count = UnreadStore.getUnreadCount(e.channelId);
if (count > 1000) return Math.floor(count / 1000) * 1000; /* only trigger rerender if it changes in thousands */
return count;
});
if (!unreadCount) return null;
return React.createElement(
'div',
{
className: MentionsBadgeClassname
},
BadgesModule.NumberBadge({ count: unreadCount, color: e.muted ? ColorConverter.darkenColor(settings.misc.backgroundColor, settings.misc.mutedChannelBadgeDarkness * 100) : settings.misc.backgroundColor, style: { color: e.muted ? ColorConverter.darkenColor(settings.misc.textColor, settings.misc.mutedChannelBadgeDarkness * 100) : settings.misc.textColor } })
);
}
Patcher.after(TextChannel.component.prototype, 'render', (_this, _, ret) => {
const popout = Utilities.findInReactTree(ret, e => e && e.type && e.type.displayName === 'Popout');
if (!popout || typeof popout.props.children !== 'function') return;
const oChildren = popout.props.children;
popout.props.children = () => {
try {
const ret = oChildren();
const props = Utilities.findInReactTree(ret, e => e && Array.isArray(e.children) && e.children.find(e => e && e.type && e.type.displayName === 'ConnectedEditButton'));
if (!props || !props.children) return ret;
const badge = React.createElement(UnreadBadge, { channelId: _this.props.channel.id, muted: _this.props.muted && !_this.props.selected });
props.children.splice(this.settings.misc.channelsDisplayOnLeft ? 0 : 2, 0, badge);
return ret;
} catch (err) {
Logger.stacktrace('Failed modifying return of original children in TextChannel!', err);
try {
return oChildren();
} catch (err) {
Logger.stacktrace('Failed returning return of original children in TextChannel!', err);
return null;
}
}
}
});
TextChannel.forceUpdateAll();
}
async patchGuildFolder(promiseState) {
const settings = this.settings;
const FolderStore = WebpackModules.getByProps('isFolderExpanded');
function BlobMaskWrapper(e) {
e.__UBR_unread_count = StoresModule.useStateFromStores([UnreadStore, MuteModule], () => {
if ((e.__UBR_folder_expanded && settings.misc.expandedFolders) || !settings.misc.folders) return 0;
let count = 0;
for (let i = 0; i < e.__UBR_guildIds.length; i++) {
const guildId = e.__UBR_guildIds[i];
if (!settings.misc.noMutedGuildsInFolderCount || (settings.misc.noMutedGuildsInFolderCount && !MuteModule.isMuted(guildId))) count += getUnreadCount(guildId, !settings.misc.noMutedChannelsInGuildsInFolderCount);
}
if (count > 1000) return Math.floor(count / 1000) * 1000; /* only trigger rerender if it changes in thousands */
return count;
});
return React.createElement(e.__UBR_old_type, e);
}
BlobMaskWrapper.displayName = 'BlobMask';
const GuildFolderMemo = WebpackModules.find(m => m.type && ((m.__powercordOriginal_type || m.type).toString().indexOf('.Messages.SERVER_FOLDER_PLACEHOLDER') !== -1 || (m.type.render && (m.type.__powercordOriginal_render || m.type.render).toString().indexOf('.Messages.SERVER_FOLDER_PLACEHOLDER') !== -1)));
Patcher.after(GuildFolderMemo.type.render ? GuildFolderMemo.type : GuildFolderMemo, GuildFolderMemo.type.render ? 'render' : 'type', (_, [props], ret) => {
const mask = Utilities.findInReactTree(ret, e => e && e.type && e.type.displayName === 'BlobMask');
if (!mask) return;
mask.props.__UBR_old_type = mask.type;
mask.props.__UBR_guildIds = props.guildIds;
mask.props.__UBR_folder_expanded = FolderStore.isFolderExpanded(props.folderId);
mask.type = BlobMaskWrapper;
});
const folders = [...document.querySelectorAll('.wrapper-21YSNc')].map(e => ReactTools.getOwnerInstance(e));
folders.forEach(instance => {
if (!instance) return;
const unpatch = Patcher.after(instance, 'render', (_, __, ret) => {
unpatch();
if (!ret) return;
ret.key = `GETGOOD${Math.random()}`;
const oRef = ret.props.setFolderRef;
ret.props.setFolderRef = (e, n) => {
_.forceUpdate();
return oRef(e, n);
};
});
instance.forceUpdate();
});
}
async patchConnectedGuild(promiseState) {
const selector = `.${XenoLib.getSingleClass('listItem', true)}`;
const ConnectedGuild = await ReactComponents.getComponentByName('DragSource(ConnectedGuild)', selector);
if (!ConnectedGuild.selector) ConnectedGuild.selector = selector;
if (promiseState.cancelled) return;
const settings = this.settings;
function PatchedConnectedGuild(e) {
/* get on my level scrublords */
e.__UBR_unread_count = StoresModule.useStateFromStores([UnreadStore, MuteModule], () => (!settings.misc.guilds || (!settings.misc.mutedGuilds && MuteModule.isMuted(e.guildId)) ? 0 : getUnreadCount(e.guildId, !settings.misc.noMutedInGuildCount)));
return e.__UBR_old_type(e);
}
PatchedConnectedGuild.displayName = 'ConnectedGuild';
Patcher.after(ConnectedGuild.component.prototype, 'render', (_this, _, ret) => {
const old = ret.props.children;
ret.props.children = e => {
const ret2 = old(e);
ret2.props.__UBR_old_type = ret2.type;
ret2.type = PatchedConnectedGuild;
return ret2;
};
});
ConnectedGuild.forceUpdateAll();
this.patchedModules.push(ConnectedGuild.forceUpdateAll.bind(ConnectedGuild));
}
async patchGuildIcon(promiseState) {
const selector = `.${XenoLib.getSingleClass('listItem', true)}`;
const Guild = await ReactComponents.getComponentByName('Guild', selector);
if (!Guild.selector) Guild.selector = selector;
if (promiseState.cancelled) return;
Patcher.after(Guild.component.prototype, 'render', (_this, _, ret) => {
const mask = Utilities.findInTree(ret, e => e && e.type && e.type.displayName === 'BlobMask', { walkable: ['props', 'children'] });
if (!mask) return;
mask.props.__UBR_unread_count = _this.props.__UBR_unread_count;
mask.props.guildId = _this.props.guildId;
});
Guild.forceUpdateAll();
}
async patchBlobMask(promiseState) {
const selector = `.${XenoLib.getSingleClass('lowerBadge wrapper')}`;
const BlobMask = await ReactComponents.getComponentByName('BlobMask', selector);
if (!BlobMask.selector) BlobMask.selector = selector;
if (promiseState.cancelled) return;
const ensureUnreadBadgeMask = _this => {
if (_this.state.unreadBadgeMask) return;
_this.state.unreadBadgeMask = new ReactSpring.Controller({
spring: 0
});
};
Patcher.after(BlobMask.component.prototype, 'componentDidMount', _this => {
if (typeof _this.props.__UBR_unread_count !== 'number') return;
ensureUnreadBadgeMask(_this);
_this.state.unreadBadgeMask
.update({
spring: !!_this.props.__UBR_unread_count,
immediate: true
})
.start();
});
Patcher.after(BlobMask.component.prototype, 'componentWillUnmount', _this => {
if (typeof _this.props.__UBR_unread_count !== 'number') return;
if (!_this.state.unreadBadgeMask) return;
if (typeof _this.state.unreadBadgeMask.destroy === 'function') _this.state.unreadBadgeMask.destroy();
else _this.state.unreadBadgeMask.dispose();
_this.state.unreadBadgeMask = null;
});
Patcher.after(BlobMask.component.prototype, 'componentDidUpdate', (_this, [{ __UBR_unread_count }]) => {
if (typeof _this.props.__UBR_unread_count !== 'number' || _this.props.__UBR_unread_count === __UBR_unread_count) return;
ensureUnreadBadgeMask(_this);
_this.state.unreadBadgeMask
.update({
spring: !!_this.props.__UBR_unread_count,
immediate: !document.hasFocus(),
config: {
friction: 40,
tension: 900,
mass: 1
}
})
.start();
});
const LowerBadgeClassname = XenoLib.joinClassNames(XenoLib.getClass('wrapper lowerBadge'), 'unread-badge');
Patcher.after(BlobMask.component.prototype, 'render', (_this, _, ret) => {
if (typeof _this.props.__UBR_unread_count !== 'number') return;
const badges = Utilities.findInTree(ret, e => e && e.type && e.type.displayName === 'TransitionGroup', { walkable: ['props', 'children'] });
const masks = Utilities.findInTree(ret, e => e && e.type === 'mask', { walkable: ['props', 'children'] });
if (!badges || !masks) return;
ensureUnreadBadgeMask(_this);
/* if count is 0, we're animating out, and as such, it's better to at least still display the old
count while animating out
*/
const counter = _this.props.__UBR_unread_count || _this.state.__UBR_old_unread_count;
if (_this.props.__UBR_unread_count) _this.state.__UBR_old_unread_count = _this.props.__UBR_unread_count;
const width = BadgesModule.getBadgeWidthForValue(counter);
const unreadCountMaskSpring = (_this.state.unreadBadgeMask.animated || _this.state.unreadBadgeMask.springs).spring;
masks.props.children.push(
React.createElement(ReactSpring.animated.rect, {
x: -4,
y: 28,
width: width + 8,
height: 24,
rx: 12,
ry: 12,
opacity: unreadCountMaskSpring.to([0, 0.5, 1], [0, 0, 1]),
transform: unreadCountMaskSpring.to([0, 1], [-16, 0]).to(e => `translate(${e} ${-e})`),
fill: 'black'
})
);
badges.props.children.unshift(
React.createElement(
BadgeContainer,
{
className: LowerBadgeClassname,
animatedStyle: {
opacity: unreadCountMaskSpring.to([0, 0.5, 1], [0, 0, 1]),
transform: unreadCountMaskSpring.to(e => `translate(${-20 + 20 * e} ${-1 * (16 - 16 * e)})`)
}
},
React.createElement(BadgesModule.NumberBadge, { count: counter, color: this.settings.misc.backgroundColor, style: { color: this.settings.misc.textColor } })
)
);
});
BlobMask.forceUpdateAll();
}
showChangelog = () => XenoLib.showChangelog(`${this.name} has been updated!`, this.version, this._config.changelog);
getSettingsPanel = () =>
this.buildSettingsPanel()
.append(new XenoLib.Settings.PluginFooter(() => this.showChangelog()))
.getElement();
};
};
/* Finalize */
/* shared getters */
const BasePlugin = cl =>
class extends cl {
constructor() {
super();
Object.defineProperties(this, {
name: { get: () => config.info.name },
short: { get: () => config.info.name.split('').reduce((acc, char) => acc + (char === char.toUpperCase() ? char : '')) },
author: { get: () => config.info.authors.map(author => author.name).join(', ') },
version: { get: () => config.info.version },
description: { get: () => config.info.description }
});
}
};
/* this new lib loader is lit */
let ZeresPluginLibraryOutdated = false;
let XenoLibOutdated = false;
try {
const a = (c, a) => ((c = c.split('.').map(b => parseInt(b))), (a = a.split('.').map(b => parseInt(b))), !!(a[0] > c[0])) || !!(a[0] == c[0] && a[1] > c[1]) || !!(a[0] == c[0] && a[1] == c[1] && a[2] > c[2]),
b = (b, c) => ((b && b._config && b._config.info && b._config.info.version && a(b._config.info.version, c))),
c = BdApi.Plugins.get('ZeresPluginLibrary'),
d = BdApi.Plugins.get('XenoLib');
b(c, '1.2.27') && (ZeresPluginLibraryOutdated = !0), b(d, '1.3.35') && (XenoLibOutdated = !0);
} catch (a) {
console.error('Error checking if libraries are out of date', a);
}
/* to anyone asking "why are you checking if x is out of date", well you see, sometimes, for whatever reason
the libraries are sometimes not updating for people. Either it doesn't check for an update, or the request
for some odd reason just fails. Yet, plugins update just fine with the same domain.
*/
return !global.ZeresPluginLibrary || !global.XenoLib || ZeresPluginLibraryOutdated || XenoLibOutdated
? class extends BasePlugin(class { }) {
constructor() {
super();
this._XL_PLUGIN = true;
this.getName = () => this.name.replace(/\s+/g, '');
this.getAuthor = () => this.author;
this.getVersion = () => this.version;
this.getDescription = () => this.description + (global.BetterDiscordConfig ? '' : ' You are missing libraries for this plugin, please enable the plugin and click Download Now.');
this.start = this.load = this.handleMissingLib;
}
start() { }
stop() { }
handleMissingLib() {
const a = !!window.powercord && -1 !== (window.bdConfig && window.bdConfig.dataPath || "").indexOf("bdCompat") && "function" == typeof BdApi.__getPluginConfigPath, b = BdApi.findModuleByProps("openModal", "hasModalOpen");
if (b && b.hasModalOpen(`${this.name}_DEP_MODAL`)) return;
const c = !global.XenoLib, d = !global.ZeresPluginLibrary, e = c && d || (c || d) && (XenoLibOutdated || ZeresPluginLibraryOutdated),
f = (() => { let a = ""; return c || d ? a += `Missing${XenoLibOutdated || ZeresPluginLibraryOutdated ? " and outdated" : ""} ` : (XenoLibOutdated || ZeresPluginLibraryOutdated) && (a += `Outdated `), a += `${e ? "Libraries" : "Library"} `, a })(),
g = (() => {
let a = `The ${e ? "libraries" : "library"} `; return c || XenoLibOutdated ? (a += "XenoLib ", (d || ZeresPluginLibraryOutdated) && (a += "and ZeresPluginLibrary ")) : (d || ZeresPluginLibraryOutdated) && (a += "ZeresPluginLibrary "),
a += `required for ${this.name} ${e ? "are" : "is"} ${c || d ? "missing" : ""}${XenoLibOutdated || ZeresPluginLibraryOutdated ? c || d ? " and/or outdated" : "outdated" : ""}.`, a
})(), h = BdApi.findModuleByDisplayName("Text"), i = BdApi.findModuleByDisplayName("ConfirmModal"),
j = () => BdApi.alert(f, BdApi.React.createElement("span", { style: { color: "white" } }, BdApi.React.createElement("div", {}, g), `Due to a slight mishap however, you'll have to download the libraries yourself. This is not intentional, something went wrong, errors are in console.`,
d || ZeresPluginLibraryOutdated ? BdApi.React.createElement("div", {}, BdApi.React.createElement("a", { href: "https://betterdiscord.net/ghdl?id=2252", target: "_blank" }, "Click here to download ZeresPluginLibrary")) : null, c || XenoLibOutdated ? BdApi.React.createElement("div", {},
BdApi.React.createElement("a", { href: "https://betterdiscord.net/ghdl?id=3169", target: "_blank" }, "Click here to download XenoLib")) : null)); if (global.ohgodohfuck) return; if (!b || !i || !h) return console.error(`Missing components:${(b ? "" : " ModalStack") + (i ? "" : " ConfirmationModalComponent") + (h ? "" : "TextElement")}`),
j(); class k extends BdApi.React.PureComponent {
constructor(a) {
super(a), this.state = { hasError: !1 }, this.componentDidCatch = a => (console.error(`Error in ${this.props.label}, screenshot or copy paste the error above to Lighty for help.`), this.setState({ hasError: !0 }), "function" == typeof this.props.onError && this.props.onError(a)),
this.render = () => this.state.hasError ? null : this.props.children
}
} let m = !1; const n = b.openModal(c => {
if (m) return null; try {
return BdApi.React.createElement(k, { label: "missing dependency modal", onError: () => (b.closeModal(n), j()) }, BdApi.React.createElement(i,
Object.assign({
header: f, children: BdApi.React.createElement(h, { size: h.Sizes.SIZE_16, children: [`${g} Please click Download Now to download ${e ? "them" : "it"}.`] }), red: !1, confirmText: "Download Now", cancelText: "Cancel", onCancel: c.onClose, onConfirm: () => {
const c = require("request"), d = require("fs"),
e = require("path"), f = BdApi.Plugins && BdApi.Plugins.folder ? BdApi.Plugins.folder : window.ContentManager.pluginsFolder, g = () => {
global.XenoLib && !XenoLibOutdated || c("https://gitdab.com/Dollar3795/LightcordPlugins/raw/branch/master/Plugins/1XenoLib.plugin.js", (c, g, h) => {
try {
if (c || 200 !== g.statusCode) return b.closeModal(n),
j(); d.writeFile(e.join(f, "1XenoLib.plugin.js"), h, () => { BdApi.isSettingEnabled("fork-ps-5") && !a || BdApi.Plugins.reload(this.getName()) })
} catch (a) { console.error("Fatal error downloading XenoLib", a), b.closeModal(n), j() }
})
}; !global.ZeresPluginLibrary || ZeresPluginLibraryOutdated ? c("https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js",
(a, c, h) => { try { if (a || 200 !== c.statusCode) return b.closeModal(n), j(); d.writeFile(e.join(f, "0PluginLibrary.plugin.js"), h, () => { }), g() } catch (a) { console.error("Fatal error downloading ZeresPluginLibrary", a), b.closeModal(n), j() } }) : g()
}
}, c, { onClose: () => { } })))
} catch (a) { return console.error("There has been an error constructing the modal", a), m = !0, b.closeModal(n), j(), null }
}, { modalKey: `${this.name}_DEP_MODAL` });
}
}
: buildPlugin(global.ZeresPluginLibrary.buildPlugin(config), BasePlugin);
})();
/*@end@*/