STR v2.0.9

This commit is contained in:
_Lighty_ 2020-02-24 21:58:30 +01:00
parent 6bcfd6f427
commit 0f06bc2d23
3 changed files with 240 additions and 172 deletions

View File

@ -1,4 +1,9 @@
# [SaveToRedux](https://1lighty.github.io/BetterDiscordStuff/?plugin=SaveToRedux "SaveToRedux") Changelog
### 2.0.9
- Added new conflict option. If a file already exists, it can open up the Save As... modal to set a custom name instead.
- Added a Randomize button to the Save As... modal.
- Properly sanitizing filenames now.
### 2.0.8
- Fixed crash if XenoLib or ZeresPluginLib were missing

View File

@ -18,7 +18,8 @@ Dynamic options must be wrapped like ${OPTION}
Warn - Always warn if a file with the same name exists
Overwrite
Append number - appends a number in paranthesis
Append random
Append random
Save As... - lets you enter a custom name instead
#### Misc
##### Context menu option at the bottom instead of top
Force the Save * To option to stay at the bottom at all times

View File

@ -41,16 +41,16 @@ var SaveToRedux = (() => {
twitter_username: ''
}
],
version: '2.0.8',
version: '2.0.9',
description: 'Allows you to save images, videos, profile icons, server icons, reactions, emotes and custom status emotes to any folder quickly.',
github: 'https://github.com/1Lighty',
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/SaveToRedux/SaveToRedux.plugin.js'
},
changelog: [
{
title: 'sad',
title: 'QOL',
type: 'fixed',
items: ['Fixed crash if XenoLib or ZeresPluginLib were missing']
items: ['Added new conflict option. If a file already exists, it can open up the Save As... modal to set a custom name instead.', 'Added a Randomize button to the Save As... modal.', 'Properly sanitizing filenames now.']
}
],
defaultConfig: [
@ -89,7 +89,8 @@ var SaveToRedux = (() => {
{ name: 'Warn', value: 0 },
{ name: 'Overwrite', value: 1 },
{ name: 'Append number: (1)', value: 2 },
{ name: 'Append random', value: 3 }
{ name: 'Append random', value: 3 },
{ name: 'Save as...', value: 4 }
]
},
{ name: 'User and Server icons get saved by the users or servers name, instead of randomized', id: 'saveByName', type: 'switch', value: true },
@ -107,19 +108,20 @@ var SaveToRedux = (() => {
const ContextMenuSubMenuItem = WebpackModules.getByDisplayName('FluxContainer(SubMenuItem)');
const TextComponent = WebpackModules.getByDisplayName('Text');
const getEmojiURL = (WebpackModules.getByProps('getEmojiURL') || {}).getEmojiURL;
const showAlertModal = (WebpackModules.find(m => m.show && m.show.toString().search(/\w\.minorText,\w=\w\.onConfirmSecondary/)) || {}).show;
const getEmojiURL = WebpackModules.getByProps('getEmojiURL').getEmojiURL;
const showAlertModal = WebpackModules.find(m => m.show && m.show.toString().search(/\w\.minorText,\w=\w\.onConfirmSecondary/)).show;
const dialog = require('electron').remote.dialog;
const openSaveDialog = dialog.showSaveDialogSync || dialog.showSaveDialog;
const openOpenDialog = dialog.showOpenDialogSync || dialog.showOpenDialog;
const openItem = require('electron').shell.openItem;
const DelayedCall = WebpackModules.getByProps('DelayedCall').DelayedCall;
const FsModule = require('fs');
const RequestModule = require('request');
const PathModule = require('path');
const MimeTypesModule = require('mime-types');
const FormItem = WebpackModules.getByDisplayName('FormItem');
const Messages = (WebpackModules.getByProps('Messages') || {}).Messages;
const Messages = WebpackModules.getByProps('Messages').Messages;
const TextInput = WebpackModules.getByDisplayName('TextInput');
const AvatarModule = WebpackModules.getByProps('getChannelIconURL');
@ -218,6 +220,48 @@ var SaveToRedux = (() => {
}
}
/*
* I DO NOT OWN THESE TWO
*/
function sanitizeFileName(r, e) {
// https://github.com/parshap/node-sanitize-filename
function n(r, n) {
function o(r, e, n) {
// https://github.com/parshap/truncate-utf8-bytes
function t(r) {
return r >= 55296 && 56319 >= r;
}
function u(r) {
return r >= 56320 && 57343 >= r;
}
if ('string' != typeof e) throw new Error('Input must be string');
for (var i, f, c = e.length, o = 0, l = 0; c > l; l += 1) {
if (((i = e.charCodeAt(l)), (f = e[l]), t(i) && u(e.charCodeAt(l + 1)) && ((l += 1), (f += e[l])), (o += r(f)), o === n)) return e.slice(0, l + 1);
if (o > n) return e.slice(0, l - f.length + 1);
}
return e;
}
if ('string' != typeof r) throw new Error('Input must be string');
var l = Buffer.byteLength.bind(Buffer),
a = o.bind(null, l),
p = r
.replace(t, n)
.replace(u, n)
.replace(i, n)
.replace(f, n)
.replace(c, n);
return a(p, e.extLength);
}
var t = /[\/\?<>\\:\*\|"]/g,
u = /[\x00-\x1f\x80-\x9f]/g,
i = /^\.+$/,
f = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i,
c = /[\. ]+$/,
o = (e && e.replacement) || '',
l = n(r, o);
return '' === o ? l : n(l, '');
}
return class SaveToRedux extends Plugin {
constructor() {
super();
@ -261,6 +305,12 @@ var SaveToRedux = (() => {
.ST-modal {
min-height: 320px;
}
.ST-randomize {
justify-content: unset;
}
.ST-randomize > .${XenoLib.getSingleClass('lookBlank contents')} {
margin: 0;
}
`
);
this.lastUsedFolder = -1;
@ -307,20 +357,26 @@ var SaveToRedux = (() => {
async patchReactions(promiseState) {
const Reaction = await ReactComponents.getComponentByName('Reaction', `.${XenoLib.getSingleClass('reactionMe reactions')} > div:not(.${XenoLib.getSingleClass('reactionMe reactionBtn')})`);
if (promiseState.cancelled) return;
Patcher.after(Reaction.component.prototype, 'render', (_this, _, ret) => {
const unpatch = Patcher.after(Reaction.component.prototype, 'render', (_this, _, ret) => {
const oChildren = ret.props.children;
ret.props.children = e => {
const oChRet = oChildren(e);
const url = _this.props.emoji.id ? getEmojiURL({ id: _this.props.emoji.id, animated: _this.props.emoji.animated }) : WebpackModules.getByProps('getURL').getURL(_this.props.emoji.name);
XenoLib.createSharedContext(
() => {
const submenu = this.constructMenu(url.split('?')[0], 'Reaction', _this.props.emoji.name);
return XenoLib.createContextMenuGroup([submenu]);
},
oChRet.props,
'MESSAGE_REACTIONS'
);
return oChRet;
try {
const oChRet = oChildren(e);
const url = _this.props.emoji.id ? getEmojiURL({ id: _this.props.emoji.id, animated: _this.props.emoji.animated }) : WebpackModules.getByProps('getURL').getURL(_this.props.emoji.name);
XenoLib.createSharedContext(
() => {
const submenu = this.constructMenu(url.split('?')[0], 'Reaction', _this.props.emoji.name);
return XenoLib.createContextMenuGroup([submenu]);
},
oChRet.props,
'MESSAGE_REACTIONS'
);
return oChRet;
} catch (e) {
Logger.stacktrace('Error in Reaction patch', e);
unpatch(); // for the better..
return null;
}
};
});
Reaction.forceUpdateAll();
@ -346,7 +402,7 @@ var SaveToRedux = (() => {
return DiscordAPI.currentChannel.members.reduce((p, c) => (p ? `${p}, ${c.username}` : c.username), '');
}
formatFilename(name, previewDate, previewRand) {
formatFilename(name, previewDate, previewRand, extension, throwFail) {
const date = previewDate || new Date();
const rand = previewRand || this.rand();
let ret = 'INTERNAL_ERROR';
@ -368,7 +424,7 @@ var SaveToRedux = (() => {
break;
case 4: // custom
// options file rand date time day month year hours minutes seconds name
return Utilities.formatTString(this.settings.saveOptions.customFileName, {
ret = Utilities.formatTString(this.settings.saveOptions.customFileName, {
rand,
file: name,
date: date
@ -376,8 +432,8 @@ var SaveToRedux = (() => {
.split('/')
.join('-'),
time: `${date.getMinutes()}-${date.getSeconds()}-${date.getMilliseconds()}`,
day: date.getDay(),
month: date.getMonth(),
day: date.getDate(), // note to self: getDate gives you the day of month
month: date.getMonth() + 1, // getMonth gives 0-11
year: date.getFullYear(),
hours: date.getHours(),
minutes: date.getMinutes(),
@ -385,17 +441,19 @@ var SaveToRedux = (() => {
name: this.getLocationName()
});
}
if (this.settings.saveOptions.appendCurrentName && (DiscordAPI.currentGuild || DiscordAPI.currentChannel)) {
const name = this.getLocationName();
if (name) ret += `-${name}`;
if (this.settings.saveOptions.fileNameType !== 4) {
if (this.settings.saveOptions.appendCurrentName && (DiscordAPI.currentGuild || DiscordAPI.currentChannel)) {
const name = this.getLocationName();
if (name) ret += `-${name}`;
}
}
ret = sanitizeFileName(ret, { extLength: extension ? 255 - (extension.length + 1) : 255 });
if (!ret.length && throwFail) throw 'CUST_ERROR_1';
return ret;
}
formatURL(url, requiresSize, customName, fallbackExtension, proxiedUrl) {
// url = url.replace(/\/$/, '');
if (url.indexOf('/a_') !== -1) url = url.replace('.webp', '.gif').replace('.png', '.gif');
else url = url.replace('.webp', '.png');
if (requiresSize) url += '?size=2048';
const match = url.match(/(?:\/)([^\/]+?)(?:(?:\.)([^.\/?:]+)){0,1}(?:[^\w\/\.]+\w+){0,1}(?:(?:\?[^\/]+){0,1}|(?:\/){0,1})$/);
let name = customName || match[1];
@ -404,9 +462,15 @@ var SaveToRedux = (() => {
extension = name;
name = url.match(/\/\/media.tenor.co\/[^\/]+\/([^\/]+)\//)[1];
} else if (url.indexOf('//i.giphy.com/media/') !== -1) name = url.match(/\/\/i\.giphy\.com\/media\/([^\/]+)\//)[1];
name = this.formatFilename(name);
let forceSaveAs = false;
try {
name = this.formatFilename(name, undefined, undefined, extension, true);
} catch (e) {
if (e !== 'CUST_ERROR_1') throw e;
forceSaveAs = true;
}
const isTrusted = isTrustedDomain(url);
const ret = { fileName: (extension && `${name}.${extension}`) || name, url: isTrusted ? url : proxiedUrl || url, name, extension, untrusted: !isTrusted && !proxiedUrl };
const ret = { fileName: (extension && `${name}.${extension}`) || name, url: isTrusted ? url : proxiedUrl || url, name, extension, untrusted: !isTrusted && !proxiedUrl, forceSaveAs };
// Logger.info(`[formatURL] url \`${url}\` requiresSize \`${requiresSize}\` customName \`${customName}\`, ret ${JSON.stringify(ret, '', 1)}`);
return ret;
}
@ -460,12 +524,83 @@ var SaveToRedux = (() => {
}
};
const saveFile = (path, basePath, openOnSave, dontWarn) => {
const saveAs = (folder, onOk) => {
let val = formattedurl.name;
let inputRef = null;
let delayedCall = new DelayedCall(350, () => {
inputRef.props.value = val = sanitizeFileName(val, 255 - (formattedurl.extension ? formattedurl.extension.length + 1 : 0));
inputRef.forceUpdate();
});
Modals.showModal(
'Save as...',
React.createElement(
FormItem,
{
title: 'Name your file'
},
React.createElement(TextInput, {
maxLength: 255 - (formattedurl.extension ? formattedurl.extension.length + 1 : 0),
ref: e => (inputRef = e),
value: val,
onChange: e => {
val = e;
inputRef.props.value = val;
if (val.trim() !== sanitizeFileName(val.trim(), 255 - (formattedurl.extension ? formattedurl.extension.length + 1 : 0))) inputRef.props.error = 'Invalid characters in name';
else inputRef.props.error = undefined;
inputRef.forceUpdate();
},
placeholder: formattedurl.name,
autoFocus: true,
error: !folder ? 'Invalid filename, please set a name' : folder === -1 ? 'File already exists, try another name' : undefined
}),
React.createElement(XenoLib.ReactComponents.Button, {
children: 'Randomize',
className: XenoLib.joinClassNames(DiscordClasses.Margins.marginBottom20.value, XenoLib.getClass('input reset'), 'ST-randomize'),
color: XenoLib.ReactComponents.Button.Colors.PRIMARY,
look: XenoLib.ReactComponents.ButtonOptions.ButtonLooks.LINK,
onClick: () => {
val = this.rand();
inputRef.props.value = val;
inputRef.forceUpdate();
}
})
),
{
confirmText: 'Save',
onCancel: e => console.log('oof'),
onConfirm: () => {
const onDoShitOrWhateverFuckThisShitMan = val => {
if (!folder || folder === -1) return onOk(val);
this.lastUsedFolder = this.folders.findIndex(m => m === folder);
if (!val.length) val = formattedurl.name;
else formattedurl.name = val;
saveFile(folder.path + `/${val}${formattedurl.extension ? '.' + formattedurl.extension : ''}`, folder.path, false, false);
};
const sanitized = sanitizeFileName(val, 255 - (formattedurl.extension ? formattedurl.extension.length + 1 : 0));
if (val !== sanitized) {
if (!sanitized.length) return saveAs(undefined, onOk);
return Modals.showConfirmationModal('Invalid characters', `There are invalid characters in the filename. Do you want to strip them? Resulting filename will be ${sanitized}`, {
onConfirm: () => {
onOk(sanitized);
onDoShitOrWhateverFuckThisShitMan(sanitized);
}
});
}
onDoShitOrWhateverFuckThisShitMan(val);
}
}
);
};
const saveFile = (path, basePath, openOnSave, dontWarn, resolved) => {
try {
FsModule.accessSync(PathModule.dirname(path), FsModule.constants.W_OK);
} catch (err) {
Logger.stacktrace('Failed to save to folder', err);
return BdApi.showToast(`Error saving to folder: ${err.message.match(/.*: (.*), access '/)[1]}`, { type: 'error' });
}
const handleSaveAs = invFilename => saveAs(invFilename ? -1 : undefined, fileName => ((formattedurl.forceSaveAs = false), saveFile(`${basePath}/${fileName}${formattedurl.extension ? '.' + formattedurl.extension : ''}`, basePath, openOnSave, dontWarn, true)));
if (formattedurl.forceSaveAs && !resolved) return handleSaveAs();
if (!dontWarn && FsModule.existsSync(path)) {
const handleConflict = mode => {
switch (mode) {
@ -485,10 +620,12 @@ var SaveToRedux = (() => {
case 3: {
path = `${basePath}/${formattedurl.name}-${this.rand()}${formattedurl.extension ? '.' + formattedurl.extension : ''}`;
}
case 4:
return handleSaveAs(true);
}
download(path, openOnSave);
};
if (this.settings.saveOptions.conflictingFilesHandle) {
if (this.settings.saveOptions.conflictingFilesHandle && !formattedurl.forceSaveAs) {
handleConflict(this.settings.saveOptions.conflictingFilesHandle);
} else {
let ref1, ref2;
@ -518,6 +655,10 @@ var SaveToRedux = (() => {
{
name: 'Append random',
value: 3
},
{
name: 'Save as...',
value: 4
}
],
value: 1,
@ -578,44 +719,7 @@ var SaveToRedux = (() => {
const path = folder.path + `/${formattedurl.fileName}`;
saveFile(path, folder.path);
}),
XenoLib.createContextMenuItem('Save As...', () => {
let val = '';
let inputRef = null;
Modals.showModal(
'Save as...',
React.createElement(
FormItem,
{
className: DiscordClasses.Margins.marginBottom20,
title: 'Name your file'
},
React.createElement(TextInput, {
maxLength: DiscordConstants.MAX_GUILD_FOLDER_NAME_LENGTH,
ref: e => (inputRef = e),
value: val,
onChange: e => {
val = e;
inputRef.props.value = e;
inputRef.forceUpdate();
},
placeholder: formattedurl.name,
autoFocus: true
})
),
{
confirmText: 'Save',
onConfirm: () => {
this.lastUsedFolder = this.folders.findIndex(m => m === folder);
if (!val.length) val = formattedurl.name;
else formattedurl.name = val;
saveFile(folder.path + `/${val}${formattedurl.extension ? '.' + formattedurl.extension : ''}`, folder.path, false, false);
}
}
);
/* const path = this.openSaveDialog({ defaultPath: folder.path + `/${formattedurl.fileName}` });
if (!path) return BdApi.showToast('Maybe next time.');
saveFile(path, undefined, false, true); */
}),
XenoLib.createContextMenuItem('Save As...', () => saveAs(folder)),
XenoLib.createContextMenuItem('Save And Open', () => {
this.lastUsedFolder = this.folders.findIndex(m => m === folder);
const path = folder.path + `/${formattedurl.fileName}`;
@ -731,6 +835,10 @@ var SaveToRedux = (() => {
const isImage = e => /\.{0,1}(png|jpe?g|webp|gif|svg)$/i.test(e);
const isVideo = e => /\.{0,1}(mp4|webm|mov)$/i.test(e);
const isAudio = e => /\.{0,1}(mp3|ogg|wav|flac)$/i.test(e);
const useCorrectShit = () => {
if (url.indexOf('/a_') !== -1) url = url.replace('.webp', '.gif').replace('.png', '.gif');
else url = url.replace('.webp', '.png');
};
if (type === 'NATIVE_IMAGE' || type === 'MESSAGE_MAIN') {
let src;
if (type === 'NATIVE_IMAGE') {
@ -817,6 +925,7 @@ var SaveToRedux = (() => {
url = _this.props.guild.getIconURL();
if (!url) return;
if (this.settings.saveOptions.saveByName) customName = _this.props.guild.name;
useCorrectShit();
} else {
if (_this.props.user && _this.props.user.getAvatarURL) {
saveType = 'Avatar';
@ -826,6 +935,7 @@ var SaveToRedux = (() => {
url = AvatarModule.getChannelIconURL(_this.props.channel);
saveType = 'Icon';
} else return /* hurr durr? */;
useCorrectShit();
if (url.startsWith('/assets/')) url = 'https://discordapp.com' + url;
}
try {
@ -914,116 +1024,68 @@ var SaveToRedux = (() => {
}
stop() {}
load() {
const XenoLibMissing = !global.XenoLib;
const ezlibMissing = !global.XenoLib;
const zlibMissing = !global.ZeresPluginLibrary;
const bothLibsMissing = XenoLibMissing && zlibMissing;
const bothLibsMissing = ezlibMissing && zlibMissing;
const header = `Missing ${(bothLibsMissing && 'Libraries') || 'Library'}`;
const content = `The ${(bothLibsMissing && 'Libraries') || 'Library'} ${(zlibMissing && 'ZeresPluginLibrary') || ''} ${(XenoLibMissing && (zlibMissing ? 'and XenoLib' : 'XenoLib')) || ''} required for ${this.name} ${(bothLibsMissing && 'are') || 'is'} missing.`;
const content = `The ${(bothLibsMissing && 'Libraries') || 'Library'} ${(zlibMissing && 'ZeresPluginLibrary') || ''} ${(ezlibMissing && (zlibMissing ? 'and XenoLib' : 'XenoLib')) || ''} required for ${this.name} ${(bothLibsMissing && 'are') || 'is'} missing.`;
const ModalStack = BdApi.findModuleByProps('push', 'update', 'pop', 'popWithKey');
const TextElement = BdApi.findModuleByProps('Sizes', 'Weights');
const ConfirmationModal = BdApi.findModule(m => m.defaultProps && m.key && m.key() === 'confirm-modal');
const onFail = () => BdApi.getCore().alert(header, `${content}<br/>Due to a slight mishap however, you'll have to download the libraries yourself. After opening the links, do CTRL + S to download the library.<br/>${(zlibMissing && '<br/><a href="https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js"target="_blank">Click here to download ZeresPluginLibrary</a>') || ''}${(zlibMissing && '<br/><a href="http://localhost:7474/XenoLib.js"target="_blank">Click here to download XenoLib</a>') || ''}`);
if (!ModalStack || !ConfirmationModal || !TextElement) return onFail();
class TempErrorBoundary extends BdApi.React.PureComponent {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(err, inf) {
console.error(`Error in ${this.props.label}, screenshot or copy paste the error above to Lighty for help.`);
this.setState({ hasError: true });
if (typeof this.props.onError === 'function') this.props.onError(err);
}
render() {
if (this.state.hasError) return null;
return this.props.children;
}
}
let modalId;
const onHeckWouldYouLookAtThat = (() => {
if (!global.pluginModule || !global.BDEvents) return;
if (XenoLibMissing) {
const listener = () => {
BDEvents.off('xenolib-loaded', listener);
ModalStack.popWithKey(modalId); /* make it easier on the user */
pluginModule.reloadPlugin(this.name);
};
BDEvents.on('xenolib-loaded', listener);
return () => BDEvents.off('xenolib-loaded', listener);
} else {
const onLoaded = e => {
if (e !== 'ZeresPluginLibrary') return;
BDEvents.off('plugin-loaded', onLoaded);
ModalStack.popWithKey(modalId); /* make it easier on the user */
pluginModule.reloadPlugin(this.name);
};
BDEvents.on('plugin-loaded', onLoaded);
return () => BDEvents.off('plugin-loaded', onLoaded);
}
})();
modalId = ModalStack.push(props => {
ModalStack.push(props => {
return BdApi.React.createElement(
TempErrorBoundary,
{
label: 'missing dependency modal',
onError: () => {
ModalStack.popWithKey(modalId); /* smh... */
onFail();
}
},
BdApi.React.createElement(
ConfirmationModal,
Object.assign(
{
header,
children: [BdApi.React.createElement(TextElement, { color: TextElement.Colors.PRIMARY, children: [`${content} Please click Download Now to install ${(bothLibsMissing && 'them') || 'it'}.`] })],
red: false,
confirmText: 'Download Now',
cancelText: 'Cancel',
onConfirm: () => {
onHeckWouldYouLookAtThat();
const request = require('request');
const fs = require('fs');
const path = require('path');
const waitForLibLoad = callback => {
if (!global.BDEvents) return callback();
const onLoaded = e => {
if (e !== 'ZeresPluginLibrary') return;
BDEvents.off('plugin-loaded', onLoaded);
callback();
ConfirmationModal,
Object.assign(
{
header,
children: [BdApi.React.createElement(TextElement, { color: TextElement.Colors.PRIMARY, children: [`${content} Please click Download Now to install ${(bothLibsMissing && 'them') || 'it'}.`] })],
red: false,
confirmText: 'Download Now',
cancelText: 'Cancel',
onConfirm: () => {
const request = require('request');
const fs = require('fs');
const path = require('path');
const waitForLibLoad = callback => {
if (!global.BDEvents) return callback();
const onLoaded = e => {
if (e !== 'ZeresPluginLibrary') return;
BDEvents.off('plugin-loaded', onLoaded);
callback();
};
BDEvents.on('plugin-loaded', onLoaded);
};
const onDone = () => {
if (!global.pluginModule || (!global.BDEvents && !global.XenoLib)) return;
if (!global.BDEvents || global.XenoLib) pluginModule.reloadPlugin(this.name);
else {
const listener = () => {
pluginModule.reloadPlugin(this.name);
BDEvents.off('xenolib-loaded', listener);
};
BDEvents.on('plugin-loaded', onLoaded);
};
const onDone = () => {
if (!global.pluginModule || (!global.BDEvents && !global.XenoLib)) return;
if (!global.BDEvents || global.XenoLib) pluginModule.reloadPlugin(this.name);
else {
const listener = () => {
BDEvents.off('xenolib-loaded', listener);
pluginModule.reloadPlugin(this.name);
};
BDEvents.on('xenolib-loaded', listener);
}
};
const downloadXenoLib = () => {
if (global.XenoLib) return onDone();
request('https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/1XenoLib.plugin.js', (error, response, body) => {
if (error) return onFail();
onDone();
fs.writeFile(path.join(window.ContentManager.pluginsFolder, '1XenoLib.plugin.js'), body, () => {});
});
};
if (!global.ZeresPluginLibrary) {
request('https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js', (error, response, body) => {
if (error) return onFail();
waitForLibLoad(downloadXenoLib);
fs.writeFile(path.join(window.ContentManager.pluginsFolder, '0PluginLibrary.plugin.js'), body, () => {});
});
} else downloadXenoLib();
}
},
props
)
BDEvents.on('xenolib-loaded', listener);
}
};
const downloadXenoLib = () => {
if (global.XenoLib) return onDone();
request('https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/1XenoLib.plugin.js', (error, response, body) => {
if (error) return onFail();
onDone();
fs.writeFile(path.join(window.ContentManager.pluginsFolder, '1XenoLib.plugin.js'), body, () => {});
});
};
if (!global.ZeresPluginLibrary) {
request('https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js', (error, response, body) => {
if (error) return onFail();
waitForLibLoad(downloadXenoLib);
fs.writeFile(path.join(window.ContentManager.pluginsFolder, '0PluginLibrary.plugin.js'), body, () => {});
});
} else downloadXenoLib();
}
},
props
)
);
});