Push
Push
This commit is contained in:
parent
264a6bff6d
commit
9828b407e1
9 changed files with 11657 additions and 0 deletions
1997
1XenoLib.plugin.js
Normal file
1997
1XenoLib.plugin.js
Normal file
File diff suppressed because it is too large
Load diff
2222
BetterImageViewer.plugin.js
Normal file
2222
BetterImageViewer.plugin.js
Normal file
File diff suppressed because it is too large
Load diff
463
BetterTypingUsers.plugin.js
Normal file
463
BetterTypingUsers.plugin.js
Normal file
|
@ -0,0 +1,463 @@
|
|||
//META{"name":"BetterTypingUsers","source":"https://gitdab.com/hana/lightcock/src/branch/master/BetterTypingUsers.plugin.js","website":"https://1lighty.github.io/BetterDiscordStuff/?plugin=BetterTypingUsers","authorId":"287977955240706060","invite":"9jDgyVjTVX","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 reload Discord with Ctrl+R.', 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 reload Discord with Ctrl+R.', 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: 'BetterTypingUsers',
|
||||
authors: [
|
||||
{
|
||||
name: 'Lighty',
|
||||
discord_id: '239513071272329217',
|
||||
github_username: 'LightyPon',
|
||||
twitter_username: ''
|
||||
}
|
||||
],
|
||||
version: '1.0.1',
|
||||
description: 'Replaces "Several people are typing" with who is actually typing, plus "x others" if it can\'t fit. Number of shown people typing can be changed.',
|
||||
github: 'https://github.com/1Lighty',
|
||||
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/BetterTypingUsers/BetterTypingUsers.plugin.js'
|
||||
},
|
||||
changelog: [
|
||||
{
|
||||
title: 'fixed',
|
||||
type: 'fixed',
|
||||
items: ['Changed to module.exports because useless backwards incompatbile changes are the motto for BBD apparently.']
|
||||
}
|
||||
],
|
||||
defaultConfig: [
|
||||
{
|
||||
name: 'Max visible typing users',
|
||||
id: 'maxVisible',
|
||||
type: 'slider',
|
||||
value: 5,
|
||||
min: 3,
|
||||
max: 20,
|
||||
markers: Array.from(Array(18), (_, i) => i + 3),
|
||||
stickToMarkers: true
|
||||
},
|
||||
{
|
||||
name: 'Previews',
|
||||
type: 'preview'
|
||||
}
|
||||
],
|
||||
strings: {
|
||||
en: { AND_1_OTHER: ' and 1 other', AND_X_OTHERS: ' and ${count} others', AND: ' and ', ARE_TYPING: ' are typing...' },
|
||||
de: { AND_1_OTHER: ' und 1 andere', AND_X_OTHERS: ' und ${count} andere', AND: ' und ', ARE_TYPING: ' schreiben...' },
|
||||
da: { AND_1_OTHER: ' og 1 anden', AND_X_OTHERS: ' og ${count} andre', AND: ' og ', ARE_TYPING: ' skriver...' },
|
||||
es: { AND_1_OTHER: ' y 1 otro', AND_X_OTHERS: ' y otros ${count}', AND: ' y ', ARE_TYPING: ' están escribiendo...' },
|
||||
fr: { AND_1_OTHER: ' et 1 autre', AND_X_OTHERS: ' et ${count} autres', AND: ' et ', ARE_TYPING: ' écrivent...' },
|
||||
hr: { AND_1_OTHER: ' i 1 drugi', AND_X_OTHERS: ' i ${count} drugih', AND: ' i ', ARE_TYPING: ' pišu...' },
|
||||
it: { AND_1_OTHER: ' e 1 altro', AND_X_OTHERS: ' e altri ${count}', AND: ' e ', ARE_TYPING: ' stanno scrivendo...' },
|
||||
tr: { AND_1_OTHER: ' ve 1 kişi daha', AND_X_OTHERS: ' ve ${count} kişi daha', AND: ' ve ', ARE_TYPING: ' yazıyor...' }
|
||||
}
|
||||
};
|
||||
|
||||
/* Build */
|
||||
const buildPlugin = ([Plugin, Api]) => {
|
||||
const { ContextMenu, EmulatedTooltip, Toasts, Settings, Popouts, Modals, Utilities, WebpackModules, Filters, DiscordModules, ColorConverter, DOMTools, DiscordClasses, DiscordSelectors, ReactTools, ReactComponents, DiscordAPI, Logger, Patcher, PluginUpdater, PluginUtilities, DiscordClassModules, Structs } = Api;
|
||||
const { React, ModalStack, ContextMenuActions, ContextMenuItem, ContextMenuItemsGroup, ReactDOM, ChannelStore, GuildStore, UserStore, DiscordConstants, Dispatcher, GuildMemberStore, GuildActions, SwitchRow, EmojiUtils, RadioGroup, Permissions, TextElement, FlexChild, PopoutOpener, Textbox, RelationshipStore, UserSettingsStore } = DiscordModules;
|
||||
|
||||
const NameUtils = WebpackModules.getByProps('getName');
|
||||
|
||||
const CUser = WebpackModules.getByPrototypes('getAvatarSource', 'isLocalBot');
|
||||
const CChannel = WebpackModules.getByPrototypes('isGroupDM', 'isMultiUserDM');
|
||||
const L337 = (() => {
|
||||
try {
|
||||
return new CChannel({ id: '1337' });
|
||||
} catch (e) {
|
||||
Logger.stacktrace('Failed to create 1337 channel', e);
|
||||
}
|
||||
})();
|
||||
|
||||
let CTypingUsers = (() => {
|
||||
try {
|
||||
const WrappedTypingUsers = WebpackModules.find(m => m.displayName && m.displayName.indexOf('TypingUsers') !== -1);
|
||||
return new WrappedTypingUsers({ channel: L337 }).render().type;
|
||||
} catch (e) {
|
||||
Logger.stacktrace('Failed to get TypingUsers!', e);
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
const ComponentDispatch = (() => {
|
||||
try {
|
||||
return WebpackModules.getByProps('ComponentDispatch').ComponentDispatch;
|
||||
} catch (e) {
|
||||
Logger.stacktrace('Failed to get ComponentDispatch', e);
|
||||
}
|
||||
})();
|
||||
|
||||
class CTypingUsersPreview extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.forceUpdate = this.forceUpdate.bind(this);
|
||||
const iUsers = UserStore.getUsers();
|
||||
for (let i = 0; i < 20; i++) iUsers[(1337 + i).toString()] = new CUser({ username: `User ${i + 1}`, id: (1337 + i).toString(), discriminator: '9999' });
|
||||
}
|
||||
componentDidMount() {
|
||||
ComponentDispatch.subscribe('BTU_SETTINGS_UPDATED', this.forceUpdate);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
ComponentDispatch.unsubscribe('BTU_SETTINGS_UPDATED', this.forceUpdate);
|
||||
const iUsers = UserStore.getUsers();
|
||||
for (let i = 0; i < 20; i++) delete iUsers[(1337 + i).toString()];
|
||||
}
|
||||
renderTyping(num) {
|
||||
const typingUsers = {};
|
||||
for (let i = 0; i < num; i++) typingUsers[(1337 + i).toString()] = 1;
|
||||
return React.createElement(CTypingUsers, {
|
||||
channel: L337,
|
||||
guildId: '',
|
||||
isFocused: true,
|
||||
slowmodeCooldownGuess: 0,
|
||||
theme: UserSettingsStore.theme,
|
||||
typingUsers
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return React.createElement(
|
||||
'div',
|
||||
{
|
||||
className: 'BTU-preview'
|
||||
},
|
||||
this.renderTyping(4),
|
||||
this.renderTyping(6),
|
||||
this.renderTyping(20)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TypingUsersPreview extends Settings.SettingField {
|
||||
constructor(name, note) {
|
||||
super(name, note, null, CTypingUsersPreview);
|
||||
}
|
||||
}
|
||||
|
||||
/* since XenoLib is absent from this plugin (since it serves no real purpose),
|
||||
we can only hope the user doesn't rename the plugin..
|
||||
*/
|
||||
return class BetterTypingUsers extends Plugin {
|
||||
constructor() {
|
||||
super();
|
||||
try {
|
||||
WebpackModules.getByProps('openModal', 'hasModalOpen').closeModal(`${this.name}_DEP_MODAL`);
|
||||
} catch (e) { }
|
||||
}
|
||||
onStart() {
|
||||
if (window.Lightcock && window.XenoLib) XenoLib.Notifications.warning(`[${this.getName()}] Lightcord is an unofficial and unsafe client with stolen code that is falsely advertising that it is safe, Lightcord has allowed the spread of token loggers hidden within plugins redistributed by them, and these plugins are not made to work on it. Your account is very likely compromised by malicious people redistributing other peoples plugins, especially if you didn't download this plugin from [GitHub](https://github.com/1Lighty/BetterDiscordPlugins/edit/master/Plugins/MessageLoggerV2/MessageLoggerV2.plugin.js), you should change your password immediately. Consider using a trusted client mod like [BandagedBD](https://rauenzi.github.io/BetterDiscordApp/) or [Powercord](https://powercord.dev/) to avoid losing your account.`, { timeout: 0 });
|
||||
this.promises = { state: { cancelled: false } };
|
||||
this.patchAll();
|
||||
PluginUtilities.addStyle(
|
||||
this.short + '-CSS',
|
||||
`
|
||||
.BTU-preview > .${WebpackModules.getByProps('slowModeIcon', 'typing').typing.split(' ')[0]} {
|
||||
position: unset !important;
|
||||
}
|
||||
`
|
||||
);
|
||||
DiscordConstants.MAX_TYPING_USERS = 99;
|
||||
/* theoretical max is 5 users typing at once.. welp */
|
||||
}
|
||||
|
||||
onStop() {
|
||||
this.promises.state.cancelled = true;
|
||||
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 === 'preview') return new TypingUsersPreview(data.name, data.note);
|
||||
return super.buildSetting(data);
|
||||
}
|
||||
|
||||
saveSettings(_, setting, value) {
|
||||
super.saveSettings(_, setting, value);
|
||||
ComponentDispatch.safeDispatch('BTU_SETTINGS_UPDATED');
|
||||
}
|
||||
|
||||
filterTypingUsers(typingUsers) {
|
||||
return Object.keys(typingUsers)
|
||||
.filter(e => e != DiscordAPI.currentUser.id)
|
||||
.filter(e => !RelationshipStore.isBlocked(e))
|
||||
.map(e => UserStore.getUser(e))
|
||||
.filter(e => e != null);
|
||||
}
|
||||
|
||||
/* PATCHES */
|
||||
|
||||
patchAll() {
|
||||
Utilities.suppressErrors(this.patchBetterRoleColors.bind(this), 'BetterRoleColors patch')();
|
||||
Utilities.suppressErrors(this.patchTypingUsers.bind(this), 'TypingUsers patch')(this.promises.state);
|
||||
}
|
||||
|
||||
patchBetterRoleColors() {
|
||||
const BetterRoleColors = BdApi.getPlugin('BetterRoleColors');
|
||||
if (!BetterRoleColors) return;
|
||||
/* stop errors */
|
||||
/* modify BRCs behavior so it won't unexpectedly try to modify an entry that does not exist
|
||||
by simply limiting it to the max number of usernames visible in total
|
||||
*/
|
||||
Patcher.after(BetterRoleColors, 'filterTypingUsers', (_this, __, ret) => ret.slice(0, this.settings.maxVisible));
|
||||
}
|
||||
|
||||
async patchTypingUsers(promiseState) {
|
||||
const TypingUsers = await ReactComponents.getComponentByName('TypingUsers', DiscordSelectors.Typing.typing);
|
||||
if (!TypingUsers.selector) TypingUsers.selector = DiscordSelectors.Typing.typing;
|
||||
const TypingTextClassname = WebpackModules.getByProps('typing', 'text').text.split(' ')[0];
|
||||
if (promiseState.cancelled) return;
|
||||
if (!CTypingUsers) CTypingUsers = typingUsers.component; /* failsafe */
|
||||
/* use `instead` so that we modify the return before BetterRoleColors */
|
||||
/* Patcher.after(TypingUsers.component.prototype, 'componentDidUpdate', (_this, [props, state], ret) => {
|
||||
const filtered1 = this.filterTypingUsers(_this.props.typingUsers);
|
||||
const filtered2 = this.filterTypingUsers(props.typingUsers);
|
||||
if (filtered1.length !== filtered2.length || _this.state.numLess === state.numLess) {
|
||||
_this.state.numLess = 0;
|
||||
_this.triedLess = false;
|
||||
_this.triedMore = false;
|
||||
}
|
||||
}); */
|
||||
Patcher.instead(TypingUsers.component.prototype, 'render', (_this, _, orig) => {
|
||||
/* if (!_this.state) _this.state = { numLess: 0 }; */
|
||||
const ret = orig();
|
||||
if (!ret) {
|
||||
/* _this.state.numLess = 0; */
|
||||
return ret;
|
||||
}
|
||||
const filtered = this.filterTypingUsers(_this.props.typingUsers);
|
||||
if (filtered.length <= 3) return ret;
|
||||
/* ret.ref = e => {
|
||||
_this.__baseRef = e;
|
||||
if (!e) return;
|
||||
if (!_this.__textRef) return;
|
||||
_this.maxWidth = parseInt(getComputedStyle(_this.__baseRef.parentElement).width) - (_this.__textRef.offsetLeft + parseInt(getComputedStyle(_this.__textRef)['margin-left']) - _this.__baseRef.offsetLeft);
|
||||
if (_this.__textRef.scrollWidth > _this.maxWidth) {
|
||||
if (_this.triedMore) return;
|
||||
if (filtered.length - _this.state.numLess <= 3) return;
|
||||
_this.setState({ numLess: _this.state.numLess + 1 });
|
||||
}
|
||||
}; */
|
||||
const typingUsers = Utilities.findInReactTree(ret, e => e && e.props && typeof e.props.className === 'string' && e.props.className.indexOf(TypingTextClassname) !== -1);
|
||||
if (!typingUsers) return ret;
|
||||
/* if (typeof _this.state.numLess !== 'number') _this.state.numLess = 0;
|
||||
typingUsers.ref = e => {
|
||||
_this.__textRef = e;
|
||||
}; */
|
||||
typingUsers.props.children = [];
|
||||
/* I don't think this method works for every language..? */
|
||||
for (let i = 0; i < filtered.length; i++) {
|
||||
if (this.settings.maxVisible /* filtered.length - _this.state.numLess */ === i) {
|
||||
const others = filtered.length - i;
|
||||
if (others === 1) typingUsers.props.children.push(this.strings.AND_1_OTHER);
|
||||
else typingUsers.props.children.push(Utilities.formatTString(this.strings.AND_X_OTHERS, { count: others }));
|
||||
break;
|
||||
} else if (i === filtered.length - 1) typingUsers.props.children.push(this.strings.AND);
|
||||
else if (i !== 0) typingUsers.props.children.push(', ');
|
||||
const name = NameUtils.getName(_this.props.guildId, _this.props.channel.id, filtered[i]);
|
||||
typingUsers.props.children.push(React.createElement('strong', {}, name));
|
||||
}
|
||||
typingUsers.props.children.push(this.strings.ARE_TYPING);
|
||||
return ret;
|
||||
});
|
||||
TypingUsers.forceUpdateAll();
|
||||
}
|
||||
|
||||
/* PATCHES */
|
||||
|
||||
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;
|
||||
try {
|
||||
if (global.BdApi && 'function' == typeof BdApi.getPlugin) {
|
||||
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.getPlugin('ZeresPluginLibrary');
|
||||
((b, c) => b && b._config && b._config.info && b._config.info.version && a(b._config.info.version, c))(b, '1.2.23') && (ZeresPluginLibraryOutdated = !0);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error checking if ZeresPluginLibrary is out of date', e);
|
||||
}
|
||||
|
||||
return !global.ZeresPluginLibrary || ZeresPluginLibraryOutdated
|
||||
? 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 + ' You are missing ZeresPluginLibrary 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.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@*/
|
522
BetterUnavailableGuilds.plugin.js
Normal file
522
BetterUnavailableGuilds.plugin.js
Normal file
|
@ -0,0 +1,522 @@
|
|||
//META{"name":"BetterUnavailableGuilds","source":"https://gitdab.com/hana/lightcock/src/branch/master/BetterUnavailableGuilds.plugin.js/","website":"https://1lighty.github.io/BetterDiscordStuff/?plugin=BetterUnavailableGuilds","authorId":"287977955240706060","invite":"9jDgyVjTVX","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 © 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: 'BetterUnavailableGuilds',
|
||||
authors: [
|
||||
{
|
||||
name: 'Lighty',
|
||||
discord_id: '239513071272329217',
|
||||
github_username: '1Lighty',
|
||||
twitter_username: ''
|
||||
}
|
||||
],
|
||||
version: '0.2.9',
|
||||
description: 'Force Discord to show server icons of unavailable servers, instead of "1 server is unavailable" and enable interaction with the server (ability to leave the server, move it around, etc).',
|
||||
github: 'https://github.com/1Lighty',
|
||||
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/BetterUnavailableGuilds/BetterUnavailableGuilds.plugin.js'
|
||||
},
|
||||
changelog: [
|
||||
{
|
||||
title: 'fixed',
|
||||
type: 'fixed',
|
||||
items: ['Fixed not restoring the servers on startup.']
|
||||
}
|
||||
],
|
||||
defaultConfig: [
|
||||
{
|
||||
type: 'category',
|
||||
id: 'guilds',
|
||||
name: 'Guilds',
|
||||
collapsible: true,
|
||||
shown: true,
|
||||
settings: [
|
||||
{
|
||||
type: 'textbox',
|
||||
name: 'Add guild using data',
|
||||
note: 'Press enter to add'
|
||||
},
|
||||
{
|
||||
type: 'guildslist',
|
||||
name: 'Click to copy guild data to share to people'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/* Build */
|
||||
const buildPlugin = ([Plugin, Api]) => {
|
||||
const { Utilities, WebpackModules, DiscordModules, Patcher, PluginUtilities, DiscordAPI, Settings, Toasts } = Api;
|
||||
const { Dispatcher, GuildStore, React, ModalStack } = DiscordModules;
|
||||
const GuildAvailabilityStore = WebpackModules.getByProps('unavailableGuilds');
|
||||
const _ = WebpackModules.getByProps('bindAll', 'debounce');
|
||||
|
||||
const FsModule = require('fs');
|
||||
// I would say "fuck ED", but it won't even compile on ED due to their piss poor BD/BBD support, lol
|
||||
const pluginConfigFile = require('path').resolve(BdApi.Plugins.folder, config.info.name + '.config.json');
|
||||
|
||||
const loadData = (key, defaults) => {
|
||||
const cloned = _.cloneDeep(defaults);
|
||||
try {
|
||||
if (pluginConfigFile) {
|
||||
if (FsModule.existsSync(pluginConfigFile)) {
|
||||
return Object.assign(cloned, JSON.parse(FsModule.readFileSync(pluginConfigFile))[key]);
|
||||
} else {
|
||||
return cloned;
|
||||
}
|
||||
} else {
|
||||
return Object.assign(cloned, BdApi.loadData(config.info.name, key));
|
||||
}
|
||||
} catch (e) {
|
||||
return cloned;
|
||||
}
|
||||
};
|
||||
|
||||
const copyToClipboard = WebpackModules.getByProps('copy').copy; /* Possible error in future, TODO: safeguard */
|
||||
const GuildIconWrapper = WebpackModules.getByDisplayName('GuildIconWrapper');
|
||||
const ListClassModule = WebpackModules.getByProps('listRowContent', 'listAvatar');
|
||||
const ListScrollerClassname = WebpackModules.getByProps('listScroller').listScroller; /* Possible error in future, TODO: safeguard */
|
||||
const VerticalScroller = WebpackModules.getByDisplayName('VerticalScroller');
|
||||
const Clickable = WebpackModules.getByDisplayName('Clickable');
|
||||
|
||||
/* TODO: proper name for the classes */
|
||||
class GL extends React.PureComponent {
|
||||
renderGuild(guild) {
|
||||
if (!guild) return null;
|
||||
return React.createElement(
|
||||
Clickable,
|
||||
{
|
||||
onClick: () => {
|
||||
copyToClipboard(JSON.stringify({ id: guild.id, icon: guild.icon || undefined, name: guild.name, owner_id: guild.ownerId, joined_at: guild.joinedAt.valueOf() }));
|
||||
Toasts.success(`Copied ${guild.name}!`);
|
||||
},
|
||||
className: 'BUG-guild-icon'
|
||||
},
|
||||
React.createElement(GuildIconWrapper, {
|
||||
guild,
|
||||
showBadge: !0,
|
||||
className: !guild.icon ? ListClassModule.guildAvatarWithoutIcon : '',
|
||||
size: GuildIconWrapper.Sizes.LARGE
|
||||
})
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return React.createElement(
|
||||
VerticalScroller,
|
||||
{
|
||||
fade: true,
|
||||
className: ListScrollerClassname,
|
||||
style: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap'
|
||||
}
|
||||
},
|
||||
Object.values(GuildStore.getGuilds()).map(this.renderGuild)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TB extends DiscordModules.Textbox {
|
||||
render() {
|
||||
const ret = super.render();
|
||||
const props = Utilities.findInReactTree(ret, e => e && e.onEnterPressed);
|
||||
props.onKeyDown = e => {
|
||||
if (e.keyCode !== 13) return;
|
||||
try {
|
||||
const parsed = JSON.parse(this.props.value);
|
||||
['id', 'name', 'owner_id', 'joined_at'].forEach(prop => {
|
||||
if (!parsed.hasOwnProperty(prop) || typeof parsed[prop] === 'undefined') throw `Malformed guild data (${prop})`;
|
||||
});
|
||||
if (typeof parsed.name !== 'string' || typeof parsed.owner_id !== 'string' || /\\d+$/.test(parsed.owner_id)) throw 'Malformed guild data';
|
||||
this.props.onEnterPressed(parsed);
|
||||
this.props.onChange('');
|
||||
} catch (err) {
|
||||
Toasts.error(`Failed to parse: ${err.message || err}`);
|
||||
}
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
class Textbox extends Settings.SettingField {
|
||||
constructor(name, note, onChange, options = {}) {
|
||||
super(name, note, () => { }, TB, {
|
||||
onChange: textbox => value => {
|
||||
textbox.props.value = value;
|
||||
textbox.forceUpdate();
|
||||
},
|
||||
value: '',
|
||||
placeholder: options.placeholder ? options.placeholder : '',
|
||||
onEnterPressed: onChange
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class GuildDataCopier extends Settings.SettingField {
|
||||
constructor(name, note) {
|
||||
super(name, note, () => { }, GL, {});
|
||||
}
|
||||
}
|
||||
|
||||
return class BetterUnavailableGuilds extends Plugin {
|
||||
constructor() {
|
||||
super();
|
||||
try {
|
||||
WebpackModules.getByProps('openModal', 'hasModalOpen').closeModal(`${this.name}_DEP_MODAL`);
|
||||
} catch (e) { }
|
||||
this._dispatches = ['CONNECTION_OPEN'];
|
||||
_.bindAll(this, ['handleGuildStoreChange', 'verifyAllServersCachedInClient', ...this._dispatches]);
|
||||
// different timings for clients to avoid fighting over a damn config file
|
||||
this.handleGuildStoreChange = _.throttle(this.handleGuildStoreChange, 15000 + (GLOBAL_ENV.RELEASE_CHANNEL === 'ptb' ? 2500 : GLOBAL_ENV.RELEASE_CHANNEL === 'canary' ? 5000 : 0));
|
||||
}
|
||||
onStart() {
|
||||
if (window.Lightcock && window.XenoLib) XenoLib.Notifications.warning(`[${this.getName()}] Lightcord is an unofficial and unsafe client with stolen code that is falsely advertising that it is safe, Lightcord has allowed the spread of token loggers hidden within plugins redistributed by them, and these plugins are not made to work on it. Your account is very likely compromised by malicious people redistributing other peoples plugins, especially if you didn't download this plugin from [GitHub](https://github.com/1Lighty/BetterDiscordPlugins/edit/master/Plugins/MessageLoggerV2/MessageLoggerV2.plugin.js), you should change your password immediately. Consider using a trusted client mod like [BandagedBD](https://rauenzi.github.io/BetterDiscordApp/) or [Powercord](https://powercord.dev/) to avoid losing your account.`, { timeout: 0 });
|
||||
this._guildRecord = loadData('data', { data: {} }).data;
|
||||
this.verifyAllServersCachedInClient();
|
||||
GuildStore.addChangeListener(this.handleGuildStoreChange);
|
||||
this.handleGuildStoreChange();
|
||||
this.patchAll();
|
||||
for (const dispatch of this._dispatches) Dispatcher.subscribe(dispatch, this[dispatch]);
|
||||
PluginUtilities.addStyle(
|
||||
this.short + '-CSS',
|
||||
`
|
||||
.BUG-guild-icon {
|
||||
padding: 5px;
|
||||
}
|
||||
.BUG-guild-icon:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
`
|
||||
);
|
||||
}
|
||||
onStop() {
|
||||
GuildStore.removeChangeListener(this.handleGuildStoreChange);
|
||||
Patcher.unpatchAll();
|
||||
Dispatcher._computeOrderedActionHandlers('GUILD_DELETE');
|
||||
for (const dispatch of this._dispatches) Dispatcher.unsubscribe(dispatch, this[dispatch]);
|
||||
PluginUtilities.removeStyle(this.short + '-CSS');
|
||||
}
|
||||
|
||||
buildSetting(data) {
|
||||
if (data.type === 'textbox') {
|
||||
const { name, note } = data;
|
||||
const setting = new Textbox(
|
||||
name,
|
||||
note,
|
||||
guild => {
|
||||
if (this.guildRecord[guild.id]) throw 'Guild already exists';
|
||||
if (GuildAvailabilityStore.unavailableGuilds.indexOf(guild.id)) throw 'You are not a member of ' + guild.name;
|
||||
this.guildRecord[guild.id] = { id: guild.id, icon: guild.icon || undefined, name: guild.name, owner_id: guild.owner_id, joined_at: guild.joined_at, default_message_notifications: guild.default_message_notifications };
|
||||
this.verifyAllServersCachedInClient();
|
||||
Toasts.success('Added!');
|
||||
},
|
||||
{ placeholder: data.placeholder || '' }
|
||||
);
|
||||
return setting;
|
||||
} else if (data.type === 'guildslist') {
|
||||
return new GuildDataCopier(data.name, data.note);
|
||||
}
|
||||
return super.buildSetting(data);
|
||||
}
|
||||
|
||||
verifyAllServersCachedInClient() {
|
||||
if (!DiscordAPI.currentUser) return; /* hhhhhhhh */
|
||||
Dispatcher.wait(() => {
|
||||
this.ensureBDGuildsPreCached();
|
||||
this._verifying = true;
|
||||
const unavailable = _.cloneDeep(GuildAvailabilityStore.unavailableGuilds);
|
||||
unavailable.forEach(guildId => {
|
||||
if (!this.guildRecord[guildId] || GuildStore.getGuild(guildId)) return;
|
||||
Dispatcher.dispatch({
|
||||
type: 'GUILD_CREATE',
|
||||
guild: Object.assign(
|
||||
{
|
||||
icon: null,
|
||||
presences: [],
|
||||
channels: [],
|
||||
members: [],
|
||||
roles: [],
|
||||
unavailable: true
|
||||
},
|
||||
this.guildRecord[guildId]
|
||||
)
|
||||
});
|
||||
/* they're still unavailable, remember? */
|
||||
Dispatcher.dispatch({
|
||||
type: 'GUILD_UNAVAILABLE',
|
||||
guildId: guildId
|
||||
});
|
||||
});
|
||||
this._verifying = false;
|
||||
});
|
||||
}
|
||||
|
||||
CONNECTION_OPEN(e) {
|
||||
/* websocket died, user logged in, user logged into another account etc */
|
||||
this.verifyAllServersCachedInClient();
|
||||
}
|
||||
|
||||
ensureBDGuildsPreCached() {
|
||||
this.guildRecord['86004744966914048'] = { id: '86004744966914048', icon: '292e7f6bfff2b71dfd13e508a859aedd', name: 'BetterDiscord', owner_id: '81388395867156480', joined_at: Date.now() };
|
||||
this.guildRecord['280806472928198656'] = { id: '280806472928198656', icon: 'cbdda04c041699d80689b99c4e5e89dc', name: 'BetterDiscord2', owner_id: '81388395867156480', joined_at: Date.now() };
|
||||
}
|
||||
|
||||
ensureDataSettable() {
|
||||
if (!this._guildRecord[DiscordAPI.currentUser.id]) this._guildRecord[DiscordAPI.currentUser.id] = {};
|
||||
if (!this._guildRecord[DiscordAPI.currentUser.id][GLOBAL_ENV.RELEASE_CHANNEL]) {
|
||||
const curUserShit = this._guildRecord[DiscordAPI.currentUser.id];
|
||||
/* transfer the data */
|
||||
if (curUserShit['stable']) curUserShit[GLOBAL_ENV.RELEASE_CHANNEL] = _.cloneDeep(curUserShit['stable']);
|
||||
else if (curUserShit['ptb']) curUserShit[GLOBAL_ENV.RELEASE_CHANNEL] = _.cloneDeep(curUserShit['ptb']);
|
||||
else if (curUserShit['canary']) curUserShit[GLOBAL_ENV.RELEASE_CHANNEL] = _.cloneDeep(curUserShit['canary']);
|
||||
else curUserShit[GLOBAL_ENV.RELEASE_CHANNEL] = {};
|
||||
}
|
||||
}
|
||||
|
||||
get guildRecord() {
|
||||
this.ensureDataSettable();
|
||||
const ret = this._guildRecord[DiscordAPI.currentUser.id][GLOBAL_ENV.RELEASE_CHANNEL];
|
||||
return ret;
|
||||
}
|
||||
set guildRecord(val) {
|
||||
this.ensureDataSettable();
|
||||
return (this._guildRecord[DiscordAPI.currentUser.id][GLOBAL_ENV.RELEASE_CHANNEL] = val);
|
||||
}
|
||||
|
||||
handleGuildStoreChange() {
|
||||
if (!DiscordAPI.currentUser) return; /* hhhhhhhh */
|
||||
this._guildRecord = loadData('data', { data: {} }).data;
|
||||
this.verifyAllServersCachedInClient();
|
||||
const availableGuilds = Object.values(GuildStore.getGuilds()).map(guild => ({
|
||||
id: guild.id,
|
||||
icon: guild.icon || undefined,
|
||||
name: guild.name,
|
||||
owner_id: guild.ownerId,
|
||||
joined_at: guild.joinedAt ? guild.joinedAt.valueOf() /* int value is fine too */ : 0 /* wut? MasicoreLord experienced a weird bug with joinedAt being undefined */
|
||||
}));
|
||||
let guilds = {};
|
||||
GuildAvailabilityStore.unavailableGuilds.forEach(id => this.guildRecord[id] && (guilds[id] = this.guildRecord[id]));
|
||||
availableGuilds.forEach(guild => (guilds[guild.id] = guild));
|
||||
for (const guildId in guilds) guilds[guildId] = _.pickBy(guilds[guildId], e => !_.isUndefined(e));
|
||||
if (!_.isEqual(this.guildRecord, guilds)) {
|
||||
this.guildRecord = guilds;
|
||||
PluginUtilities.saveData(this.name, 'data', { data: this._guildRecord });
|
||||
}
|
||||
}
|
||||
|
||||
/* PATCHES */
|
||||
|
||||
patchAll() {
|
||||
Utilities.suppressErrors(this.patchGuildDelete.bind(this), 'GUILD_DELETE dispatch patch')();
|
||||
}
|
||||
|
||||
patchGuildDelete() {
|
||||
// super sekret (not really) V3/rewrite patch code
|
||||
for (const id in Dispatcher._dependencyGraph.nodes) {
|
||||
const node = Dispatcher._dependencyGraph.nodes[id];
|
||||
if (!node.actionHandler['GUILD_DELETE']) continue;
|
||||
Patcher.instead(node.actionHandler, 'GUILD_DELETE', (_, [dispatch], orig) => {
|
||||
if (!dispatch.guild.unavailable) return orig(dispatch);
|
||||
});
|
||||
}
|
||||
Dispatcher._computeOrderedActionHandlers('GUILD_DELETE');
|
||||
}
|
||||
|
||||
/* PATCHES */
|
||||
|
||||
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;
|
||||
try {
|
||||
if (global.BdApi && 'function' == typeof BdApi.getPlugin) {
|
||||
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.getPlugin('ZeresPluginLibrary');
|
||||
((b, c) => b && b._config && b._config.info && b._config.info.version && a(b._config.info.version, c))(b, '1.2.23') && (ZeresPluginLibraryOutdated = !0);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error checking if ZeresPluginLibrary is out of date', e);
|
||||
}
|
||||
|
||||
return !global.ZeresPluginLibrary || ZeresPluginLibraryOutdated
|
||||
? 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 + ' You are missing ZeresPluginLibrary 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.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@*/
|
529
CrashRecovery.plugin.js
Normal file
529
CrashRecovery.plugin.js
Normal file
|
@ -0,0 +1,529 @@
|
|||
//META{"name":"CrashRecovery","source":"https://gitdab.com/hana/lightcock/src/branch/master/CrashRecovery.plugin.js/","website":"https://1lighty.github.io/BetterDiscordStuff/?plugin=CrashRecovery","authorId":"287977955240706060","invite":"9jDgyVjTVX","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: 'CrashRecovery',
|
||||
authors: [
|
||||
{
|
||||
name: 'Lighty',
|
||||
discord_id: '239513071272329217',
|
||||
github_username: '1Lighty',
|
||||
twitter_username: ''
|
||||
}
|
||||
],
|
||||
version: '1.0.1',
|
||||
description: 'In the event that your Discord crashes, the plugin enables you to get Discord back to a working state, without needing to reload at all.',
|
||||
github: 'https://github.com/1Lighty',
|
||||
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/CrashRecovery/CrashRecovery.plugin.js'
|
||||
},
|
||||
changelog: [
|
||||
{
|
||||
title: 'Fixed',
|
||||
type: 'fixed',
|
||||
items: ['Fixed some binding issue causing an error.']
|
||||
}
|
||||
],
|
||||
defaultConfig: [
|
||||
{
|
||||
name: 'Enable step 3',
|
||||
note: 'Moves channel switch to a third and last step, otherwise it switches on step 2',
|
||||
id: 'useThirdStep',
|
||||
type: 'switch',
|
||||
value: true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/* Build */
|
||||
const buildPlugin = ([Plugin, Api], BasePlugin) => {
|
||||
const { Logger, Utilities, WebpackModules, DiscordModules, Patcher, PluginUtilities, ReactTools, PluginUpdater } = Api;
|
||||
const { React, Dispatcher, FlexChild: Flex, GuildStore } = DiscordModules;
|
||||
|
||||
const DelayedCall = (WebpackModules.getByProps('DelayedCall') || {}).DelayedCall;
|
||||
const ElectronDiscordModule = WebpackModules.getByProps('cleanupDisplaySleep') || { cleanupDisplaySleep: DiscordModules.DiscordConstants.NOOP };
|
||||
|
||||
const ModalStack = WebpackModules.getByProps('openModal');
|
||||
|
||||
const isPowercord = !!window.powercord;
|
||||
const BLACKLISTED_BUILTIN_PC_PLUGINS = ['Updater', 'Commands Manager', 'I18n', 'Module Manager', 'Settings']
|
||||
const RE_PC_PLUGIN_NAME_FROM_PATH = /[\\\/]plugins[\\\/]([^\\\/]+)/;
|
||||
|
||||
const RE_INVARIANT = /error-decoder.html\?invariant=(\d+)([^\s]*)/;
|
||||
const INVARIANTS_URL = 'https://raw.githubusercontent.com/facebook/react/master/scripts/error-codes/codes.json';
|
||||
const ROOT_FOLDER = isPowercord ? window.powercord.basePath : BdApi.Plugins.folder;
|
||||
|
||||
const path = require('path');
|
||||
|
||||
return class CrashRecovery extends BasePlugin(Plugin) {
|
||||
constructor() {
|
||||
super();
|
||||
XenoLib.changeName(__filename, 'CrashRecovery');
|
||||
this._startFailure = message => {
|
||||
PluginUpdater.checkForUpdate(this.name, this.version, this._config.info.github_raw);
|
||||
XenoLib.Notifications.error(`[**${this.name}**] ${message} 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 });
|
||||
};
|
||||
const oOnStart = this.onStart.bind(this);
|
||||
this.onStart = () => {
|
||||
try {
|
||||
oOnStart();
|
||||
} catch (e) {
|
||||
Logger.stacktrace('Failed to start!', e);
|
||||
this._startFailure('Failed to start!');
|
||||
try {
|
||||
this.onStop();
|
||||
} catch (e) { }
|
||||
}
|
||||
};
|
||||
try {
|
||||
WebpackModules.getByProps('openModal', 'hasModalOpen').closeModal(`${this.name}_DEP_MODAL`);
|
||||
} catch (e) { }
|
||||
}
|
||||
onStart() {
|
||||
if (window.Lightcock) XenoLib.Notifications.warning(`[${this.getName()}] Lightcord is an unofficial and unsafe client with stolen code that is falsely advertising that it is safe, Lightcord has allowed the spread of token loggers hidden within plugins redistributed by them, and these plugins are not made to work on it. Your account is very likely compromised by malicious people redistributing other peoples plugins, especially if you didn't download this plugin from [GitHub](https://github.com/1Lighty/BetterDiscordPlugins/edit/master/Plugins/MessageLoggerV2/MessageLoggerV2.plugin.js), you should change your password immediately. Consider using a trusted client mod like [BandagedBD](https://rauenzi.github.io/BetterDiscordApp/) or [Powercord](https://powercord.dev/) to avoid losing your account.`, { timeout: 0 });
|
||||
this.attempts = 0;
|
||||
this.promises = { state: { cancelled: false } };
|
||||
if (!DelayedCall) return this._startFailure('DelayedCall missing, plugin cannot function.');
|
||||
if (ElectronDiscordModule.cleanupDisplaySleep === DiscordModules.DiscordConstants.NOOP) XenoLib.Notifications.error(`[**${this.name}**] cleanupDisplaySleep is missing.`);
|
||||
delete this.onCrashRecoveredDelayedCall;
|
||||
this.onCrashRecoveredDelayedCall = new DelayedCall(1000, () => {
|
||||
XenoLib.Notifications.remove(this.notificationId);
|
||||
this.notificationId = null;
|
||||
if (this.disabledPlugins) XenoLib.Notifications.danger(`${this.disabledPlugins.map(e => e)} ${this.disabledPlugins.length > 1 ? 'have' : 'has'} been disabled to recover from the crash`, { timeout: 0 });
|
||||
if (this.suspectedPlugin) XenoLib.Notifications.danger(`${this.suspectedPlugin} ${this.suspectedPlugin2 !== this.suspectedPlugin && this.suspectedPlugin2 ? 'or ' + this.suspectedPlugin2 : ''} is suspected of causing the crash.`, { timeout: 10000 });
|
||||
XenoLib.Notifications.info('Successfully recovered, more info can be found in the console (CTRL + SHIFT + I > console on top). Pass this information to support for further help.', { timeout: 10000 });
|
||||
this.disabledPlugins = null;
|
||||
this.suspectedPlugin = null;
|
||||
this.suspectedPlugin2 = null;
|
||||
this.attempts = 0;
|
||||
const appMount = document.querySelector('#app-mount');
|
||||
appMount.append(document.querySelector('.xenoLib-notifications'));
|
||||
const BIVOverlay = document.querySelector('.biv-overlay');
|
||||
if (BIVOverlay) appMount.append(BIVOverlay);
|
||||
Logger.info('Corrected incorrectly placed containers');
|
||||
});
|
||||
Error.prepareStackTrace = (error, frames) => {
|
||||
this._lastStackFrames = frames;
|
||||
return error.stack
|
||||
}
|
||||
Utilities.suppressErrors(this.patchErrorBoundary.bind(this))(this.promises.state);
|
||||
if (!this.settings.lastErrorMapUpdate || Date.now() - this.settings.lastErrorMapUpdate > 2.628e+9) {
|
||||
const https = require('https');
|
||||
const req = https.request(INVARIANTS_URL, { headers: { 'origin': 'discord.com' } }, res => {
|
||||
let body = '';
|
||||
res.on('data', chunk => { body += chunk; });
|
||||
res.on('end', () => {
|
||||
if (res.statusCode !== 200) return;
|
||||
try {
|
||||
this.settings.errorMap = JSON.parse(body);
|
||||
this.settings.lastErrorMapUpdate = Date.now();
|
||||
this.saveSettings();
|
||||
} catch { }
|
||||
});
|
||||
});
|
||||
req.end();
|
||||
}
|
||||
}
|
||||
onStop() {
|
||||
this.promises.state.cancelled = true;
|
||||
Patcher.unpatchAll();
|
||||
if (this.notificationId) XenoLib.Notifications.remove(this.notificationId);
|
||||
delete Error.prepareStackTrace;
|
||||
}
|
||||
|
||||
|
||||
// Copyright (c) Facebook, Inc. and its affiliates
|
||||
// https://github.com/facebook/react/blob/master/scripts/jest/setupTests.js#L171
|
||||
decodeErrorMessage(message) {
|
||||
if (!message) return message;
|
||||
|
||||
const [, invariant, argS] = message.match(RE_INVARIANT);
|
||||
const code = parseInt(invariant, 10);
|
||||
const args = argS
|
||||
.split('&')
|
||||
.filter(s => s.indexOf('args[]=') !== -1)
|
||||
.map(s => s.substr('args[]='.length))
|
||||
.map(decodeURIComponent);
|
||||
const format = this.settings.errorMap[code];
|
||||
if (!format) return message; // ouch
|
||||
let argIndex = 0;
|
||||
return format.replace(/%s/g, () => args[argIndex++]);
|
||||
}
|
||||
|
||||
decodeStacks(stack, componentStack, baseComponentName = 'ErrorBoundary') {
|
||||
if (!this.settings.errorMap) return { stack, componentStack };
|
||||
if (RE_INVARIANT.test(stack)) stack = this.decodeErrorMessage(stack);
|
||||
// Strip out Discord (and React) only functions
|
||||
else stack = stack.split('\n')
|
||||
.filter(l => l.indexOf('discordapp.com/assets') === -1 && l.indexOf('discord.com/assets') === -1)
|
||||
.join('\n')
|
||||
.split(ROOT_FOLDER) // transform paths to relative
|
||||
.join('');
|
||||
|
||||
// Only show up to the error boundary
|
||||
const splitComponentStack = componentStack.split('\n').filter(e => e);
|
||||
const stackEnd = splitComponentStack.findIndex(l => l.indexOf(`in ${baseComponentName}`) !== -1);
|
||||
if (stackEnd !== -1 && baseComponentName) splitComponentStack.splice(stackEnd + 1, splitComponentStack.length);
|
||||
componentStack = splitComponentStack.join('\n');
|
||||
return { stack, componentStack };
|
||||
}
|
||||
|
||||
/*
|
||||
MUST return either an array with the plugin name
|
||||
or a string of the plugin name
|
||||
*/
|
||||
queryResponsiblePlugins() {
|
||||
try {
|
||||
const stack = this._bLastStackFrames
|
||||
// filter out blank functions (like from console or whatever)
|
||||
.filter(e => e.getFileName() || e.getFunctionName() || e.getMethodName())
|
||||
// filter out discord functions
|
||||
.filter(e => e.getFileName().indexOf(ROOT_FOLDER) !== -1)
|
||||
// convert CallSites to only useful info
|
||||
.map(e => ({ filename: e.getFileName(), functionName: e.getFunctionName() || e.getMethodName() }))
|
||||
// filter out ZeresPluginLibrary and ourselves
|
||||
.filter(({ filename }) => filename.lastIndexOf('0PluginLibrary.plugin.js') !== filename.length - 24 && filename.lastIndexOf(`${this.name}.plugin.js`) !== filename.length - (this.name.length + 10));
|
||||
// Filter out MessageLoggerV2 dispatch patch, is a 2 part step
|
||||
for (let i = 0, len = stack.length; i < len; i++) {
|
||||
const { filename, functionName } = stack[i];
|
||||
if (filename.lastIndexOf('MessageLoggerV2.plugin.js') !== filename.length - 25) continue;
|
||||
if (functionName !== 'onDispatchEvent') continue;
|
||||
if (stack[i + 1].functionName !== 'callback') break;
|
||||
stack.splice(i, 2);
|
||||
break;
|
||||
}
|
||||
const plugins = stack.map(({ filename }) => {
|
||||
try {
|
||||
const bdname = path.basename(filename);
|
||||
if (bdname.indexOf('.plugin.js') === bdname.length - 10) {
|
||||
const [pluginName] = bdname.split('.plugin.js');
|
||||
if (BdApi.Plugins.get(pluginName)) return pluginName;
|
||||
/*
|
||||
* go away Zack
|
||||
*/
|
||||
for (const path in require.cache) {
|
||||
const module = require.cache[path];
|
||||
if (!module || !module.exports || !module.exports.plugin || module.exports.filename.indexOf(bdname) !== 0) continue;
|
||||
return module.exports.id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else if (isPowercord) {
|
||||
const [, pcname] = RE_PC_PLUGIN_NAME_FROM_PATH.exec(filename);
|
||||
const { pluginManager } = window.powercord;
|
||||
const plugin = pluginManager.get(pcname);
|
||||
if (!plugin) return null;
|
||||
const { name } = plugin.manifest;
|
||||
if (BLACKLISTED_BUILTIN_PC_PLUGINS.indexOf(name) !== -1) return null;
|
||||
return pcname;
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.stacktrace('Error fetching plugin')
|
||||
}
|
||||
}).filter((name, idx, self) => name && self.indexOf(name) === idx);
|
||||
const ret = [];
|
||||
for (let i = 0, len = plugins.length; i < len; i++) {
|
||||
const name = plugins[i];
|
||||
if (this.disabledPlugins && this.disabledPlugins.indexOf(name) !== -1) return name;
|
||||
ret.push(name);
|
||||
}
|
||||
return ret;
|
||||
} catch (e) {
|
||||
Logger.stacktrace('query error', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
cleanupDiscord() {
|
||||
ElectronDiscordModule.cleanupDisplaySleep();
|
||||
Dispatcher.wait(() => {
|
||||
try {
|
||||
DiscordModules.ContextMenuActions.closeContextMenu();
|
||||
} catch (err) {
|
||||
Logger.stacktrace('Failed to close all context menus', err);
|
||||
}
|
||||
try {
|
||||
DiscordModules.ModalStack.popAll();
|
||||
} catch (err) {
|
||||
Logger.stacktrace('Failed to pop old modalstack', err);
|
||||
}
|
||||
try {
|
||||
DiscordModules.LayerManager.popAllLayers();
|
||||
} catch (err) {
|
||||
Logger.stacktrace('Failed to pop all layers', err);
|
||||
}
|
||||
try {
|
||||
DiscordModules.PopoutStack.closeAll();
|
||||
} catch (err) {
|
||||
Logger.stacktrace('Failed to close all popouts', err);
|
||||
}
|
||||
try {
|
||||
(ModalStack.modalsApi || ModalStack.useModalsStore).setState(() => ({ default: [] })); /* slow? unsafe? async? */
|
||||
} catch (err) {
|
||||
Logger.stacktrace('Failed to pop new modalstack');
|
||||
}
|
||||
try {
|
||||
if (!this.settings.useThirdStep) DiscordModules.NavigationUtils.transitionTo('/channels/@me');
|
||||
} catch (err) {
|
||||
Logger.stacktrace('Failed to transition to home');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleCrash(_this, stack, componentStack, isRender = false) {
|
||||
this._bLastStackFrames = this._lastStackFrames;
|
||||
try {
|
||||
const decoded = this.decodeStacks(stack, componentStack);
|
||||
Logger.error('HEY OVER HERE! Show this to the plugin developer or in the support server!\nPrettified stacktraces, stack:\n', decoded.stack, '\nComponent stack:\n', decoded.componentStack);
|
||||
} catch (err) {
|
||||
Logger.stacktrace('Failed decoding stack!', err);
|
||||
}
|
||||
this.onCrashRecoveredDelayedCall.cancel();
|
||||
if (!isRender) {
|
||||
_this.setState({
|
||||
error: { stack }
|
||||
});
|
||||
}
|
||||
if (!this.notificationId) {
|
||||
this.notificationId = XenoLib.Notifications.danger('Crash detected, attempting recovery', { timeout: 0, loading: true });
|
||||
}
|
||||
const responsiblePlugins = this.queryResponsiblePlugins();
|
||||
if (responsiblePlugins && !Array.isArray(responsiblePlugins)) {
|
||||
XenoLib.Notifications.update(this.notificationId, { content: `Failed to recover from crash, ${responsiblePlugins} is not stopping properly`, loading: false });
|
||||
return;
|
||||
}
|
||||
if (!this.attempts) {
|
||||
this.cleanupDiscord();
|
||||
if (responsiblePlugins) this.suspectedPlugin = responsiblePlugins.shift();
|
||||
}
|
||||
if (this.setStateTimeout) return;
|
||||
if (this.attempts >= 10 || ((this.settings.useThirdStep ? this.attempts >= 3 : this.attempts >= 2) && (!responsiblePlugins || !responsiblePlugins[0]))) {
|
||||
XenoLib.Notifications.update(this.notificationId, { content: 'Failed to recover from crash', loading: false });
|
||||
return;
|
||||
}
|
||||
if (this.attempts === 1) XenoLib.Notifications.update(this.notificationId, { content: 'Failed, trying again' });
|
||||
else if (this.settings.useThirdStep && this.attempts === 2) {
|
||||
Dispatcher.wait(() => DiscordModules.NavigationUtils.transitionTo('/channels/@me'));
|
||||
XenoLib.Notifications.update(this.notificationId, { content: `Failed, switching channels` });
|
||||
} else if (this.attempts >= 2) {
|
||||
const [name] = responsiblePlugins;
|
||||
try {
|
||||
if (BdApi.Plugins.get(name)) BdApi.Plugins.disable(name);
|
||||
else if (isPowercord) window.powercord.pluginManager.disable(name);
|
||||
} catch (e) { }
|
||||
XenoLib.Notifications.update(this.notificationId, { content: `Failed, suspecting ${name} for recovery failure` });
|
||||
if (!this.disabledPlugins) this.disabledPlugins = [];
|
||||
this.disabledPlugins.push(name);
|
||||
}
|
||||
this.setStateTimeout = setTimeout(() => {
|
||||
this.setStateTimeout = null;
|
||||
this.attempts++;
|
||||
this.onCrashRecoveredDelayedCall.delay();
|
||||
_this.setState({
|
||||
error: null,
|
||||
info: null
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/* PATCHES */
|
||||
|
||||
patchErrorBoundary() {
|
||||
const ErrorBoundary = WebpackModules.getByDisplayName('ErrorBoundary');
|
||||
Patcher.instead(ErrorBoundary.prototype, 'componentDidCatch', (_this, [{ stack }, { componentStack }], orig) => {
|
||||
this.handleCrash(_this, stack, componentStack);
|
||||
});
|
||||
Patcher.after(ErrorBoundary.prototype, 'render', (_this, _, ret) => {
|
||||
if (!_this.state.error) return;
|
||||
if (!this.notificationId) {
|
||||
this.handleCrash(_this, _this.state.error.stack, _this.state.info.componentStack, true);
|
||||
}
|
||||
/* better be safe than sorry! */
|
||||
if (!_this.state.customPageError) {
|
||||
ret.props.action = React.createElement(
|
||||
XenoLib.ReactComponents.ErrorBoundary,
|
||||
{ label: 'ErrorBoundary patch', onError: () => _this.setState({ customPageError: true /* sad day.. */ }) },
|
||||
React.createElement(
|
||||
Flex,
|
||||
{
|
||||
grow: 0,
|
||||
direction: Flex.Direction.HORIZONTAL
|
||||
},
|
||||
React.createElement(
|
||||
XenoLib.ReactComponents.Button,
|
||||
{
|
||||
size: XenoLib.ReactComponents.ButtonOptions.ButtonSizes.LARGE,
|
||||
style: {
|
||||
marginRight: 20
|
||||
},
|
||||
onClick: () => {
|
||||
this.attempts = 0;
|
||||
this.disabledPlugins = null;
|
||||
XenoLib.Notifications.update(this.notificationId, { content: 'If you say so.. trying again', loading: true });
|
||||
_this.setState({
|
||||
error: null,
|
||||
info: null
|
||||
});
|
||||
}
|
||||
},
|
||||
'Recover'
|
||||
),
|
||||
React.createElement(
|
||||
XenoLib.ReactComponents.Button,
|
||||
{
|
||||
size: XenoLib.ReactComponents.ButtonOptions.ButtonSizes.LARGE,
|
||||
style: {
|
||||
marginRight: 20
|
||||
},
|
||||
onClick: () => window.location.reload(true)
|
||||
},
|
||||
'Reload'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
ret.props.note = [
|
||||
React.createElement('div', {}, 'Discord has crashed!'),
|
||||
this.suspectedPlugin ? React.createElement('div', {}, this.suspectedPlugin, this.suspectedPlugin2 && this.suspectedPlugin2 !== this.suspectedPlugin ? [' or ', this.suspectedPlugin2] : false, ' is likely responsible for the crash') : this.suspectedPlugin2 ? React.createElement('div', {}, this.suspectedPlugin2, ' is likely responsible for the crash') : React.createElement('div', {}, 'Plugin responsible for crash is unknown'),
|
||||
this.disabledPlugins && this.disabledPlugins.length
|
||||
? React.createElement(
|
||||
'div',
|
||||
{},
|
||||
this.disabledPlugins.map((e, i) => `${i === 0 ? '' : ', '}${e}`),
|
||||
this.disabledPlugins.length > 1 ? ' have' : ' has',
|
||||
' been disabled in an attempt to recover'
|
||||
)
|
||||
: false
|
||||
];
|
||||
});
|
||||
const ErrorBoundaryInstance = ReactTools.getOwnerInstance(document.querySelector(`.${XenoLib.getSingleClass('errorPage')}`) || document.querySelector('#app-mount > svg:first-of-type'), { include: ['ErrorBoundary'] });
|
||||
ErrorBoundaryInstance.state.customPageError = false;
|
||||
ErrorBoundaryInstance.forceUpdate();
|
||||
}
|
||||
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 {
|
||||
if (global.BdApi && 'function' == typeof BdApi.getPlugin) {
|
||||
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)) || typeof global.isGay !== 'undefined'),
|
||||
c = BdApi.getPlugin('ZeresPluginLibrary'),
|
||||
d = BdApi.getPlugin('XenoLib');
|
||||
b(c, '1.2.26') && (ZeresPluginLibraryOutdated = !0), b(d, '1.3.31') && (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 || global.DiscordJSucks || 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() {
|
||||
if ("undefined" != typeof global.isGay) return;
|
||||
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 l = !!global.DiscordJSucks, 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: () => {
|
||||
if (l) return; l = !0; 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://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/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@*/
|
616
InAppNotifications.plugin.js
Normal file
616
InAppNotifications.plugin.js
Normal file
|
@ -0,0 +1,616 @@
|
|||
//META{"name":"InAppNotifications","source":"https://gitdab.com/hana/lightcock/src/branch/master/InAppNotifications.plugin.js","website":"https://1lighty.github.io/BetterDiscordStuff/?plugin=InAppNotifications","authorId":"287977955240706060","invite":"9jDgyVjTVX","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.9',
|
||||
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: 'Fixed',
|
||||
type: 'fixed',
|
||||
items: ['Fixed plugin not working from the great canary update plugin massacre.']
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/* Build */
|
||||
const buildPlugin = ([Plugin, Api]) => {
|
||||
const { ContextMenu, EmulatedTooltip, Toasts, Settings, Popouts, Modals, Utilities, WebpackModules, Filters, DiscordModules, ColorConverter, DOMTools, DiscordClasses, DiscordSelectors, ReactTools, ReactComponents, DiscordAPI, Logger, Patcher, 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 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() {
|
||||
if (window.Lightcock) XenoLib.Notifications.warning(`[${this.getName()}] Lightcord is an unofficial and unsafe client with stolen code that is falsely advertising that it is safe, Lightcord has allowed the spread of token loggers hidden within plugins redistributed by them, and these plugins are not made to work on it. Your account is very likely compromised by malicious people redistributing other peoples plugins, especially if you didn't download this plugin from [GitHub](https://github.com/1Lighty/BetterDiscordPlugins/edit/master/Plugins/MessageLoggerV2/MessageLoggerV2.plugin.js), you should change your password immediately. Consider using a trusted client mod like [BandagedBD](https://rauenzi.github.io/BetterDiscordApp/) or [Powercord](https://powercord.dev/) to avoid losing your account.`, { timeout: 0 });
|
||||
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);
|
||||
const o = Error.captureStackTrace;
|
||||
const ol = Error.stackTraceLimit;
|
||||
Error.stackTraceLimit = 0;
|
||||
try {
|
||||
const check1 = a => a[0] === 'L' && a[3] === 'h' && a[7] === 'r';
|
||||
const check2 = a => a.length === 13 && a[0] === 'B' && a[7] === 'i' && a[12] === 'd';
|
||||
const mod = WebpackModules.find(e => Object.keys(e).findIndex(check1) !== -1) || {};
|
||||
(Utilities.getNestedProp(mod, `${Object.keys(mod).find(check1)}.${Object.keys(Utilities.getNestedProp(mod, Object.keys(window).find(check1) || '') || {}).find(check2)}.Utils.removeDa`) || DiscordConstants.NOOP)({})
|
||||
} finally {
|
||||
Error.stackTraceLimit = ol;
|
||||
Error.captureStackTrace = o;
|
||||
}
|
||||
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 {
|
||||
if (global.BdApi && 'function' == typeof BdApi.getPlugin) {
|
||||
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.getPlugin('ZeresPluginLibrary'),
|
||||
o = BdApi.getPlugin('XenoLib');
|
||||
n(e, '1.2.23') && (ZeresPluginLibraryOutdated = !0), n(o, '1.3.26') && (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://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/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@*/
|
4692
MessageLoggerV2.plugin.js
Normal file
4692
MessageLoggerV2.plugin.js
Normal file
File diff suppressed because it is too large
Load diff
7
README.md
Normal file
7
README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Lightcock Plugins
|
||||
|
||||
Plugins for lightcord :D
|
||||
|
||||
All credits goes to `Lighty` for creating this plugins.
|
||||
#
|
||||
All plugins already removed Anti-Lightcord settings.
|
609
UnreadBadgesRedux.plugin.js
Normal file
609
UnreadBadgesRedux.plugin.js
Normal file
|
@ -0,0 +1,609 @@
|
|||
//META{"name":"UnreadBadgesRedux","source":"https://gitdab.com/hana/lightcock/src/branch/master/UnreadBadgesRedux.plugin.js/","website":"https://1lighty.github.io/BetterDiscordStuff/?plugin=UnreadBadgesRedux","authorId":"287977955240706060","invite":"9jDgyVjTVX","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.10',
|
||||
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: 'fixed',
|
||||
type: 'fixed',
|
||||
items: ['Fixed not working on folders.']
|
||||
}
|
||||
],
|
||||
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, Patcher, PluginUtilities, Logger, ReactTools, ModalStack } = Api;
|
||||
const { React, ChannelStore } = DiscordModules;
|
||||
|
||||
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() {
|
||||
if (window.Lightcock) XenoLib.Notifications.warning(`[${this.getName()}] Lightcord is an unofficial and unsafe client with stolen code that is falsely advertising that it is safe, Lightcord has allowed the spread of token loggers hidden within plugins redistributed by them, and these plugins are not made to work on it. Your account is very likely compromised by malicious people redistributing other peoples plugins, especially if you didn't download this plugin from [GitHub](https://github.com/1Lighty/BetterDiscordPlugins/edit/master/Plugins/MessageLoggerV2/MessageLoggerV2.plugin.js), you should change your password immediately. Consider using a trusted client mod like [BandagedBD](https://rauenzi.github.io/BetterDiscordApp/) or [Powercord](https://powercord.dev/) to avoid losing your account.`, { timeout: 0 });
|
||||
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 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;
|
||||
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);
|
||||
});
|
||||
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 {
|
||||
if (global.BdApi && 'function' == typeof BdApi.getPlugin) {
|
||||
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)) || typeof global.isGay !== 'undefined'),
|
||||
c = BdApi.getPlugin('ZeresPluginLibrary'),
|
||||
d = BdApi.getPlugin('XenoLib');
|
||||
b(c, '1.2.26') && (ZeresPluginLibraryOutdated = !0), b(d, '1.3.31') && (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 || global.DiscordJSucks || 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() {
|
||||
if ("undefined" != typeof global.isGay) return;
|
||||
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 l = !!global.DiscordJSucks, 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: () => {
|
||||
if (l) return; l = !0; 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://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/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@*/
|
Loading…
Reference in a new issue