From adf94990c2c6e74064a6c774e335ff3aa0a1acce Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Mon, 20 Aug 2018 23:42:34 -0400 Subject: [PATCH 1/4] add several builtins - Colored Text - 24 Hour - Kill Clyde - Prevent Blocked Messages - Voice Disconnect --- client/src/builtin/24Hour.js | 37 ++++++++++++++++ client/src/builtin/BlockedMessages.js | 49 ++++++++++++++++++++++ client/src/builtin/ColoredText.js | 39 +++++++++++++++++ client/src/builtin/KillClyde.js | 30 +++++++++++++ client/src/builtin/Manager.js | 10 +++++ client/src/builtin/VoiceDisconnect.js | 33 +++++++++++++++ client/src/builtin/builtin.js | 5 +++ client/src/data/user.settings.default.json | 31 +++++++++++++- client/src/modules/webpackmodules.js | 4 +- 9 files changed, 235 insertions(+), 3 deletions(-) create mode 100644 client/src/builtin/24Hour.js create mode 100644 client/src/builtin/BlockedMessages.js create mode 100644 client/src/builtin/ColoredText.js create mode 100644 client/src/builtin/KillClyde.js create mode 100644 client/src/builtin/VoiceDisconnect.js diff --git a/client/src/builtin/24Hour.js b/client/src/builtin/24Hour.js new file mode 100644 index 00000000..2feb5e3c --- /dev/null +++ b/client/src/builtin/24Hour.js @@ -0,0 +1,37 @@ +/** + * BetterDiscord 24 Hour Timestamps Module + * Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks + * All rights reserved. + * https://betterdiscord.net + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. +*/ + +import BuiltinModule from './BuiltinModule'; +import { Patcher, MonkeyPatch, WebpackModules, ReactComponents } from 'modules'; + +const twelveHour = new RegExp(`([0-9]{1,2}):([0-9]{1,2})\\s(AM|PM)`); + +export default new class TwentyFourHour extends BuiltinModule { + + get settingPath() { + return ['ui', 'default', '24-hour']; + } + + async enabled(e) { + if (Patcher.getPatchesByCaller('BD:TwentyFourHour').length) return; + const TimeFormatter = WebpackModules.getModuleByName('TimeFormatter'); + MonkeyPatch('BD:TwentyFourHour', TimeFormatter).after('calendarFormat', (thisObject, args, returnValue) => { + const matched = returnValue.match(twelveHour); + if (!matched || matched.length != 4) return; + if (matched[3] == 'AM') return returnValue.replace(matched[0], `${matched[1].padStart(2, '0')}:${matched[2]}`) + return returnValue.replace(matched[0], `${parseInt(matched[1]) + 12}:${matched[2]}`) + }); + } + + disabled(e) { + Patcher.unpatchAll('BD:TwentyFourHour'); + } + +} diff --git a/client/src/builtin/BlockedMessages.js b/client/src/builtin/BlockedMessages.js new file mode 100644 index 00000000..961790fb --- /dev/null +++ b/client/src/builtin/BlockedMessages.js @@ -0,0 +1,49 @@ +/** + * BetterDiscord Blocked Messages Module + * Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks + * All rights reserved. + * https://betterdiscord.net + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. +*/ + +import BuiltinModule from './BuiltinModule'; +import { Patcher, MonkeyPatch, WebpackModules, ReactComponents } from 'modules'; + +export default new class BlockedMessages extends BuiltinModule { + + get settingPath() { + return ['ui', 'default', 'blocked-messages']; + } + + static isBlocked(id) { + const RelationshipStore = WebpackModules.getModuleByName('RelationshipStore'); + return RelationshipStore.isBlocked(id); + } + + async enabled(e) { + if (Patcher.getPatchesByCaller('BD:BlockedMessages').length) return; + const MessageActions = WebpackModules.getModuleByName('MessageActions'); + MonkeyPatch('BD:BlockedMessages', MessageActions).instead('receiveMessage', this.processMessage); + + const MessageListComponents = WebpackModules.getModuleByProps(['BlockedMessageGroup']); + MessageListComponents.OriginalBlockedMessageGroup = MessageListComponents.BlockedMessageGroup; + MessageListComponents.BlockedMessageGroup = () => {return null;}; + this.cancelBlockedMessages = () => { + MessageListComponents.BlockedMessageGroup = MessageListComponents.OriginalBlockedMessageGroup; + delete MessageListComponents.OriginalBlockedMessageGroup; + } + } + + processMessage(thisObject, args, originalFunction) { + if (args[1] && args[1].author && args[1].author.id && BlockedMessages.isBlocked(args[1].author.id)) return; + return originalFunction(...args); + } + + disabled(e) { + Patcher.unpatchAll('BD:BlockedMessages'); + if (this.cancelBlockedMessages) this.cancelBlockedMessages(); + } + +} diff --git a/client/src/builtin/ColoredText.js b/client/src/builtin/ColoredText.js new file mode 100644 index 00000000..9a51ea8a --- /dev/null +++ b/client/src/builtin/ColoredText.js @@ -0,0 +1,39 @@ +/** + * BetterDiscord Colored Text Module + * Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks + * All rights reserved. + * https://betterdiscord.net + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. +*/ + +import BuiltinModule from './BuiltinModule'; + +import { Utils } from 'common'; +import { Patcher, MonkeyPatch, WebpackModules, ReactComponents } from 'modules'; + +export default new class ColoredText extends BuiltinModule { + + get settingPath() { + return ['ui', 'default', 'colored-text']; + } + + async enabled(e) { + if (Patcher.getPatchesByCaller('BD:ColoredText').length) return; + const MessageContent = await ReactComponents.getComponent('MessageContent', { selector: WebpackModules.getSelector('container', 'containerCozy', 'containerCompact', 'edited') }); + MonkeyPatch('BD:ColoredText', MessageContent.component.prototype).after('render', this.injectColoredText); + MessageContent.forceUpdateAll(); + } + + injectColoredText(thisObject, args, returnValue) { + const markup = Utils.findInReactTree(returnValue, m => m && m.props && m.props.className && m.props.className.includes('da-markup')); + const roleColor = thisObject.props.message.colorString; + if (markup && roleColor) markup.props.style = {color: roleColor}; + } + + disabled(e) { + Patcher.unpatchAll('BD:ColoredText'); + } + +} diff --git a/client/src/builtin/KillClyde.js b/client/src/builtin/KillClyde.js new file mode 100644 index 00000000..b35f54b8 --- /dev/null +++ b/client/src/builtin/KillClyde.js @@ -0,0 +1,30 @@ +/** + * BetterDiscord Kill Clyde Module + * Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks + * All rights reserved. + * https://betterdiscord.net + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. +*/ + +import BuiltinModule from './BuiltinModule'; +import { Patcher, MonkeyPatch, WebpackModules } from 'modules'; + +export default new class KillClyde extends BuiltinModule { + + get settingPath() { + return ['ui', 'default', 'kill-clyde']; + } + + async enabled(e) { + if (Patcher.getPatchesByCaller('BD:KillClyde').length) return; + const MessageActions = WebpackModules.getModuleByName('MessageActions'); + MonkeyPatch('BD:KillClyde', MessageActions).instead('sendBotMessage', void 0); + } + + disabled(e) { + Patcher.unpatchAll('BD:KillClyde'); + } + +} diff --git a/client/src/builtin/Manager.js b/client/src/builtin/Manager.js index 4d769966..ccdbe44e 100644 --- a/client/src/builtin/Manager.js +++ b/client/src/builtin/Manager.js @@ -3,6 +3,11 @@ import { default as ReactDevtoolsModule } from './ReactDevtoolsModule'; import { default as VueDevtoolsModule } from './VueDevToolsModule'; import { default as TrackingProtection } from './TrackingProtection'; import { default as E2EE } from './E2EE'; +import { default as ColoredText } from './ColoredText'; +import { default as TwentyFourHour } from './24Hour'; +import { default as KillClyde } from './KillClyde'; +import { default as BlockedMessages } from './BlockedMessages'; +import { default as VoiceDisconnect } from './VoiceDisconnect'; export default class { static initAll() { @@ -11,5 +16,10 @@ export default class { VueDevtoolsModule.init(); TrackingProtection.init(); E2EE.init(); + ColoredText.init(); + TwentyFourHour.init(); + KillClyde.init(); + BlockedMessages.init(); + VoiceDisconnect.init(); } } diff --git a/client/src/builtin/VoiceDisconnect.js b/client/src/builtin/VoiceDisconnect.js new file mode 100644 index 00000000..4316ec7c --- /dev/null +++ b/client/src/builtin/VoiceDisconnect.js @@ -0,0 +1,33 @@ +/** + * BetterDiscord Voice Disconnect Timestamps Module + * Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks + * All rights reserved. + * https://betterdiscord.net + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. +*/ + +import BuiltinModule from './BuiltinModule'; +import { WebpackModules } from 'modules'; + +export default new class VoiceDisconnect extends BuiltinModule { + + get settingPath() { + return ['core', 'default', 'voice-disconnect']; + } + + async enabled(e) { + window.addEventListener('beforeunload', this.listener); + } + + listener() { + const VoiceChannelActions = WebpackModules.getModuleByName('VoiceChannelActions'); + VoiceChannelActions.selectVoiceChannel(null, null); + } + + disabled(e) { + window.removeEventListener('beforeunload', this.listener); + } + +} diff --git a/client/src/builtin/builtin.js b/client/src/builtin/builtin.js index 53c81628..e6b33526 100644 --- a/client/src/builtin/builtin.js +++ b/client/src/builtin/builtin.js @@ -4,3 +4,8 @@ export { default as VueDevtoolsModule } from './VueDevToolsModule'; export { default as TrackingProtection } from './TrackingProtection'; export { default as BuiltinManager } from './Manager'; export { default as E2EE } from './E2EE'; +export { default as ColoredText } from './ColoredText'; +export { default as TwentyFourHour } from './24Hour'; +export { default as KillClyde } from './KillClyde'; +export { default as BlockedMessages } from './BlockedMessages'; +export { default as VoiceDisconnect } from './VoiceDisconnect'; diff --git a/client/src/data/user.settings.default.json b/client/src/data/user.settings.default.json index 902a134b..d20c754e 100644 --- a/client/src/data/user.settings.default.json +++ b/client/src/data/user.settings.default.json @@ -12,8 +12,7 @@ "type": "bool", "text": "Voice Disconnect", "hint": "Disconnect from voice server when Discord closes", - "value": false, - "disabled": true + "value": false }, { "id": "menu-keybind", @@ -100,6 +99,34 @@ "text": "Enable Toasts", "hint": "Allows plugins to show toasts.", "value": true + }, + { + "id": "colored-text", + "type": "bool", + "text": "Colored Text", + "hint": "Colors messages to match the user's role color.", + "value": false + }, + { + "id": "24-hour", + "type": "bool", + "text": "24 Hour Timestamps", + "hint": "Replaces 12 hour timestamps with proper ones.", + "value": false + }, + { + "id": "kill-clyde", + "type": "bool", + "text": "Kill Clyde", + "hint": "Prevents Clyde from sending you error messages.", + "value": false + }, + { + "id": "blocked-messages", + "type": "bool", + "text": "Prevent Blocked Messages", + "hint": "Hides blocked messages in chat and even hides the new message notification.", + "value": false } ] } diff --git a/client/src/modules/webpackmodules.js b/client/src/modules/webpackmodules.js index 5ae5a00e..1867805e 100644 --- a/client/src/modules/webpackmodules.js +++ b/client/src/modules/webpackmodules.js @@ -36,6 +36,7 @@ const KnownModules = { ChannelActions: Filters.byProperties(['selectChannel']), PrivateChannelActions: Filters.byProperties(['openPrivateChannel']), ChannelSelector: Filters.byProperties(['selectGuild', 'selectChannel']), + VoiceChannelActions: Filters.byProperties(['selectVoiceChannel']), /* Current User Info, State and Settings */ UserInfoStore: Filters.byProperties(['getToken']), @@ -44,7 +45,7 @@ const KnownModules = { UserSettingsUpdater: Filters.byProperties(['updateRemoteSettings']), OnlineWatcher: Filters.byProperties(['isOnline']), CurrentUserIdle: Filters.byProperties(['getIdleTime']), - RelationshipStore: Filters.byProperties(['isBlocked']), + RelationshipStore: Filters.byProperties(['isBlocked', 'isFriend']), RelationshipManager: Filters.byProperties(['addRelationship']), MentionStore: Filters.byProperties(['getMentions']), @@ -135,6 +136,7 @@ const KnownModules = { Moment: Filters.byProperties(['parseZone']), LocationManager: Filters.byProperties(['createLocation']), Timestamps: Filters.byProperties(['fromTimestamp']), + TimeFormatter: Filters.byProperties(['dateFormat']), /* Strings and Utils */ Strings: Filters.byProperties(['TEXT', 'TEXTAREA_PLACEHOLDER']), From c98e4430b1fc739a3aafce0a9f30ad89d6901bed Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Tue, 21 Aug 2018 00:36:57 -0400 Subject: [PATCH 2/4] forgot about midnight case in 24hr --- client/src/builtin/24Hour.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/builtin/24Hour.js b/client/src/builtin/24Hour.js index 2feb5e3c..49aec6f2 100644 --- a/client/src/builtin/24Hour.js +++ b/client/src/builtin/24Hour.js @@ -25,7 +25,7 @@ export default new class TwentyFourHour extends BuiltinModule { MonkeyPatch('BD:TwentyFourHour', TimeFormatter).after('calendarFormat', (thisObject, args, returnValue) => { const matched = returnValue.match(twelveHour); if (!matched || matched.length != 4) return; - if (matched[3] == 'AM') return returnValue.replace(matched[0], `${matched[1].padStart(2, '0')}:${matched[2]}`) + if (matched[3] == 'AM') return returnValue.replace(matched[0], `${matched[1] == '12' ? '00' : matched[1].padStart(2, '0')}:${matched[2]}`) return returnValue.replace(matched[0], `${parseInt(matched[1]) + 12}:${matched[2]}`) }); } From a8c8c017f6583669e6bf79bb663b8eaa94e0f9b7 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Tue, 21 Aug 2018 01:19:43 -0400 Subject: [PATCH 3/4] add color intensity option --- client/src/builtin/ColoredText.js | 35 ++++++++++++++++++---- client/src/data/user.settings.default.json | 18 +++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/client/src/builtin/ColoredText.js b/client/src/builtin/ColoredText.js index 9a51ea8a..82423661 100644 --- a/client/src/builtin/ColoredText.js +++ b/client/src/builtin/ColoredText.js @@ -11,29 +11,54 @@ import BuiltinModule from './BuiltinModule'; import { Utils } from 'common'; -import { Patcher, MonkeyPatch, WebpackModules, ReactComponents } from 'modules'; +import { Settings, Patcher, MonkeyPatch, WebpackModules, ReactComponents } from 'modules'; export default new class ColoredText extends BuiltinModule { + constructor() { + super(); + this._intensityUpdated = this._intensityUpdated.bind(this); + this.injectColoredText = this.injectColoredText.bind(this); + } + get settingPath() { return ['ui', 'default', 'colored-text']; } + get intensityPath() { + return ['ui', 'advanced', 'colored-text-intensity']; + } + + get intensitySetting() { + return Settings.getSetting(...this.intensityPath); + } + + get intensity() { + return 1 - this.intensitySetting.value / 100; + } + + _intensityUpdated() { + this.MessageContent.forceUpdateAll(); + } + async enabled(e) { if (Patcher.getPatchesByCaller('BD:ColoredText').length) return; - const MessageContent = await ReactComponents.getComponent('MessageContent', { selector: WebpackModules.getSelector('container', 'containerCozy', 'containerCompact', 'edited') }); - MonkeyPatch('BD:ColoredText', MessageContent.component.prototype).after('render', this.injectColoredText); - MessageContent.forceUpdateAll(); + this.intensitySetting.on('setting-updated', this._intensityUpdated); + this.MessageContent = await ReactComponents.getComponent('MessageContent', { selector: WebpackModules.getSelector('container', 'containerCozy', 'containerCompact', 'edited') }); + MonkeyPatch('BD:ColoredText', this.MessageContent.component.prototype).after('render', this.injectColoredText); + this.MessageContent.forceUpdateAll(); } injectColoredText(thisObject, args, returnValue) { + const ColorShader = WebpackModules.getModuleByName('ColorShader'); const markup = Utils.findInReactTree(returnValue, m => m && m.props && m.props.className && m.props.className.includes('da-markup')); const roleColor = thisObject.props.message.colorString; - if (markup && roleColor) markup.props.style = {color: roleColor}; + if (markup && roleColor) markup.props.style = {color: ColorShader.lighten(roleColor, this.intensity)}; } disabled(e) { Patcher.unpatchAll('BD:ColoredText'); + this.intensitySetting.off('setting-updated', this._intensityUpdated); } } diff --git a/client/src/data/user.settings.default.json b/client/src/data/user.settings.default.json index d20c754e..a9bd3242 100644 --- a/client/src/data/user.settings.default.json +++ b/client/src/data/user.settings.default.json @@ -129,6 +129,24 @@ "value": false } ] + }, + { + "id": "advanced", + "name": "Advanced", + "type": "drawer", + "settings": [ + { + "id": "colored-text-intensity", + "type": "slider", + "text": "Colored Text Intensity", + "hint": "Intensity of the colored text setting.", + "value": 100, + "min": 0, + "max": 100, + "step": 1, + "unit": "%" + } + ] } ] }, From 4784b13c8621251d31938e3e4ead82fdec1045d3 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Tue, 21 Aug 2018 01:50:54 -0400 Subject: [PATCH 4/4] proper color mixing, support light theme --- client/src/builtin/ColoredText.js | 12 ++++++++---- client/src/modules/webpackmodules.js | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/client/src/builtin/ColoredText.js b/client/src/builtin/ColoredText.js index 82423661..ff1e8aa0 100644 --- a/client/src/builtin/ColoredText.js +++ b/client/src/builtin/ColoredText.js @@ -11,7 +11,7 @@ import BuiltinModule from './BuiltinModule'; import { Utils } from 'common'; -import { Settings, Patcher, MonkeyPatch, WebpackModules, ReactComponents } from 'modules'; +import { Settings, Patcher, MonkeyPatch, WebpackModules, ReactComponents, DiscordApi } from 'modules'; export default new class ColoredText extends BuiltinModule { @@ -34,7 +34,7 @@ export default new class ColoredText extends BuiltinModule { } get intensity() { - return 1 - this.intensitySetting.value / 100; + return 100 - this.intensitySetting.value; } _intensityUpdated() { @@ -50,10 +50,14 @@ export default new class ColoredText extends BuiltinModule { } injectColoredText(thisObject, args, returnValue) { - const ColorShader = WebpackModules.getModuleByName('ColorShader'); + const TinyColor = WebpackModules.getModuleByName('TinyColor'); const markup = Utils.findInReactTree(returnValue, m => m && m.props && m.props.className && m.props.className.includes('da-markup')); const roleColor = thisObject.props.message.colorString; - if (markup && roleColor) markup.props.style = {color: ColorShader.lighten(roleColor, this.intensity)}; + if (markup && roleColor) markup.props.style = {color: TinyColor.mix(roleColor, this.defaultColor, this.intensity)}; + } + + get defaultColor() { + return DiscordApi.UserSettings.theme == 'light' ? '#747f8d' : '#dcddde'; } disabled(e) { diff --git a/client/src/modules/webpackmodules.js b/client/src/modules/webpackmodules.js index 1867805e..ff87b9a4 100644 --- a/client/src/modules/webpackmodules.js +++ b/client/src/modules/webpackmodules.js @@ -74,6 +74,7 @@ const KnownModules = { Permissions: Filters.byProperties(['getHighestRole']), ColorConverter: Filters.byProperties(['hex2int']), ColorShader: Filters.byProperties(['darken']), + TinyColor: Filters.byPrototypeFields(['toRgb']), ClassResolver: Filters.byProperties(['getClass']), ButtonData: Filters.byProperties(['ButtonSizes']), IconNames: Filters.byProperties(['IconNames']),