STR v2.2.0 sticker download/conversion support
This commit is contained in:
parent
64f01b1f57
commit
dfca0324aa
|
@ -1,4 +1,9 @@
|
||||||
# [SaveToRedux](https://1lighty.github.io/BetterDiscordStuff/?plugin=SaveToRedux "SaveToRedux") Changelog
|
# [SaveToRedux](https://1lighty.github.io/BetterDiscordStuff/?plugin=SaveToRedux "SaveToRedux") Changelog
|
||||||
|
### 2.2.0
|
||||||
|
- Added support for stickers, they can now be downloaded properly.
|
||||||
|
- Animated stickers will be turned into a shareable GIF, but the option of downloaing the source is still available.
|
||||||
|
- Lottie sticker download resolution can be changed in settings, default is 160x160.
|
||||||
|
|
||||||
### 2.1.5
|
### 2.1.5
|
||||||
- Changed to module.exports because useless backwards incompatbile changes are the motto for BBD apparently.
|
- Changed to module.exports because useless backwards incompatbile changes are the motto for BBD apparently.
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
# SaveToRedux [![download](https://i.imgur.com/OAHgjZu.png)](https://1lighty.github.io/BetterDiscordStuff/?plugin=SaveToRedux&dl=1 "SaveToRedux")
|
# SaveToRedux [![download](https://i.imgur.com/OAHgjZu.png)](https://1lighty.github.io/BetterDiscordStuff/?plugin=SaveToRedux&dl=1 "SaveToRedux")
|
||||||
Allows you to save images, videos, profile icons, server icons, reactions, emotes and custom status emotes to any folder quickly, as well as install plugins from direct links.
|
Allows you to save images, videos, profile icons, server icons, reactions, emotes, custom status emotes and stickers to any folder quickly, as well as install plugins from direct links.
|
||||||
### Features
|
### Features
|
||||||
Right click on an image, video, file, user, server icon, group DM or emote to be able to set folders and save to folders, under the **Save * To** context menu submenu.
|
Right click on an image, video, file, user, server icon, group DM, emote or sticker to be able to set folders and save to folders, under the **Save * To** context menu submenu.
|
||||||
With * being what you're saving, eg Image, Video, Emoji, File or Icon
|
With * being what you're saving, eg Image, Video, Emoji, File or Icon
|
||||||
Right clicking a theme or plugin attachment or link will show you the option of installing it.
|
Right clicking a theme or plugin attachment or link will show you the option of installing it.
|
||||||
### Preview
|
### Preview
|
||||||
Right click on nearly any image, video, file, user, server icon, group DM or emote
|
Right click on nearly any image, video, file, user, server icon, group DM, emote or sticker
|
||||||
![preview](https://i.imgur.com/htOuqtw.png)
|
![preview](https://i.imgur.com/htOuqtw.png)
|
||||||
![preview2](https://cdn.discordapp.com/attachments/389049952732446733/694622056213512292/5jsZjnlrCBkz.png)
|
![preview2](https://cdn.discordapp.com/attachments/389049952732446733/694622056213512292/5jsZjnlrCBkz.png)
|
||||||
### Settings
|
### Settings
|
||||||
#### File Save Settings
|
#### File Save Settings
|
||||||
##### File name
|
##### File name
|
||||||
Original - Save as original filename
|
Original - Save as original filename
|
||||||
Date - Save as current localized time
|
Date - Save as current localized time
|
||||||
Random - Use random characters
|
Random - Use random characters
|
||||||
Original + Random - Append random to the end of the original file name
|
Original + Random - Append random to the end of the original file name
|
||||||
Custom - Set your own name saving
|
Custom - Set your own name saving
|
||||||
##### Custom File name
|
##### Custom File name
|
||||||
Set file name when saving, can be anything static, or anything dynamic like file, rand, date, time, day, month, year, hours, minutes and seconds.
|
Set file name when saving, can be anything static, or anything dynamic like file, rand, date, time, day, month, year, hours, minutes and seconds.
|
||||||
Dynamic options must be wrapped like ${OPTION}
|
Dynamic options must be wrapped like ${OPTION}
|
||||||
##### Conflicting Filename Mode
|
##### Conflicting Filename Mode
|
||||||
Warn - Always warn if a file with the same name exists
|
Warn - Always warn if a file with the same name exists
|
||||||
Overwrite
|
Overwrite
|
||||||
Append number - appends a number in paranthesis
|
Append number - appends a number in paranthesis
|
||||||
Append random
|
Append random
|
||||||
Save As... - lets you enter a custom name instead
|
Save As... - lets you enter a custom name instead
|
||||||
#### Misc
|
#### Misc
|
||||||
##### Context menu option at the bottom instead of top
|
##### Context menu option at the bottom instead of top
|
||||||
|
|
|
@ -2,24 +2,24 @@
|
||||||
/*@cc_on
|
/*@cc_on
|
||||||
@if (@_jscript)
|
@if (@_jscript)
|
||||||
|
|
||||||
// Offer to self-install for clueless users that try to run this directly.
|
// Offer to self-install for clueless users that try to run this directly.
|
||||||
var shell = WScript.CreateObject('WScript.Shell');
|
var shell = WScript.CreateObject('WScript.Shell');
|
||||||
var fs = new ActiveXObject('Scripting.FileSystemObject');
|
var fs = new ActiveXObject('Scripting.FileSystemObject');
|
||||||
var pathPlugins = shell.ExpandEnvironmentStrings('%APPDATA%\\BetterDiscord\\plugins');
|
var pathPlugins = shell.ExpandEnvironmentStrings('%APPDATA%\\BetterDiscord\\plugins');
|
||||||
var pathSelf = WScript.ScriptFullName;
|
var pathSelf = WScript.ScriptFullName;
|
||||||
// Put the user at ease by addressing them in the first person
|
// 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);
|
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)) {
|
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);
|
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)) {
|
} 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);
|
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) {
|
} 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);
|
fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
|
||||||
// Show the user where to put plugins in the future
|
// Show the user where to put plugins in the future
|
||||||
shell.Exec('explorer ' + pathPlugins);
|
shell.Exec('explorer ' + pathPlugins);
|
||||||
shell.Popup('I\'m installed!\nJust go to settings, plugins and enable me!', 0, 'Successfully installed', 0x40);
|
shell.Popup('I\'m installed!\nJust go to settings, plugins and enable me!', 0, 'Successfully installed', 0x40);
|
||||||
}
|
}
|
||||||
WScript.Quit();
|
WScript.Quit();
|
||||||
|
|
||||||
@else@*/
|
@else@*/
|
||||||
/*
|
/*
|
||||||
|
@ -41,16 +41,16 @@ module.exports = (() => {
|
||||||
twitter_username: ''
|
twitter_username: ''
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
version: '2.1.5',
|
version: '2.2.0',
|
||||||
description: 'Allows you to save images, videos, profile icons, server icons, reactions, emotes and custom status emotes to any folder quickly, as well as install plugins from direct links.',
|
description: 'Allows you to save images, videos, profile icons, server icons, reactions, emotes, custom status emotes and stickers to any folder quickly, as well as install plugins from direct links.',
|
||||||
github: 'https://github.com/1Lighty',
|
github: 'https://github.com/1Lighty',
|
||||||
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/SaveToRedux/SaveToRedux.plugin.js'
|
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/SaveToRedux/SaveToRedux.plugin.js'
|
||||||
},
|
},
|
||||||
changelog: [
|
changelog: [
|
||||||
{
|
{
|
||||||
title: 'fixed',
|
title: 'added',
|
||||||
type: 'fixed',
|
type: 'added',
|
||||||
items: ['Changed to module.exports because useless backwards incompatbile changes are the motto for BBD apparently.']
|
items: ['Added support for stickers, they can now be downloaded properly.', 'Animated stickers will be turned into a shareable GIF, but the option of downloaing the source is still available.', 'Lottie sticker download resolution can be changed in settings, default is 160x160.']
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
defaultConfig: [
|
defaultConfig: [
|
||||||
|
@ -94,7 +94,17 @@ module.exports = (() => {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ name: 'User and Server icons get saved by the users or servers name, instead of randomized', id: 'saveByName', type: 'switch', value: true },
|
{ name: 'User and Server icons get saved by the users or servers name, instead of randomized', id: 'saveByName', type: 'switch', value: true },
|
||||||
{ name: 'Append server name or DM name to image/file name', id: 'appendCurrentName', type: 'switch', value: false }
|
{ name: 'Append server name or DM name to image/file name', id: 'appendCurrentName', type: 'switch', value: false },
|
||||||
|
{
|
||||||
|
name: 'Lottie sticker save size',
|
||||||
|
id: 'lottieSize',
|
||||||
|
type: 'radio',
|
||||||
|
value: 0,
|
||||||
|
options: [
|
||||||
|
{ name: 'Default 160x160', value: 0 },
|
||||||
|
{ name: 'Max size 320x320', value: 1 }
|
||||||
|
]
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ type: 'category', id: 'misc', name: 'Misc', collapsible: true, shown: false, settings: [{ name: 'Context menu option at the bottom instead of top', id: 'contextMenuOnBottom', type: 'switch', value: true }] }
|
{ type: 'category', id: 'misc', name: 'Misc', collapsible: true, shown: false, settings: [{ name: 'Context menu option at the bottom instead of top', id: 'contextMenuOnBottom', type: 'switch', value: true }] }
|
||||||
|
@ -113,7 +123,7 @@ module.exports = (() => {
|
||||||
const ret = ConfirmModal(props);
|
const ret = ConfirmModal(props);
|
||||||
if (props.size) ret.props.size = props.size;
|
if (props.size) ret.props.size = props.size;
|
||||||
return ret;
|
return ret;
|
||||||
} catch(err) {
|
} catch (err) {
|
||||||
if (props.onCancel) props.onCancel();
|
if (props.onCancel) props.onCancel();
|
||||||
else props.onClose();
|
else props.onClose();
|
||||||
return null;
|
return null;
|
||||||
|
@ -125,7 +135,7 @@ module.exports = (() => {
|
||||||
|
|
||||||
const Modals = {
|
const Modals = {
|
||||||
showModal(title, content, options) {
|
showModal(title, content, options) {
|
||||||
return ModalStack.openModal(e => React.createElement(ConfirmationModal, Object.assign({title, children: content, cancelText: 'Cancel'}, e, options)));
|
return ModalStack.openModal(e => React.createElement(ConfirmationModal, Object.assign({ title, children: content, cancelText: 'Cancel' }, e, options)));
|
||||||
},
|
},
|
||||||
showConfirmationModal(title, content, options) {
|
showConfirmationModal(title, content, options) {
|
||||||
return this.showModal(title, React.createElement(Markdown, null, content), options);
|
return this.showModal(title, React.createElement(Markdown, null, content), options);
|
||||||
|
@ -312,6 +322,17 @@ module.exports = (() => {
|
||||||
|
|
||||||
const useIdealExtensions = url => (url.indexOf('/a_') !== -1 ? url.replace('.webp', '.gif').replace('.png', '.gif') : url.replace('.webp', '.png'));
|
const useIdealExtensions = url => (url.indexOf('/a_') !== -1 ? url.replace('.webp', '.gif').replace('.png', '.gif') : url.replace('.webp', '.png'));
|
||||||
|
|
||||||
|
const ImageWrapperClassname = XenoLib.getSingleClass('clickable imageWrapper');
|
||||||
|
const StickerUtils = WebpackModules.getByProps('getStickerAssetUrl');
|
||||||
|
const { StickerFormat } = WebpackModules.getByProps('StickerFormat') || {};
|
||||||
|
const WasmLottie = WebpackModules.getByPrototypes('get_rgba');
|
||||||
|
|
||||||
|
const webWorkerData = `importScripts('https://1lighty.github.io/BetterDiscordPlugins/Plugins/SaveToRedux/res/worker.js');`;
|
||||||
|
const workerDataURL = window.URL.createObjectURL(new Blob([webWorkerData], { type: 'text/javascript' }));
|
||||||
|
|
||||||
|
const StickerClasses = WebpackModules.getByProps('pngImage', 'lottieCanvas');
|
||||||
|
|
||||||
|
|
||||||
return class SaveToRedux extends Plugin {
|
return class SaveToRedux extends Plugin {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -359,6 +380,9 @@ module.exports = (() => {
|
||||||
div[id$="-str"] + .${XenoLib.getSingleClass('layerContainer layer')} {
|
div[id$="-str"] + .${XenoLib.getSingleClass('layerContainer layer')} {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
div[id$="-str-stickers"] + .${XenoLib.getSingleClass('layerContainer layer')} {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
this.lastUsedFolder = -1;
|
this.lastUsedFolder = -1;
|
||||||
|
@ -400,6 +424,7 @@ module.exports = (() => {
|
||||||
Utilities.suppressErrors(this.patchEmojiPicker.bind(this), 'EmojiPicker patch')(this.promises.state);
|
Utilities.suppressErrors(this.patchEmojiPicker.bind(this), 'EmojiPicker patch')(this.promises.state);
|
||||||
Utilities.suppressErrors(this.patchReactions.bind(this), 'Reaction patch')(this.promises.state);
|
Utilities.suppressErrors(this.patchReactions.bind(this), 'Reaction patch')(this.promises.state);
|
||||||
this.patchContextMenus();
|
this.patchContextMenus();
|
||||||
|
this.patchStickerStorePicker();
|
||||||
}
|
}
|
||||||
|
|
||||||
patchEmojiPicker() {
|
patchEmojiPicker() {
|
||||||
|
@ -461,6 +486,25 @@ module.exports = (() => {
|
||||||
Reaction.forceUpdateAll();
|
Reaction.forceUpdateAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
patchStickerStorePicker() {
|
||||||
|
const StickerStorePicker = WebpackModules.find(m => {
|
||||||
|
if (!m || !m.type) return false;
|
||||||
|
const typeString = String(m.type);
|
||||||
|
for (const string of ['.getStickerItemProps', '.inspectedSticker']) if (typeString.indexOf(string) === -1) return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
Patcher.after(StickerStorePicker, 'type', (_, [props], ret) => {
|
||||||
|
for (const stickerClickable of ret.props.children) {
|
||||||
|
const { sticker } = Utilities.findInReactTree(stickerClickable, e => e && e.sticker && e.sticker.id) || {};
|
||||||
|
if (!sticker) continue;
|
||||||
|
const url = StickerUtils.getStickerAssetUrl(sticker);
|
||||||
|
XenoLib.createSharedContext(stickerClickable, () => XenoLib.createContextMenuGroup([
|
||||||
|
this.constructMenu(url.split('?')[0], 'Sticker', sticker.name, () => { }, undefined, '', { id: sticker.id, type: sticker.format_type })
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
patchContextMenus() {
|
patchContextMenus() {
|
||||||
this.patchUserContextMenus();
|
this.patchUserContextMenus();
|
||||||
this.patchImageContextMenus();
|
this.patchImageContextMenus();
|
||||||
|
@ -509,12 +553,13 @@ module.exports = (() => {
|
||||||
);
|
);
|
||||||
if (!Array.isArray(menu)) return;
|
if (!Array.isArray(menu)) return;
|
||||||
const [state, setState] = React.useState({});
|
const [state, setState] = React.useState({});
|
||||||
|
const extraData = {};
|
||||||
let src;
|
let src;
|
||||||
let saveType = 'File';
|
let saveType = 'File';
|
||||||
let url = '';
|
let url = '';
|
||||||
let proxiedUrl = '';
|
let proxiedUrl = '';
|
||||||
let customName = '';
|
let customName = '';
|
||||||
if (isImageMenu && (Utilities.getNestedProp(props, 'target.parentNode.className') || '').indexOf(XenoLib.getSingleClass('clickable imageWrapper')) !== -1) {
|
if (isImageMenu && (Utilities.getNestedProp(props, 'target.parentNode.className') || '').indexOf(ImageWrapperClassname) !== -1) {
|
||||||
const inst = ReactTools.getOwnerInstance(Utilities.getNestedProp(props, 'target.parentNode.parentNode'));
|
const inst = ReactTools.getOwnerInstance(Utilities.getNestedProp(props, 'target.parentNode.parentNode'));
|
||||||
proxiedUrl = props.src;
|
proxiedUrl = props.src;
|
||||||
if (inst) src = inst.props.original;
|
if (inst) src = inst.props.original;
|
||||||
|
@ -525,6 +570,17 @@ module.exports = (() => {
|
||||||
proxiedUrl = '';
|
proxiedUrl = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const targetClassName = Utilities.getNestedProp(props, 'target.className') || '';
|
||||||
|
if (StickerClasses && (targetClassName.indexOf(StickerClasses.lottieCanvas.split(' ')[0]) !== -1 || targetClassName.indexOf(StickerClasses.pngImage.split(' ')[0]) !== -1)) {
|
||||||
|
const { memoizedProps } = Utilities.findInTree(ReactTools.getReactInstance(props.target), e => e && e.type && e.type.displayName === 'StickerMessage', { walkable: ['return'] });
|
||||||
|
const { sticker } = memoizedProps || {};
|
||||||
|
if (!sticker) return;
|
||||||
|
src = StickerUtils.getStickerAssetUrl(sticker);
|
||||||
|
customName = sticker.name;
|
||||||
|
extraData.type = sticker.format_type;
|
||||||
|
extraData.id = sticker.id;
|
||||||
|
saveType = 'Sticker';
|
||||||
|
}
|
||||||
if (!src) src = Utilities.getNestedProp(props, 'attachment.href') || Utilities.getNestedProp(props, 'attachment.url');
|
if (!src) src = Utilities.getNestedProp(props, 'attachment.href') || Utilities.getNestedProp(props, 'attachment.url');
|
||||||
/* is that enough specific cases? */
|
/* is that enough specific cases? */
|
||||||
if (typeof src === 'string') {
|
if (typeof src === 'string') {
|
||||||
|
@ -572,42 +628,44 @@ module.exports = (() => {
|
||||||
}
|
}
|
||||||
url = src;
|
url = src;
|
||||||
if (!url) return;
|
if (!url) return;
|
||||||
if (isImage(url) || url.indexOf('//steamuserimages') !== -1) saveType = 'Image';
|
if (saveType !== 'Sticker') {
|
||||||
else if (isVideo(url)) saveType = 'Video';
|
if (isImage(url) || url.indexOf('//steamuserimages') !== -1) saveType = 'Image';
|
||||||
else if (isAudio(url)) saveType = 'Audio';
|
else if (isVideo(url)) saveType = 'Video';
|
||||||
if (url.indexOf('app.com/emojis/') !== -1) {
|
else if (isAudio(url)) saveType = 'Audio';
|
||||||
saveType = 'Emoji';
|
if (url.indexOf('app.com/emojis/') !== -1) {
|
||||||
const emojiId = url.split('emojis/')[1].split('.')[0];
|
saveType = 'Emoji';
|
||||||
const emoji = EmojiUtils.getDisambiguatedEmojiContext().getById(emojiId);
|
const emojiId = url.split('emojis/')[1].split('.')[0];
|
||||||
if (!emoji) {
|
const emoji = EmojiUtils.getDisambiguatedEmojiContext().getById(emojiId);
|
||||||
if (!DiscordAPI.currentChannel || !this.channelMessages[DiscordAPI.currentChannel.id]) return;
|
if (!emoji) {
|
||||||
const message = this.channelMessages[DiscordAPI.currentChannel.id]._array.find(m => m.content.indexOf(emojiId) !== -1);
|
if (!DiscordAPI.currentChannel || !this.channelMessages[DiscordAPI.currentChannel.id]) return;
|
||||||
if (message && message.content) {
|
const message = this.channelMessages[DiscordAPI.currentChannel.id]._array.find(m => m.content.indexOf(emojiId) !== -1);
|
||||||
const group = message.content.match(new RegExp(`<a?:([^:>]*):${emojiId}>`));
|
if (message && message.content) {
|
||||||
if (group && group[1]) customName = group[1];
|
const group = message.content.match(new RegExp(`<a?:([^:>]*):${emojiId}>`));
|
||||||
|
if (group && group[1]) customName = group[1];
|
||||||
|
}
|
||||||
|
if (!customName) {
|
||||||
|
const alt = props.target.alt;
|
||||||
|
if (alt) customName = alt.split(':')[1] || alt;
|
||||||
|
}
|
||||||
|
} else customName = emoji.name;
|
||||||
|
} else if (state.__STR_extension) {
|
||||||
|
if (isImage(state.__STR_extension)) saveType = 'Image';
|
||||||
|
else if (isVideo(state.__STR_extension)) saveType = 'Video';
|
||||||
|
else if (isAudio(state.__STR_extension)) saveType = 'Audio';
|
||||||
|
} else if (url.indexOf('//discordapp.com/assets/') !== -1 && props.target && props.target.className.indexOf('emoji') !== -1) {
|
||||||
|
const alt = props.target.alt;
|
||||||
|
if (alt) {
|
||||||
|
customName = alt.split(':')[1] || alt;
|
||||||
|
const name = EmojiStore.convertSurrogateToName(customName);
|
||||||
|
if (name) {
|
||||||
|
const match = name.match(EmojiStore.EMOJI_NAME_RE);
|
||||||
|
if (match) customName = EmojiStore.getByName(match[1]).uniqueName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!customName) {
|
saveType = 'Emoji';
|
||||||
const alt = props.target.alt;
|
} else if (url.indexOf('.plugin.js') === url.length - 10) saveType = 'Plugin';
|
||||||
if (alt) customName = alt.split(':')[1] || alt;
|
else if (url.indexOf('.theme.css') === url.length - 10) saveType = 'Theme';
|
||||||
}
|
}
|
||||||
} else customName = emoji.name;
|
|
||||||
} else if (state.__STR_extension) {
|
|
||||||
if (isImage(state.__STR_extension)) saveType = 'Image';
|
|
||||||
else if (isVideo(state.__STR_extension)) saveType = 'Video';
|
|
||||||
else if (isAudio(state.__STR_extension)) saveType = 'Audio';
|
|
||||||
} else if (url.indexOf('//discordapp.com/assets/') !== -1 && props.target && props.target.className.indexOf('emoji') !== -1) {
|
|
||||||
const alt = props.target.alt;
|
|
||||||
if (alt) {
|
|
||||||
customName = alt.split(':')[1] || alt;
|
|
||||||
const name = EmojiStore.convertSurrogateToName(customName);
|
|
||||||
if (name) {
|
|
||||||
const match = name.match(EmojiStore.EMOJI_NAME_RE);
|
|
||||||
if (match) customName = EmojiStore.getByName(match[1]).uniqueName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
saveType = 'Emoji';
|
|
||||||
} else if (url.indexOf('.plugin.js') === url.length - 10) saveType = 'Plugin';
|
|
||||||
else if (url.indexOf('.theme.css') === url.length - 10) saveType = 'Theme';
|
|
||||||
try {
|
try {
|
||||||
const submenu = this.constructMenu(
|
const submenu = this.constructMenu(
|
||||||
url.split('?')[0],
|
url.split('?')[0],
|
||||||
|
@ -618,14 +676,15 @@ module.exports = (() => {
|
||||||
if (!isTrustedDomain(targetUrl)) return;
|
if (!isTrustedDomain(targetUrl)) return;
|
||||||
state.__STR_requesting = true;
|
state.__STR_requesting = true;
|
||||||
RequestModule.head(targetUrl, (err, res) => {
|
RequestModule.head(targetUrl, (err, res) => {
|
||||||
if (err) return setState({ __STR_requesting: false, __STR_requested: true });
|
if (err || res.statusCode !== 200) return setState({ __STR_requesting: false, __STR_requested: true });
|
||||||
const extension = MimeTypesModule.extension(res.headers['content-type']);
|
const extension = MimeTypesModule.extension(res.headers['content-type']);
|
||||||
setState({ __STR_requesting: false, __STR_requested: true, __STR_extension: extension });
|
setState({ __STR_requesting: false, __STR_requested: true, __STR_extension: extension });
|
||||||
});
|
});
|
||||||
targetUrl;
|
targetUrl;
|
||||||
},
|
},
|
||||||
state.__STR_extension,
|
state.__STR_extension,
|
||||||
proxiedUrl
|
proxiedUrl,
|
||||||
|
extraData
|
||||||
);
|
);
|
||||||
const group = XenoLib.createContextMenuGroup([submenu]);
|
const group = XenoLib.createContextMenuGroup([submenu]);
|
||||||
if (this.settings.misc.contextMenuOnBottom) menu.push(group);
|
if (this.settings.misc.contextMenuOnBottom) menu.push(group);
|
||||||
|
@ -743,7 +802,7 @@ module.exports = (() => {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
formatURL(url, requiresSize, customName, fallbackExtension, proxiedUrl, failNum = 0, forceKeepOriginal = false) {
|
formatURL(url, requiresSize, customName, fallbackExtension, proxiedUrl, failNum = 0, forceKeepOriginal = false, forceExtension = false) {
|
||||||
// url = url.replace(/\/$/, '');
|
// url = url.replace(/\/$/, '');
|
||||||
if (requiresSize) url += '?size=2048';
|
if (requiresSize) url += '?size=2048';
|
||||||
else if (url.indexOf('twimg.com/') !== -1) url = url.replace(':small', ':orig').replace(':medium', ':orig').replace(':large', ':orig');
|
else if (url.indexOf('twimg.com/') !== -1) url = url.replace(':small', ':orig').replace(':medium', ':orig').replace(':large', ':orig');
|
||||||
|
@ -753,7 +812,7 @@ module.exports = (() => {
|
||||||
}
|
}
|
||||||
const match = url.match(/(?:\/)([^\/]+?)(?:(?:\.)([^.\/?:]+)){0,1}(?:[^\w\/\.]+\w+){0,1}(?:(?:\?[^\/]+){0,1}|(?:\/){0,1})$/);
|
const match = url.match(/(?:\/)([^\/]+?)(?:(?:\.)([^.\/?:]+)){0,1}(?:[^\w\/\.]+\w+){0,1}(?:(?:\?[^\/]+){0,1}|(?:\/){0,1})$/);
|
||||||
let name = customName || match[1];
|
let name = customName || match[1];
|
||||||
let extension = match[2] || fallbackExtension;
|
let extension = forceExtension || match[2] || fallbackExtension;
|
||||||
if (url.indexOf('//media.tenor.co') !== -1) {
|
if (url.indexOf('//media.tenor.co') !== -1) {
|
||||||
extension = name;
|
extension = name;
|
||||||
name = url.match(/\/\/media.tenor.co\/[^\/]+\/([^\/]+)\//)[1];
|
name = url.match(/\/\/media.tenor.co\/[^\/]+\/([^\/]+)\//)[1];
|
||||||
|
@ -772,10 +831,17 @@ module.exports = (() => {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructMenu(url, type, customName, onNoExtension = () => { }, fallbackExtension, proxiedUrl) {
|
constructMenu(url, type, customName, onNoExtension = () => { }, fallbackExtension, proxiedUrl, extraData = {}) {
|
||||||
const subItems = [];
|
const subItems = [];
|
||||||
const folderSubMenus = [];
|
const folderSubMenus = [];
|
||||||
const formattedurl = this.formatURL(url, type === 'Icon' || type === 'Avatar', customName, fallbackExtension, proxiedUrl, 0, type === 'Theme' || type === 'Plugin');
|
let forcedExtension = false;
|
||||||
|
if (type === 'Sticker') {
|
||||||
|
if (extraData.isStickerSubMenu) {
|
||||||
|
if (extraData.type === StickerFormat.APNG) forcedExtension = 'apng';
|
||||||
|
}
|
||||||
|
else forcedExtension = extraData.type === StickerFormat.PNG ? 'png' : 'gif';
|
||||||
|
}
|
||||||
|
const formattedurl = this.formatURL(url, type === 'Icon' || type === 'Avatar', customName, fallbackExtension, proxiedUrl, 0, type === 'Theme' || type === 'Plugin', forcedExtension);
|
||||||
if (!formattedurl.extension) onNoExtension(formattedurl.url);
|
if (!formattedurl.extension) onNoExtension(formattedurl.url);
|
||||||
let notifId;
|
let notifId;
|
||||||
let downloadAttempts = 0;
|
let downloadAttempts = 0;
|
||||||
|
@ -796,23 +862,67 @@ module.exports = (() => {
|
||||||
return `${bytes.toFixed(1)}${noUnit && unit.a === units[u] ? '' : ' ' + units[u]}`;
|
return `${bytes.toFixed(1)}${noUnit && unit.a === units[u] ? '' : ' ' + units[u]}`;
|
||||||
}
|
}
|
||||||
const unit = { a: '' };
|
const unit = { a: '' };
|
||||||
const update = () => XenoLib.Notifications.update(notifId, { content: `Downloading ${type} ${humanFileSize(receivedBytes, false, true, unit)}/${humanFileSize(totalBytes, false, false, unit)}`, progress: (receivedBytes / totalBytes) * 100 });
|
const update = () => XenoLib.Notifications.update(notifId, { content: `Downloading ${type} ${humanFileSize(receivedBytes.length, false, true, unit)}/${humanFileSize(totalBytes, false, false, unit)}`, progress: (receivedBytes.length / totalBytes) * 100 });
|
||||||
const throttledUpdate = XenoLib._.throttle(update, 50);
|
const throttledUpdate = XenoLib._.throttle(update, 50);
|
||||||
let totalBytes = 0;
|
let totalBytes = 0;
|
||||||
let receivedBytes = 0;
|
let receivedBytes = '';
|
||||||
const req = RequestModule(formattedurl.url);
|
const req = RequestModule({ url: formattedurl.url, encoding: null }, async (_, __, body) => {
|
||||||
|
if (type !== 'Sticker' || extraData.type === StickerFormat.PNG || extraData.isStickerSubMenu) return; // do not convert it
|
||||||
|
XenoLib.Notifications.remove(notifId);
|
||||||
|
notifId = undefined;
|
||||||
|
let sNotifId = XenoLib.Notifications.info(`Converting sticker to gif..`, { timeout: 0, loading: true });
|
||||||
|
let worker = null;
|
||||||
|
let lottieWASM = null;
|
||||||
|
try {
|
||||||
|
worker = new Worker(workerDataURL);
|
||||||
|
if (extraData.type === StickerFormat.APNG) {
|
||||||
|
await new Promise(res => {
|
||||||
|
worker.onmessage = res;
|
||||||
|
worker.postMessage(['CONVERT-APNG', body]);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
lottieWASM = new WasmLottie(receivedBytes);
|
||||||
|
const size = this.settings.saveOptions.lottieSize ? 320 : 160;
|
||||||
|
const frames = [];
|
||||||
|
for (let i = 0, framesCount = lottieWASM.frames; i < framesCount; i++) frames.push(new Uint8ClampedArray(lottieWASM.get_bgra(i, size, size)));
|
||||||
|
await new Promise(async res => {
|
||||||
|
worker.onmessage = res;
|
||||||
|
worker.postMessage(['CONVERT-FRAMES', { width: size, height: size, framerate: 60, frames }]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const { data } = await new Promise(res => {
|
||||||
|
worker.onmessage = res;
|
||||||
|
worker.postMessage(['DONE']);
|
||||||
|
});
|
||||||
|
XenoLib.Notifications.update(sNotifId, { content: `Saving..` });
|
||||||
|
FsModule.writeFileSync(path, Buffer.from(data));
|
||||||
|
XenoLib.Notifications.remove(sNotifId);
|
||||||
|
sNotifId = undefined;
|
||||||
|
if (openOnSave) openPath(path);
|
||||||
|
BdApi.showToast(`Saved to '${PathModule.resolve(path)}'`, { type: 'success' });
|
||||||
|
} catch (err) {
|
||||||
|
Logger.stacktrace('Failed converting to GIF', err);
|
||||||
|
BdApi.showToast(`Failed to save sticker..`, { type: 'error' });
|
||||||
|
} finally {
|
||||||
|
if (lottieWASM) lottieWASM.drop();
|
||||||
|
if (worker) worker.terminate();
|
||||||
|
}
|
||||||
|
});
|
||||||
req
|
req
|
||||||
.on('data', chunk => {
|
.on('data', chunk => {
|
||||||
receivedBytes += chunk.length;
|
receivedBytes += chunk;
|
||||||
throttledUpdate();
|
throttledUpdate();
|
||||||
})
|
})
|
||||||
.on('response', res => {
|
.on('response', res => {
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
totalBytes = parseInt(res.headers['content-length']);
|
totalBytes = parseInt(res.headers['content-length']);
|
||||||
update();
|
update();
|
||||||
|
if (type === 'Sticker' && extraData.type !== StickerFormat.PNG && !extraData.isStickerSubMenu) return; // do not stream download because we need to convert it first
|
||||||
req
|
req
|
||||||
.pipe(FsModule.createWriteStream(path))
|
.pipe(FsModule.createWriteStream(path))
|
||||||
.on('finish', () => {
|
.on('finish', () => {
|
||||||
|
XenoLib.Notifications.remove(notifId);
|
||||||
|
notifId = undefined;
|
||||||
if (openOnSave) openPath(path);
|
if (openOnSave) openPath(path);
|
||||||
BdApi.showToast(`Saved to '${PathModule.resolve(path)}'`, { type: 'success' });
|
BdApi.showToast(`Saved to '${PathModule.resolve(path)}'`, { type: 'success' });
|
||||||
})
|
})
|
||||||
|
@ -824,7 +934,7 @@ module.exports = (() => {
|
||||||
} else if (res.statusCode == 404) {
|
} else if (res.statusCode == 404) {
|
||||||
if (shouldDoMultiAttempts && downloadAttempts < 2) {
|
if (shouldDoMultiAttempts && downloadAttempts < 2) {
|
||||||
downloadAttempts++;
|
downloadAttempts++;
|
||||||
const newUrl = this.formatURL(url, type === 'Icon' || type === 'Avatar', customName, fallbackExtension, proxiedUrl, downloadAttempts, type === 'Theme' || type === 'Plugin').url;
|
const newUrl = this.formatURL(url, type === 'Icon' || type === 'Avatar', customName, fallbackExtension, proxiedUrl, downloadAttempts, type === 'Theme' || type === 'Plugin', forcedExtension).url;
|
||||||
if (newUrl !== formattedurl.url) {
|
if (newUrl !== formattedurl.url) {
|
||||||
formattedurl.url = newUrl;
|
formattedurl.url = newUrl;
|
||||||
return downloadEx(path, openOnSave);
|
return downloadEx(path, openOnSave);
|
||||||
|
@ -1041,7 +1151,7 @@ module.exports = (() => {
|
||||||
return XenoLib.createContextMenuSubMenu(
|
return XenoLib.createContextMenuSubMenu(
|
||||||
folder.name,
|
folder.name,
|
||||||
[
|
[
|
||||||
XenoLib.createContextMenuItem(
|
extraData.onlyFolderSave ? null : XenoLib.createContextMenuItem(
|
||||||
'Remove Folder',
|
'Remove Folder',
|
||||||
() => {
|
() => {
|
||||||
this.folders.splice(idx, 1);
|
this.folders.splice(idx, 1);
|
||||||
|
@ -1050,7 +1160,7 @@ module.exports = (() => {
|
||||||
},
|
},
|
||||||
'remove-folder'
|
'remove-folder'
|
||||||
),
|
),
|
||||||
XenoLib.createContextMenuItem(
|
extraData.onlyFolderSave ? null : XenoLib.createContextMenuItem(
|
||||||
'Open Folder',
|
'Open Folder',
|
||||||
() => {
|
() => {
|
||||||
openPath(folder.path);
|
openPath(folder.path);
|
||||||
|
@ -1076,7 +1186,7 @@ module.exports = (() => {
|
||||||
},
|
},
|
||||||
'save-and-open'
|
'save-and-open'
|
||||||
),
|
),
|
||||||
XenoLib.createContextMenuItem(
|
extraData.onlyFolderSave ? null : XenoLib.createContextMenuItem(
|
||||||
'Edit',
|
'Edit',
|
||||||
() => {
|
() => {
|
||||||
let __name = folder.name.slice(0);
|
let __name = folder.name.slice(0);
|
||||||
|
@ -1119,7 +1229,10 @@ module.exports = (() => {
|
||||||
for (const folderIDX in this.folders) folderSubMenus.push(folderSubMenu(this.folders[folderIDX], folderIDX));
|
for (const folderIDX in this.folders) folderSubMenus.push(folderSubMenu(this.folders[folderIDX], folderIDX));
|
||||||
subItems.push(
|
subItems.push(
|
||||||
...folderSubMenus,
|
...folderSubMenus,
|
||||||
XenoLib.createContextMenuItem(
|
type === 'Sticker' && !extraData.isStickerSubMenu && extraData.type !== StickerFormat.PNG ?
|
||||||
|
XenoLib.createContextMenuSubMenu(`Save ${extraData.type === StickerFormat.LOTTIE ? 'Lottie JSON' : 'APNG'}`, this.constructMenu(url, type, customName, onNoExtension, fallbackExtension, proxiedUrl, { ...extraData, onlyItems: true, isStickerSubMenu: true, onlyFolderSave: true }), 'str-stickers')
|
||||||
|
: null,
|
||||||
|
extraData.onlyFolderSave ? null : XenoLib.createContextMenuItem(
|
||||||
'Add Folder',
|
'Add Folder',
|
||||||
() => {
|
() => {
|
||||||
dialog
|
dialog
|
||||||
|
@ -1215,6 +1328,7 @@ module.exports = (() => {
|
||||||
)
|
)
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
if (extraData.onlyItems) return subItems;
|
||||||
return XenoLib.createContextMenuSubMenu(`Save ${type} To`, subItems, 'str', {
|
return XenoLib.createContextMenuSubMenu(`Save ${type} To`, subItems, 'str', {
|
||||||
action: () => {
|
action: () => {
|
||||||
if (this.lastUsedFolder === -1) return BdApi.showToast('No folder has been used yet', { type: 'error' });
|
if (this.lastUsedFolder === -1) return BdApi.showToast('No folder has been used yet', { type: 'error' });
|
||||||
|
@ -1272,7 +1386,7 @@ module.exports = (() => {
|
||||||
n = (n, e) => n && n._config && n._config.info && n._config.info.version && i(n._config.info.version, e),
|
n = (n, e) => n && n._config && n._config.info && n._config.info.version && i(n._config.info.version, e),
|
||||||
e = BdApi.getPlugin('ZeresPluginLibrary'),
|
e = BdApi.getPlugin('ZeresPluginLibrary'),
|
||||||
o = BdApi.getPlugin('XenoLib');
|
o = BdApi.getPlugin('XenoLib');
|
||||||
n(e, '1.2.23') && (ZeresPluginLibraryOutdated = !0), n(o, '1.3.26') && (XenoLibOutdated = !0);
|
n(e, '1.2.24') && (ZeresPluginLibraryOutdated = !0), n(o, '1.3.29') && (XenoLibOutdated = !0);
|
||||||
}
|
}
|
||||||
} catch (i) {
|
} catch (i) {
|
||||||
console.error('Error checking if libraries are out of date', i);
|
console.error('Error checking if libraries are out of date', i);
|
||||||
|
@ -1300,87 +1414,87 @@ module.exports = (() => {
|
||||||
stop() { }
|
stop() { }
|
||||||
handleMissingLib() {
|
handleMissingLib() {
|
||||||
const a = BdApi.findModuleByProps('openModal', 'hasModalOpen');
|
const a = BdApi.findModuleByProps('openModal', 'hasModalOpen');
|
||||||
if (a && a.hasModalOpen(`${this.name}_DEP_MODAL`)) return;
|
if (a && a.hasModalOpen(`${this.name}_DEP_MODAL`)) return;
|
||||||
const b = !global.XenoLib,
|
const b = !global.XenoLib,
|
||||||
c = !global.ZeresPluginLibrary,
|
c = !global.ZeresPluginLibrary,
|
||||||
d = (b && c) || ((b || c) && (XenoLibOutdated || ZeresPluginLibraryOutdated)),
|
d = (b && c) || ((b || c) && (XenoLibOutdated || ZeresPluginLibraryOutdated)),
|
||||||
e = (() => {
|
e = (() => {
|
||||||
let a = '';
|
let a = '';
|
||||||
return b || c ? (a += `Missing${XenoLibOutdated || ZeresPluginLibraryOutdated ? ' and outdated' : ''} `) : (XenoLibOutdated || ZeresPluginLibraryOutdated) && (a += `Outdated `), (a += `${d ? 'Libraries' : 'Library'} `), a;
|
return b || c ? (a += `Missing${XenoLibOutdated || ZeresPluginLibraryOutdated ? ' and outdated' : ''} `) : (XenoLibOutdated || ZeresPluginLibraryOutdated) && (a += `Outdated `), (a += `${d ? 'Libraries' : 'Library'} `), a;
|
||||||
})(),
|
})(),
|
||||||
f = (() => {
|
f = (() => {
|
||||||
let a = `The ${d ? 'libraries' : 'library'} `;
|
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;
|
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'),
|
g = BdApi.findModuleByDisplayName('Text'),
|
||||||
h = BdApi.findModuleByDisplayName('ConfirmModal'),
|
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));
|
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();
|
if (!a || !h || !g) return console.error(`Missing components:${(a ? '' : ' ModalStack') + (h ? '' : ' ConfirmationModalComponent') + (g ? '' : 'TextElement')}`), i();
|
||||||
class j extends BdApi.React.PureComponent {
|
class j extends BdApi.React.PureComponent {
|
||||||
constructor(a) {
|
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));
|
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;
|
let k = !1,
|
||||||
const m = a.openModal(
|
l = !1;
|
||||||
b => {
|
const m = a.openModal(
|
||||||
if (l) return null;
|
b => {
|
||||||
try {
|
if (l) return null;
|
||||||
return BdApi.React.createElement(
|
try {
|
||||||
j,
|
return BdApi.React.createElement(
|
||||||
{ label: 'missing dependency modal', onError: () => (a.closeModal(m), i()) },
|
j,
|
||||||
BdApi.React.createElement(
|
{ label: 'missing dependency modal', onError: () => (a.closeModal(m), i()) },
|
||||||
h,
|
BdApi.React.createElement(
|
||||||
Object.assign(
|
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'}.`] }),
|
header: e,
|
||||||
red: !1,
|
children: BdApi.React.createElement(g, { size: g.Sizes.SIZE_16, children: [`${f} Please click Download Now to download ${d ? 'them' : 'it'}.`] }),
|
||||||
confirmText: 'Download Now',
|
red: !1,
|
||||||
cancelText: 'Cancel',
|
confirmText: 'Download Now',
|
||||||
onCancel: b.onClose,
|
cancelText: 'Cancel',
|
||||||
onConfirm: () => {
|
onCancel: b.onClose,
|
||||||
if (k) return;
|
onConfirm: () => {
|
||||||
k = !0;
|
if (k) return;
|
||||||
const b = require('request'),
|
k = !0;
|
||||||
c = require('fs'),
|
const b = require('request'),
|
||||||
d = require('path'),
|
c = require('fs'),
|
||||||
e = BdApi.Plugins && BdApi.Plugins.folder ? BdApi.Plugins.folder : window.ContentManager.pluginsFolder,
|
d = require('path'),
|
||||||
f = () => {
|
e = BdApi.Plugins && BdApi.Plugins.folder ? BdApi.Plugins.folder : window.ContentManager.pluginsFolder,
|
||||||
(global.XenoLib && !XenoLibOutdated) ||
|
f = () => {
|
||||||
b('https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/1XenoLib.plugin.js', (b, f, g) => {
|
(global.XenoLib && !XenoLibOutdated) ||
|
||||||
try {
|
b('https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/1XenoLib.plugin.js', (b, f, g) => {
|
||||||
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 {
|
try {
|
||||||
if (b || 200 !== g.statusCode) return a.closeModal(m), i();
|
if (b || 200 !== f.statusCode) return a.closeModal(m), i();
|
||||||
c.writeFile(d.join(e, '0PluginLibrary.plugin.js'), h, () => {}), f();
|
c.writeFile(d.join(e, '1XenoLib.plugin.js'), g, () => { });
|
||||||
} catch (b) {
|
} catch (b) {
|
||||||
console.error('Fatal error downloading ZeresPluginLibrary', b), a.closeModal(m), i();
|
console.error('Fatal error downloading XenoLib', b), a.closeModal(m), i();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
: f();
|
};
|
||||||
}
|
!global.ZeresPluginLibrary || ZeresPluginLibraryOutdated
|
||||||
},
|
? b('https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js', (b, g, h) => {
|
||||||
b,
|
try {
|
||||||
{ onClose: () => {} }
|
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;
|
} 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` }
|
},
|
||||||
);
|
{ modalKey: `${this.name}_DEP_MODAL` }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
get [Symbol.toStringTag]() {
|
get [Symbol.toStringTag]() {
|
||||||
return 'Plugin';
|
return 'Plugin';
|
||||||
|
|
|
@ -27,6 +27,6 @@ Show a notification in Discord when someone sends a message, just like on mobile
|
||||||
Saves all deleted and purged messages, as well as all edit history and ghost pings. With highly configurable ignore options, and even restoring deleted messages after restarting Discord.
|
Saves all deleted and purged messages, as well as all edit history and ghost pings. With highly configurable ignore options, and even restoring deleted messages after restarting Discord.
|
||||||
|
|
||||||
## [SaveToRedux](https://github.com/1Lighty/BetterDiscordPlugins/tree/master/Plugins/SaveToRedux "SaveToRedux")
|
## [SaveToRedux](https://github.com/1Lighty/BetterDiscordPlugins/tree/master/Plugins/SaveToRedux "SaveToRedux")
|
||||||
Allows you to save images, videos, profile icons, server icons, reactions, emotes and custom status emotes to any folder quickly, as well as install plugins from direct links.
|
Allows you to save images, videos, profile icons, server icons, reactions, emotes, custom status emotes and stickers to any folder quickly, as well as install plugins from direct links.
|
||||||
## [UnreadBadgesRedux](https://github.com/1Lighty/BetterDiscordPlugins/tree/master/Plugins/UnreadBadgesRedux "UnreadBadgesRedux")
|
## [UnreadBadgesRedux](https://github.com/1Lighty/BetterDiscordPlugins/tree/master/Plugins/UnreadBadgesRedux "UnreadBadgesRedux")
|
||||||
Shows an unread badge on folders, server icons and channels, all toggleable with the count adjustable.
|
Shows an unread badge on folders, server icons and channels, all toggleable with the count adjustable.
|
||||||
|
|
Loading…
Reference in New Issue