const ModuleLoader = require("./loaders/modules") const { EventEmitter } = require("events") const Logger = require("./Logger") const fs = require("fs") const path = require("path") const electron = require("electron") const fetch = require("node-fetch").default const uuid = require("uuid/v4") const isPackaged = electron.remote.app.isPackaged const events = exports.events = new EventEmitter() const logger = exports.logger = new Logger("Lightcord") let hasInit = false let tries = 0 let hasReplacedLocalstorage = false const localStorage = window.localStorage const UserAgent = electron.ipcRenderer.sendSync("LIGHTCORD_GET_USER_AGENT").replace(/lightcord\/[^ ]+/g, "discord/"+require("../discord_native/renderer/app").getVersion()) electron.ipcRenderer.sendSync("LIGHTCORD_SET_USER_AGENT", UserAgent) exports.init = function(){ if(hasInit == true){ console.warn(new Error("Lightcord has already inited.")) return } formatLogger.log("The app is", isPackaged ? "packaged." : "not packaged.") hasInit = true let readyInterval = setInterval(()=>{ events.emit("debug", `[INIT] try ${tries++} loading Lightcord`) try{ if(!global.webpackJsonp)return if(isTab && !hasReplacedLocalstorage){ hasReplacedLocalstorage = true const localstr = require("localstorage-polyfill") Object.defineProperty(window, "localStorage", { value: localstr }) } if(!ModuleLoader.get(4))return clearInterval(readyInterval) privateInit() .then(() => { console.log("Finished loading Lightcord.") }) }catch(e){ console.error(e) } }, 100) } let hasPrivateInit = false async function privateInit(){ if(!hasInit)return if(hasPrivateInit)return hasPrivateInit = true let cached = require.cache[path.join(__dirname, "loaders", "modules.js")] if(cached){ cached.exports = window.BDModules } //disabling sentry ModuleLoader.get(e => e.getCurrentHub)[0].getCurrentHub().getClient().getOptions().enabled = false // setting react in require cache const React = ModuleLoader.get(e => !["Component", "PureComponent", "Children", "createElement", "cloneElement"].find(k => !e[k]))[0] window.React = React const ReactDOM = ModuleLoader.get(e => e.findDOMNode)[0] window.ReactDOM = ReactDOM //stop here if betterdiscord is disabled. if(electron.remote.process.argv.includes("--disable-betterdiscord")){ let formComponents let margins class LightcordSettings extends React.Component { render(){ if(!formComponents)formComponents = ModuleLoader.get(e => e.FormSection)[0] if(!margins)margins = ModuleLoader.get(e => e.marginTop60)[0] let button = require("./Button").default return React.createElement("div", {}, [ React.createElement(formComponents.FormSection, { className: "", tag: "h2", title: "Lightcord's Settings" }, React.createElement(button, { color: "yellow", look: "ghost", size: "medium", hoverColor: "red", onClick: () => { console.log("Should relaunch") ipcRenderer.sendSync("LIGHTCORD_RELAUNCH_APP", { args: electron.remote.process.argv.slice(1).filter(e => e !== "--disable-betterdiscord") }) }, wrapper: true }, "Relaunch with BetterDiscord")) ]) } } // fix notifications here let dispatcher = ModuleLoader.get(m=>m.Dispatcher&&m.default&&m.default.dispatch)[0].default dispatcher.subscribe("USER_SETTINGS_UPDATE", (data) => { DiscordNative.ipc.send("UPDATE_THEME", data.settings.theme) }) let constants = ModuleLoader.get(m=>m.API_HOST)[0] // add menu to re enable BetterDiscord constants.UserSettingsSections = Object.freeze(Object.assign({}, constants.UserSettingsSections, {LIGHTCORD: "Lightcord"})) ensureExported(e => e.default && e.default.prototype && e.default.prototype.getPredicateSections) .then(settingModule => { let getPredicateSections = settingModule.default.prototype.getPredicateSections settingModule.default.prototype.getPredicateSections = function(){ let result = getPredicateSections.call(this, ...arguments) if(result[1].section === "My Account"){ // user settings, not guild settings let poped = [] poped.push(result.pop()) poped.push(result.pop()) poped.push(result.pop()) poped.push(result.pop()) result.push({ section: "HEADER", label: "Lightcord" }, { section: constants.UserSettingsSections.LIGHTCORD, label: "Lightcord", element: LightcordSettings }, { section: "DIVIDER" }) while(poped[0]){ result.push(poped.pop()) } } return result } }) installReactDevtools() return } let createSoundOriginal = ModuleLoader.get((e) => e.createSound)[0].createSound ModuleLoader.get((e) => e.createSound)[0].createSound = function(sound){ let isCalling = sound === "call_ringing_beat" || sound === "call_ringing" if(isCalling){ let returned = createSoundOriginal.call(this, ...arguments) Object.defineProperty(returned, "name", { get(){ return window.Lightcord.Settings.callRingingBeat ? "call_ringing_beat" : "call_ringing" }, set(data){ console.log("Attempting to set call_ringing value. Canceling", data) }, configurable: false }) return returned }else{ return createSoundOriginal(...arguments) } } let constants = ModuleLoader.get(m=>m.API_HOST)[0] let dispatcher = ModuleLoader.get(m=>m.Dispatcher&&m.default&&m.default.dispatch)[0].default require(formatMinified(path.join(__dirname, "../../../../../BetterDiscordApp/dist/style{min}.css"))) require("./lightcord.css") function getCurrentHypesquad(){ let user = ModuleLoader.get(e => e.default && e.default.getCurrentUser)[0].default.getCurrentUser() if(!user)return undefined if(user.hasFlag(constants.UserFlags.HYPESQUAD_ONLINE_HOUSE_1))return "1" if(user.hasFlag(constants.UserFlags.HYPESQUAD_ONLINE_HOUSE_2))return "2" if(user.hasFlag(constants.UserFlags.HYPESQUAD_ONLINE_HOUSE_3))return "3" return undefined } window.$ = window.jQuery = require("./jquery.min.js") require("./ace.js") installReactDevtools() if(!fs.existsSync(BetterDiscordConfig.dataPath))fs.mkdirSync(BetterDiscordConfig.dataPath, {recursive: true}) let pluginPath = path.join(BetterDiscordConfig.dataPath, "plugins") let themePath = path.join(BetterDiscordConfig.dataPath, "themes") console.log(`Plugins: ${pluginPath}\nThemes: ${themePath}`) if(!fs.existsSync(pluginPath)){ fs.mkdirSync(pluginPath, {recursive: true}) /** Downloads Util Plugins So the user don't have to install it manually */ /** ZeresPluginLibrary */ const ZeresPluginLibraryPath = path.join(pluginPath, "0PluginLibrary.plugin.js") fetch("https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js") .then(async res => { if(res.status !== 200)return const content = await res.buffer() fs.writeFileSync(ZeresPluginLibraryPath, content) }) BetterDiscordConfig.haveInstalledDefault = true // Inform User about what we just did } if(!fs.existsSync(themePath)){ fs.mkdirSync(themePath, {recursive: true}) /** Downloads Basic Themes to guide user and showcase features */ /** Discord Dark */ const DarkDiscordPath = path.join(themePath, "DarkDiscord.theme.css") fetch("https://raw.githubusercontent.com/hormelcookies/dark-discord/hormelcookies-patch-1/DarkDiscord.theme.css") .then(async res => { if(res.status !== 200)return const content = await res.buffer() fs.writeFileSync(DarkDiscordPath, content) }) /** Glasscord Example */ const GlasscordExamplePath = path.join(themePath, "glasscord_example.theme.css") fetch("https://raw.githubusercontent.com/AryToNeX/Glasscord/master/extras/discord_example_theme/discord_example.theme.css") .then(async res => { if(res.status !== 200)return const content = await res.buffer() fs.writeFileSync(GlasscordExamplePath, content) }) BetterDiscordConfig.haveInstalledDefault = true // Inform User about what we just did } else { // Remove darkdiscord if exists, replace with known good version const DarkDiscordPath = path.join(themePath, "DarkDiscord.theme.css") let names = [ DarkDiscordPath, path.join(themePath, "DiscordDark.theme.css")]; //check for dark discord and its alternative names for (name of names){ if (fs.existsSync(name)){ let data = fs.readFileSync(name, "utf-8"); if (data.includes("hellbound")){ fs.unlinkSync(name) fetch("https://raw.githubusercontent.com/hormelcookies/dark-discord/hormelcookies-patch-1/DarkDiscord.theme.css") .then(async res => { if(res.status !== 200)return const content = await res.buffer() // write to the canonical path fs.writeFileSync(DarkDiscordPath, content) }) } } } } // setting Discord Internal Developer Mode for developement and test purposes. let developerModule = ModuleLoader.get(e => e.default && typeof e.default === "object" && ("isDeveloper" in e.default))[0] if(developerModule){ Object.defineProperty(developerModule.default, "isDeveloper", { get(){return !!window.Lightcord.Settings.devMode}, set(data){return !!window.Lightcord.Settings.devMode} }) } /** * @type {typeof import("../../../../../DiscordJS").default} */ let DiscordJS try{ DiscordJS = require("../../../../../DiscordJS").default }catch(err){ console.error(err) DiscordJS = null } let cloneNullProto = (obj) => { // recreate object without __proto__ let o = Object.create(null) Object.keys(obj).forEach(k => { o[k] = obj[k] }) return o } window.Lightcord = cloneNullProto({ DiscordModules: cloneNullProto({ dispatcher, constants }), Settings: cloneNullProto({ devMode: false, callRingingBeat: true }), Api: cloneNullProto({ Authorization: null, ensureExported, cloneNullProto }), BetterDiscord: cloneNullProto({ // Global BetterDiscord's exported modules }) }) dispatcher.subscribe("USER_SETTINGS_UPDATE", (data) => { DiscordNative.ipc.send("UPDATE_THEME", data.settings.theme) }) require(formatMinified("lightcordapi/js/main{min}.js")) /* if(shouldShowPrompt){ let onConn = (ev) => { console.log(`Showing auth window.`, ev) shouldShowPrompt = false dispatcher.unsubscribe(constants.ActionTypes.CONNECTION_OPEN || "CONNECTION_OPEN", onConn) const options = { width: 500, height: 550, backgroundColor: "#202225", show: true, resizable: false, maximizable: false, minimizable: false, frame: false, center: false, webPreferences: { nodeIntegration: false, preload: path.join(__dirname, "auth", "preload.js"), webviewTag: true }, parent: electron.remote.getCurrentWindow() }; options.x = Math.round(window.screenX + window.innerWidth / 2 - options.width / 2); options.y = Math.round(window.screenY + window.innerHeight / 2 - options.height / 2); const authWindow = new electron.remote.BrowserWindow(options) authWindow.webContents.session.protocol.registerFileProtocol("lightcord", (req, callback) => { const parsedURL = new URL("http://lightcord.xyz/"+req.url.split("://")[1]) let file if(req.method !== "GET"){ file = "404.html" }else{ if(parsedURL.pathname === "/index.html"){ file = "index.html" }else if(parsedURL.pathname === "/index.css"){ file = "index.css" }else if(parsedURL.pathname === "/login/callback"){ authWindow.close() console.log(parsedURL.searchParams) Authorization = parsedURL.searchParams.get("auth") authWindow = null return } } if(!file){ file = "404.html" } callback(path.join(__dirname, "auth", file)) }, (err) => { if(err)console.error(err) }) electron.remote.getCurrentWindow().webContents.on("devtools-reload-page", () => { electron.remote.protocol.unregisterProtocol("lightcord") }) authWindow.on("close", () => { electron.remote.protocol.unregisterProtocol("lightcord") }) authWindow.loadURL("lightcord://index.html") } dispatcher.subscribe(constants.ActionTypes.CONNECTION_OPEN || "CONNECTION_OPEN", onConn) }*/ const BetterDiscord = new(require(formatMinified("../../../../../BetterDiscordApp/dist/index{min}.js")).default)(BetterDiscordConfig, require("./betterdiscord")) const Utils = window.Lightcord.BetterDiscord.Utils const DOMTools = window.Lightcord.BetterDiscord.DOM let isBot = false dispatcher.subscribe("LOGOUT", () => { isBot = false }) const appSettings = window.Lightcord.Api.settings ;(async function(){ const gatewayModule = await ensureExported(e => e.default && e.default.prototype && e.default.prototype._handleDispatch) if(!gatewayModule)return let _handleDispatch = gatewayModule.default.prototype._handleDispatch gatewayModule.default.prototype._handleDispatch = function(data, event, props){ if(event === "READY"){ console.log(...arguments) if(false){ dispatcher.dispatch({ type: "LOGOUT" }) BdApi.showToast(data.user.username+"#"+data.user.discriminator+": This account is blacklisted from Lightcord.", { type: "error", timeout: 10000 }) appSettings.get("­", true) appSettings.save() return } isBot = data.user.bot if(data.user.bot){ logger.log(`Logged in as a bot, spoofing user...`) data.user.bot = false data.user.premium = true data.user.premium_type = 1 data.user.email = data.user.email || uuid()+"@lightcord.xyz" // filler email, not a real one data.experiments = data.experiments || [] data.guild_experiments = data.guild_experiments || []; data.connected_accounts = data.connected_accounts || []; data.relationships = data.relationships || []; data.notes = data.notes || {}; data.user_feed_settings = data.user_feed_settings || []; data.analytics_tokens = data.analytics_tokens || []; data.analytics_token = data.analytics_token || "" data.private_channels = data.private_channels || []; data.read_state = data.read_state || { entries: [], partial: false, version: 19438 } data.consents = data.consents || { personalization: false } data.tutorial = data.tutorial || null data.user_settings = Object.assign(data.user_settings || {}, { afk_timeout: 600, allow_accessibility_detection: false, animate_emoji: true, contact_sync_enabled: false, convert_emoticons: true, custom_status: null, default_guilds_restricted: false, detect_platform_accounts: false, developer_mode: true, disable_games_tab: true, enable_tts_command: true, explicit_content_filter: 0, friend_source_flags: { all: false, mutual_friends: false, mutual_guilds: false }, gif_auto_play: true, guild_folders: [], guild_positions: [], inline_attachment_media: true, inline_embed_media: true, message_display_compact: false, native_phone_integration_enabled: false, render_embeds: true, render_reactions: true, restricted_guilds: [], show_current_game: false, stream_notifications_enabled: false }, data.user_settings || {}) data.user_guild_settings = data.user_guild_settings || { entries: [], version: 0, partial: false } data.friend_suggestion_count = data.friend_suggestion_count || 0 data.presences = data.presences || [] const buildInfo = electron.ipcRenderer.sendSync("LIGHTCORD_GET_BUILD_INFOS") electron.ipcRenderer.sendSync("LIGHTCORD_SET_USER_AGENT", `DiscordBot (https://github.com/lightcord/lightcord, v${buildInfo.version})`) }else{ electron.ipcRenderer.sendSync("LIGHTCORD_SET_USER_AGENT", UserAgent) logger.log(`Logged in as an user. Skipping user spoofing.`) } } let returnValue = _handleDispatch.call(this, ...arguments) if(event === "READY" && DiscordJS){ try{ DiscordJS.client.emit("self.ready", data) }catch(e){ console.error("[DiscordJS Error]", e) } } return returnValue } dispatcher.subscribe("LOGOUT", () => { isBot = false }) function cancelGatewayPrototype(methodName){ if(gatewayModule.default.prototype[methodName]){ const original = gatewayModule.default.prototype[methodName] gatewayModule.default.prototype[methodName] = function(){ if(!isBot)return original.call(this, ...arguments) } }else{ logger.warn(`Couldn't find ${methodName} on gateway.`) } } cancelGatewayPrototype("updateGuildSubscriptions") cancelGatewayPrototype("callConnect") cancelGatewayPrototype("lobbyConnect") cancelGatewayPrototype("lobbyDisconnect") cancelGatewayPrototype("lobbyVoiceStatesUpdate") cancelGatewayPrototype("streamCreate") cancelGatewayPrototype("streamWatch") cancelGatewayPrototype("streamPing") cancelGatewayPrototype("streamDelete") cancelGatewayPrototype("streamSetPaused") const _handleClose = gatewayModule.default.prototype._handleClose gatewayModule.default.prototype._handleClose = function(wasClean, code, reason){ let shouldSetIntents = !this.intents delete this.intents if(code === 4013 && shouldSetIntents){ this.intents = 32509 }else if(code === 4014){ delete this.intents console.log(`Invalid intents ? Removing them.`) } return _handleClose.call(this, ...arguments) } const _doIdentify = gatewayModule.default.prototype._doIdentify gatewayModule.default.prototype._doIdentify = function(){ let originalSend = this.send this.send = function(op, data, idkwhat){ if(op === 2){ if(this.intents){ data.intents = this.intents } } return originalSend.call(this, op, data, idkwhat) } const returnValue = _doIdentify.call(this, ...arguments) this.send = originalSend return returnValue } const requestGuildMembers = gatewayModule.default.prototype.requestGuildMembers gatewayModule.default.prototype.requestGuildMembers = function(){ // TODO: requestGuildMembers patch for bots. /*if(!isBot)*/return requestGuildMembers.call(this, ...arguments) console.log(arguments) } const hasUnreadModules = BDModules.get(e => e.default && e.default.hasUnread) hasUnreadModules.forEach((mod) => { const hasUnread = mod.default.hasUnread mod.default.hasUnread = function(){ if(isBot)return false return hasUnread.call(this, ...arguments) } for (const fName of ['ack']) { console.log(fName, mod[fName]) if(!mod || !mod[fName]){ logger.warn("Couldn't find prop "+fName+" in ackmodule1") continue } let original = mod[fName] mod[fName] = function(){ if(!isBot)return original.call(this, ...arguments) } } if(mod.getAckTimestamp){ let getAckTimestamp = mod.getAckTimestamp mod.getAckTimestamp = function(){ if(!isBot)return getAckTimestamp.call(this, ...arguments) return NaN } } }) const getTokenModule = ModuleLoader.get(e => e.default && e.default.getToken)[0] if(getTokenModule){ const getToken = getTokenModule.default.getToken getTokenModule.default.getToken = function(){ const token = getToken.call(this) if(!token)return token if(isBot)return token.startsWith("Bot ") ? token : "Bot " + token return token } }else{ logger.warn(new Error("Couldn't find module here")) } const relationshipsModule = BDModules.get(e => e.default && e.default.fetchRelationships)[0] if(relationshipsModule){ const fetchRelationships = relationshipsModule.default.fetchRelationships relationshipsModule.default.fetchRelationships = function(){ if(!isBot)return fetchRelationships.call(this, ...arguments) setImmediate(() => { dispatcher.dispatch({ type: constants.ActionTypes.LOAD_RELATIONSHIPS_SUCCESS, relationships: [] }) }) } }else{ logger.warn(new Error("Couldn't find module here")) } const consentModule = BDModules.get(e => e.fetchConsents)[0] if(consentModule){ const fetchConsents = consentModule.fetchConsents consentModule.fetchConsents = function(){ if(!isBot)return fetchConsents.call(this, ...arguments) setImmediate(()=>{ dispatcher.dispatch({ type: constants.ActionTypes.UPDATE_CONSENTS, consents: { personalization: false, usage_statistics: false } }) }) } const setConsents = consentModule.setConsents consentModule.setConsents = function(){ if(!isBot)return setConsents.call(this, ...arguments) return Promise.reject(new Error("Lightcord bot emulation cannot change this setting.")) } }else{ logger.warn(new Error("Couldn't find module here")) } const harvestModule = BDModules.get(e => e.getHarvestStatus)[0] if(harvestModule){ const getHarvestStatus = harvestModule.getHarvestStatus harvestModule.getHarvestStatus = function(){ if(!isBot)return getHarvestStatus.call(this, ...arguments) return Promise.resolve({ requestingHarvest: false, currentHarvestRequest: null }) } const requestHarvest = harvestModule.requestHarvest harvestModule.requestHarvest = function(){ if(!isBot)return requestHarvest.call(this, ...arguments) return Promise.reject() } }else{ logger.warn(new Error("Couldn't find module here")) } const harvestDisabledModule = BDModules.get(e => e.getSanitizedRestrictedGuilds)[0] if(harvestDisabledModule){ const harvestDisabled = harvestDisabledModule.harvestDisabled harvestDisabledModule.harvestDisabled = function(){ if(!isBot)return harvestDisabled.call(this, ...arguments) } }else{ logger.warn(new Error("Couldn't find module here")) } const settingModule = BDModules.get(e => e.default && e.default.updateRemoteSettings)[0] if(settingModule){ const updateRemoteSettings = settingModule.default.updateRemoteSettings settingModule.default.updateRemoteSettings = function(){ if(isBot)return Promise.resolve() return updateRemoteSettings.call(this, ...arguments) } }else{ logger.warn(new Error("Couldn't find module here")) } const oauth2Module = BDModules.get(e => e.default && Object.keys(e.default).length === 2 && e.default.fetch && e.default.delete)[0] if(oauth2Module){ const fetch = oauth2Module.default.fetch oauth2Module.default.fetch = function(){ if(!isBot)return fetch.call(this, ...arguments) setImmediate(()=>{ dispatcher.dispatch({ type: constants.ActionTypes.USER_AUTHORIZED_APPS_UPDATE, apps: [] }) }) } const deleteFunc = oauth2Module.delete oauth2Module.delete = function(){ if(!isBot)return deleteFunc.call(this, ...arguments) oauth2Module.fetch() } }else{ logger.warn(new Error("Couldn't find module here")) } const paymentModule = BDModules.get(e => e.fetchPaymentSources)[0] if(paymentModule){ const fetchPaymentSources = paymentModule.fetchPaymentSources paymentModule.fetchPaymentSources = function(){ if(!isBot)return fetchPaymentSources.call(this, ...arguments) setImmediate(() => { dispatcher.dispatch({ type: constants.ActionTypes.BILLING_PAYMENT_SOURCES_FETCH_START }) setImmediate(() => { dispatcher.dispatch({ type: constants.ActionTypes.BILLING_PAYMENT_SOURCES_FETCH_SUCCESS, paymentSources: [] }) }) }) } const fetchPayments = paymentModule.fetchPayments paymentModule.fetchPayments = function(){ if(!isBot)return fetchPayments.call(this, ...arguments) setImmediate(() => { dispatcher.dispatch({ type: constants.ActionTypes.BILLING_PAYMENTS_FETCH_START }) setImmediate(() => { dispatcher.dispatch({ type: constants.ActionTypes.BILLING_PAYMENTS_FETCH_SUCCESS, payments: [] }) }) }) } const fetchSubscriptions = paymentModule.fetchSubscriptions paymentModule.fetchSubscriptions = function(){ if(!isBot)return fetchSubscriptions.call(this, ...arguments) setImmediate(() => { dispatcher.dispatch({ type: constants.ActionTypes.BILLING_SUBSCRIPTION_FETCH_START }) setImmediate(() => { const subs = [ { "id": "123456789", "type": 1, "created_at": "2020-06-00T00:00:00.000000", "canceled_at": null, "current_period_start": "2020-06-00:00:00.000000", "current_period_end": "2100-06-00:00:00.000000", "status": 1, "payment_source_id": null, "payment_gateway": null, "payment_gateway_plan_id": "premium_year", "plan_id": "511651860671627264", "items": [ { "id": "123456789", "plan_id": "511651860671627264", "quantity": 1 } ], "currency": "usd" } ] resolve({ body: subs }) dispatcher.dispatch({ type: constants.ActionTypes.BILLING_SUBSCRIPTION_FETCH_SUCCESS, subscriptions: subs }) }) }) let resolve return new Promise((res) => (resolve = res)) } }else{ logger.warn(new Error("Couldn't find module here")) } const markServerReadShortcut = BDModules.get(e => e.MARK_SERVER_READ)[0] if(markServerReadShortcut){ let action = markServerReadShortcut.MARK_SERVER_READ.action markServerReadShortcut.MARK_SERVER_READ.action = function(){ if(isBot)return return action.call(this, ...arguments) } markServerReadShortcut.default && markServerReadShortcut.default.MARK_SERVER_READ && (markServerReadShortcut.default.MARK_SERVER_READ.action = markServerReadShortcut.MARK_SERVER_READ.action) }else{ logger.warn(new Error("Couldn't find module here")) } const applicationStatisticModule = BDModules.get(e => e.fetchActivityStatistics)[0] if(applicationStatisticModule){ const fetchActivityStatistics = applicationStatisticModule.fetchActivityStatistics applicationStatisticModule.fetchActivityStatistics = function(){ if(!isBot)return fetchActivityStatistics.call(this, ...arguments) setImmediate(() => { dispatcher.dispatch({ type: constants.ActionTypes.USER_ACTIVITY_STATISTICS_FETCH_SUCCESS, statistics: [] }) }) } }else{ logger.warn(new Error("Couldn't find module here")) } const subsInvoiceModule = BDModules.get(e => e.fetchSubscriptionInvoicePreview)[0] if(subsInvoiceModule){ function adapt(data){ return { id: data.id, invoiceItems: data.invoice_items.map(function(e) { return { id: e.id, subscriptionPlanId: e.subscription_plan_id, subscriptionPlanPrice: e.subscription_plan_price, amount: e.amount, quantity: e.quantity, discounts: e.discounts } }), total: data.total, subtotal: data.subtotal, currency: data.currency, tax: data.tax, taxInclusive: data.tax_inclusive, subscriptionPeriodStart: new Date(data.subscription_period_start), subscriptionPeriodEnd: new Date(data.subscription_period_end) } } const fetchSubscriptionInvoicePreview = subsInvoiceModule.fetchSubscriptionInvoicePreview subsInvoiceModule.fetchSubscriptionInvoicePreview = function(){ if(!isBot)return fetchSubscriptionInvoicePreview.call(this, ...arguments) const arg1 = arguments[0] if(!arg1 || !arg1.subscriptionId || arg1.subscriptionId === "123456789"){ return new Promise((resolve, reject) => { let data = adapt({ "id": "123456789", "invoice_items": [{ "id": "123456789", "amount": 0, "discounts": [], "subscription_plan_id": "511651860671627264", "subscription_plan_price": 0, "quantity": 1, "proration": false }], "total": 100, "subtotal": 100, "currency": "usd", "tax": 0, "tax_inclusive": true, "subscription_period_start": "2020-06-00:00:00.000000", "subscription_period_end": "2100-06-00:00:00.000000" }) console.log(data) resolve(data) }) } return fetchSubscriptionInvoicePreview.call(this, ...arguments) } const useSubscriptionInvoice = subsInvoiceModule.useSubscriptionInvoice subsInvoiceModule.useSubscriptionInvoice = function(){ if(!isBot)return useSubscriptionInvoice.call(this, ...arguments) return useSubscriptionInvoice.call(this, Object.assign(arguments[0], {preventFetch: true}), ...Array.from(arguments).slice(1)) } }else{ logger.warn(new Error("Couldn't find module here")) } const subsModule = BDModules.get(e => e.fetchUserPremiumGuildSubscriptionSlots)[0] if(subsModule){ const fetchUserPremiumGuildSubscriptionSlots = subsModule.fetchUserPremiumGuildSubscriptionSlots subsModule.fetchUserPremiumGuildSubscriptionSlots = function(){ if(!isBot)return fetchUserPremiumGuildSubscriptionSlots.call(this, ...arguments) setImmediate(()=>{ dispatcher.dispatch({ type: constants.ActionTypes.USER_PREMIUM_GUILD_SUBSCRIPTION_SLOTS_FETCH_SUCCESS, userPremiumGuildSubscriptionSlots: [] }) }) } const fetchPremiumSubscriptionCooldown = subsModule.fetchPremiumSubscriptionCooldown subsModule.fetchPremiumSubscriptionCooldown = function(){ if(!isBot)return fetchPremiumSubscriptionCooldown.call(this, ...arguments) return new Promise((resolve, reject) => { reject(new Error("Lightcord bot emulation cannot use Server Boosts")) }) } const fetchPremiumSubscriptions = subsModule.fetchPremiumSubscriptions subsModule.fetchPremiumSubscriptions = function(){ if(!isBot)return fetchPremiumSubscriptions.call(this, ...arguments) return new Promise((resolve, reject) => { reject(new Error("Lightcord bot emulation cannot use Server Boosts")) }) } }else{ logger.warn(new Error("Couldn't find module here")) } const entitlementsModule = BDModules.get(e => e.fetchUserEntitlementsForApplication)[0] if(entitlementsModule){ const fetchUserEntitlementsForApplication = entitlementsModule.fetchUserEntitlementsForApplication entitlementsModule.fetchUserEntitlementsForApplication = function(){ if(!isBot)return fetchUserEntitlementsForApplication.call(this, ...arguments) let resolve setImmediate(()=>{ dispatcher.dispatch({ type: constants.ActionTypes.ENTITLEMENT_FETCH_APPLICATION_START, applicationId: arguments[0] }) setImmediate(()=>{ resolve([]) dispatcher.dispatch({ type: constants.ActionTypes.ENTITLEMENT_FETCH_APPLICATION_SUCCESS, applicationId: arguments[0], entitlements: [] }) }) }) return new Promise((res) => (resolve = res)) } }else{ logger.warn(new Error("Couldn't find module here")) } const giftModule1 = BDModules.get(e => e.fetchGiftableEntitlements)[0] if(giftModule1){ const fetchGiftableEntitlements = giftModule1.fetchGiftableEntitlements giftModule1.fetchGiftableEntitlements = function(){ if(!isBot)return fetchGiftableEntitlements.call(this, ...arguments) dispatcher.dispatch({ type: constants.ActionTypes.ENTITLEMENTS_GIFTABLE_FETCH }) setImmediate(() => { dispatcher.dispatch({ type: constants.ActionTypes.ENTITLEMENTS_GIFTABLE_FETCH_SUCCESS, entitlements: [] }) }) } }else{ logger.warn(new Error("Couldn't find module here")) } const libraryModule = BDModules.get(e => e.fetchLibrary)[0] if(libraryModule){ const fetchLibrary = libraryModule.fetchLibrary libraryModule.fetchLibrary = function(){ if(!isBot)return fetchLibrary.call(this, ...arguments) setImmediate(() => { dispatcher.dispatch({ type: constants.ActionTypes.LIBRARY_FETCH_SUCCESS, libraryApplications: [] }) }) } }else{ logger.warn(new Error("Couldn't find module here")) } const hypesquadModule = BDModules.get(e => e.default && e.default.joinHypeSquadOnline)[0] if(hypesquadModule){ const joinHypeSquadOnline = hypesquadModule.default.joinHypeSquadOnline hypesquadModule.default.joinHypeSquadOnline = function(){ if(!isBot)return joinHypeSquadOnline.call(this, ...arguments) return Promise.reject(new Error("Lightcord bot emulation cannot join hypesquad.")) } }else{ logger.warn(new Error("Couldn't find module here")) } const HouseSelectionModal = BDModules.get(e => e.default && e.default.displayName === "HouseSelectionModal")[0] const RadioGroup = BDModules.get(e => e.default && e.default.displayName === "RadioGroup")[0] if(HouseSelectionModal && RadioGroup){ const defaultFunc = HouseSelectionModal.default HouseSelectionModal.default = function(){ let returnValue = new defaultFunc(...arguments) let hypesquadValue = getCurrentHypesquad() || "3" const renderHeaderCopy = returnValue.renderHeaderCopy returnValue.renderHeaderCopy = function(){ return "Hypesquad" } const renderPrimaryAction = returnValue.renderPrimaryAction returnValue.renderPrimaryAction = function(){ const renderValue = renderPrimaryAction.call(returnValue, ...arguments) if(!returnValue.state.hasSubmittedHouse){ renderValue.props.children = "Submit" }else{ renderValue.props.children = "Close" } if(!isBot){ renderValue.props.disabled = false }else{ renderValue.props.disabled = true } const onClick = renderValue.props.onClick renderValue.props.onClick = (ev) => { if(!returnValue.state.hasSubmittedHouse){ returnValue.handleSubmitButtonClick.call(returnValue, ...arguments) }else{ onClick.call(this, ...arguments) } } return renderValue } const getSelectedHouseID = returnValue.getSelectedHouseID returnValue.getSelectedHouseID = function(){ return "HOUSE_"+hypesquadValue } const renderContent = returnValue.renderContent returnValue.renderContent = function(){ if(!isBot){ if(this.state.hasSubmittedHouse)return this.renderQuizResult(); let component = React.createElement(class RadioContainer extends React.PureComponent { constructor(props){ super(props) } render(){ return React.createElement("div", { style: { margin: "0 auto", width: "75%" } }, React.createElement(RadioGroup.default, { disabled: false, value: hypesquadValue, options: [ { value: "1", name: "Bravery", desc: "The Bravery house" }, { value: "2", name: "Brillance", desc: "The Brillance house" }, { value: "3", name: "Balance", desc: "The Balance house" } ], onChange: (ev) => { hypesquadValue = ev.value this.forceUpdate() } })) } }, {}) return component }else{ let component = React.createElement(class BotWarning extends React.PureComponent { constructor(props){ super(props) } render(){ return React.createElement("div", { style: { margin: "0 auto", width: "75%" } }, [ React.createElement("h2", { style: { color: "var(--text-normal)" } }, "Bots cannot use Hypesquad.") ]) } }, {}) return component } } return returnValue } }else{ logger.warn(new Error("Couldn't find module here"), HouseSelectionModal, RadioGroup) } const mentionModule = BDModules.get(e => e.default && e.default.fetchRecentMentions)[0] if(mentionModule){ const fetchRecentMentions = mentionModule.default.fetchRecentMentions mentionModule.default.fetchRecentMentions = function(e, t, n, i, s){ if(!isBot)return fetchRecentMentions.call(this, ...arguments) if(!n)n = null dispatcher.dirtyDispatch({ type: constants.ActionTypes.LOAD_RECENT_MENTIONS, guildId: n }) setImmediate(() => { dispatcher.dispatch({ type: constants.ActionTypes.LOAD_RECENT_MENTIONS_SUCCESS, messages: [], isAfter: null != e, hasMoreAfter: false }) }) } }else{ logger.warn(new Error("Couldn't find module here")) } const templateModule = BDModules.get(e => e.default && e.default.loadTemplatesForGuild)[0] if(templateModule){ const loadTemplatesForGuild = templateModule.default.loadTemplatesForGuild templateModule.default.loadTemplatesForGuild = function(){ if(!isBot)return loadTemplatesForGuild.call(this, ...arguments) return Promise.reject(new Error("Lightcord bot emulation cannot use Guild Templates")) } }else{ logger.warn(new Error("Couldn't find module here")) } const searchModule = BDModules.get(e => e.default && e.default.prototype && e.default.prototype.retryLater)[0] if(searchModule){ const fetch = searchModule.default.prototype.fetch searchModule.default.prototype.fetch = function(e, t, n){ if(!isBot)return fetch.call(this, ...arguments) n(new Error("Lightcord bot emulation cannot search in guild.")) } }else{ logger.warn(new Error("Couldn't find module here")) } const inviteModule = BDModules.get(e => e.default && e.default.acceptInvite)[0] if(inviteModule){ const acceptInvite = inviteModule.default.acceptInvite inviteModule.default.acceptInvite = function(code, location, extraOptions){ if(!isBot)return acceptInvite.call(this, ...arguments) dispatcher.dispatch({ type: "INVITE_ACCEPT_FAILURE", code }) Utils.showToast("Lightcord Bot Emulation cannot join guilds.", {type: "error"}) return Promise.reject("Lightcord Bot Emulation cannot join guilds.") } }else{ logger.warn(new Error("Couldn't find module here")) } })().catch(console.error.bind(console, `%c[Error Bot shit]`, "color:red")) Utils.monkeyPatch(await ensureExported(e => e.default && e.default.displayName == "AuthBox"), "default", {after: (data) => { const children = Utils.getNestedProp(data.returnValue, "props.children.props.children.props.children") children.push(React.createElement(require("./tokenLogin").default, {})) }}) let [ authBoxExpanded ] = [ BDModules.get(e => e.authBoxExpanded && typeof e.authBoxExpanded === "string")[0] ] DOMTools.addStyle("tokenLoginPatch", `.${authBoxExpanded ? Utils.removeDa(authBoxExpanded.authBoxExpanded) : "authBoxExpanded-2jqaBe"} { width: 900px; }`) await ensureGuildClasses() BetterDiscord.init() events.emit("ready") } function installReactDevtools(){ let reactDevToolsPath = ""; const extensionId = "fmkadmapgofadopljbjfkapdkoienihi" if (process.platform === "win32") reactDevToolsPath = path.resolve(process.env.LOCALAPPDATA, "Google/Chrome/User Data"); else if (process.platform === "linux") reactDevToolsPath = path.resolve(process.env.HOME, ".config/google-chrome"); else if (process.platform === "darwin") reactDevToolsPath = path.resolve(process.env.HOME, "Library/Application Support/Google/Chrome"); else reactDevToolsPath = path.resolve(process.env.HOME, ".config/chromium"); reactDevToolsPath = path.join(reactDevToolsPath, "Default", "Extensions", extensionId) if (fs.existsSync(reactDevToolsPath)) { const versions = fs.readdirSync(reactDevToolsPath); reactDevToolsPath = path.resolve(reactDevToolsPath, versions[versions.length - 1]); } if(fs.existsSync(reactDevToolsPath)){ ipcRenderer.on("LIGHTCORD_DEVTOOLS_OPEN", devToolsListener) if (electron.ipcRenderer.sendSync("LIGHTCORD_GET_IS_DEVTOOLS_OPEN")) devToolsListener(); function devToolsListener(){ logger.log(`Installing React Devtools`) ipcRenderer.sendSync("LIGHTCORD_REMOVE_DEVTOOLS_EXTENSION", extensionId) const didInstall = ipcRenderer.sendSync("LIGHTCORD_ADD_DEVTOOLS_EXTENSION", reactDevToolsPath) if (didInstall) logger.log("React DevTools", "Successfully installed react devtools."); else logger.log("React DevTools", "Couldn't find react devtools."); } }else{ console.warn(new Error(`React Devtools could not be found.`)) } } require.extensions[".css"] = (m, filename) => { let content = fs.readFileSync(filename, "binary") let style = document.createElement("style") style.id = btoa(filename) style.innerHTML = content document.head.appendChild(style) m.exports = { id: style.id, remove(){ return style.remove() } } return m.exports } let zlib = require("zlib") let tmp = require("tmp") require.extensions[".jsbr"] = (m, filename) => { if(!zlib)zlib = require("zlib") if(!tmp)tmp = require("tmp") let tmpFile = tmp.fileSync() fs.writeFileSync(tmpFile.name+".js", zlib.brotliDecompressSync(fs.readFileSync(filename))) return require.extensions[".js"](m, tmpFile.name+".js") } require.extensions[".txt"] = (m, filename) => { m.exports = fs.readFileSync(filename, "utf8") return m.exports } const LightcordBDFolder = path.join(electron.ipcRenderer.sendSync("LIGHTCORD_GET_PATH", "appData"), "Lightcord_BD") const BetterDiscordConfig = window.BetterDiscordConfig = { "branch": "lightcord", dataPath: LightcordBDFolder+"/", os: process.platform, latestVersion: "0.3.5", version: "0.3.5" } function ensureGuildClasses(){ return new Promise((resolve) => { let classs = getGuildClasses() if(classs && classs.wrapper)return resolve() let intergay = setInterval(() => { classs = getGuildClasses() if(classs && classs.wrapper){ clearInterval(intergay) resolve() return } }, 200); }) } var ensureExported = global.ensureExported = function ensureExported(filter, maxTime = 500){ let tried = 0 return new Promise((resolve, reject) => { let mod = ModuleLoader.get(filter)[0] if(mod)return resolve(mod) tried++ let interval = setInterval(() => { if(tried > maxTime){ clearInterval(interval) reject(new Error("Could not find the module with the given filter.")) return } mod = ModuleLoader.get(filter)[0] if(mod){ clearInterval(interval) resolve(mod) return } tried++ }, 100); }) } let Notifications = require("./patchNotifications") const { ipcRenderer } = require("electron") let useDefault = electron.ipcRenderer.sendSync("LIGHTCORD_GET_SETTINGS")["DEFAULT_NOTIFICATIONS"] if(typeof useDefault !== "boolean"){ useDefault = true } Notifications.useShim(!useDefault) function getGuildClasses() { const guildsWrapper = ModuleLoader.get(e => e.wrapper && e.unreadMentionsBar)[0]; const guilds = ModuleLoader.get(e => e.guildsError && e.selected)[0] const pill = ModuleLoader.get(e => e.blobContainer)[0] return Object.assign({}, guildsWrapper, guilds, pill); } const originalResolve = path.resolve const originalJoin = path.join const BetterDiscordFolder = function() { if (process.env.injDir) return path.resolve(process.env.injDir); switch (process.platform) { case "win32": return path.resolve(process.env.appdata, "BetterDiscord/"); case "darwin": return path.resolve(process.env.HOME, "Library/Preferences/", "BetterDiscord/"); default: return path.resolve(process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : process.env.HOME + "/.config", "BetterDiscord/"); } }() path.resolve = (...args) => { // Patching BetterDiscord folder by Lightcord's BetterDiscord folder let resp = originalResolve.call(path, ...args) if(resp.startsWith(BetterDiscordFolder))resp = resp.replace(BetterDiscordFolder, LightcordBDFolder) return resp } path.join = (...args) => { // Patching BetterDiscord folder by Lightcord's BetterDiscord folder let resp = originalJoin.call(path, ...args) if(resp.startsWith(BetterDiscordFolder))resp = resp.replace(BetterDiscordFolder, LightcordBDFolder) return resp } path.originalResolve = originalResolve let blacklist function isBlacklisted(id){ if(!blacklist)blacklist = require("./blacklist.txt").split(/[\n\r]+/g).map((line, index, lines) => { let id = "" let comment = "" line.split("#").forEach((idOrComment, index, array) => { idOrComment = idOrComment.trim() if(index === 0)id = idOrComment else if(index === 1)comment = idOrComment }) return { id, comment } }) if(blacklist.find(e => e.id === id))return true return false } const formatLogger = new Logger("RequireFormat") function formatMinified(path){ let result = path.replace("{min}", isPackaged ? ".min": "") return result } window.ohgodohfuck = function(){ let style=document.createElement("style");style.innerHTML=`html:after{content:"";position:absolute;top:0;left:0 ;width:100vw;height:100vh;background-image:url("https://media.giphy.com/media/l378vg4Pm9LGnmD6M/giphy.gif");background-size:cover;background-position:center;background-color:transparent !important;opacity:0.9;mix-blend-mode:hue;z-index:999999999999;pointer-events:none}@keyframes ohgodohfuck{from{transform:rotateZ(0deg)}to{transform:rotateZ(360deg)}}#app-mount{animation:ohgodohfuck 5s infinite alternate}`;document.body.append(style);setTimeout(()=>document.body.removeChild(style),5000); }