2019-05-28 23:27:25 +02:00
import Utilties from "./utilities" ;
import Config from "../data/config" ;
import Settings from "../data/settingscookie" ;
import BDV2 from "./bdv2" ;
import EmoteModule from "./emotes" ;
import QuickEmoteMenu from "./emotemenu" ;
import VoiceMode from "./voicemode" ;
import DevMode from "./devmode" ;
import PluginModule from "./pluginmanager" ;
import ThemeModule from "./thememanager" ;
import DataStore from "./datastore" ;
import { PublicServers , SettingsPanel } from "ui" ;
2019-05-28 20:19:48 +02:00
function Core ( config ) {
2019-05-28 23:27:25 +02:00
Object . assign ( Config , config ) ;
2019-05-28 20:19:48 +02:00
}
Core . prototype . init = async function ( ) {
2019-05-28 23:27:25 +02:00
if ( Config . version < Config . minSupportedVersion ) {
this . alert ( "Not Supported" , "BetterDiscord v" + Config . version + " (your version)" + " is not supported by the latest js (" + Config . bbdVersion + ").<br><br> Please download the latest version from <a href='https://github.com/rauenzi/BetterDiscordApp/releases/latest' target='_blank'>GitHub</a>" ) ;
2019-05-28 20:19:48 +02:00
return ;
}
2019-05-28 23:27:25 +02:00
if ( Config . updater . LatestVersion > Config . version ) {
2019-05-28 20:19:48 +02:00
this . alert ( "Update Available" , `
2019-05-28 23:27:25 +02:00
An update for BandagedBD is available ( $ { Config . updater . LatestVersion } ) ! Please Reinstall ! < br / > < br / >
2019-05-28 20:19:48 +02:00
< a href = 'https://github.com/rauenzi/BetterDiscordApp/releases/latest' target = '_blank' > Download Installer < / a >
` );
}
2019-05-28 23:27:25 +02:00
Utilties . log ( "Startup" , "Initializing Settings" ) ;
2019-05-28 20:19:48 +02:00
this . initSettings ( ) ;
2019-05-28 23:27:25 +02:00
this . emoteModule = new EmoteModule ( ) ;
this . quickEmoteMenu = new QuickEmoteMenu ( ) ;
Utilties . log ( "Startup" , "Initializing EmoteModule" ) ;
window . emotePromise = this . emoteModule . init ( ) . then ( ( ) => {
this . emoteModule . initialized = true ;
Utilties . log ( "Startup" , "Initializing QuickEmoteMenu" ) ;
this . quickEmoteMenu . init ( ) ;
2019-05-28 20:19:48 +02:00
} ) ;
2019-05-28 23:27:25 +02:00
this . publicServersModule = new PublicServers ( ) ;
2019-05-28 20:19:48 +02:00
2019-05-28 23:27:25 +02:00
this . voiceMode = new VoiceMode ( ) ;
this . dMode = new DevMode ( ) ;
2019-05-28 20:19:48 +02:00
this . injectExternals ( ) ;
await this . checkForGuilds ( ) ;
BDV2 . initialize ( ) ;
2019-05-28 23:27:25 +02:00
Utilties . log ( "Startup" , "Updating Settings" ) ;
this . settingsPanel = new SettingsPanel ( ) ;
this . settingsPanel . initializeSettings ( ) ;
2019-05-28 20:19:48 +02:00
2019-05-28 23:27:25 +02:00
Utilties . log ( "Startup" , "Loading Plugins" ) ;
this . pluginModule = new PluginModule ( ) ;
const pluginErrors = this . pluginModule . loadPlugins ( ) ;
2019-05-28 20:19:48 +02:00
2019-05-28 23:27:25 +02:00
Utilties . log ( "Startup" , "Loading Themes" ) ;
this . themeModule = new ThemeModule ( ) ;
const themeErrors = this . themeModule . loadThemes ( ) ;
2019-05-28 20:19:48 +02:00
$ ( "#customcss" ) . detach ( ) . appendTo ( document . head ) ;
window . addEventListener ( "beforeunload" , function ( ) {
2019-05-28 23:27:25 +02:00
if ( Settings [ "bda-dc-0" ] ) document . querySelector ( ".btn.btn-disconnect" ) . click ( ) ;
2019-05-28 20:19:48 +02:00
} ) ;
2019-05-28 23:27:25 +02:00
this . publicServersModule . initialize ( ) ;
2019-05-28 20:19:48 +02:00
2019-05-28 23:27:25 +02:00
this . emoteModule . autoCapitalize ( ) ;
2019-05-28 20:19:48 +02:00
2019-05-28 23:27:25 +02:00
Utilties . log ( "Startup" , "Removing Loading Icon" ) ;
2019-05-28 20:19:48 +02:00
document . getElementsByClassName ( "bd-loaderv2" ) [ 0 ] . remove ( ) ;
2019-05-28 23:27:25 +02:00
Utilties . log ( "Startup" , "Initializing Main Observer" ) ;
2019-05-28 20:19:48 +02:00
this . initObserver ( ) ;
// Show loading errors
2019-05-28 23:27:25 +02:00
if ( Settings [ "fork-ps-1" ] ) {
Utilties . log ( "Startup" , "Collecting Startup Errors" ) ;
this . showContentErrors ( { plugins : pluginErrors , themes : themeErrors } ) ;
2019-05-28 20:19:48 +02:00
}
// if (!DataStore.getBDData(bbdVersion)) {
// BdApi.alert("BBD Updated!", ["Lots of things were fixed in this update like Public Servers, Minimal Mode, Dark Mode and 24 Hour Timestamps.", BdApi.React.createElement("br"), BdApi.React.createElement("br"), "Feel free to test them all out!"]);
// DataStore.setBDData(bbdVersion, true);
// }
} ;
Core . prototype . checkForGuilds = function ( ) {
return new Promise ( resolve => {
const checkForGuilds = function ( ) {
const wrapper = BDV2 . guildClasses . wrapper . split ( " " ) [ 0 ] ;
const guild = BDV2 . guildClasses . listItem . split ( " " ) [ 0 ] ;
const blob = BDV2 . guildClasses . blobContainer . split ( " " ) [ 0 ] ;
2019-05-28 23:27:25 +02:00
if ( document . querySelectorAll ( ` . ${ wrapper } . ${ guild } . ${ blob } ` ) . length > 0 ) return resolve ( Config . deferLoaded = true ) ;
2019-05-28 20:19:48 +02:00
setTimeout ( checkForGuilds , 100 ) ;
} ;
$ ( document ) . ready ( function ( ) {
setTimeout ( checkForGuilds , 100 ) ;
} ) ;
} ) ;
} ;
Core . prototype . injectExternals = async function ( ) {
2019-05-28 23:27:25 +02:00
await Utilties . injectJs ( "https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js" ) ;
// if (require.original) window.require = require.original;
2019-05-28 20:19:48 +02:00
} ;
Core . prototype . initSettings = function ( ) {
DataStore . initialize ( ) ;
2019-05-28 23:27:25 +02:00
if ( ! DataStore . getSettingGroup ( "settings" ) ) return this . saveSettings ( ) ;
const savedSettings = this . loadSettings ( ) ;
$ ( "<style id=\"customcss\">" ) . text ( atob ( DataStore . getBDData ( "bdcustomcss" ) ) ) . appendTo ( document . head ) ;
for ( const setting in savedSettings ) {
if ( savedSettings [ setting ] !== undefined ) Settings [ setting ] = savedSettings [ setting ] ;
2019-05-28 20:19:48 +02:00
}
2019-05-28 23:27:25 +02:00
this . saveSettings ( ) ;
2019-05-28 20:19:48 +02:00
} ;
Core . prototype . saveSettings = function ( ) {
2019-05-28 23:27:25 +02:00
DataStore . setSettingGroup ( "settings" , Settings ) ;
2019-05-28 20:19:48 +02:00
} ;
Core . prototype . loadSettings = function ( ) {
2019-05-28 23:27:25 +02:00
Settings = DataStore . getSettingGroup ( "settings" ) ;
2019-05-28 20:19:48 +02:00
} ;
Core . prototype . initObserver = function ( ) {
const mainObserver = new MutationObserver ( ( mutations ) => {
for ( let i = 0 , mlen = mutations . length ; i < mlen ; i ++ ) {
let mutation = mutations [ i ] ;
2019-05-28 23:27:25 +02:00
if ( typeof pluginModule !== "undefined" ) this . pluginModule . rawObserver ( mutation ) ;
2019-05-28 20:19:48 +02:00
// if there was nothing added, skip
if ( ! mutation . addedNodes . length || ! ( mutation . addedNodes [ 0 ] instanceof Element ) ) continue ;
let node = mutation . addedNodes [ 0 ] ;
if ( node . classList . contains ( "layer-3QrUeG" ) ) {
if ( node . getElementsByClassName ( "guild-settings-base-section" ) . length ) node . setAttribute ( "layer-id" , "server-settings" ) ;
if ( node . getElementsByClassName ( "socialLinks-3jqNFy" ) . length ) {
node . setAttribute ( "layer-id" , "user-settings" ) ;
node . setAttribute ( "id" , "user-settings" ) ;
2019-05-28 23:27:25 +02:00
if ( ! document . getElementById ( "bd-settings-sidebar" ) ) this . settingsPanel . renderSidebar ( ) ;
2019-05-28 20:19:48 +02:00
}
}
// Emoji Picker
2019-05-28 23:27:25 +02:00
if ( node . classList . contains ( "popout-3sVMXz" ) && ! node . classList . contains ( "popoutLeft-30WmrD" ) && node . getElementsByClassName ( "emojiPicker-3m1S-j" ) . length ) this . quickEmoteMenu . obsCallback ( node ) ;
2019-05-28 20:19:48 +02:00
}
} ) ;
mainObserver . observe ( document , {
childList : true ,
subtree : true
} ) ;
} ;
Core . prototype . inject24Hour = function ( ) {
if ( this . cancel24Hour ) return ;
const twelveHour = new RegExp ( ` ([0-9]{1,2}):([0-9]{1,2}) \\ s(AM|PM) ` ) ;
const convert = ( data ) => {
2019-05-28 23:27:25 +02:00
if ( ! Settings [ "bda-gs-6" ] ) return ;
2019-05-28 20:19:48 +02:00
const matched = data . returnValue . match ( twelveHour ) ;
if ( ! matched || matched . length !== 4 ) return ;
if ( matched [ 3 ] === "AM" ) return data . returnValue = data . returnValue . replace ( matched [ 0 ] , ` ${ matched [ 1 ] === "12" ? "00" : matched [ 1 ] . padStart ( 2 , "0" ) } : ${ matched [ 2 ] } ` ) ;
return data . returnValue = data . returnValue . replace ( matched [ 0 ] , ` ${ matched [ 1 ] === "12" ? "12" : parseInt ( matched [ 1 ] ) + 12 } : ${ matched [ 2 ] } ` ) ;
} ;
2019-05-28 23:27:25 +02:00
const cancelCozy = Utilties . monkeyPatch ( BDV2 . TimeFormatter , "calendarFormat" , { after : convert } ) ; // Called in Cozy mode
const cancelCompact = Utilties . monkeyPatch ( BDV2 . TimeFormatter , "dateFormat" , { after : convert } ) ; // Called in Compact mode
2019-05-28 20:19:48 +02:00
this . cancel24Hour = ( ) => { cancelCozy ( ) ; cancelCompact ( ) ; } ; // Cancel both
} ;
Core . prototype . injectColoredText = function ( ) {
if ( this . cancelColoredText ) return ;
2019-05-28 23:27:25 +02:00
this . cancelColoredText = Utilties . monkeyPatch ( BDV2 . MessageContentComponent . prototype , "render" , { after : ( data ) => {
if ( ! Settings [ "bda-gs-7" ] ) return ;
Utilties . monkeyPatch ( data . returnValue . props , "children" , { silent : true , after : ( { returnValue } ) => {
2019-05-28 20:19:48 +02:00
const markup = returnValue . props . children [ 1 ] ;
const roleColor = data . thisObject . props . message . colorString ;
if ( markup && roleColor ) markup . props . style = { color : roleColor } ;
return returnValue ;
} } ) ;
} } ) ;
} ;
Core . prototype . removeColoredText = function ( ) {
document . querySelectorAll ( ".markup-2BOw-j" ) . forEach ( elem => {
elem . style . setProperty ( "color" , "" ) ;
} ) ;
} ;
Core . prototype . alert = function ( title , content ) {
let modal = $ ( ` <div class="bd-modal-wrapper theme-dark">
< div class = "bd-backdrop backdrop-1wrmKB" > < / d i v >
< div class = "bd-modal modal-1UGdnR" >
< div class = "bd-modal-inner inner-1JeGVc" >
< div class = "header header-1R_AjF" >
< div class = "title" > $ { title } < / d i v >
< / d i v >
< div class = "bd-modal-body" >
< div class = "scroller-wrap fade" >
< div class = "scroller" >
$ { content }
< / d i v >
< / d i v >
< / d i v >
< div class = "footer footer-2yfCgX" >
< button type = "button" > Okay < / b u t t o n >
< / d i v >
< / d i v >
< / d i v >
< / d i v > ` ) ;
modal . find ( ".footer button" ) . on ( "click" , ( ) => {
modal . addClass ( "closing" ) ;
setTimeout ( ( ) => { modal . remove ( ) ; } , 300 ) ;
} ) ;
modal . find ( ".bd-backdrop" ) . on ( "click" , ( ) => {
modal . addClass ( "closing" ) ;
setTimeout ( ( ) => { modal . remove ( ) ; } , 300 ) ;
} ) ;
modal . appendTo ( "#app-mount" ) ;
} ;
Core . prototype . showContentErrors = function ( { plugins : pluginErrors = [ ] , themes : themeErrors = [ ] } ) {
if ( ! pluginErrors || ! themeErrors ) return ;
if ( ! pluginErrors . length && ! themeErrors . length ) return ;
let modal = $ ( ` <div class="bd-modal-wrapper theme-dark">
< div class = "bd-backdrop backdrop-1wrmKB" > < / d i v >
< div class = "bd-modal bd-content-modal modal-1UGdnR" >
< div class = "bd-modal-inner inner-1JeGVc" >
< div class = "header header-1R_AjF" > < div class = "title" > Content Errors < / d i v > < / d i v >
< div class = "bd-modal-body" >
< div class = "tab-bar-container" >
< div class = "tab-bar TOP" >
< div class = "tab-bar-item" > Plugins < / d i v >
< div class = "tab-bar-item" > Themes < / d i v >
< / d i v >
< / d i v >
< div class = "table-header" >
< div class = "table-column column-name" > Name < / d i v >
< div class = "table-column column-message" > Message < / d i v >
< div class = "table-column column-error" > Error < / d i v >
< / d i v >
< div class = "scroller-wrap fade" >
< div class = "scroller" >
< / d i v >
< / d i v >
< / d i v >
< div class = "footer footer-2yfCgX" >
< button type = "button" > Okay < / b u t t o n >
< / d i v >
< / d i v >
< / d i v >
< / d i v > ` ) ;
function generateTab ( errors ) {
let container = $ ( ` <div class="errors"> ` ) ;
for ( let err of errors ) {
let error = $ ( ` <div class="error">
< div class = "table-column column-name" > $ { err . name ? err . name : err . file } < / d i v >
< div class = "table-column column-message" > $ { err . message } < / d i v >
< div class = "table-column column-error" > < a class = "error-link" href = "" > $ { err . error ? err . error . message : "" } < / a > < / d i v >
< / d i v > ` ) ;
container . append ( error ) ;
if ( err . error ) {
error . find ( "a" ) . on ( "click" , ( e ) => {
e . preventDefault ( ) ;
2019-05-28 23:27:25 +02:00
Utilties . err ( "ContentManager" , ` Error details for ${ err . name ? err . name : err . file } . ` , err . error ) ;
2019-05-28 20:19:48 +02:00
} ) ;
}
}
return container ;
}
let tabs = [ generateTab ( pluginErrors ) , generateTab ( themeErrors ) ] ;
modal . find ( ".tab-bar-item" ) . on ( "click" , ( e ) => {
e . preventDefault ( ) ;
modal . find ( ".tab-bar-item" ) . removeClass ( "selected" ) ;
$ ( e . target ) . addClass ( "selected" ) ;
modal . find ( ".scroller" ) . empty ( ) . append ( tabs [ $ ( e . target ) . index ( ) ] ) ;
} ) ;
modal . find ( ".footer button" ) . on ( "click" , ( ) => {
modal . addClass ( "closing" ) ;
setTimeout ( ( ) => { modal . remove ( ) ; } , 300 ) ;
} ) ;
modal . find ( ".bd-backdrop" ) . on ( "click" , ( ) => {
modal . addClass ( "closing" ) ;
setTimeout ( ( ) => { modal . remove ( ) ; } , 300 ) ;
} ) ;
modal . appendTo ( "#app-mount" ) ;
if ( pluginErrors . length ) modal . find ( ".tab-bar-item" ) [ 0 ] . click ( ) ;
else modal . find ( ".tab-bar-item" ) [ 1 ] . click ( ) ;
} ;
/ * *
* This shows a toast similar to android towards the bottom of the screen .
*
* @ param { string } content The string to show in the toast .
* @ param { object } options Options object . Optional parameter .
* @ param { string } options . type Changes the type of the toast stylistically and semantically . Choices : "" , "info" , "success" , "danger" / "error" , "warning" / "warn" . Default : ""
* @ param { boolean } options . icon Determines whether the icon should show corresponding to the type . A toast without type will always have no icon . Default : true
* @ param { number } options . timeout Adjusts the time ( in ms ) the toast should be shown for before disappearing automatically . Default : 3000
* /
Core . prototype . showToast = function ( content , options = { } ) {
2019-05-28 23:27:25 +02:00
if ( ! Config . deferLoaded ) return ;
2019-05-28 20:19:48 +02:00
if ( ! document . querySelector ( ".bd-toasts" ) ) {
let toastWrapper = document . createElement ( "div" ) ;
toastWrapper . classList . add ( "bd-toasts" ) ;
let boundingElement = document . querySelector ( ".chat-3bRxxu form, #friends, .noChannel-Z1DQK7, .activityFeed-28jde9" ) ;
toastWrapper . style . setProperty ( "left" , boundingElement ? boundingElement . getBoundingClientRect ( ) . left + "px" : "0px" ) ;
toastWrapper . style . setProperty ( "width" , boundingElement ? boundingElement . offsetWidth + "px" : "100%" ) ;
toastWrapper . style . setProperty ( "bottom" , ( document . querySelector ( ".chat-3bRxxu form" ) ? document . querySelector ( ".chat-3bRxxu form" ) . offsetHeight : 80 ) + "px" ) ;
document . querySelector ( ".app, .app-2rEoOp" ) . appendChild ( toastWrapper ) ;
}
const { type = "" , icon = true , timeout = 3000 } = options ;
let toastElem = document . createElement ( "div" ) ;
toastElem . classList . add ( "bd-toast" ) ;
if ( type ) toastElem . classList . add ( "toast-" + type ) ;
if ( type && icon ) toastElem . classList . add ( "icon" ) ;
toastElem . innerText = content ;
document . querySelector ( ".bd-toasts" ) . appendChild ( toastElem ) ;
setTimeout ( ( ) => {
toastElem . classList . add ( "closing" ) ;
setTimeout ( ( ) => {
toastElem . remove ( ) ;
if ( ! document . querySelectorAll ( ".bd-toasts .bd-toast" ) . length ) document . querySelector ( ".bd-toasts" ) . remove ( ) ;
} , 300 ) ;
} , timeout ) ;
2019-05-28 23:27:25 +02:00
} ;
export default Core ;