Fix line endings

This commit is contained in:
hormelcookies 2020-09-05 13:50:45 -07:00
parent d1b6592a3d
commit 3daf362a4e
122 changed files with 18437 additions and 18437 deletions

22
.github/FUNDING.yml vendored
View File

@ -1,12 +1,12 @@
# These are supported funding model platforms # These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username otechie: # Replace with a single Otechie username
custom: ['https://paypal.me/jenwina'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] custom: ['https://paypal.me/jenwina'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

File diff suppressed because it is too large Load Diff

View File

@ -1,37 +1,37 @@
const CleanCSS = require("clean-css") const CleanCSS = require("clean-css")
const fs = require("fs") const fs = require("fs")
const path = require("path") const path = require("path")
function minify(){ function minify(){
let start = Date.now() let start = Date.now()
console.log(`\x1b[33mMinifying...\x1b[0m`) console.log(`\x1b[33mMinifying...\x1b[0m`)
const css = fs.readFileSync(path.join(__dirname, "./src/styles/index.css"), "utf-8") const css = fs.readFileSync(path.join(__dirname, "./src/styles/index.css"), "utf-8")
fs.writeFileSync(path.join(__dirname, "./dist/style.css"), css) fs.writeFileSync(path.join(__dirname, "./dist/style.css"), css)
const output = new CleanCSS({ const output = new CleanCSS({
sourceMap: true sourceMap: true
}).minify(css) }).minify(css)
if(output.errors.length > 0){ if(output.errors.length > 0){
console.error("\x1b[31m"+output.errors.join("\n")+"\x1b[0m") console.error("\x1b[31m"+output.errors.join("\n")+"\x1b[0m")
} }
if(output.warnings.length > 0){ if(output.warnings.length > 0){
console.warn("\x1b[33m"+output.warnings.join("\n")+"\x1b[0m") console.warn("\x1b[33m"+output.warnings.join("\n")+"\x1b[0m")
} }
fs.writeFileSync(path.join(__dirname, "./dist/style.min.css"), output.styles+"\n/*# sourceMappingURL=style.min.css.map */") fs.writeFileSync(path.join(__dirname, "./dist/style.min.css"), output.styles+"\n/*# sourceMappingURL=style.min.css.map */")
fs.writeFileSync(path.join(__dirname, "./dist/style.min.css.map"), output.sourceMap) fs.writeFileSync(path.join(__dirname, "./dist/style.min.css.map"), output.sourceMap)
console.log(`\x1b[32mMinified in ${(Date.now() - start)}ms. Minified by ${Math.floor(output.stats.efficiency*100)}%\x1b[0m`) console.log(`\x1b[32mMinified in ${(Date.now() - start)}ms. Minified by ${Math.floor(output.stats.efficiency*100)}%\x1b[0m`)
} }
module.exports.minify = minify module.exports.minify = minify
if(require.main === module){ if(require.main === module){
if(process.argv.includes("--watch")){ if(process.argv.includes("--watch")){
console.log("\x1b[32mStarting watch mode.\x1b[0m") console.log("\x1b[32mStarting watch mode.\x1b[0m")
minify() minify()
console.log() console.log()
fs.watchFile(path.join(__dirname, "./src/styles/index.css"), () => { fs.watchFile(path.join(__dirname, "./src/styles/index.css"), () => {
minify() minify()
console.log() console.log()
}) })
}else{ }else{
minify() minify()
} }
} }

View File

@ -1,37 +1,37 @@
{ {
"name": "bandagedbd", "name": "bandagedbd",
"version": "0.3.5", "version": "0.3.5",
"description": "Enhances Discord adding functionality and themes.", "description": "Enhances Discord adding functionality and themes.",
"main": "src/index.js", "main": "src/index.js",
"scripts": { "scripts": {
"build": "webpack --progress --colors", "build": "webpack --progress --colors",
"watch": "webpack --progress --colors --watch", "watch": "webpack --progress --colors --watch",
"build-prod": "webpack --progress --colors --config prod.config.js", "build-prod": "webpack --progress --colors --config prod.config.js",
"watch-prod": "webpack --progress --colors --watch --config prod.config.js", "watch-prod": "webpack --progress --colors --watch --config prod.config.js",
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"minify-css": "node minify-css.js", "minify-css": "node minify-css.js",
"watch-css": "node minify-css.js --watch", "watch-css": "node minify-css.js --watch",
"deploy": "npm run build-prod && npm run minify-css" "deploy": "npm run build-prod && npm run minify-css"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/rauenzi/BetterDiscordApp.git" "url": "git+https://github.com/rauenzi/BetterDiscordApp.git"
}, },
"author": "rauenzi", "author": "rauenzi",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/rauenzi/BetterDiscordApp/issues" "url": "https://github.com/rauenzi/BetterDiscordApp/issues"
}, },
"homepage": "https://github.com/rauenzi/BetterDiscordApp#readme", "homepage": "https://github.com/rauenzi/BetterDiscordApp#readme",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.10.5", "@babel/core": "^7.10.5",
"@babel/preset-env": "^7.10.4", "@babel/preset-env": "^7.10.4",
"@babel/preset-react": "^7.10.4", "@babel/preset-react": "^7.10.4",
"@babel/register": "^7.10.5", "@babel/register": "^7.10.5",
"babel-loader": "^8.1.0", "babel-loader": "^8.1.0",
"circular-dependency-plugin": "^5.2.0", "circular-dependency-plugin": "^5.2.0",
"clean-css": "^4.2.3", "clean-css": "^4.2.3",
"webpack": "^4.44.1", "webpack": "^4.44.1",
"webpack-cli": "^3.3.12" "webpack-cli": "^3.3.12"
} }
} }

View File

@ -1,7 +1,7 @@
const config = require("./webpack.config.js") const config = require("./webpack.config.js")
config.mode = "production" config.mode = "production"
config.devtool = "source-map" config.devtool = "source-map"
config.output.filename = "index.min.js" config.output.filename = "index.min.js"
module.exports = config module.exports = config

View File

@ -1,143 +1,143 @@
export const minimumDiscordVersion = "0.0.307"; export const minimumDiscordVersion = "0.0.307";
export const currentDiscordVersion = (window.DiscordNative && window.DiscordNative.remoteApp && window.DiscordNative.remoteApp.getVersion && window.DiscordNative.remoteApp.getVersion()) || "0.0.307"; export const currentDiscordVersion = (window.DiscordNative && window.DiscordNative.remoteApp && window.DiscordNative.remoteApp.getVersion && window.DiscordNative.remoteApp.getVersion()) || "0.0.307";
export const minSupportedVersion = "0.3.0"; export const minSupportedVersion = "0.3.0";
export const bbdVersion = "0.3.5"; export const bbdVersion = "0.3.5";
export const bbdChangelog = { export const bbdChangelog = {
description: "Big things are coming.", description: "Big things are coming.",
changes: [ changes: [
{ {
title: "Bug Fixes", title: "Bug Fixes",
type: "fixed", type: "fixed",
items: [ items: [
"Some fixes related to showing modals in the `BdApi`.", "Some fixes related to showing modals in the `BdApi`.",
"Fixed the open folder buttons for plugins and themes" "Fixed the open folder buttons for plugins and themes"
] ]
} }
] ]
}; };
export const settings = { export const settings = {
"Custom css live update": {id: "bda-css-0", info: "", implemented: true, hidden: true, cat: "core"}, "Custom css live update": {id: "bda-css-0", info: "", implemented: true, hidden: true, cat: "core"},
"Custom css auto udpate": {id: "bda-css-1", info: "", implemented: true, hidden: true, cat: "core"}, "Custom css auto udpate": {id: "bda-css-1", info: "", implemented: true, hidden: true, cat: "core"},
"BetterDiscord Blue": {id: "bda-gs-b", info: "Replace Discord blue with BD Blue", implemented: false, hidden: false, cat: "core"}, "BetterDiscord Blue": {id: "bda-gs-b", info: "Replace Discord blue with BD Blue", implemented: false, hidden: false, cat: "core"},
/* Core */ /* Core */
/* ====== */ /* ====== */
"Public Servers": {id: "bda-gs-1", info: "Display public servers button", implemented: true, hidden: false, cat: "core", category: "modules"}, "Public Servers": {id: "bda-gs-1", info: "Display public servers button", implemented: true, hidden: false, cat: "core", category: "modules"},
"Minimal Mode": {id: "bda-gs-2", info: "Hide elements and reduce the size of elements.", implemented: true, hidden: false, cat: "core", category: "modules"}, "Minimal Mode": {id: "bda-gs-2", info: "Hide elements and reduce the size of elements.", implemented: true, hidden: false, cat: "core", category: "modules"},
"Voice Mode": {id: "bda-gs-4", info: "Only show voice chat", implemented: true, hidden: false, cat: "core", category: "modules"}, "Voice Mode": {id: "bda-gs-4", info: "Only show voice chat", implemented: true, hidden: false, cat: "core", category: "modules"},
"Hide Channels": {id: "bda-gs-3", info: "Hide channels in minimal mode", implemented: true, hidden: false, cat: "core", category: "modules"}, "Hide Channels": {id: "bda-gs-3", info: "Hide channels in minimal mode", implemented: true, hidden: false, cat: "core", category: "modules"},
"Dark Mode": {id: "bda-gs-5", info: "Make certain elements dark by default(wip)", implemented: true, hidden: false, cat: "core", category: "modules"}, "Dark Mode": {id: "bda-gs-5", info: "Make certain elements dark by default(wip)", implemented: true, hidden: false, cat: "core", category: "modules"},
"Voice Disconnect": {id: "bda-dc-0", info: "Disconnect from voice server when closing Discord", implemented: true, hidden: false, cat: "core", category: "modules"}, "Voice Disconnect": {id: "bda-dc-0", info: "Disconnect from voice server when closing Discord", implemented: true, hidden: false, cat: "core", category: "modules"},
"24 Hour Timestamps": {id: "bda-gs-6", info: "Replace 12hr timestamps with proper ones", implemented: true, hidden: false, cat: "core", category: "modules"}, "24 Hour Timestamps": {id: "bda-gs-6", info: "Replace 12hr timestamps with proper ones", implemented: true, hidden: false, cat: "core", category: "modules"},
"Colored Text": {id: "bda-gs-7", info: "Make text color the same as role color", implemented: true, hidden: false, cat: "core", category: "modules"}, "Colored Text": {id: "bda-gs-7", info: "Make text color the same as role color", implemented: true, hidden: false, cat: "core", category: "modules"},
"Normalize Classes": {id: "fork-ps-4", info: "Adds stable classes to elements to help themes. (e.g. adds .da-channels to .channels-Ie2l6A)", implemented: true, hidden: false, cat: "core", category: "modules"}, "Normalize Classes": {id: "fork-ps-4", info: "Adds stable classes to elements to help themes. (e.g. adds .da-channels to .channels-Ie2l6A)", implemented: true, hidden: false, cat: "core", category: "modules"},
/* Content */ /* Content */
"Content Error Modal": {id: "fork-ps-1", info: "Shows a modal with plugin/theme errors", implemented: true, hidden: false, cat: "core", category: "content manager"}, "Content Error Modal": {id: "fork-ps-1", info: "Shows a modal with plugin/theme errors", implemented: true, hidden: false, cat: "core", category: "content manager"},
"Scan Plugins": {id: "fork-ps-6", info: "Scan plugins for any threat that can be hidden inside.", implemented: true, hidden: false, cat: "core", category: "content manager"}, "Scan Plugins": {id: "fork-ps-6", info: "Scan plugins for any threat that can be hidden inside.", implemented: true, hidden: false, cat: "core", category: "content manager"},
"Show Toasts": {id: "fork-ps-2", info: "Shows a small notification for important information", implemented: true, hidden: false, cat: "core", category: "content manager"}, "Show Toasts": {id: "fork-ps-2", info: "Shows a small notification for important information", implemented: true, hidden: false, cat: "core", category: "content manager"},
"Scroll To Settings": {id: "fork-ps-3", info: "Auto-scrolls to a plugin's settings when the button is clicked (only if out of view)", implemented: true, hidden: false, cat: "core", category: "content manager"}, "Scroll To Settings": {id: "fork-ps-3", info: "Auto-scrolls to a plugin's settings when the button is clicked (only if out of view)", implemented: true, hidden: false, cat: "core", category: "content manager"},
"Automatic Loading": {id: "fork-ps-5", info: "Automatically loads, reloads, and unloads plugins and themes", implemented: true, hidden: false, cat: "core", category: "content manager"}, "Automatic Loading": {id: "fork-ps-5", info: "Automatically loads, reloads, and unloads plugins and themes", implemented: true, hidden: false, cat: "core", category: "content manager"},
"Enable Edit Button": {id: "fork-ps-7", info: "Enable an Edit Button on the plugin and theme panel.", implemented: true, hidden: false, cat: "core", category: "content manager"}, "Enable Edit Button": {id: "fork-ps-7", info: "Enable an Edit Button on the plugin and theme panel.", implemented: true, hidden: false, cat: "core", category: "content manager"},
"Themes in Popout Window": {id: "lightcord-9", info: "Enable themes in Popout Window. (For example, when detaching screenshare.)", implemented: true, hidden: false, cat: "core", category: "content manager", experimental: true}, "Themes in Popout Window": {id: "lightcord-9", info: "Enable themes in Popout Window. (For example, when detaching screenshare.)", implemented: true, hidden: false, cat: "core", category: "content manager", experimental: true},
/* Developer */ /* Developer */
"Developer Mode": {id: "bda-gs-8", info: "Developer Mode Toggle", implemented: true, hidden: false, cat: "core", category: "developer settings"}, "Developer Mode": {id: "bda-gs-8", info: "Developer Mode Toggle", implemented: true, hidden: false, cat: "core", category: "developer settings"},
"Copy Selector": {id: "fork-dm-1", info: "Adds a \"Copy Selector\" option to context menus when developer mode is active", implemented: true, hidden: false, cat: "core", category: "developer settings"}, "Copy Selector": {id: "fork-dm-1", info: "Adds a \"Copy Selector\" option to context menus when developer mode is active", implemented: true, hidden: false, cat: "core", category: "developer settings"},
/** Lightcord */ /** Lightcord */
"Disable BetterDiscord": {id: "bd-disable", info: "Disable Betterdiscord (plugins, themes, etc) (Not implemented).", implemented: false, hidden: false, cat: "lightcord", category: "Lightcord"}, "Disable BetterDiscord": {id: "bd-disable", info: "Disable Betterdiscord (plugins, themes, etc) (Not implemented).", implemented: false, hidden: false, cat: "lightcord", category: "Lightcord"},
"Blur Personal Information": {id: "lightcord-6", info: "Blur sensitive informations like email, payment infos and more.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, "Blur Personal Information": {id: "lightcord-6", info: "Blur sensitive informations like email, payment infos and more.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},
"Calling Ring Beat": {id: "lightcord-2", info: "Enable Discord's special calling beat.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, "Calling Ring Beat": {id: "lightcord-2", info: "Enable Discord's special calling beat.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},
"Developer Options": {id: "lightcord-1", info: "Enable Discord's & Lightcord's Internal Developer Options. This allow the \"Experiments\" tab, the \"Developer Options\" tab and the \"Lightcord Api\" section.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, "Developer Options": {id: "lightcord-1", info: "Enable Discord's & Lightcord's Internal Developer Options. This allow the \"Experiments\" tab, the \"Developer Options\" tab and the \"Lightcord Api\" section.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},
"Ad Block": {id: "lightcord-4", info: "Block any BOT that dms you with an invite link. Even in an embed.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, "Ad Block": {id: "lightcord-4", info: "Block any BOT that dms you with an invite link. Even in an embed.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},
"Enable Lightcord Servers": {id: "lightcord-5", info: "Enable Lightcord's servers. Disabling this will disable custom badges.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, "Enable Lightcord Servers": {id: "lightcord-5", info: "Enable Lightcord's servers. Disabling this will disable custom badges.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},
"Disable typing": {id: "lightcord-7", info: "Don't let other see you're typing.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, "Disable typing": {id: "lightcord-7", info: "Don't let other see you're typing.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},
"Lightcord Tabs": {id: "lightcord-8", info: "Allows you to launch multiple instances of Lightcord in the same window.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord", experimental: true}, "Lightcord Tabs": {id: "lightcord-8", info: "Allows you to launch multiple instances of Lightcord in the same window.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord", experimental: true},
"Use Notification Shim": {id: "lightcord-10", info: "Basically don't use Windows's Notifications.", implemented: true, hidden: process.platform !== "win32", cat: "lightcord", category: "Lightcord", experimental: false}, "Use Notification Shim": {id: "lightcord-10", info: "Basically don't use Windows's Notifications.", implemented: true, hidden: process.platform !== "win32", cat: "lightcord", category: "Lightcord", experimental: false},
/** Lightcord Window */ /** Lightcord Window */
"Always-on-Top": {id: "lightcord-3", info: "Enable the window's Always-on-Top mode, where Lightcord stays on top of other applications.", implemented: true, hidden: false, cat: "lightcord", category: "Window"}, "Always-on-Top": {id: "lightcord-3", info: "Enable the window's Always-on-Top mode, where Lightcord stays on top of other applications.", implemented: true, hidden: false, cat: "lightcord", category: "Window"},
"No Window Bounds": {id: "no_window_bound", info: "Disable Window Bounds. Can be useful if you use a window manager.", implemented: true, hidden: process.platform !== "linux", cat: "lightcord", category: "Window"}, "No Window Bounds": {id: "no_window_bound", info: "Disable Window Bounds. Can be useful if you use a window manager.", implemented: true, hidden: process.platform !== "linux", cat: "lightcord", category: "Window"},
"Enable Glasstron": {id: "enable_glasstron", info: "Enable Glasstron. If you're not using transparent themes, this can reduce lag.", implemented: true, hidden: false, cat: "lightcord", category: "Window"}, "Enable Glasstron": {id: "enable_glasstron", info: "Enable Glasstron. If you're not using transparent themes, this can reduce lag.", implemented: true, hidden: false, cat: "lightcord", category: "Window"},
/** RichPresence */ /** RichPresence */
"Enable": {id: "lightcord-presence-1", info: "Enable RichPresence below.", implemented: true, hidden: false, cat: "status"} "Enable": {id: "lightcord-presence-1", info: "Enable RichPresence below.", implemented: true, hidden: false, cat: "status"}
}; };
export const defaultCookie = { export const defaultCookie = {
"bda-gs-1": true, "bda-gs-1": true,
"bda-gs-2": false, "bda-gs-2": false,
"bda-gs-3": false, "bda-gs-3": false,
"bda-gs-4": false, "bda-gs-4": false,
"bda-gs-5": true, "bda-gs-5": true,
"bda-gs-6": false, "bda-gs-6": false,
"bda-gs-7": false, "bda-gs-7": false,
"bda-gs-8": false, "bda-gs-8": false,
"bda-es-0": true, "bda-es-0": true,
"bda-es-1": true, "bda-es-1": true,
"bda-es-2": true, "bda-es-2": true,
"bda-es-4": false, "bda-es-4": false,
"bda-es-6": true, "bda-es-6": true,
"bda-es-7": true, "bda-es-7": true,
"bda-gs-b": false, "bda-gs-b": false,
"bda-es-8": true, "bda-es-8": true,
"bda-dc-0": false, "bda-dc-0": false,
"bda-css-0": false, "bda-css-0": false,
"bda-css-1": false, "bda-css-1": false,
"bda-es-9": true, "bda-es-9": true,
"fork-dm-1": false, "fork-dm-1": false,
"fork-ps-1": true, "fork-ps-1": true,
"fork-ps-2": true, "fork-ps-2": true,
"fork-ps-3": true, "fork-ps-3": true,
"fork-ps-4": true, "fork-ps-4": true,
"fork-ps-5": true, "fork-ps-5": true,
"fork-ps-6": true, "fork-ps-6": true,
"fork-ps-7": false, "fork-ps-7": false,
"fork-es-2": false, "fork-es-2": false,
"fork-es-3": true, "fork-es-3": true,
"fork-wp-1": false, "fork-wp-1": false,
"fork-wp-2": false, "fork-wp-2": false,
"fork-beta": true, "fork-beta": true,
"lightcord-1": false, "lightcord-1": false,
"lightcord-2": true, "lightcord-2": true,
"lightcord-presence-1": false, "lightcord-presence-1": false,
"lightcord-3": false, "lightcord-3": false,
"lightcord-4": false, "lightcord-4": false,
"lightcord-5": true, "lightcord-5": true,
"lightcord-6": true, "lightcord-6": true,
"lightcord-7": false, "lightcord-7": false,
"lightcord-8": false, "lightcord-8": false,
"lightcord-9": false, "lightcord-9": false,
"lightcord-10": false, "lightcord-10": false,
"no_window_bound": false, "no_window_bound": false,
}; };
export const settingsCookie = {}; export const settingsCookie = {};
export const settingsRPC = {}; export const settingsRPC = {};
export const defaultRPC = { export const defaultRPC = {
"name": "Lightcord", "name": "Lightcord",
"application_id": "711416957718757418", "application_id": "711416957718757418",
"state": "Lightcord Client", "state": "Lightcord Client",
"details": "Browsing Discord", "details": "Browsing Discord",
"timestamps.start": Date.now(), "timestamps.start": Date.now(),
"assets.small": null, "assets.small": null,
"assets.large": "712323821037682791" "assets.large": "712323821037682791"
} }
export const lightcordSettings = {}; export const lightcordSettings = {};
export const defaultLightcordSettings = {}; export const defaultLightcordSettings = {};
export const bdpluginErrors = []; export const bdpluginErrors = [];
export const bdthemeErrors = []; // define for backwards compatibility export const bdthemeErrors = []; // define for backwards compatibility
export const bdConfig = Object.create(BetterDiscordConfig); export const bdConfig = Object.create(BetterDiscordConfig);
export const bdthemes = {}; export const bdthemes = {};
export const bdplugins = {}; export const bdplugins = {};
export const pluginCookie = {}; export const pluginCookie = {};
export const themeCookie = {}; export const themeCookie = {};

View File

@ -1,59 +1,59 @@
import localStorageFix from "./localStorageFix"; import localStorageFix from "./localStorageFix";
import loadingIcon from "./loadingIcon"; import loadingIcon from "./loadingIcon";
localStorageFix(); localStorageFix();
loadingIcon(); loadingIcon();
const deprecateGlobal = (key, value) => { const deprecateGlobal = (key, value) => {
Object.defineProperty(window, key, { Object.defineProperty(window, key, {
get() { get() {
Utils.warn("Deprecated Global", `"${key}" will be removed in future versions. Please only use BdApi.`); Utils.warn("Deprecated Global", `"${key}" will be removed in future versions. Please only use BdApi.`);
return value; return value;
} }
}); });
}; };
import * as Globals from "./0globals"; import * as Globals from "./0globals";
const globalKeys = Object.keys(Globals); const globalKeys = Object.keys(Globals);
for (const key of globalKeys) deprecateGlobal(key, Globals[key]); for (const key of globalKeys) deprecateGlobal(key, Globals[key]);
import BdApi from "./modules/bdApi"; import BdApi from "./modules/bdApi";
import BDV2 from "./modules/v2"; import BDV2 from "./modules/v2";
import pluginModule from "./modules/pluginModule"; import pluginModule from "./modules/pluginModule";
import themeModule from "./modules/themeModule"; import themeModule from "./modules/themeModule";
import Utils from "./modules/utils"; import Utils from "./modules/utils";
import BDEvents from "./modules/bdEvents"; import BDEvents from "./modules/bdEvents";
import settingsPanel from "./modules/settingsPanel"; import settingsPanel from "./modules/settingsPanel";
import DataStore from "./modules/dataStore"; import DataStore from "./modules/dataStore";
import ContentManager from "./modules/contentManager"; import ContentManager from "./modules/contentManager";
import ClassNormalizer from "./modules/classNormalizer"; import ClassNormalizer from "./modules/classNormalizer";
deprecateGlobal("BDV2", BDV2); deprecateGlobal("BDV2", BDV2);
deprecateGlobal("pluginModule", pluginModule); deprecateGlobal("pluginModule", pluginModule);
deprecateGlobal("themeModule", themeModule); deprecateGlobal("themeModule", themeModule);
deprecateGlobal("Utils", Utils); deprecateGlobal("Utils", Utils);
deprecateGlobal("BDEvents", BDEvents); deprecateGlobal("BDEvents", BDEvents);
deprecateGlobal("settingsPanel", settingsPanel); deprecateGlobal("settingsPanel", settingsPanel);
deprecateGlobal("DataStore", DataStore); deprecateGlobal("DataStore", DataStore);
deprecateGlobal("ContentManager", ContentManager); deprecateGlobal("ContentManager", ContentManager);
deprecateGlobal("ClassNormalizer", ClassNormalizer); deprecateGlobal("ClassNormalizer", ClassNormalizer);
window.BdApi = BdApi; window.BdApi = BdApi;
import Core from "./modules/core"; import Core from "./modules/core";
deprecateGlobal("mainCore", Core); deprecateGlobal("mainCore", Core);
// TODO: Change Init mode and stop using CoreWrapper. // TODO: Change Init mode and stop using CoreWrapper.
export default class CoreWrapper { export default class CoreWrapper {
constructor(bdConfig, methods) { constructor(bdConfig, methods) {
Core.setConfig(bdConfig); Core.setConfig(bdConfig);
Core.setMethods(methods); Core.setMethods(methods);
} }
init() { init() {
// deprecateGlobal("mainCore", this.mainCore); // deprecateGlobal("mainCore", this.mainCore);
Core.init(); Core.init();
} }
} }

View File

@ -1,83 +1,83 @@
import Utils from "./utils" import Utils from "./utils"
const dispatcher = window.Lightcord.DiscordModules.dispatcher const dispatcher = window.Lightcord.DiscordModules.dispatcher
const ChannelModule = BDModules.get(e => e.default && e.default.getChannel && e.default.hasChannel)[0].default const ChannelModule = BDModules.get(e => e.default && e.default.getChannel && e.default.hasChannel)[0].default
let relationShipModule = BDModules.get(e => e.default && e.default.addRelationship)[0] let relationShipModule = BDModules.get(e => e.default && e.default.addRelationship)[0]
let DMModule = BDModules.get(e => e.default && e.default.closePrivateChannel)[0] let DMModule = BDModules.get(e => e.default && e.default.closePrivateChannel)[0]
const blocked = {} const blocked = {}
let userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0] let userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0]
function getCurrentUser(){ function getCurrentUser(){
if(userModule)return userModule.default.getCurrentUser() if(userModule)return userModule.default.getCurrentUser()
return null return null
} }
export default new class AntiBotDM { export default new class AntiBotDM {
constructor(){ constructor(){
this.antiDM = this.antiDM.bind(this) this.antiDM = this.antiDM.bind(this)
this.enabled = false this.enabled = false
} }
enable(){ enable(){
if(this.enabled)return if(this.enabled)return
this.enabled = true this.enabled = true
dispatcher.subscribe("MESSAGE_CREATE", this.antiDM) dispatcher.subscribe("MESSAGE_CREATE", this.antiDM)
} }
disable(){ disable(){
if(!this.enabled)return if(!this.enabled)return
this.enabled = false this.enabled = false
dispatcher.unsubscribe("MESSAGE_CREATE", this.antiDM) dispatcher.unsubscribe("MESSAGE_CREATE", this.antiDM)
} }
antiDM(ev){ antiDM(ev){
if(!ev.message.author.bot)return if(!ev.message.author.bot)return
if(ev.message.author.id === getCurrentUser().id) if(ev.message.author.id === getCurrentUser().id)
if(ev.message.guild_id)return if(ev.message.guild_id)return
const channel = ChannelModule.getChannel(ev.message.channel_id) const channel = ChannelModule.getChannel(ev.message.channel_id)
if(!channel)return // might be broken if(!channel)return // might be broken
if(channel.type !== 1)return if(channel.type !== 1)return
if(blocked[ev.message.author.id])return // If the user unblock the bot, Don't block it again. if(blocked[ev.message.author.id])return // If the user unblock the bot, Don't block it again.
if(scanMessage(ev.message)){ if(scanMessage(ev.message)){
blocked[ev.message.author.id] = true blocked[ev.message.author.id] = true
Utils.showToast(`[AdBlock]: Blocked ${ev.message.author.username}#${ev.message.author.discriminator}`, { Utils.showToast(`[AdBlock]: Blocked ${ev.message.author.username}#${ev.message.author.discriminator}`, {
"type": "warning" "type": "warning"
}) })
if(!relationShipModule)relationShipModule = BDModules.get(e => e.default && e.default.addRelationship)[0] if(!relationShipModule)relationShipModule = BDModules.get(e => e.default && e.default.addRelationship)[0]
relationShipModule.default.addRelationship(ev.message.author.id, { relationShipModule.default.addRelationship(ev.message.author.id, {
location: "ContextMenu" location: "ContextMenu"
}, 2) }, 2)
if(!DMModule)DMModule = BDModules.get(e => e.default && e.default.closePrivateChannel)[0] if(!DMModule)DMModule = BDModules.get(e => e.default && e.default.closePrivateChannel)[0]
DMModule.default.closePrivateChannel(channel.id, false) DMModule.default.closePrivateChannel(channel.id, false)
} }
} }
} }
function scanMessage(message){ function scanMessage(message){
if(/(discord\.gg|discord\.com\/invite\/|discordapp\.com\/invite\/)/g.test(message.content))return true if(/(discord\.gg|discord\.com\/invite\/|discordapp\.com\/invite\/)/g.test(message.content))return true
if(EmbedsContains(message, "discord.gg/") || EmbedsContains(message, "discord.com/invite/") || EmbedsContains(message, "discordapp.com/invite/"))return true if(EmbedsContains(message, "discord.gg/") || EmbedsContains(message, "discord.com/invite/") || EmbedsContains(message, "discordapp.com/invite/"))return true
return false return false
} }
function EmbedsContains(message, search){ function EmbedsContains(message, search){
let embeds = message.embeds || [] let embeds = message.embeds || []
if(embeds.length === 0)return false if(embeds.length === 0)return false
return embeds.map(embed => { return embeds.map(embed => {
if(embed.type !== "rich")return false if(embed.type !== "rich")return false
if((embed.title || "").includes(search))return true if((embed.title || "").includes(search))return true
if((embed.description || "").includes(search))return true if((embed.description || "").includes(search))return true
if(((embed.footer || "") && embed.footer.text || "").includes(search))return true if(((embed.footer || "") && embed.footer.text || "").includes(search))return true
if((embed.fields || []).map(e => { if((embed.fields || []).map(e => {
return e.value.includes(search) || e.name.includes(search) return e.value.includes(search) || e.name.includes(search)
}).includes(true))return true }).includes(true))return true
return false return false
}).includes(true) }).includes(true)
} }

View File

@ -1,166 +1,166 @@
import Utils from "./utils"; import Utils from "./utils";
import v2 from "./v2"; import v2 from "./v2";
import { uuidv4 } from "./distant"; import { uuidv4 } from "./distant";
import { timingSafeEqual } from "crypto"; import { timingSafeEqual } from "crypto";
export default new class Layers { export default new class Layers {
constructor(){ constructor(){
this.layers = [] this.layers = []
window.Lightcord.BetterDiscord.Layers = this window.Lightcord.BetterDiscord.Layers = this
} }
get isInjected(){ get isInjected(){
return !!document.querySelector("div.Layers.injected") return !!document.querySelector("div.Layers.injected")
} }
inject(){ inject(){
if(this.isInjected)return if(this.isInjected)return
let [ let [
classNameLayers classNameLayers
] = [ ] = [
Utils.removeDa(v2.WebpackModules.find(e => e.layers && e.layer).layers) Utils.removeDa(v2.WebpackModules.find(e => e.layers && e.layer).layers)
] ]
const layers = document.querySelector("."+classNameLayers); const layers = document.querySelector("."+classNameLayers);
if (!layers) return false; if (!layers) return false;
const ReactInstance = Lightcord.Api.Utils.FindReact(layers, 1) const ReactInstance = Lightcord.Api.Utils.FindReact(layers, 1)
if(!ReactInstance)return layers if(!ReactInstance)return layers
this.ReactInstance = ReactInstance this.ReactInstance = ReactInstance
let render = ReactInstance.render let render = ReactInstance.render
ReactInstance.render = () => { ReactInstance.render = () => {
let returnValue = render.call(ReactInstance) let returnValue = render.call(ReactInstance)
let classList = returnValue.props.children[1].props.className.split(" ") let classList = returnValue.props.children[1].props.className.split(" ")
classList.push("Layers") classList.push("Layers")
classList.push("injected") classList.push("injected")
returnValue.props.children[1].props.className = classList.join(" ") returnValue.props.children[1].props.className = classList.join(" ")
this.layers.forEach(layer => { this.layers.forEach(layer => {
returnValue.props.children[1].props.children.push(layer[1]) returnValue.props.children[1].props.children.push(layer[1])
}) })
return returnValue return returnValue
} }
ReactInstance.forceUpdate() ReactInstance.forceUpdate()
} }
createLayer(children, props = {}){ createLayer(children, props = {}){
let id = uuidv4() let id = uuidv4()
let layer = null let layer = null
this.layers.push([id, <Layer {...props} getLayer={lay => { this.layers.push([id, <Layer {...props} getLayer={lay => {
layer = lay layer = lay
}} key={id} id={id} remove={() => { }} key={id} id={id} remove={() => {
let index = this.layers.findIndex(e => e[0] === id) let index = this.layers.findIndex(e => e[0] === id)
if(index === -1)return if(index === -1)return
this.layers.splice(index, 1) this.layers.splice(index, 1)
layer = null layer = null
if(this.ReactInstance)this.ReactInstance.forceUpdate() if(this.ReactInstance)this.ReactInstance.forceUpdate()
this.inject() this.inject()
}}>{children}</Layer>]) }}>{children}</Layer>])
if(this.ReactInstance)this.ReactInstance.forceUpdate() if(this.ReactInstance)this.ReactInstance.forceUpdate()
this.inject() this.inject()
return { return {
remove: () => { remove: () => {
if(!layer){ if(!layer){
let index = this.layers.findIndex(e => e[0] === id) let index = this.layers.findIndex(e => e[0] === id)
if(index === -1)return if(index === -1)return
this.layers.splice(index, 1) this.layers.splice(index, 1)
layer = null layer = null
if(this.ReactInstance)this.ReactInstance.forceUpdate() if(this.ReactInstance)this.ReactInstance.forceUpdate()
this.inject() this.inject()
}else{ }else{
layer.remove() layer.remove()
} }
} }
} }
} }
} }
let LayerModules let LayerModules
export class Layer extends React.Component { export class Layer extends React.Component {
constructor(){ constructor(){
super(...arguments) super(...arguments)
this.state = { this.state = {
animation: new this.modules[0].default.Value(0) animation: new this.modules[0].default.Value(0)
} }
this.props.getLayer(this) this.props.getLayer(this)
this.keydown = this.keydown.bind(this) this.keydown = this.keydown.bind(this)
} }
keydown(e){ keydown(e){
if (e.which === 27) { if (e.which === 27) {
this.remove() this.remove()
} }
} }
get modules(){ get modules(){
return LayerModules || (LayerModules = [ return LayerModules || (LayerModules = [
v2.WebpackModules.find(e => e.default && e.default.Value && e.default.div) v2.WebpackModules.find(e => e.default && e.default.Value && e.default.div)
]) ])
} }
componentWillUnmount(){ componentWillUnmount(){
window.removeEventListener("keydown", this.keydown) window.removeEventListener("keydown", this.keydown)
} }
componentDidMount(ev){ componentDidMount(ev){
window.addEventListener("keydown", this.keydown) window.addEventListener("keydown", this.keydown)
} }
remove(){ remove(){
this.props.remove() this.props.remove()
} }
componentWillEnter(ev){ componentWillEnter(ev){
this.modules[0].default.timing(this.state.animation, { this.modules[0].default.timing(this.state.animation, {
toValue: 1, toValue: 1,
duration: 100 duration: 100
}).start(ev) }).start(ev)
} }
componentWillLeave(ev){ componentWillLeave(ev){
this.modules[0].default.timing(this.state.animation, { this.modules[0].default.timing(this.state.animation, {
toValue: 0, toValue: 0,
duration: 100 duration: 100
}).start(ev) }).start(ev)
} }
render(){ render(){
let interpolation = this.state.animation.interpolate({ let interpolation = this.state.animation.interpolate({
inputRange: [0, 1], inputRange: [0, 1],
outputRange: [1.07, 1] outputRange: [1.07, 1]
}) })
let style = { let style = {
opacity: this.state.animation, opacity: this.state.animation,
transform: [{ transform: [{
scale: interpolation scale: interpolation
}] }]
} }
let Div = this.modules[0].default.div let Div = this.modules[0].default.div
return <Div className={[__SECRET_EMOTION__.css({ return <Div className={[__SECRET_EMOTION__.css({
backgroundColor:"var(--background-primary)", backgroundColor:"var(--background-primary)",
position: "absolute", position: "absolute",
top: 0, top: 0,
right: 0, right: 0,
bottom: 0, bottom: 0,
left: 0, left: 0,
display: "flex", display: "flex",
overflow: "hidden", overflow: "hidden",
WebkitBoxOrient: "vertical", WebkitBoxOrient: "vertical",
WebkitBoxDirection: "normal", WebkitBoxDirection: "normal",
msFlexDirection: "column", msFlexDirection: "column",
flexDirection: "column", flexDirection: "column",
zIndex: 101 zIndex: 101
}),process.platform==="win32"?__SECRET_EMOTION__.css({ }),process.platform==="win32"?__SECRET_EMOTION__.css({
top: "-22px", top: "-22px",
paddingTop: "22px" paddingTop: "22px"
}):null,...(this.props.className?this.props.className.split(" "):[])].filter(e=>e).join(" ") || null} style={style}> }):null,...(this.props.className?this.props.className.split(" "):[])].filter(e=>e).join(" ") || null} style={style}>
{this.props.children(() => { {this.props.children(() => {
this.remove() this.remove()
})} })}
</Div> </Div>
} }
} }
Layer.defaultProps = { Layer.defaultProps = {
id: null, id: null,
children: null children: null
} }

View File

@ -1,254 +1,254 @@
import {pluginCookie, themeCookie, bdplugins, bdthemes, settingsCookie, settings} from "../0globals"; import {pluginCookie, themeCookie, bdplugins, bdthemes, settingsCookie, settings} from "../0globals";
import mainCore from "./core"; import mainCore from "./core";
import Utils from "./utils"; import Utils from "./utils";
import BDV2 from "./v2"; import BDV2 from "./v2";
import DataStore from "./dataStore"; import DataStore from "./dataStore";
import pluginModule from "./pluginModule"; import pluginModule from "./pluginModule";
import themeModule from "./themeModule"; import themeModule from "./themeModule";
import settingsPanel from "./settingsPanel"; import settingsPanel from "./settingsPanel";
import DOM from "./domtools"; import DOM from "./domtools";
const BdApi = { const BdApi = {
get React() { return BDV2.React; }, get React() { return BDV2.React; },
get ReactDOM() { return BDV2.ReactDom; }, get ReactDOM() { return BDV2.ReactDom; },
get ReactComponent() {return BDV2.ReactComponent;}, get ReactComponent() {return BDV2.ReactComponent;},
get WindowConfigFile() {return Utils.WindowConfigFile;}, get WindowConfigFile() {return Utils.WindowConfigFile;},
get settings() {return settings;}, get settings() {return settings;},
get emotes() {return null}, // deprecated, deleted all emotes from betterdiscord. get emotes() {return null}, // deprecated, deleted all emotes from betterdiscord.
get screenWidth() { return Math.max(document.documentElement.clientWidth, window.innerWidth || 0); }, get screenWidth() { return Math.max(document.documentElement.clientWidth, window.innerWidth || 0); },
get screenHeight() { return Math.max(document.documentElement.clientHeight, window.innerHeight || 0); } get screenHeight() { return Math.max(document.documentElement.clientHeight, window.innerHeight || 0); }
}; };
BdApi.getAllWindowPreferences = function() { BdApi.getAllWindowPreferences = function() {
return Utils.getAllWindowPreferences(); return Utils.getAllWindowPreferences();
}; };
BdApi.getWindowPreference = function(key) { BdApi.getWindowPreference = function(key) {
return Utils.getWindowPreference(key); return Utils.getWindowPreference(key);
}; };
BdApi.setWindowPreference = function(key, value) { BdApi.setWindowPreference = function(key, value) {
return Utils.setWindowPreference(key, value); return Utils.setWindowPreference(key, value);
}; };
//Inject CSS to document head //Inject CSS to document head
//id = id of element //id = id of element
//css = custom css //css = custom css
BdApi.injectCSS = function (id, css) { BdApi.injectCSS = function (id, css) {
DOM.addStyle(DOM.escapeID(id), css); DOM.addStyle(DOM.escapeID(id), css);
}; };
//Clear css/remove any element //Clear css/remove any element
//id = id of element //id = id of element
BdApi.clearCSS = function (id) { BdApi.clearCSS = function (id) {
DOM.removeStyle(DOM.escapeID(id)); DOM.removeStyle(DOM.escapeID(id));
}; };
//Inject CSS to document head //Inject CSS to document head
//id = id of element //id = id of element
//css = custom css //css = custom css
BdApi.linkJS = function (id, url) { BdApi.linkJS = function (id, url) {
DOM.addScript(DOM.escapeID(id), url); DOM.addScript(DOM.escapeID(id), url);
}; };
//Clear css/remove any element //Clear css/remove any element
//id = id of element //id = id of element
BdApi.unlinkJS = function (id) { BdApi.unlinkJS = function (id) {
DOM.removeScript(DOM.escapeID(id)); DOM.removeScript(DOM.escapeID(id));
}; };
//Get another plugin //Get another plugin
//name = name of plugin //name = name of plugin
BdApi.getPlugin = function (name) { BdApi.getPlugin = function (name) {
if (bdplugins.hasOwnProperty(name)) { if (bdplugins.hasOwnProperty(name)) {
return bdplugins[name].plugin; return bdplugins[name].plugin;
} }
return null; return null;
}; };
//Get BetterDiscord Core //Get BetterDiscord Core
BdApi.getCore = function () { BdApi.getCore = function () {
Utils.warn("Deprecation Notice", `BdApi.getCore() will be removed in future versions.`); Utils.warn("Deprecation Notice", `BdApi.getCore() will be removed in future versions.`);
return mainCore; return mainCore;
}; };
/** /**
* Shows a generic but very customizable modal. * Shows a generic but very customizable modal.
* @param {string} title - title of the modal * @param {string} title - title of the modal
* @param {string} content - a string of text to display in the modal * @param {string} content - a string of text to display in the modal
*/ */
BdApi.alert = function (title, content) { BdApi.alert = function (title, content) {
return Utils.showConfirmationModal(title, content, {cancelText: null}); return Utils.showConfirmationModal(title, content, {cancelText: null});
}; };
/** /**
* Shows a generic but very customizable confirmation modal with optional confirm and cancel callbacks. * Shows a generic but very customizable confirmation modal with optional confirm and cancel callbacks.
* @param {string} title - title of the modal * @param {string} title - title of the modal
* @param {(string|ReactElement|Array<string|ReactElement>)} children - a single or mixed array of react elements and strings. Every string is wrapped in Discord's `Markdown` component so strings will show and render properly. * @param {(string|ReactElement|Array<string|ReactElement>)} children - a single or mixed array of react elements and strings. Every string is wrapped in Discord's `Markdown` component so strings will show and render properly.
* @param {object} [options] - options to modify the modal * @param {object} [options] - options to modify the modal
* @param {boolean} [options.danger=false] - whether the main button should be red or not * @param {boolean} [options.danger=false] - whether the main button should be red or not
* @param {string} [options.confirmText=Okay] - text for the confirmation/submit button * @param {string} [options.confirmText=Okay] - text for the confirmation/submit button
* @param {string} [options.cancelText=Cancel] - text for the cancel button * @param {string} [options.cancelText=Cancel] - text for the cancel button
* @param {callable} [options.onConfirm=NOOP] - callback to occur when clicking the submit button * @param {callable} [options.onConfirm=NOOP] - callback to occur when clicking the submit button
* @param {callable} [options.onCancel=NOOP] - callback to occur when clicking the cancel button * @param {callable} [options.onCancel=NOOP] - callback to occur when clicking the cancel button
* @param {string} [options.key] - key used to identify the modal. If not provided, one is generated and returned * @param {string} [options.key] - key used to identify the modal. If not provided, one is generated and returned
* @returns {string} - the key used for this modal * @returns {string} - the key used for this modal
*/ */
BdApi.showConfirmationModal = function (title, content, options = {}) { BdApi.showConfirmationModal = function (title, content, options = {}) {
return Utils.showConfirmationModal(title, content, options); return Utils.showConfirmationModal(title, content, options);
}; };
//Show toast alert //Show toast alert
BdApi.showToast = function(content, options = {}) { BdApi.showToast = function(content, options = {}) {
Utils.showToast(content, options); Utils.showToast(content, options);
}; };
// Finds module // Finds module
BdApi.findModule = function(filter) { BdApi.findModule = function(filter) {
return BDV2.WebpackModules.find(filter); return BDV2.WebpackModules.find(filter);
}; };
// Finds module // Finds module
BdApi.findAllModules = function(filter) { BdApi.findAllModules = function(filter) {
return BDV2.WebpackModules.findAll(filter); return BDV2.WebpackModules.findAll(filter);
}; };
// Finds module // Finds module
BdApi.findModuleByProps = function(...props) { BdApi.findModuleByProps = function(...props) {
return BDV2.WebpackModules.findByUniqueProperties(props); return BDV2.WebpackModules.findByUniqueProperties(props);
}; };
BdApi.findModuleByPrototypes = function(...protos) { BdApi.findModuleByPrototypes = function(...protos) {
return BDV2.WebpackModules.findByPrototypes(protos); return BDV2.WebpackModules.findByPrototypes(protos);
}; };
BdApi.findModuleByDisplayName = function(name) { BdApi.findModuleByDisplayName = function(name) {
return BDV2.WebpackModules.findByDisplayName(name); return BDV2.WebpackModules.findByDisplayName(name);
}; };
// Gets react instance // Gets react instance
BdApi.getInternalInstance = function(node) { BdApi.getInternalInstance = function(node) {
if (!(node instanceof window.jQuery) && !(node instanceof Element)) return undefined; if (!(node instanceof window.jQuery) && !(node instanceof Element)) return undefined;
if (node instanceof jQuery) node = node[0]; if (node instanceof jQuery) node = node[0];
return BDV2.getInternalInstance(node); return BDV2.getInternalInstance(node);
}; };
// Gets data // Gets data
BdApi.loadData = function(pluginName, key) { BdApi.loadData = function(pluginName, key) {
return DataStore.getPluginData(pluginName, key); return DataStore.getPluginData(pluginName, key);
}; };
BdApi.getData = BdApi.loadData; BdApi.getData = BdApi.loadData;
// Sets data // Sets data
BdApi.saveData = function(pluginName, key, data) { BdApi.saveData = function(pluginName, key, data) {
return DataStore.setPluginData(pluginName, key, data); return DataStore.setPluginData(pluginName, key, data);
}; };
BdApi.setData = BdApi.saveData; BdApi.setData = BdApi.saveData;
// Deletes data // Deletes data
BdApi.deleteData = function(pluginName, key) { BdApi.deleteData = function(pluginName, key) {
return DataStore.deletePluginData(pluginName, key); return DataStore.deletePluginData(pluginName, key);
}; };
// Patches other functions // Patches other functions
BdApi.monkeyPatch = function(what, methodName, options) { BdApi.monkeyPatch = function(what, methodName, options) {
return Utils.monkeyPatch(what, methodName, options); return Utils.monkeyPatch(what, methodName, options);
}; };
// Event when element is removed // Event when element is removed
BdApi.onRemoved = function(node, callback) { BdApi.onRemoved = function(node, callback) {
return Utils.onRemoved(node, callback); return Utils.onRemoved(node, callback);
}; };
// Wraps function in try..catch // Wraps function in try..catch
BdApi.suppressErrors = function(method, message) { BdApi.suppressErrors = function(method, message) {
return Utils.suppressErrors(method, message); return Utils.suppressErrors(method, message);
}; };
// Tests for valid JSON // Tests for valid JSON
BdApi.testJSON = function(data) { BdApi.testJSON = function(data) {
return Utils.testJSON(data); return Utils.testJSON(data);
}; };
BdApi.isPluginEnabled = function(name) { BdApi.isPluginEnabled = function(name) {
return !!pluginCookie[name]; return !!pluginCookie[name];
}; };
BdApi.isThemeEnabled = function(name) { BdApi.isThemeEnabled = function(name) {
return !!themeCookie[name]; return !!themeCookie[name];
}; };
BdApi.isSettingEnabled = function(id) { BdApi.isSettingEnabled = function(id) {
return !!settingsCookie[id]; return !!settingsCookie[id];
}; };
BdApi.enableSetting = function(id) { BdApi.enableSetting = function(id) {
return settingsPanel.onChange(id, true); return settingsPanel.onChange(id, true);
}; };
BdApi.disableSetting = function(id) { BdApi.disableSetting = function(id) {
return settingsPanel.onChange(id, false); return settingsPanel.onChange(id, false);
}; };
BdApi.toggleSetting = function(id) { BdApi.toggleSetting = function(id) {
return settingsPanel.onChange(id, !settingsCookie[id]); return settingsPanel.onChange(id, !settingsCookie[id]);
}; };
// Gets data // Gets data
BdApi.getBDData = function(key) { BdApi.getBDData = function(key) {
return DataStore.getBDData(key); return DataStore.getBDData(key);
}; };
// Sets data // Sets data
BdApi.setBDData = function(key, data) { BdApi.setBDData = function(key, data) {
return DataStore.setBDData(key, data); return DataStore.setBDData(key, data);
}; };
const makeAddonAPI = (cookie, list, manager) => new class AddonAPI { const makeAddonAPI = (cookie, list, manager) => new class AddonAPI {
get folder() {return manager.folder;} get folder() {return manager.folder;}
isEnabled(name) { isEnabled(name) {
return !!cookie[name]; return !!cookie[name];
} }
enable(name) { enable(name) {
return manager.enable(name); return manager.enable(name);
} }
disable(name) { disable(name) {
return manager.disable(name); return manager.disable(name);
} }
toggle(name) { toggle(name) {
if (cookie[name]) this.disable(name); if (cookie[name]) this.disable(name);
else this.enable(name); else this.enable(name);
} }
reload(name) { reload(name) {
return manager.reload(name); return manager.reload(name);
} }
get(name) { get(name) {
if (list.hasOwnProperty(name)) { if (list.hasOwnProperty(name)) {
if (list[name].plugin) return list[name].plugin; if (list[name].plugin) return list[name].plugin;
return list[name]; return list[name];
} }
return null; return null;
} }
getAll() { getAll() {
return Object.keys(list).map(k => this.get(k)).filter(a => a); return Object.keys(list).map(k => this.get(k)).filter(a => a);
} }
}; };
BdApi.Plugins = makeAddonAPI(pluginCookie, bdplugins, pluginModule); BdApi.Plugins = makeAddonAPI(pluginCookie, bdplugins, pluginModule);
BdApi.Themes = makeAddonAPI(themeCookie, bdthemes, themeModule); BdApi.Themes = makeAddonAPI(themeCookie, bdthemes, themeModule);
export default BdApi; export default BdApi;
window.Lightcord.BetterDiscord.BdApi = BdApi window.Lightcord.BetterDiscord.BdApi = BdApi

View File

@ -1,10 +1,10 @@
/* BDEvents */ /* BDEvents */
const EventEmitter = require("events"); const EventEmitter = require("events");
export default new class BDEvents extends EventEmitter { export default new class BDEvents extends EventEmitter {
constructor(){ constructor(){
super() super()
window.Lightcord.BetterDiscord.BDEvents = this window.Lightcord.BetterDiscord.BDEvents = this
} }
dispatch(eventName, ...args) {this.emit(eventName, ...args);} dispatch(eventName, ...args) {this.emit(eventName, ...args);}
off(eventName, eventAction) {this.removeListener(eventName, eventAction);} off(eventName, eventAction) {this.removeListener(eventName, eventAction);}
}; };

View File

@ -1,57 +1,57 @@
/** /**
* Credits to @hellbound1337 on github for the css * Credits to @hellbound1337 on github for the css
*/ */
import DOMTools from "./domtools" import DOMTools from "./domtools"
import Utils from "./utils" import Utils from "./utils"
let selectors let selectors
const removeDa = Utils.removeDa const removeDa = Utils.removeDa
function getSelectors(){ function getSelectors(){
let standardSidebarView = BDModules.get(e => e.standardSidebarView)[0] let standardSidebarView = BDModules.get(e => e.standardSidebarView)[0]
if(!standardSidebarView)return null if(!standardSidebarView)return null
let defaultClassName = removeDa(standardSidebarView.standardSidebarView) let defaultClassName = removeDa(standardSidebarView.standardSidebarView)
let selects = [] let selects = []
let userSettingsAccount = BDModules.get(e => e.userSettingsAccount)[0] let userSettingsAccount = BDModules.get(e => e.userSettingsAccount)[0]
const children = BDModules.get(e => typeof e.children === "string")[0] const children = BDModules.get(e => typeof e.children === "string")[0]
selects.push(`#app-mount .${defaultClassName} .payment-info .${removeDa(BDModules.get(e => e.description && typeof e.description === "string" && e.description.includes("formText"))[0].description)}`) selects.push(`#app-mount .${defaultClassName} .payment-info .${removeDa(BDModules.get(e => e.description && typeof e.description === "string" && e.description.includes("formText"))[0].description)}`)
selects.push(`#app-mount .${defaultClassName} .${removeDa(BDModules.get(e => e.paymentSourceRow)[0].paymentSourceRow)} .${removeDa(BDModules.get(e => e.subText && e.descriptionWrapper)[0].subText)}`) selects.push(`#app-mount .${defaultClassName} .${removeDa(BDModules.get(e => e.paymentSourceRow)[0].paymentSourceRow)} .${removeDa(BDModules.get(e => e.subText && e.descriptionWrapper)[0].subText)}`)
if(userSettingsAccount){ if(userSettingsAccount){
selects.push(`#app-mount .${defaultClassName} .${removeDa(userSettingsAccount.userSettingsAccount)} div:nth-child(2)>div:nth-child(2)>.${removeDa(BDModules.get(e => e.viewBody)[0].viewBody)}`) selects.push(`#app-mount .${defaultClassName} .${removeDa(userSettingsAccount.userSettingsAccount)} div:nth-child(2)>div:nth-child(2)>.${removeDa(BDModules.get(e => e.viewBody)[0].viewBody)}`)
selects.push(`.${removeDa(userSettingsAccount.userSettingsSecurity)} .${removeDa(children.children)} > div:nth-child(2)`) selects.push(`.${removeDa(userSettingsAccount.userSettingsSecurity)} .${removeDa(children.children)} > div:nth-child(2)`)
} }
console.log(selects) console.log(selects)
return selects return selects
} }
export default new class BlurPrivate { export default new class BlurPrivate {
constructor(){ constructor(){
this.enabled = false this.enabled = false
} }
enable(){ enable(){
if(this.enabled)return if(this.enabled)return
this.enabled = true this.enabled = true
selectors = selectors || getSelectors() selectors = selectors || getSelectors()
if(!selectors)console.error(new Error("Couldn't find selectors to blur personnal informations.")) if(!selectors)console.error(new Error("Couldn't find selectors to blur personnal informations."))
DOMTools.addStyle("blurPrivate", ` DOMTools.addStyle("blurPrivate", `
${selectors.join(", ")} { ${selectors.join(", ")} {
transition: all 150ms cubic-bezier(.55,.085,.68,.53); transition: all 150ms cubic-bezier(.55,.085,.68,.53);
filter: blur(4px); filter: blur(4px);
opacity: .8; opacity: .8;
} }
${selectors.map(e => e+":hover").join(", ")} { ${selectors.map(e => e+":hover").join(", ")} {
transition: all 150ms cubic-bezier(.55,.09,.68,.53); transition: all 150ms cubic-bezier(.55,.09,.68,.53);
filter: none; filter: none;
opacity: 1; opacity: 1;
}`) }`)
} }
disable(){ disable(){
if(!this.enabled)return if(!this.enabled)return
this.enabled = false this.enabled = false
DOMTools.removeStyle("blurPrivate") DOMTools.removeStyle("blurPrivate")
} }
} }

View File

@ -1,138 +1,138 @@
import WebpackModules from "./webpackModules"; import WebpackModules from "./webpackModules";
const normalizedPrefix = "da"; const normalizedPrefix = "da";
const randClass = new RegExp(`^(?!${normalizedPrefix}-)((?:[A-Za-z]|[0-9]|-)+)-(?:[A-Za-z]|[0-9]|-|_){6}$`); const randClass = new RegExp(`^(?!${normalizedPrefix}-)((?:[A-Za-z]|[0-9]|-)+)-(?:[A-Za-z]|[0-9]|-|_){6}$`);
export default new class ClassNormalizer { export default new class ClassNormalizer {
constructor(){ constructor(){
window.Lightcord.BetterDiscord.ClassNormalizer = this window.Lightcord.BetterDiscord.ClassNormalizer = this
} }
stop() { stop() {
if (!this.hasPatched) return; if (!this.hasPatched) return;
this.unpatchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this))); this.unpatchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this)));
this.revertElement(document.querySelector("#app-mount")); this.revertElement(document.querySelector("#app-mount"));
this.hasPatched = false; this.hasPatched = false;
} }
start() { start() {
if (this.hasPatched) return; if (this.hasPatched) return;
this.patchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this))); this.patchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this)));
this.normalizeElement(document.querySelector("#app-mount")); this.normalizeElement(document.querySelector("#app-mount"));
this.hasPatched = true; this.hasPatched = true;
this.patchDOMMethods(); this.patchDOMMethods();
} }
patchClassModules(modules) { patchClassModules(modules) {
for (const module of modules) { for (const module of modules) {
this.patchClassModule(normalizedPrefix, module); this.patchClassModule(normalizedPrefix, module);
} }
} }
unpatchClassModules(modules) { unpatchClassModules(modules) {
for (const module of modules) { for (const module of modules) {
this.unpatchClassModule(normalizedPrefix, module); this.unpatchClassModule(normalizedPrefix, module);
} }
} }
shouldIgnore(value) { shouldIgnore(value) {
if (!isNaN(value)) return true; if (!isNaN(value)) return true;
if (value.endsWith("px") || value.endsWith("ch") || value.endsWith("em") || value.endsWith("ms")) return true; if (value.endsWith("px") || value.endsWith("ch") || value.endsWith("em") || value.endsWith("ms")) return true;
if (value.startsWith("layerContainer-")) return true; if (value.startsWith("layerContainer-")) return true;
if (value.startsWith("#") && (value.length == 7 || value.length == 4)) return true; if (value.startsWith("#") && (value.length == 7 || value.length == 4)) return true;
if (value.includes("calc(") || value.includes("rgba")) return true; if (value.includes("calc(") || value.includes("rgba")) return true;
return false; return false;
} }
moduleFilter(module) { moduleFilter(module) {
if (typeof module !== "object" || Array.isArray(module)) return false; if (typeof module !== "object" || Array.isArray(module)) return false;
if (module.__esModule) return false; if (module.__esModule) return false;
if (!Object.keys(module).length) return false; if (!Object.keys(module).length) return false;
for (const baseClassName in module) { for (const baseClassName in module) {
const value = module[baseClassName]; const value = module[baseClassName];
if (typeof value !== "string") return false; if (typeof value !== "string") return false;
if (this.shouldIgnore(value)) continue; if (this.shouldIgnore(value)) continue;
if (value.split("-").length === 1) return false; if (value.split("-").length === 1) return false;
if (!randClass.test(value.split(" ")[0])) return false; if (!randClass.test(value.split(" ")[0])) return false;
} }
return true; return true;
} }
patchClassModule(componentName, classNames) { patchClassModule(componentName, classNames) {
for (const baseClassName in classNames) { for (const baseClassName in classNames) {
const value = classNames[baseClassName]; const value = classNames[baseClassName];
if (this.shouldIgnore(value)) continue; if (this.shouldIgnore(value)) continue;
const classList = value.split(" "); const classList = value.split(" ");
for (const normalClass of classList) { for (const normalClass of classList) {
const match = normalClass.match(randClass); const match = normalClass.match(randClass);
if (!match || !match.length || match.length < 2) continue; // Shouldn't ever happen since they passed the moduleFilter, but you never know if (!match || !match.length || match.length < 2) continue; // Shouldn't ever happen since they passed the moduleFilter, but you never know
const camelCase = match[1].split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join(""); const camelCase = match[1].split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join("");
classNames[baseClassName] += ` ${componentName}-${camelCase}`; classNames[baseClassName] += ` ${componentName}-${camelCase}`;
} }
} }
} }
unpatchClassModule(componentName, classNames) { unpatchClassModule(componentName, classNames) {
for (const baseClassName in classNames) { for (const baseClassName in classNames) {
const value = classNames[baseClassName]; const value = classNames[baseClassName];
if (this.shouldIgnore(value)) continue; if (this.shouldIgnore(value)) continue;
let newString = ""; let newString = "";
const classList = value.split(" "); const classList = value.split(" ");
for (const normalClass of classList) { for (const normalClass of classList) {
if (normalClass.startsWith(`${componentName}-`)) continue; if (normalClass.startsWith(`${componentName}-`)) continue;
newString += ` ${normalClass}`; newString += ` ${normalClass}`;
} }
classNames[baseClassName] = newString.trim(); classNames[baseClassName] = newString.trim();
} }
} }
normalizeElement(element) { normalizeElement(element) {
if (!(element instanceof Element)) return; if (!(element instanceof Element)) return;
const classes = element.classList; const classes = element.classList;
for (let c = 0, clen = classes.length; c < clen; c++) { for (let c = 0, clen = classes.length; c < clen; c++) {
if (!randClass.test(classes[c])) continue; if (!randClass.test(classes[c])) continue;
const match = classes[c].match(randClass)[1]; const match = classes[c].match(randClass)[1];
const newClass = match.split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join(""); const newClass = match.split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join("");
element.classList.add(`${normalizedPrefix}-${newClass}`); element.classList.add(`${normalizedPrefix}-${newClass}`);
} }
for (const child of element.children) this.normalizeElement(child); for (const child of element.children) this.normalizeElement(child);
} }
revertElement(element) { revertElement(element) {
if (!(element instanceof Element)) return; if (!(element instanceof Element)) return;
if (element.children && element.children.length) this.revertElement(element.children[0]); if (element.children && element.children.length) this.revertElement(element.children[0]);
if (element.nextElementSibling) this.revertElement(element.nextElementSibling); if (element.nextElementSibling) this.revertElement(element.nextElementSibling);
const classes = element.classList; const classes = element.classList;
const toRemove = []; const toRemove = [];
for (let c = 0; c < classes.length; c++) { for (let c = 0; c < classes.length; c++) {
if (classes[c].startsWith(`${normalizedPrefix}-`)) toRemove.push(classes[c]); if (classes[c].startsWith(`${normalizedPrefix}-`)) toRemove.push(classes[c]);
} }
element.classList.remove(...toRemove); element.classList.remove(...toRemove);
} }
patchDOMMethods() { patchDOMMethods() {
const contains = DOMTokenList.prototype.contains; const contains = DOMTokenList.prototype.contains;
DOMTokenList.prototype.contains = function(token) { DOMTokenList.prototype.contains = function(token) {
// const tokens = token.split(" "); // const tokens = token.split(" ");
return Reflect.apply(contains, this, [token.split(" ")[0]]); return Reflect.apply(contains, this, [token.split(" ")[0]]);
// return tokens.every(t => contains.call(this, t)); // return tokens.every(t => contains.call(this, t));
}; };
const add = DOMTokenList.prototype.add; const add = DOMTokenList.prototype.add;
DOMTokenList.prototype.add = function(...tokens) { DOMTokenList.prototype.add = function(...tokens) {
for (let t = 0; t < tokens.length; t++) { for (let t = 0; t < tokens.length; t++) {
tokens[t] = tokens[t].split(" ")[0]; tokens[t] = tokens[t].split(" ")[0];
} }
return Reflect.apply(add, this, tokens); return Reflect.apply(add, this, tokens);
}; };
const remove = DOMTokenList.prototype.remove; const remove = DOMTokenList.prototype.remove;
DOMTokenList.prototype.remove = function(...tokens) { DOMTokenList.prototype.remove = function(...tokens) {
for (let t = 0; t < tokens.length; t++) { for (let t = 0; t < tokens.length; t++) {
tokens[t] = tokens[t].split(" ")[0]; tokens[t] = tokens[t].split(" ")[0];
} }
return Reflect.apply(remove, this, tokens); return Reflect.apply(remove, this, tokens);
}; };
} }
}; };

View File

@ -1,342 +1,342 @@
import {bdConfig, bdplugins, bdthemes, settingsCookie} from "../0globals"; import {bdConfig, bdplugins, bdthemes, settingsCookie} from "../0globals";
import pluginModule from "./pluginModule"; import pluginModule from "./pluginModule";
import themeModule from "./themeModule"; import themeModule from "./themeModule";
import Utils from "./utils"; import Utils from "./utils";
import dataStore from "./dataStore"; import dataStore from "./dataStore";
import { encryptSettingsCache, decryptSettingsCache, processFile } from "./pluginCertifier"; import { encryptSettingsCache, decryptSettingsCache, processFile } from "./pluginCertifier";
const path = require("path"); const path = require("path");
const fs = require("fs"); const fs = require("fs");
const Module = require("module").Module; const Module = require("module").Module;
Module.globalPaths.push(path.resolve(require("electron").remote.app.getAppPath(), "node_modules")); Module.globalPaths.push(path.resolve(require("electron").remote.app.getAppPath(), "node_modules"));
class MetaError extends Error { class MetaError extends Error {
constructor(message) { constructor(message) {
super(message); super(message);
this.name = "MetaError"; this.name = "MetaError";
} }
} }
const originalJSRequire = Module._extensions[".js"]; const originalJSRequire = Module._extensions[".js"];
const originalCSSRequire = Module._extensions[".css"] ? Module._extensions[".css"] : () => {return null;}; const originalCSSRequire = Module._extensions[".css"] ? Module._extensions[".css"] : () => {return null;};
const splitRegex = /[^\S\r\n]*?(?:\r\n|\n)[^\S\r\n]*?\*[^\S\r\n]?/; const splitRegex = /[^\S\r\n]*?(?:\r\n|\n)[^\S\r\n]*?\*[^\S\r\n]?/;
const escapedAtRegex = /^\\@/; const escapedAtRegex = /^\\@/;
export let addonCache = {} export let addonCache = {}
let hasPatched = false let hasPatched = false
export default new class ContentManager { export default new class ContentManager {
constructor() { constructor() {
this.timeCache = {}; this.timeCache = {};
this.watchers = {}; this.watchers = {};
} }
patchExtensions(){ patchExtensions(){
if(hasPatched)return if(hasPatched)return
hasPatched = true hasPatched = true
Module._extensions[".js"] = this.getContentRequire("plugin"); Module._extensions[".js"] = this.getContentRequire("plugin");
Module._extensions[".css"] = this.getContentRequire("theme"); Module._extensions[".css"] = this.getContentRequire("theme");
} }
get pluginsFolder() {return this._pluginsFolder || (this._pluginsFolder = fs.realpathSync(path.resolve(bdConfig.dataPath + "plugins/")));} get pluginsFolder() {return this._pluginsFolder || (this._pluginsFolder = fs.realpathSync(path.resolve(bdConfig.dataPath + "plugins/")));}
get themesFolder() {return this._themesFolder || (this._themesFolder = fs.realpathSync(path.resolve(bdConfig.dataPath + "themes/")));} get themesFolder() {return this._themesFolder || (this._themesFolder = fs.realpathSync(path.resolve(bdConfig.dataPath + "themes/")));}
loadAddonCertifierCache(){ loadAddonCertifierCache(){
if(typeof dataStore.getSettingGroup("PluginCertifierHashes") !== "string"){ if(typeof dataStore.getSettingGroup("PluginCertifierHashes") !== "string"){
dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache("{}")) dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache("{}"))
}else{ }else{
try{ try{
addonCache = JSON.parse(decryptSettingsCache(dataStore.getSettingGroup("PluginCertifierHashes"))) addonCache = JSON.parse(decryptSettingsCache(dataStore.getSettingGroup("PluginCertifierHashes")))
}catch(e){ }catch(e){
dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache("{}")) dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache("{}"))
addonCache = {} addonCache = {}
} }
} }
Object.keys(addonCache) Object.keys(addonCache)
.forEach(key => { .forEach(key => {
let value = addonCache[key] let value = addonCache[key]
if(!value || typeof value !== "object" || Array.isArray(value))return delete addonCache[key] if(!value || typeof value !== "object" || Array.isArray(value))return delete addonCache[key]
let props = [{ let props = [{
key: "timestamp", key: "timestamp",
type: "number" type: "number"
}, { }, {
key: "result", key: "result",
type: "object" type: "object"
}, { }, {
key: "hash", key: "hash",
type: "string" type: "string"
}] }]
for(let prop of props){ for(let prop of props){
if(!(prop.key in value) || typeof value[prop.key] !== prop.type){ if(!(prop.key in value) || typeof value[prop.key] !== prop.type){
delete addonCache[key] delete addonCache[key]
return return
} }
} }
if(value.hash !== key){ if(value.hash !== key){
delete addonCache[key] delete addonCache[key]
return return
} }
if(value.result.suspect){ // refetch from remote to be sure you're up to date. if(value.result.suspect){ // refetch from remote to be sure you're up to date.
delete addonCache[key] delete addonCache[key]
return return
} }
}) })
this.saveAddonCache() this.saveAddonCache()
} }
saveAddonCache(){ saveAddonCache(){
dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache(JSON.stringify(addonCache))) dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache(JSON.stringify(addonCache)))
} }
watchContent(contentType) { watchContent(contentType) {
if (this.watchers[contentType]) return; if (this.watchers[contentType]) return;
const isPlugin = contentType === "plugin"; const isPlugin = contentType === "plugin";
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder; const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
const fileEnding = isPlugin ? ".plugin.js" : ".theme.css"; const fileEnding = isPlugin ? ".plugin.js" : ".theme.css";
this.watchers[contentType] = fs.watch(baseFolder, {persistent: false}, async (eventType, filename) => { this.watchers[contentType] = fs.watch(baseFolder, {persistent: false}, async (eventType, filename) => {
if (!eventType || !filename || !filename.endsWith(fileEnding)) return; if (!eventType || !filename || !filename.endsWith(fileEnding)) return;
await new Promise(r => setTimeout(r, 50)); await new Promise(r => setTimeout(r, 50));
try {fs.statSync(path.resolve(baseFolder, filename));} try {fs.statSync(path.resolve(baseFolder, filename));}
catch (err) { catch (err) {
if (err.code !== "ENOENT") return; if (err.code !== "ENOENT") return;
delete this.timeCache[filename]; delete this.timeCache[filename];
if (isPlugin) return pluginModule.unloadPlugin(filename); if (isPlugin) return pluginModule.unloadPlugin(filename);
return themeModule.unloadTheme(filename); return themeModule.unloadTheme(filename);
} }
if (!fs.statSync(path.resolve(baseFolder, filename)).isFile()) return; if (!fs.statSync(path.resolve(baseFolder, filename)).isFile()) return;
const stats = fs.statSync(path.resolve(baseFolder, filename)); const stats = fs.statSync(path.resolve(baseFolder, filename));
if (!stats || !stats.mtime || !stats.mtime.getTime()) return; if (!stats || !stats.mtime || !stats.mtime.getTime()) return;
if (typeof(stats.mtime.getTime()) !== "number") return; if (typeof(stats.mtime.getTime()) !== "number") return;
if (this.timeCache[filename] == stats.mtime.getTime()) return; if (this.timeCache[filename] == stats.mtime.getTime()) return;
this.timeCache[filename] = stats.mtime.getTime(); this.timeCache[filename] = stats.mtime.getTime();
if (eventType == "rename") { if (eventType == "rename") {
if (isPlugin) await pluginModule.loadPlugin(filename); if (isPlugin) await pluginModule.loadPlugin(filename);
else await themeModule.loadTheme(filename); else await themeModule.loadTheme(filename);
} }
if (eventType == "change") { if (eventType == "change") {
if (isPlugin) await pluginModule.reloadPlugin(filename); if (isPlugin) await pluginModule.reloadPlugin(filename);
else await themeModule.reloadTheme(filename); else await themeModule.reloadTheme(filename);
} }
}); });
} }
unwatchContent(contentType) { unwatchContent(contentType) {
if (!this.watchers[contentType]) return; if (!this.watchers[contentType]) return;
this.watchers[contentType].close(); this.watchers[contentType].close();
delete this.watchers[contentType]; delete this.watchers[contentType];
} }
extractMeta(content) { extractMeta(content) {
const firstLine = content.split("\n")[0]; const firstLine = content.split("\n")[0];
const hasOldMeta = firstLine.includes("//META"); const hasOldMeta = firstLine.includes("//META");
if (hasOldMeta) return this.parseOldMeta(content); if (hasOldMeta) return this.parseOldMeta(content);
const hasNewMeta = firstLine.includes("/**"); const hasNewMeta = firstLine.includes("/**");
if (hasNewMeta) return this.parseNewMeta(content); if (hasNewMeta) return this.parseNewMeta(content);
throw new MetaError("META was not found."); throw new MetaError("META was not found.");
} }
parseOldMeta(content) { parseOldMeta(content) {
const meta = content.split("\n")[0]; const meta = content.split("\n")[0];
const rawMeta = meta.substring(meta.lastIndexOf("//META") + 6, meta.lastIndexOf("*//")); const rawMeta = meta.substring(meta.lastIndexOf("//META") + 6, meta.lastIndexOf("*//"));
if (meta.indexOf("META") < 0) throw new MetaError("META was not found."); if (meta.indexOf("META") < 0) throw new MetaError("META was not found.");
const parsed = Utils.testJSON(rawMeta); const parsed = Utils.testJSON(rawMeta);
if (!parsed) throw new MetaError("META could not be parsed."); if (!parsed) throw new MetaError("META could not be parsed.");
if (!parsed.name) throw new MetaError("META missing name data."); if (!parsed.name) throw new MetaError("META missing name data.");
parsed.format = "json"; parsed.format = "json";
return parsed; return parsed;
} }
parseNewMeta(content) { parseNewMeta(content) {
const block = content.split("/**", 2)[1].split("*/", 1)[0]; const block = content.split("/**", 2)[1].split("*/", 1)[0];
const out = {}; const out = {};
let field = ""; let field = "";
let accum = ""; let accum = "";
for (const line of block.split(splitRegex)) { for (const line of block.split(splitRegex)) {
if (line.length === 0) continue; if (line.length === 0) continue;
if (line.charAt(0) === "@" && line.charAt(1) !== " ") { if (line.charAt(0) === "@" && line.charAt(1) !== " ") {
out[field] = accum; out[field] = accum;
const l = line.indexOf(" "); const l = line.indexOf(" ");
field = line.substr(1, l - 1); field = line.substr(1, l - 1);
accum = line.substr(l + 1); accum = line.substr(l + 1);
} }
else { else {
accum += " " + line.replace("\\n", "\n").replace(escapedAtRegex, "@"); accum += " " + line.replace("\\n", "\n").replace(escapedAtRegex, "@");
} }
} }
out[field] = accum.trim(); out[field] = accum.trim();
delete out[""]; delete out[""];
out.format = "jsdoc"; out.format = "jsdoc";
return out; return out;
} }
getContentRequire(type) { getContentRequire(type) {
const isPlugin = type === "plugin"; const isPlugin = type === "plugin";
const self = this; const self = this;
const originalRequire = isPlugin ? originalJSRequire : originalCSSRequire; const originalRequire = isPlugin ? originalJSRequire : originalCSSRequire;
return function(module, filename) { return function(module, filename) {
const baseFolder = isPlugin ? self.pluginsFolder : self.themesFolder; const baseFolder = isPlugin ? self.pluginsFolder : self.themesFolder;
const possiblePath = path.resolve(baseFolder, path.basename(filename)); const possiblePath = path.resolve(baseFolder, path.basename(filename));
if (!fs.existsSync(possiblePath) || filename !== fs.realpathSync(possiblePath)) return Reflect.apply(originalRequire, this, arguments); if (!fs.existsSync(possiblePath) || filename !== fs.realpathSync(possiblePath)) return Reflect.apply(originalRequire, this, arguments);
let content = fs.readFileSync(filename, "utf8"); let content = fs.readFileSync(filename, "utf8");
content = Utils.stripBOM(content); content = Utils.stripBOM(content);
const stats = fs.statSync(filename); const stats = fs.statSync(filename);
const meta = self.extractMeta(content); const meta = self.extractMeta(content);
meta.filename = path.basename(filename); meta.filename = path.basename(filename);
meta.added = stats.atimeMs; meta.added = stats.atimeMs;
meta.modified = stats.mtimeMs; meta.modified = stats.mtimeMs;
meta.size = stats.size; meta.size = stats.size;
if (!isPlugin) { if (!isPlugin) {
meta.css = content; meta.css = content;
if (meta.format == "json") meta.css = meta.css.split("\n").slice(1).join("\n"); if (meta.format == "json") meta.css = meta.css.split("\n").slice(1).join("\n");
content = `module.exports = ${JSON.stringify(meta)};`; content = `module.exports = ${JSON.stringify(meta)};`;
} }
if (isPlugin) { if (isPlugin) {
module._compile(content, module.filename); module._compile(content, module.filename);
const didExport = !Utils.isEmpty(module.exports); const didExport = !Utils.isEmpty(module.exports);
if (didExport) { if (didExport) {
meta.type = module.exports; meta.type = module.exports;
module.exports = meta; module.exports = meta;
content = ""; content = "";
} }
else { else {
Utils.warn("Module Not Exported", `${meta.name}, please start setting module.exports`); Utils.warn("Module Not Exported", `${meta.name}, please start setting module.exports`);
content += `\nmodule.exports = ${JSON.stringify(meta)};\nmodule.exports.type = ${meta.exports || meta.name};`; content += `\nmodule.exports = ${JSON.stringify(meta)};\nmodule.exports.type = ${meta.exports || meta.name};`;
} }
} }
module._compile(content, filename); module._compile(content, filename);
}; };
} }
makePlaceholderPlugin(data) { makePlaceholderPlugin(data) {
return {plugin: { return {plugin: {
start: () => {}, start: () => {},
getName: () => {return data.name || data.filename;}, getName: () => {return data.name || data.filename;},
getAuthor: () => {return "???";}, getAuthor: () => {return "???";},
getDescription: () => {return data.message ? data.message : "This plugin was unable to be loaded. Check the author's page for updates.";}, getDescription: () => {return data.message ? data.message : "This plugin was unable to be loaded. Check the author's page for updates.";},
getVersion: () => {return "???";} getVersion: () => {return "???";}
}, },
name: data.name || data.filename, name: data.name || data.filename,
filename: data.filename, filename: data.filename,
source: data.source ? data.source : "", source: data.source ? data.source : "",
website: data.website ? data.website : "" website: data.website ? data.website : ""
}; };
} }
async loadContent(filename, type) { async loadContent(filename, type) {
if (typeof(filename) === "undefined" || typeof(type) === "undefined") return; if (typeof(filename) === "undefined" || typeof(type) === "undefined") return;
const isPlugin = type === "plugin"; const isPlugin = type === "plugin";
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder; const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
if(settingsCookie["fork-ps-6"]){ if(settingsCookie["fork-ps-6"]){
let result = await new Promise(resolve => { let result = await new Promise(resolve => {
processFile(path.resolve(baseFolder, filename), (result) => { processFile(path.resolve(baseFolder, filename), (result) => {
console.log(result) console.log(result)
resolve(result) resolve(result)
}, (hash) => { }, (hash) => {
resolve({ resolve({
suspect: false, suspect: false,
hash: hash, hash: hash,
filename: filename, filename: filename,
name: filename name: filename
}) })
}, true) }, true)
}) })
if(result){ if(result){
addonCache[result.hash] = { addonCache[result.hash] = {
timestamp: Date.now(), timestamp: Date.now(),
hash: result.hash, hash: result.hash,
result: result result: result
} }
this.saveAddonCache() this.saveAddonCache()
if(result.suspect){ if(result.suspect){
return { return {
name: filename, name: filename,
file: filename, file: filename,
message: "This plugin might be dangerous ("+result.harm+").", message: "This plugin might be dangerous ("+result.harm+").",
error: new Error("This plugin might be dangerous ("+result.harm+").") error: new Error("This plugin might be dangerous ("+result.harm+").")
} }
} }
} }
} }
try {window.require(path.resolve(baseFolder, filename));} try {window.require(path.resolve(baseFolder, filename));}
catch (error) {return {name: filename, file: filename, message: "Could not be compiled.", error: {message: error.message, stack: error.stack}};} catch (error) {return {name: filename, file: filename, message: "Could not be compiled.", error: {message: error.message, stack: error.stack}};}
const content = window.require(path.resolve(baseFolder, filename)); const content = window.require(path.resolve(baseFolder, filename));
if(!content.name)return {name: filename, file: filename, message: "Cannot escape the ID.", error: new Error("Cannot read property 'replace' of undefined")} if(!content.name)return {name: filename, file: filename, message: "Cannot escape the ID.", error: new Error("Cannot read property 'replace' of undefined")}
content.id = Utils.escapeID(content.name); content.id = Utils.escapeID(content.name);
//if(!id)return {name: filename, file: filename, message: "Invalid ID", error: new Error("Please fix the name of "+filename+". BetterDiscord can't escape an ID.")} //if(!id)return {name: filename, file: filename, message: "Invalid ID", error: new Error("Please fix the name of "+filename+". BetterDiscord can't escape an ID.")}
if (isPlugin) { if (isPlugin) {
if (!content.type) return; if (!content.type) return;
try { try {
content.plugin = new content.type(); content.plugin = new content.type();
delete bdplugins[content.plugin.getName()]; delete bdplugins[content.plugin.getName()];
bdplugins[content.plugin.getName()] = content; bdplugins[content.plugin.getName()] = content;
} }
catch (error) {return {name: filename, file: filename, message: "Could not be constructed.", error: {message: error.message, stack: error.stack}};} catch (error) {return {name: filename, file: filename, message: "Could not be constructed.", error: {message: error.message, stack: error.stack}};}
} }
else { else {
delete bdthemes[content.name]; delete bdthemes[content.name];
bdthemes[content.name] = content; bdthemes[content.name] = content;
} }
} }
unloadContent(filename, type) { unloadContent(filename, type) {
if (typeof(filename) === "undefined" || typeof(type) === "undefined") return; if (typeof(filename) === "undefined" || typeof(type) === "undefined") return;
const isPlugin = type === "plugin"; const isPlugin = type === "plugin";
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder; const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
try { try {
delete window.require.cache[window.require.resolve(path.resolve(baseFolder, filename))]; delete window.require.cache[window.require.resolve(path.resolve(baseFolder, filename))];
} }
catch (err) {return {name: filename, file: filename, message: "Could not be unloaded.", error: {message: err.message, stack: err.stack}};} catch (err) {return {name: filename, file: filename, message: "Could not be unloaded.", error: {message: err.message, stack: err.stack}};}
} }
isLoaded(filename, type) { isLoaded(filename, type) {
const isPlugin = type === "plugin"; const isPlugin = type === "plugin";
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder; const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
try {window.require.cache[window.require.resolve(path.resolve(baseFolder, filename))];} try {window.require.cache[window.require.resolve(path.resolve(baseFolder, filename))];}
catch (err) {return false;} catch (err) {return false;}
return true; return true;
} }
async reloadContent(filename, type) { async reloadContent(filename, type) {
const cantUnload = this.unloadContent(filename, type); const cantUnload = this.unloadContent(filename, type);
if (cantUnload) return cantUnload; if (cantUnload) return cantUnload;
return await this.loadContent(filename, type); return await this.loadContent(filename, type);
} }
loadNewContent(type) { loadNewContent(type) {
const isPlugin = type === "plugin"; const isPlugin = type === "plugin";
const fileEnding = isPlugin ? ".plugin.js" : ".theme.css"; const fileEnding = isPlugin ? ".plugin.js" : ".theme.css";
const basedir = isPlugin ? this.pluginsFolder : this.themesFolder; const basedir = isPlugin ? this.pluginsFolder : this.themesFolder;
const files = fs.readdirSync(basedir); const files = fs.readdirSync(basedir);
const contentList = Object.values(isPlugin ? bdplugins : bdthemes); const contentList = Object.values(isPlugin ? bdplugins : bdthemes);
const removed = contentList.filter(t => !files.includes(t.filename)).map(c => isPlugin ? c.plugin.getName() : c.name); const removed = contentList.filter(t => !files.includes(t.filename)).map(c => isPlugin ? c.plugin.getName() : c.name);
const added = files.filter(f => !contentList.find(t => t.filename == f) && f.endsWith(fileEnding) && fs.statSync(path.resolve(basedir, f)).isFile()); const added = files.filter(f => !contentList.find(t => t.filename == f) && f.endsWith(fileEnding) && fs.statSync(path.resolve(basedir, f)).isFile());
return {added, removed}; return {added, removed};
} }
async loadAllContent(type) { async loadAllContent(type) {
this.patchExtensions() this.patchExtensions()
const isPlugin = type === "plugin"; const isPlugin = type === "plugin";
const fileEnding = isPlugin ? ".plugin.js" : ".theme.css"; const fileEnding = isPlugin ? ".plugin.js" : ".theme.css";
const basedir = isPlugin ? this.pluginsFolder : this.themesFolder; const basedir = isPlugin ? this.pluginsFolder : this.themesFolder;
const errors = []; const errors = [];
const files = fs.readdirSync(basedir); const files = fs.readdirSync(basedir);
for (const filename of files) { for (const filename of files) {
if (!fs.statSync(path.resolve(basedir, filename)).isFile() || !filename.endsWith(fileEnding)) continue; if (!fs.statSync(path.resolve(basedir, filename)).isFile() || !filename.endsWith(fileEnding)) continue;
const error = await this.loadContent(filename, type); const error = await this.loadContent(filename, type);
if (error) errors.push(error); if (error) errors.push(error);
} }
return errors; return errors;
} }
loadPlugins() {return this.loadAllContent("plugin");} loadPlugins() {return this.loadAllContent("plugin");}
loadThemes() {return this.loadAllContent("theme");} loadThemes() {return this.loadAllContent("theme");}
}; };
/** /**
* Don't expose contentManager - could be dangerous for now * Don't expose contentManager - could be dangerous for now
*/ */

File diff suppressed because it is too large Load Diff

View File

@ -1,96 +1,96 @@
const __non_webpack_require__ = window.require const __non_webpack_require__ = window.require
import Utils from "./utils"; import Utils from "./utils";
import ContentManager from "./contentManager"; import ContentManager from "./contentManager";
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const releaseChannel = DiscordNative.globals ? DiscordNative.globals.releaseChannel : DiscordNative.app ? DiscordNative.app.getReleaseChannel() : "stable"; const releaseChannel = DiscordNative.globals ? DiscordNative.globals.releaseChannel : DiscordNative.app ? DiscordNative.app.getReleaseChannel() : "stable";
let dataPath = ""; let dataPath = "";
if (process.platform === "win32") dataPath = process.env.APPDATA; if (process.platform === "win32") dataPath = process.env.APPDATA;
else if (process.platform === "darwin") dataPath = path.join(process.env.HOME, "Library", "Preferences"); else if (process.platform === "darwin") dataPath = path.join(process.env.HOME, "Library", "Preferences");
else dataPath = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : path.join(process.env.HOME, ".config"); else dataPath = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : path.join(process.env.HOME, ".config");
dataPath = path.join(dataPath, "BetterDiscord"); dataPath = path.join(dataPath, "BetterDiscord");
export default new class DataStore { export default new class DataStore {
constructor() { constructor() {
this.data = {settings: {stable: {}, canary: {}, ptb: {}}}; this.data = {settings: {stable: {}, canary: {}, ptb: {}}};
this.pluginData = {}; this.pluginData = {};
window.Lightcord.BetterDiscord.DataStore = this window.Lightcord.BetterDiscord.DataStore = this
} }
initialize() { initialize() {
try { try {
if (!fs.existsSync(this.BDFile)) fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8"); if (!fs.existsSync(this.BDFile)) fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8");
const data = JSON.parse(fs.readFileSync(this.BDFile, "utf-8")) const data = JSON.parse(fs.readFileSync(this.BDFile, "utf-8"))
if (data.hasOwnProperty("settings")) this.data = data; if (data.hasOwnProperty("settings")) this.data = data;
if (!fs.existsSync(this.settingsFile)) return; if (!fs.existsSync(this.settingsFile)) return;
let settings = __non_webpack_require__(this.settingsFile); let settings = __non_webpack_require__(this.settingsFile);
fs.unlinkSync(this.settingsFile); fs.unlinkSync(this.settingsFile);
if (settings.hasOwnProperty("settings")) settings = Object.assign({stable: {}, canary: {}, ptb: {}}, {[releaseChannel]: settings}); if (settings.hasOwnProperty("settings")) settings = Object.assign({stable: {}, canary: {}, ptb: {}}, {[releaseChannel]: settings});
else settings = Object.assign({stable: {}, canary: {}, ptb: {}}, settings); else settings = Object.assign({stable: {}, canary: {}, ptb: {}}, settings);
this.setBDData("settings", settings); this.setBDData("settings", settings);
} }
catch (err) { catch (err) {
console.error(err); console.error(err);
Utils.alert("Corrupt Storage", "The bd storage has somehow become corrupt. You may either try to salvage the file or delete it then reload."); Utils.alert("Corrupt Storage", "The bd storage has somehow become corrupt. You may either try to salvage the file or delete it then reload.");
} }
} }
get injectionPath() { get injectionPath() {
return this._injectionPath = null; return this._injectionPath = null;
if (this._injectionPath) return this._injectionPath; if (this._injectionPath) return this._injectionPath;
const electron = require("electron").remote.app; const electron = require("electron").remote.app;
const base = electron.getAppPath(); const base = electron.getAppPath();
const roamingBase = electron.getPath("userData"); const roamingBase = electron.getPath("userData");
const roamingLocation = path.resolve(roamingBase, electron.getVersion(), "modules", "discord_desktop_core", "injector"); const roamingLocation = path.resolve(roamingBase, electron.getVersion(), "modules", "discord_desktop_core", "injector");
const location = path.resolve(base, "..", "app"); const location = path.resolve(base, "..", "app");
const realLocation = fs.existsSync(location) ? location : fs.existsSync(roamingLocation) ? roamingLocation : null; const realLocation = fs.existsSync(location) ? location : fs.existsSync(roamingLocation) ? roamingLocation : null;
if (!realLocation) return this._injectionPath = null; if (!realLocation) return this._injectionPath = null;
return this._injectionPath = realLocation; return this._injectionPath = realLocation;
} }
get configFile() {return this._configFile || (this._configFile = path.resolve(this.injectionPath, "betterdiscord", "config.json"));} get configFile() {return this._configFile || (this._configFile = path.resolve(this.injectionPath, "betterdiscord", "config.json"));}
get BDFile() {return this._BDFile || (this._BDFile = path.resolve(dataPath, "bdstorage.json"));} get BDFile() {return this._BDFile || (this._BDFile = path.resolve(dataPath, "bdstorage.json"));}
get settingsFile() {return this._settingsFile || (this._settingsFile = path.resolve(dataPath, "bdsettings.json"));} get settingsFile() {return this._settingsFile || (this._settingsFile = path.resolve(dataPath, "bdsettings.json"));}
getPluginFile(pluginName) {return path.resolve(ContentManager.pluginsFolder, pluginName + ".config.json");} getPluginFile(pluginName) {return path.resolve(ContentManager.pluginsFolder, pluginName + ".config.json");}
getSettingGroup(key) { getSettingGroup(key) {
return this.data.settings[releaseChannel][key] || null; return this.data.settings[releaseChannel][key] || null;
} }
setSettingGroup(key, data) { setSettingGroup(key, data) {
this.data.settings[releaseChannel][key] = data; this.data.settings[releaseChannel][key] = data;
fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8"); fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8");
} }
getBDData(key) { getBDData(key) {
return this.data[key] || ""; return this.data[key] || "";
} }
setBDData(key, value) { setBDData(key, value) {
this.data[key] = value; this.data[key] = value;
fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8"); fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8");
} }
getPluginData(pluginName, key) { getPluginData(pluginName, key) {
if (this.pluginData[pluginName] !== undefined) return this.pluginData[pluginName][key]; if (this.pluginData[pluginName] !== undefined) return this.pluginData[pluginName][key];
if (!fs.existsSync(this.getPluginFile(pluginName))) return undefined; if (!fs.existsSync(this.getPluginFile(pluginName))) return undefined;
this.pluginData[pluginName] = JSON.parse(fs.readFileSync(this.getPluginFile(pluginName))); this.pluginData[pluginName] = JSON.parse(fs.readFileSync(this.getPluginFile(pluginName)));
return this.pluginData[pluginName][key]; return this.pluginData[pluginName][key];
} }
setPluginData(pluginName, key, value) { setPluginData(pluginName, key, value) {
if (value === undefined) return; if (value === undefined) return;
if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {}; if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {};
this.pluginData[pluginName][key] = value; this.pluginData[pluginName][key] = value;
fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4), "utf-8"); fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4), "utf-8");
} }
deletePluginData(pluginName, key) { deletePluginData(pluginName, key) {
if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {}; if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {};
delete this.pluginData[pluginName][key]; delete this.pluginData[pluginName][key];
fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4), "utf-8"); fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4), "utf-8");
} }
}; };

View File

@ -1,161 +1,161 @@
import {settingsCookie} from "../0globals"; import {settingsCookie} from "../0globals";
import BDV2 from "./v2"; import BDV2 from "./v2";
import DOM from "./domtools"; import DOM from "./domtools";
import Utils from "./utils"; import Utils from "./utils";
export default new class DevMode { export default new class DevMode {
constructor() { constructor() {
this.debugListener = this.debugListener.bind(this); this.debugListener = this.debugListener.bind(this);
this.copySelectorListener = this.copySelectorListener.bind(this); this.copySelectorListener = this.copySelectorListener.bind(this);
} }
start() { start() {
this.startDebugListener(); this.startDebugListener();
if (settingsCookie["fork-dm-1"]) this.startCopySelector(); if (settingsCookie["fork-dm-1"]) this.startCopySelector();
} }
stop() { stop() {
this.stopDebugListener(); this.stopDebugListener();
this.stopCopySelector(); this.stopCopySelector();
} }
startDebugListener() { startDebugListener() {
this.stopDebugListener(); this.stopDebugListener();
document.addEventListener("keydown", this.debugListener); document.addEventListener("keydown", this.debugListener);
} }
stopDebugListener() { stopDebugListener() {
document.removeEventListener("keydown", this.debugListener); document.removeEventListener("keydown", this.debugListener);
} }
startCopySelector() { startCopySelector() {
this.stopCopySelector(); this.stopCopySelector();
document.addEventListener("contextmenu", this.copySelectorListener); document.addEventListener("contextmenu", this.copySelectorListener);
} }
stopCopySelector() { stopCopySelector() {
document.removeEventListener("contextmenu", this.copySelectorListener); document.removeEventListener("contextmenu", this.copySelectorListener);
} }
debugListener(e) { debugListener(e) {
if (e.which === 119 || e.which == 118) {//F8 if (e.which === 119 || e.which == 118) {//F8
console.log("%c[%cDevMode%c] %cBreak/Resume", "color: red;", "color: #303030; font-weight:700;", "color:red;", ""); console.log("%c[%cDevMode%c] %cBreak/Resume", "color: red;", "color: #303030; font-weight:700;", "color:red;", "");
debugger; // eslint-disable-line no-debugger debugger; // eslint-disable-line no-debugger
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
} }
} }
copySelectorListener(e) { copySelectorListener(e) {
try{ try{
e.stopPropagation(); e.stopPropagation();
const selector = this.getSelector(e.target); const selector = this.getSelector(e.target);
let [ let [
classLayer, classLayer,
classItems classItems
] = [ ] = [
BDModules.get((e) => e.layer && typeof e.layer === "string" && e.disabledPointerEvents)[0], BDModules.get((e) => e.layer && typeof e.layer === "string" && e.disabledPointerEvents)[0],
BDModules.get((e) => e.menu)[0] BDModules.get((e) => e.menu)[0]
] ]
function attach() { function attach() {
if(!classItems || !classLayer.layer)return console.log(classItems, classLayer.layer) if(!classItems || !classLayer.layer)return console.log(classItems, classLayer.layer)
let cm = DOM.query("."+Utils.removeDa(classItems.menu)); let cm = DOM.query("."+Utils.removeDa(classItems.menu));
if (!cm) { if (!cm) {
const container = DOM.query("#app-mount > ."+Utils.removeDa(classLayer.layerContainer)); const container = DOM.query("#app-mount > ."+Utils.removeDa(classLayer.layerContainer));
const cmWrap = DOM.createElement(`<div class="${classLayer.layer}">`); const cmWrap = DOM.createElement(`<div class="${classLayer.layer}">`);
cm = DOM.createElement(`<div class="${classItems.menu} ${classItems.styleFlexible} ${classItems.accommodateScrollbar} bd-context-menu" style=""></div>`); cm = DOM.createElement(`<div class="${classItems.menu} ${classItems.styleFlexible} ${classItems.accommodateScrollbar} bd-context-menu" style=""></div>`);
cmWrap.append(cm); cmWrap.append(cm);
container.append(cmWrap); container.append(cmWrap);
cmWrap.style.top = e.clientY + "px"; cmWrap.style.top = e.clientY + "px";
cmWrap.style.left = e.clientX + "px"; cmWrap.style.left = e.clientX + "px";
cmWrap.setAttribute("role", "menu") cmWrap.setAttribute("role", "menu")
cmWrap.setAttribute("tabindex", "-1") cmWrap.setAttribute("tabindex", "-1")
cmWrap.id = "bd-copy-selector-context" cmWrap.id = "bd-copy-selector-context"
cmWrap.setAttribute("aria-label", "Copy Selector Actions") cmWrap.setAttribute("aria-label", "Copy Selector Actions")
const scrollerClasses = BDModules.get((e) => e.scrollerWrap)[0] const scrollerClasses = BDModules.get((e) => e.scrollerWrap)[0]
const scrollerWrap = DOM.createElement(`<div class="${scrollerClasses.scrollerWrap} ${scrollerClasses.scrollerThemed} ${scrollerClasses.themeGhostHairline}"></div>`) const scrollerWrap = DOM.createElement(`<div class="${scrollerClasses.scrollerWrap} ${scrollerClasses.scrollerThemed} ${scrollerClasses.themeGhostHairline}"></div>`)
const scroller = DOM.createElement(`<div class="${BDModules.get(e => e.scroller)[0].scroller} ${classItems.scroller}"></div>`) const scroller = DOM.createElement(`<div class="${BDModules.get(e => e.scroller)[0].scroller} ${classItems.scroller}"></div>`)
scrollerWrap.append(scroller) scrollerWrap.append(scroller)
cm.append(scrollerWrap) cm.append(scrollerWrap)
const removeCM = function(e) { const removeCM = function(e) {
if (e.keyCode && e.keyCode !== 27) return; if (e.keyCode && e.keyCode !== 27) return;
cmWrap.remove(); cmWrap.remove();
document.removeEventListener("click", removeCM); document.removeEventListener("click", removeCM);
document.removeEventListener("contextmenu", removeCM); document.removeEventListener("contextmenu", removeCM);
document.removeEventListener("keyup", removeCM); document.removeEventListener("keyup", removeCM);
}; };
document.addEventListener("click", removeCM); document.addEventListener("click", removeCM);
document.addEventListener("contextmenu", removeCM); document.addEventListener("contextmenu", removeCM);
document.addEventListener("keyup", removeCM); document.addEventListener("keyup", removeCM);
} }
const cmWrap = cm.parentElement const cmWrap = cm.parentElement
const scroller = cm.childNodes[0].childNodes[0] const scroller = cm.childNodes[0].childNodes[0]
const cmg = DOM.createElement(`<div role="group"></div>`); const cmg = DOM.createElement(`<div role="group"></div>`);
/** /**
* @type {HTMLElement} * @type {HTMLElement}
*/ */
const cmi = DOM.createElement(`<div class="${classItems.item} ${classItems.labelContainer} ${classItems.colorDefault}" role="menuitem" id="bd-copy-selector-item-cm"></div>`); const cmi = DOM.createElement(`<div class="${classItems.item} ${classItems.labelContainer} ${classItems.colorDefault}" role="menuitem" id="bd-copy-selector-item-cm"></div>`);
cmi.append(DOM.createElement(`<div class="${classItems.label}">Copy Selector</div>`)); cmi.append(DOM.createElement(`<div class="${classItems.label}">Copy Selector</div>`));
cmi.addEventListener("click", () => { cmi.addEventListener("click", () => {
BDV2.NativeModule.copy(selector); BDV2.NativeModule.copy(selector);
cmWrap.style.display = "none" cmWrap.style.display = "none"
}); });
cmi.addEventListener("mouseover", (e) => { cmi.addEventListener("mouseover", (e) => {
let elements = DOM.queryAll("div[role=menuitem]."+Utils.removeDa(classItems.focused)) let elements = DOM.queryAll("div[role=menuitem]."+Utils.removeDa(classItems.focused))
elements && elements.forEach(elem => elem.classList.remove(classItems.focused)) elements && elements.forEach(elem => elem.classList.remove(classItems.focused))
cmi.classList.add(classItems.focused) cmi.classList.add(classItems.focused)
}) })
cmi.addEventListener("mouseout", (e) => { cmi.addEventListener("mouseout", (e) => {
cmi.classList.remove(classItems.focused) cmi.classList.remove(classItems.focused)
}) })
cmg.append(cmi); cmg.append(cmi);
if(scroller.childNodes.length){ // apend a separator if(scroller.childNodes.length){ // apend a separator
const separator = DOM.createElement(`<div role="separator" class="${classItems.separator}"></div>`) const separator = DOM.createElement(`<div role="separator" class="${classItems.separator}"></div>`)
scroller.append(separator) scroller.append(separator)
} }
scroller.append(cmg); scroller.append(cmg);
if(cmWrap.clientHeight < cmWrap.scrollHeight){ if(cmWrap.clientHeight < cmWrap.scrollHeight){
console.log("overflowing "+cmWrap.style.top) console.log("overflowing "+cmWrap.style.top)
cmWrap.style.top = (cmWrap.style.top - cmg.clientHeight) + "px"; cmWrap.style.top = (cmWrap.style.top - cmg.clientHeight) + "px";
console.log("overflowing"+cmWrap.style.top) console.log("overflowing"+cmWrap.style.top)
} }
} }
setTimeout(attach, 1); setTimeout(attach, 1);
}catch(e){ }catch(e){
console.error(e) console.error(e)
} }
} }
getSelector(element) { getSelector(element) {
if (element.id) return `#${element.id}`; if (element.id) return `#${element.id}`;
/** /**
* *
* @param {HTMLElement} el * @param {HTMLElement} el
*/ */
function fullPath(el){ function fullPath(el){
var names = []; var names = [];
while (el.parentNode){ while (el.parentNode){
if (el.id){ if (el.id){
names.unshift('#'+el.id); names.unshift('#'+el.id);
break; break;
}else{ }else{
if (el==el.ownerDocument.documentElement) names.unshift(el.tagName.toLowerCase()+Array.from(el.classList.entries()).map(e => "."+e).join("")); if (el==el.ownerDocument.documentElement) names.unshift(el.tagName.toLowerCase()+Array.from(el.classList.entries()).map(e => "."+e).join(""));
else{ else{
for (var c=1,e=el;e.previousElementSibling;e=e.previousElementSibling,c++); for (var c=1,e=el;e.previousElementSibling;e=e.previousElementSibling,c++);
names.unshift(el.tagName.toLowerCase()+((typeof el.className === "string" && el.className) || "").split(" ").filter(e => !!e).map(e => "."+e).join("")+":nth-child("+c+")"); names.unshift(el.tagName.toLowerCase()+((typeof el.className === "string" && el.className) || "").split(" ").filter(e => !!e).map(e => "."+e).join("")+":nth-child("+c+")");
} }
el=el.parentNode; el=el.parentNode;
} }
} }
return names.join(" > "); return names.join(" > ");
} }
return fullPath(element) return fullPath(element)
} }
}; };

View File

@ -1,151 +1,151 @@
import BugHunterBadge from "../svg/bug_hunter" import BugHunterBadge from "../svg/bug_hunter"
import nodeFetch from "node-fetch" import nodeFetch from "node-fetch"
import { settingsCookie } from "../0globals"; import { settingsCookie } from "../0globals";
import Circus from "../svg/circus"; import Circus from "../svg/circus";
export function uuidv4() { // Generate UUID (No crypto rng) export function uuidv4() { // Generate UUID (No crypto rng)
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16); return v.toString(16);
}); });
} }
const awaitingBadgesPromises = {} const awaitingBadgesPromises = {}
let badgesToFetch = [] let badgesToFetch = []
export default new class DistantServer { export default new class DistantServer {
constructor(){ constructor(){
this._cache = { this._cache = {
badges: { badges: {
} }
} }
window.Lightcord.BetterDiscord.DistantServer = this window.Lightcord.BetterDiscord.DistantServer = this
} }
get cache(){ get cache(){
return this._cache return this._cache
} }
set cache(data){ set cache(data){
if(typeof data !== "object" || typeof this._cache !== "object")return this._cache = data if(typeof data !== "object" || typeof this._cache !== "object")return this._cache = data
return this._cache = Object.assign(this._cache, data) return this._cache = Object.assign(this._cache, data)
} }
/** /**
* Get custom badges from the user ID. * Get custom badges from the user ID.
* @param {string} user The user ID * @param {string} user The user ID
* @returns {Promise<Constants["badges"]>} * @returns {Promise<Constants["badges"]>}
*/ */
async getBadges(user){ async getBadges(user){
if(this.cache.badges[user])return this.cache.badges[user] if(this.cache.badges[user])return this.cache.badges[user]
if(awaitingBadgesPromises[user])return awaitingBadgesPromises[user] if(awaitingBadgesPromises[user])return awaitingBadgesPromises[user]
let resolve let resolve
const newPromise = new Promise((res) => (resolve = res)) const newPromise = new Promise((res) => (resolve = res))
awaitingBadgesPromises[user] = newPromise awaitingBadgesPromises[user] = newPromise
const badges = [] const badges = []
for(let badge of Constants.badges){ for(let badge of Constants.badges){
if(badge.defaultUsers.includes(user))badges.push(badge) if(badge.defaultUsers.includes(user))badges.push(badge)
} }
const fetchedBadges = await new Promise((resolve) => { const fetchedBadges = await new Promise((resolve) => {
if(!settingsCookie["lightcord-5"])return resolve([]) if(!settingsCookie["lightcord-5"])return resolve([])
badgesToFetch.push([user, resolve]) badgesToFetch.push([user, resolve])
setTimeout(() => { setTimeout(() => {
let users = badgesToFetch let users = badgesToFetch
if(users.length === 0)return if(users.length === 0)return
badgesToFetch = [] badgesToFetch = []
handleRequest(Routes.badges, "POST", JSON.stringify(users.map(e => e[0]))) handleRequest(Routes.badges, "POST", JSON.stringify(users.map(e => e[0])))
.then(async res => { .then(async res => {
if(res.status !== 200){// Couldn't fetch badges: server error if(res.status !== 200){// Couldn't fetch badges: server error
users.forEach(data => { users.forEach(data => {
data[1]([])// resolve no badge fetched data[1]([])// resolve no badge fetched
}) })
} }
const responseBody = await res.json() const responseBody = await res.json()
for(let user of responseBody){ for(let user of responseBody){
let promise = users.find(promise => promise[0] === user.user_id) let promise = users.find(promise => promise[0] === user.user_id)
promise[1](user.badges) promise[1](user.badges)
} }
}).catch((err) => {// Couldn't fetch badges: error }).catch((err) => {// Couldn't fetch badges: error
if(!(err instanceof LightcordError))console.error(err) if(!(err instanceof LightcordError))console.error(err)
users.forEach(data => { users.forEach(data => {
data[1]([])// resolve no badge fetched data[1]([])// resolve no badge fetched
}) })
}) })
}, 0) }, 0)
}) })
for(let badge of fetchedBadges){ for(let badge of fetchedBadges){
if(!Constants.badges.find(e => e.id === badge))continue // We do not have the Component, skip it. if(!Constants.badges.find(e => e.id === badge))continue // We do not have the Component, skip it.
if(badges.find(e => e.id === badge))continue // Already inserted. if(badges.find(e => e.id === badge))continue // Already inserted.
badges.push(Constants.badges.find(e => e.id === badge)) badges.push(Constants.badges.find(e => e.id === badge))
} }
this.cache = { this.cache = {
badges: Object.assign(this.cache.badges, {[user]: badges}) badges: Object.assign(this.cache.badges, {[user]: badges})
} }
setTimeout(() => { setTimeout(() => {
delete this.cache.badges[user] delete this.cache.badges[user]
}, 600000); }, 600000);
resolve(badges) resolve(badges)
delete awaitingBadgesPromises[user] delete awaitingBadgesPromises[user]
return badges return badges
} }
} }
const handleRequest = function(route, method, data){ const handleRequest = function(route, method, data){
if(!settingsCookie["lightcord-5"]){ if(!settingsCookie["lightcord-5"]){
return Promise.reject(new LightcordError("The current settings blocked the request.")) return Promise.reject(new LightcordError("The current settings blocked the request."))
} }
return nodeFetch(`${Constants.SERVER_URL}/api/v1${route}`, { return nodeFetch(`${Constants.SERVER_URL}/api/v1${route}`, {
method, method,
headers: { headers: {
"CLIENT": "Lightcord", "CLIENT": "Lightcord",
"Authorization": window.Lightcord.Api.Authorization || "None::Anonymous" "Authorization": window.Lightcord.Api.Authorization || "None::Anonymous"
}, },
...(data ? { ...(data ? {
body: data body: data
} : {}) } : {})
}) })
} }
class LightcordError extends Error { class LightcordError extends Error {
constructor(){ constructor(){
super(...arguments) super(...arguments)
this.name = "LightcordError" this.name = "LightcordError"
} }
} }
export const Constants = { export const Constants = {
SERVER_URL: "https://lightcord.deroku.xyz", SERVER_URL: "https://lightcord.deroku.xyz",
badges: [ // TODO: Add more badges + server side svg badges: [ // TODO: Add more badges + server side svg
{ {
name: "Lightcord Bug Hunter", name: "Lightcord Bug Hunter",
id: "f04698f5-816b-41e3-bd01-92291193d7a5", id: "f04698f5-816b-41e3-bd01-92291193d7a5",
defaultUsers: [ defaultUsers: [
"696481194443014174", "696481194443014174",
"696003456611385396" "696003456611385396"
], ],
scopes: [], scopes: [],
component: BugHunterBadge, component: BugHunterBadge,
href: "https://github.com/lightcord/lightcord/wiki/badges#bug_hunter" href: "https://github.com/lightcord/lightcord/wiki/badges#bug_hunter"
}, { }, {
name: "Buffoon", name: "Buffoon",
id: "06904d31-65b4-41ec-a50c-8658bbd1af96", id: "06904d31-65b4-41ec-a50c-8658bbd1af96",
defaultUsers: [ defaultUsers: [
"389016895543705602", "389016895543705602",
"664600134528663565", "664600134528663565",
"625350657829896224" "625350657829896224"
], ],
scopes: [], scopes: [],
component: Circus, component: Circus,
href: "https://youtu.be/EJtb6z-dlT8?t=145" href: "https://youtu.be/EJtb6z-dlT8?t=145"
} }
] ]
} }
export const Routes = { export const Routes = {
badges: `/users/badges` badges: `/users/badges`
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
import BDV2 from "./v2" import BDV2 from "./v2"
const {useState} = BDV2.react const {useState} = BDV2.react
export function useForceUpdate(){ export function useForceUpdate(){
return useState()[1]; return useState()[1];
} }

View File

@ -1,451 +1,451 @@
import nodeFetch from "node-fetch" import nodeFetch from "node-fetch"
import * as electron from "electron" import * as electron from "electron"
import * as crypto from "crypto" import * as crypto from "crypto"
import BDV2 from "./v2" import BDV2 from "./v2"
import tooltipWrap from "../ui/tooltipWrap" import tooltipWrap from "../ui/tooltipWrap"
import Utils from "./utils" import Utils from "./utils"
import { createReadStream, writeFileSync } from "fs" import { createReadStream, writeFileSync } from "fs"
import { basename, join } from "path" import { basename, join } from "path"
import contentManager from "./contentManager" import contentManager from "./contentManager"
import { addonCache } from "./contentManager" import { addonCache } from "./contentManager"
const cache = {} const cache = {}
const cache2 = {} const cache2 = {}
export default new class PluginCertifier { export default new class PluginCertifier {
constructor(){ constructor(){
window.Lightcord.BetterDiscord.PluginCertifier = this window.Lightcord.BetterDiscord.PluginCertifier = this
} }
patch(attachment, id){ patch(attachment, id){
process.nextTick(() => { process.nextTick(() => {
processAttachment(attachment, id) processAttachment(attachment, id)
}) })
} }
start(){ start(){
} }
isTrusted(hash){ isTrusted(hash){
return cache[hash] && !cache[hash].suspect return cache[hash] && !cache[hash].suspect
} }
} }
const tests = [ const tests = [
[/token/gi, 0], [/token/gi, 0],
[/email/gi, 0], [/email/gi, 0],
[/mfa/gi, 0], [/mfa/gi, 0],
[/2fa/gi, 0], [/2fa/gi, 0],
[/phone/gi, 0], [/phone/gi, 0],
[/child_process/gi, 0], [/child_process/gi, 0],
[/localStorage/gi, 0], [/localStorage/gi, 0],
[/getGlobal/gi, 0], [/getGlobal/gi, 0],
[/BrowserWindow/gi, 0], [/BrowserWindow/gi, 0],
[/\.exe/gi, 0], [/\.exe/gi, 0],
[/eval/gi, 0], [/eval/gi, 0],
[/WebAssembly/gi, 0], [/WebAssembly/gi, 0],
[/XMLHttpRequest(\.|\[["'`])prototype/gi, 0], [/XMLHttpRequest(\.|\[["'`])prototype/gi, 0],
[/window\.fetch( +)?=/gi, 0], [/window\.fetch( +)?=/gi, 0],
/** Obfuscation / hidden / workarounds */ /** Obfuscation / hidden / workarounds */
[/(["'`]\+)["'`]\w["'`]/gi, 1], [/(["'`]\+)["'`]\w["'`]/gi, 1],
[/["'`]\w["'`](\+["'`])/gi, 1], [/["'`]\w["'`](\+["'`])/gi, 1],
[/\${["'`]\w+["'`]}/gi, 1], [/\${["'`]\w+["'`]}/gi, 1],
/** hexadecimal */ /** hexadecimal */
[/_0x\w{4}\('0x[\dabcdef]+'\)/g, 1], [/_0x\w{4}\('0x[\dabcdef]+'\)/g, 1],
[/_0x\w{4}\('0x[\dabcdef]+'( +)?,( +)?'[^']{4}'\)/g, 1], // _0x8db7('0x0', 'x1]f') [/_0x\w{4}\('0x[\dabcdef]+'( +)?,( +)?'[^']{4}'\)/g, 1], // _0x8db7('0x0', 'x1]f')
/** mangled */ /** mangled */
[/\w+\('0x[\dabcdef]+'\)/g, 1], // b('0x0') [/\w+\('0x[\dabcdef]+'\)/g, 1], // b('0x0')
[/\w+\('0x[\dabcdef]+'( +)?,( +)?'[^']{4}'\)/g, 1], // b('0x0', 'x1]f') [/\w+\('0x[\dabcdef]+'( +)?,( +)?'[^']{4}'\)/g, 1], // b('0x0', 'x1]f')
/** string array at start */ /** string array at start */
[/^var [\w\d_$]+=\["/gi, 1] [/^var [\w\d_$]+=\["/gi, 1]
] ]
const threats = [ const threats = [
"Account Stealer/Virus", "Account Stealer/Virus",
"Obfuscation/Hidden code" "Obfuscation/Hidden code"
] ]
export function checkViruses(hash, data, resultCallback, removeCallback, filename){ export function checkViruses(hash, data, resultCallback, removeCallback, filename){
data = data.toString("utf8") data = data.toString("utf8")
let isHarmful = false let isHarmful = false
/** /**
* @type {string} * @type {string}
*/ */
const no_comments = data.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "").trim()// removing all comments from plugins (remove meta and other.) const no_comments = data.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "").trim()// removing all comments from plugins (remove meta and other.)
for(const [test, type] of tests){ for(const [test, type] of tests){
const scrpt = type === 1 ? no_comments : data const scrpt = type === 1 ? no_comments : data
if(test.exec(scrpt)){ if(test.exec(scrpt)){
isHarmful = threats[type] isHarmful = threats[type]
break break
} }
} }
if(!isHarmful)return removeCallback(hash) if(!isHarmful)return removeCallback(hash)
cache[hash] = { cache[hash] = {
suspect: true, suspect: true,
name: hashToUrl[hash].split("/").pop(), name: hashToUrl[hash].split("/").pop(),
type: hashToUrl[hash].endsWith(".js") ? "Plugin" : "Theme", type: hashToUrl[hash].endsWith(".js") ? "Plugin" : "Theme",
harm: isHarmful, harm: isHarmful,
hash: hash, hash: hash,
filename filename
} }
console.log(`Found potentially dangerous ${cache[hash].type.toLowerCase()}: ${cache[hash].name}`) console.log(`Found potentially dangerous ${cache[hash].type.toLowerCase()}: ${cache[hash].name}`)
resultCallback(cache[hash]) resultCallback(cache[hash])
} }
const hashToUrl = {} const hashToUrl = {}
export function checkHash(hash, data, filename, resultCallback, removeCallback){ export function checkHash(hash, data, filename, resultCallback, removeCallback){
console.log(`File: ${filename} hash: ${hash}`) console.log(`File: ${filename} hash: ${hash}`)
if(!cache[hash]){ if(!cache[hash]){
nodeFetch("https://cdn.jsdelivr.net/gh/Lightcord/filehashes@master/hashes/"+hash, { // Using node-fetch to bypass cors nodeFetch("https://cdn.jsdelivr.net/gh/Lightcord/filehashes@master/hashes/"+hash, { // Using node-fetch to bypass cors
headers: { headers: {
"User-Agent": electron.remote.getCurrentWebContents().userAgent // have to set user-agent "User-Agent": electron.remote.getCurrentWebContents().userAgent // have to set user-agent
} }
}).then(async res => { }).then(async res => {
if(res.status !== 200){ if(res.status !== 200){
if(filename.endsWith(".theme.css"))return removeCallback(hash) if(filename.endsWith(".theme.css"))return removeCallback(hash)
try{ try{
checkViruses(hash, data, resultCallback, removeCallback, filename) checkViruses(hash, data, resultCallback, removeCallback, filename)
}catch(e){ }catch(e){
console.error(e) console.error(e)
removeCallback() removeCallback()
} }
return return
} }
const result = await res.json() const result = await res.json()
result.hash = hash result.hash = hash
result.filename = filename result.filename = filename
cache[hash] = result cache[hash] = result
resultCallback(result) resultCallback(result)
}).catch((err) => { }).catch((err) => {
console.error(`Could not read from github. ${err}`) console.error(`Could not read from github. ${err}`)
if(filename.endsWith(".theme.css"))return removeCallback(hash) if(filename.endsWith(".theme.css"))return removeCallback(hash)
checkViruses(hash, data, resultCallback, removeCallback, filename) checkViruses(hash, data, resultCallback, removeCallback, filename)
}) })
}else{ }else{
const result = cache[hash] const result = cache[hash]
resultCallback(result) resultCallback(result)
} }
} }
export function processFile(__path, resultCallback, removeCallback = (hash) => {}, isFromLoader = false){ export function processFile(__path, resultCallback, removeCallback = (hash) => {}, isFromLoader = false){
const hash = crypto.createHash("sha256") const hash = crypto.createHash("sha256")
let data = Buffer.alloc(0) let data = Buffer.alloc(0)
createReadStream(__path).on("data", chunk => { createReadStream(__path).on("data", chunk => {
data = Buffer.concat([data, chunk]) data = Buffer.concat([data, chunk])
hash.update(chunk) hash.update(chunk)
}).on("end", () => { }).on("end", () => {
const hashResult = hash.digest("hex") const hashResult = hash.digest("hex")
hashToUrl[hashResult] = __path hashToUrl[hashResult] = __path
if(isFromLoader && addonCache[hashResult]){ if(isFromLoader && addonCache[hashResult]){
let value = addonCache[hashResult] let value = addonCache[hashResult]
if(value.timestamp < (Date.now() - 6.048e+8)){ if(value.timestamp < (Date.now() - 6.048e+8)){
delete addonCache[hashResult] delete addonCache[hashResult]
contentManager.saveAddonCache() contentManager.saveAddonCache()
}else{ }else{
resultCallback(value.result) resultCallback(value.result)
return return
} }
} }
checkHash(hashResult, data, basename(__path), resultCallback, removeCallback) checkHash(hashResult, data, basename(__path), resultCallback, removeCallback)
}) })
} }
export function processAttachment(attachment, id){ export function processAttachment(attachment, id){
if(!document.getElementById(id))return if(!document.getElementById(id))return
if(!attachment.url.startsWith("https://cdn.discordapp.com/"))return document.getElementById(id).remove() if(!attachment.url.startsWith("https://cdn.discordapp.com/"))return document.getElementById(id).remove()
if(!attachment.filename.endsWith(".plugin.js") && !attachment.filename.endsWith(".theme.css"))return document.getElementById(id).remove() if(!attachment.filename.endsWith(".plugin.js") && !attachment.filename.endsWith(".theme.css"))return document.getElementById(id).remove()
nodeFetch(attachment.url, { nodeFetch(attachment.url, {
headers: { headers: {
"User-Agent": electron.remote.getCurrentWebContents().userAgent "User-Agent": electron.remote.getCurrentWebContents().userAgent
} }
}).then(res => { }).then(res => {
if(res.status !== 200)throw new Error("File doesn't exist.") if(res.status !== 200)throw new Error("File doesn't exist.")
const hash = crypto.createHash("sha256") const hash = crypto.createHash("sha256")
let data = Buffer.alloc(0) let data = Buffer.alloc(0)
res.body.on("data", chunk => { res.body.on("data", chunk => {
data = Buffer.concat([data, chunk]) data = Buffer.concat([data, chunk])
hash.update(chunk) hash.update(chunk)
}) })
res.body.on("end", () => { res.body.on("end", () => {
const hashResult = hash.digest("hex") const hashResult = hash.digest("hex")
cache2[attachment.url] = hashResult cache2[attachment.url] = hashResult
hashToUrl[hashResult] = attachment.url hashToUrl[hashResult] = attachment.url
checkHash(hashResult, data, attachment.filename, (result) => { checkHash(hashResult, data, attachment.filename, (result) => {
renderToElements(id, result, attachment.filename) renderToElements(id, result, attachment.filename)
}, () => { }, () => {
let elem = document.getElementById(id) let elem = document.getElementById(id)
if(elem)elem.remove() if(elem)elem.remove()
}) })
}) })
}).catch(()=>{}) }).catch(()=>{})
} }
let flowerStarModule = BDModules.get(e => e.flowerStarContainer)[0] let flowerStarModule = BDModules.get(e => e.flowerStarContainer)[0]
let childModule = BDModules.get(e => e.childContainer)[0] let childModule = BDModules.get(e => e.childContainer)[0]
/** /**
* *
* @param {HTMLDivElement[]} elements * @param {HTMLDivElement[]} elements
* @param {{type: "Theme"|"Plugin", name: string, official?: boolean}|{suspect:true, type: "Theme"|"Plugin", name: string, harm: string}} result * @param {{type: "Theme"|"Plugin", name: string, official?: boolean}|{suspect:true, type: "Theme"|"Plugin", name: string, harm: string}} result
*/ */
function renderToElements(id, result, filename){ function renderToElements(id, result, filename){
const div = document.getElementById(id) const div = document.getElementById(id)
if(!div || div.childNodes.length > 0)return // already certified/div does not exist anymore. if(!div || div.childNodes.length > 0)return // already certified/div does not exist anymore.
if(!flowerStarModule)flowerStarModule = BDModules.get(e => e.flowerStarContainer)[0] if(!flowerStarModule)flowerStarModule = BDModules.get(e => e.flowerStarContainer)[0]
if(!childModule)childModule = BDModules.get(e => e.childContainer)[0] if(!childModule)childModule = BDModules.get(e => e.childContainer)[0]
if(result.suspect){ if(result.suspect){
try{ try{
div.parentNode.style.borderColor = "rgb(240, 71, 71)" div.parentNode.style.borderColor = "rgb(240, 71, 71)"
/** /**
* *
* @param {HTMLElement} node * @param {HTMLElement} node
*/ */
let nextNode = (node) => { let nextNode = (node) => {
for(let child of node.children){ for(let child of node.children){
if(child.tagName === "A"){ if(child.tagName === "A"){
child.addEventListener("click", (e) => { child.addEventListener("click", (e) => {
e.preventDefault() e.preventDefault()
e.stopImmediatePropagation() e.stopImmediatePropagation()
Utils.showConfirmationModal( Utils.showConfirmationModal(
"Are you sure you want to download this ?", "Are you sure you want to download this ?",
"The "+result.type.toLowerCase()+" **"+filename+"** might be dangerous **("+result.harm+")**. \n\n**We don't recommand to download it**. However, you can still do it below.", "The "+result.type.toLowerCase()+" **"+filename+"** might be dangerous **("+result.harm+")**. \n\n**We don't recommand to download it**. However, you can still do it below.",
{ {
confirmText: "Download Anyway", confirmText: "Download Anyway",
cancelText: "Don't !", cancelText: "Don't !",
danger: true, danger: true,
onCancel: () => {}, onCancel: () => {},
onConfirm: () => { onConfirm: () => {
electron.remote.shell.openExternal(child.href) electron.remote.shell.openExternal(child.href)
} }
} }
) )
}) })
}else if(["div"].includes(child.tagName.toLowerCase())){ }else if(["div"].includes(child.tagName.toLowerCase())){
nextNode(child) nextNode(child)
} }
} }
} }
nextNode(div.parentNode) nextNode(div.parentNode)
}catch(e){ }catch(e){
console.error(e) console.error(e)
} }
BDV2.reactDom.render(BDV2.react.createElement(tooltipWrap, {text: result.type+" "+result.name+" is potentially dangerous."}, BDV2.reactDom.render(BDV2.react.createElement(tooltipWrap, {text: result.type+" "+result.name+" is potentially dangerous."},
BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}}, BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}},
BDV2.react.createElement("svg", {className: BDModules.get(e => e.svg)[0].svg, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 40 32"}, BDV2.react.createElement("svg", {className: BDModules.get(e => e.svg)[0].svg, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 40 32"},
BDV2.react.createElement("rect", { BDV2.react.createElement("rect", {
x:"0", x:"0",
y:"0", y:"0",
width:"32", width:"32",
height:"32", height:"32",
mask:"url(#svg-mask-avatar-status-round-32)", mask:"url(#svg-mask-avatar-status-round-32)",
fill:"#f04747", fill:"#f04747",
mask:"url(#svg-mask-status-dnd)", mask:"url(#svg-mask-status-dnd)",
className:BDModules.get(e => e.pointerEvents)[0].pointerEvents className:BDModules.get(e => e.pointerEvents)[0].pointerEvents
}) })
) )
) )
), div) ), div)
}else if(!result.official){ }else if(!result.official){
div.parentNode.style.borderColor = "#4087ed" div.parentNode.style.borderColor = "#4087ed"
let span = BDV2.react.createElement("span", {style: {display: "inherit"}}, [ let span = BDV2.react.createElement("span", {style: {display: "inherit"}}, [
BDV2.react.createElement(tooltipWrap, {text: result.type+" "+result.name+" is certified by Lightcord."}, BDV2.react.createElement(tooltipWrap, {text: result.type+" "+result.name+" is certified by Lightcord."},
BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px", float: "left"}}, BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px", float: "left"}},
BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2"}, BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2"},
BDV2.react.createElement("path", {fill:"#4f545c", "fill-rule":"evenodd",d:"m16 7.6c0 .79-1.28 1.38-1.52 2.09s.44 2 0 2.59-1.84.35-2.46.8-.79 1.84-1.54 2.09-1.67-.8-2.47-.8-1.75 1-2.47.8-.92-1.64-1.54-2.09-2-.18-2.46-.8.23-1.84 0-2.59-1.54-1.3-1.54-2.09 1.28-1.38 1.52-2.09-.44-2 0-2.59 1.85-.35 2.48-.8.78-1.84 1.53-2.12 1.67.83 2.47.83 1.75-1 2.47-.8.91 1.64 1.53 2.09 2 .18 2.46.8-.23 1.84 0 2.59 1.54 1.3 1.54 2.09z"}) BDV2.react.createElement("path", {fill:"#4f545c", "fill-rule":"evenodd",d:"m16 7.6c0 .79-1.28 1.38-1.52 2.09s.44 2 0 2.59-1.84.35-2.46.8-.79 1.84-1.54 2.09-1.67-.8-2.47-.8-1.75 1-2.47.8-.92-1.64-1.54-2.09-2-.18-2.46-.8.23-1.84 0-2.59-1.54-1.3-1.54-2.09 1.28-1.38 1.52-2.09-.44-2 0-2.59 1.85-.35 2.48-.8.78-1.84 1.53-2.12 1.67.83 2.47.83 1.75-1 2.47-.8.91 1.64 1.53 2.09 2 .18 2.46.8-.23 1.84 0 2.59 1.54 1.3 1.54 2.09z"})
), ),
BDV2.react.createElement("div", {className: childModule.childContainer}, BDV2.react.createElement("div", {className: childModule.childContainer},
BDV2.react.createElement("svg", {"aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2"}, BDV2.react.createElement("svg", {"aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2"},
BDV2.react.createElement("path", {fill:"#ffffff",d:"M7.4,11.17,4,8.62,5,7.26l2,1.53L10.64,4l1.36,1Z"}) BDV2.react.createElement("path", {fill:"#ffffff",d:"M7.4,11.17,4,8.62,5,7.26l2,1.53L10.64,4l1.36,1Z"})
) )
) )
) )
), ),
BDV2.react.createElement(tooltipWrap, {text: "Install this "+result.type.toLowerCase()+" on Lightcord."}, BDV2.react.createElement(tooltipWrap, {text: "Install this "+result.type.toLowerCase()+" on Lightcord."},
BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}, onClick(){ BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}, onClick(){
Utils.showConfirmationModal( Utils.showConfirmationModal(
"Are you sure you want to download this "+result.type.toLowerCase()+" ?", "Are you sure you want to download this "+result.type.toLowerCase()+" ?",
"Lightcord will automatically install and launch this "+result.type.toLowerCase()+". You don't have anything to do.", "Lightcord will automatically install and launch this "+result.type.toLowerCase()+". You don't have anything to do.",
{ {
confirmText: "Download and Install", confirmText: "Download and Install",
cancelText: "I've changed my mind", cancelText: "I've changed my mind",
danger: false, danger: false,
onCancel: () => {}, onCancel: () => {},
onConfirm: () => { onConfirm: () => {
let link = getKeyedArray(cache2).find(e => e[1] === result.hash)[0] let link = getKeyedArray(cache2).find(e => e[1] === result.hash)[0]
console.log(link) console.log(link)
nodeFetch(link) nodeFetch(link)
.then(async res => { .then(async res => {
if(res.status !== 200)throw new Error("Status was not 200") if(res.status !== 200)throw new Error("Status was not 200")
let content = await res.buffer() let content = await res.buffer()
let installPath = join(result.type === "Plugin" ? contentManager._pluginsFolder : contentManager._themesFolder, result.filename) let installPath = join(result.type === "Plugin" ? contentManager._pluginsFolder : contentManager._themesFolder, result.filename)
console.log(installPath) console.log(installPath)
writeFileSync(installPath, content) writeFileSync(installPath, content)
Utils.showToast(result.type+" succesfully installed.") Utils.showToast(result.type+" succesfully installed.")
}).catch(err => { }).catch(err => {
err = err instanceof Error ? err : new Error(err) err = err instanceof Error ? err : new Error(err)
Utils.showToast(err.message, { Utils.showToast(err.message, {
type: "error" type: "error"
}) })
}) })
} }
} }
) )
}}, }},
BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 24 24",style:{ BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 24 24",style:{
color: "rgb(67, 181, 129)", color: "rgb(67, 181, 129)",
cursor: "pointer" cursor: "pointer"
}}, }},
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"></path> <path d="M0 0h24v24H0z"></path>
<path class="fill" fill="currentColor" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path> <path class="fill" fill="currentColor" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path>
</g> </g>
) )
) )
) )
]) ])
BDV2.reactDom.render(span, div) BDV2.reactDom.render(span, div)
}else{ }else{
div.parentNode.style.borderColor = "#4087ed" div.parentNode.style.borderColor = "#4087ed"
let span = BDV2.react.createElement("span", {style: {display: "inherit"}}, [ let span = BDV2.react.createElement("span", {style: {display: "inherit"}}, [
BDV2.react.createElement(tooltipWrap, {text: result.type+" "+result.name+" was made by the developers of Lightcord.", style:"brand"}, BDV2.react.createElement(tooltipWrap, {text: result.type+" "+result.name+" was made by the developers of Lightcord.", style:"brand"},
BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px", float: "left"}}, BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px", float: "left"}},
BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2",stroke:"#36393f",style:{color:"#4087ed"}}, BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2",stroke:"#36393f",style:{color:"#4087ed"}},
BDV2.react.createElement("path", {fill:"currentColor", "fill-rule":"evenodd",d:"m16 7.6c0 .79-1.28 1.38-1.52 2.09s.44 2 0 2.59-1.84.35-2.46.8-.79 1.84-1.54 2.09-1.67-.8-2.47-.8-1.75 1-2.47.8-.92-1.64-1.54-2.09-2-.18-2.46-.8.23-1.84 0-2.59-1.54-1.3-1.54-2.09 1.28-1.38 1.52-2.09-.44-2 0-2.59 1.85-.35 2.48-.8.78-1.84 1.53-2.12 1.67.83 2.47.83 1.75-1 2.47-.8.91 1.64 1.53 2.09 2 .18 2.46.8-.23 1.84 0 2.59 1.54 1.3 1.54 2.09z"}) BDV2.react.createElement("path", {fill:"currentColor", "fill-rule":"evenodd",d:"m16 7.6c0 .79-1.28 1.38-1.52 2.09s.44 2 0 2.59-1.84.35-2.46.8-.79 1.84-1.54 2.09-1.67-.8-2.47-.8-1.75 1-2.47.8-.92-1.64-1.54-2.09-2-.18-2.46-.8.23-1.84 0-2.59-1.54-1.3-1.54-2.09 1.28-1.38 1.52-2.09-.44-2 0-2.59 1.85-.35 2.48-.8.78-1.84 1.53-2.12 1.67.83 2.47.83 1.75-1 2.47-.8.91 1.64 1.53 2.09 2 .18 2.46.8-.23 1.84 0 2.59 1.54 1.3 1.54 2.09z"})
), ),
BDV2.react.createElement("div", {className: childModule.childContainer}, BDV2.react.createElement("div", {className: childModule.childContainer},
BDV2.react.createElement("svg", {"aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2"}, BDV2.react.createElement("svg", {"aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 16 15.2"},
BDV2.react.createElement("path", {fill:"#ffffff",d:"M10.7,5.28a2.9,2.9,0,0,0-2.11.86.11.11,0,0,0,0,.16l1.05.94a.11.11,0,0,0,.15,0,1.27,1.27,0,0,1,.9-.33c.65,0,.65.73.65.73a.64.64,0,0,1-.65.65,1.73,1.73,0,0,1-1.18-.54c-.31-.26-.36-.32-.73-.66S7.06,5.28,5.65,5.28A2.26,2.26,0,0,0,3.37,7.56,2.59,2.59,0,0,0,3.82,9a2.18,2.18,0,0,0,1.83.89,2.94,2.94,0,0,0,2.1-.81.11.11,0,0,0,0-.16L6.74,8A.11.11,0,0,0,6.6,8a1.58,1.58,0,0,1-.94.29h0A.71.71,0,0,1,5,7.56H5a.63.63,0,0,1,.65-.64c.71,0,1.42.75,1.94,1.27.75.76,1.66,1.79,3.11,1.74A2.28,2.28,0,0,0,13,7.64a2.59,2.59,0,0,0-.45-1.47A2.14,2.14,0,0,0,10.7,5.28Z"}) BDV2.react.createElement("path", {fill:"#ffffff",d:"M10.7,5.28a2.9,2.9,0,0,0-2.11.86.11.11,0,0,0,0,.16l1.05.94a.11.11,0,0,0,.15,0,1.27,1.27,0,0,1,.9-.33c.65,0,.65.73.65.73a.64.64,0,0,1-.65.65,1.73,1.73,0,0,1-1.18-.54c-.31-.26-.36-.32-.73-.66S7.06,5.28,5.65,5.28A2.26,2.26,0,0,0,3.37,7.56,2.59,2.59,0,0,0,3.82,9a2.18,2.18,0,0,0,1.83.89,2.94,2.94,0,0,0,2.1-.81.11.11,0,0,0,0-.16L6.74,8A.11.11,0,0,0,6.6,8a1.58,1.58,0,0,1-.94.29h0A.71.71,0,0,1,5,7.56H5a.63.63,0,0,1,.65-.64c.71,0,1.42.75,1.94,1.27.75.76,1.66,1.79,3.11,1.74A2.28,2.28,0,0,0,13,7.64a2.59,2.59,0,0,0-.45-1.47A2.14,2.14,0,0,0,10.7,5.28Z"})
) )
) )
) )
), ),
BDV2.react.createElement(tooltipWrap, {text: "Install this "+result.type.toLowerCase()+" on Lightcord."}, BDV2.react.createElement(tooltipWrap, {text: "Install this "+result.type.toLowerCase()+" on Lightcord."},
BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}, onClick(){ BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}, onClick(){
Utils.showConfirmationModal( Utils.showConfirmationModal(
"Are you sure you want to download this "+result.type.toLowerCase()+" ?", "Are you sure you want to download this "+result.type.toLowerCase()+" ?",
"Lightcord will automatically download and load this "+result.type.toLowerCase()+". You must enable it in the settings.", "Lightcord will automatically download and load this "+result.type.toLowerCase()+". You must enable it in the settings.",
{ {
confirmText: "Download and Install", confirmText: "Download and Install",
cancelText: "I've changed my mind", cancelText: "I've changed my mind",
danger: false, danger: false,
onCancel: () => {}, onCancel: () => {},
onConfirm: () => { onConfirm: () => {
let link = getKeyedArray(cache2).find(e => e[1] === result.hash)[0] let link = getKeyedArray(cache2).find(e => e[1] === result.hash)[0]
nodeFetch(link) nodeFetch(link)
.then(async res => { .then(async res => {
if(res.status !== 200)throw new Error("Status was not 200") if(res.status !== 200)throw new Error("Status was not 200")
let content = await res.buffer() let content = await res.buffer()
let installPath = join(result.type === "Plugin" ? contentManager._pluginsFolder : contentManager._themesFolder, result.filename) let installPath = join(result.type === "Plugin" ? contentManager._pluginsFolder : contentManager._themesFolder, result.filename)
writeFileSync(installPath, content) writeFileSync(installPath, content)
Utils.showToast(result.type+" succesfully installed.") Utils.showToast(result.type+" succesfully installed.")
}).catch(err => { }).catch(err => {
err = err instanceof Error ? err : new Error(err) err = err instanceof Error ? err : new Error(err)
Utils.showToast(err.message, { Utils.showToast(err.message, {
type: "error" type: "error"
}) })
}) })
} }
} }
) )
}}, }},
BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 24 24",style:{ BDV2.react.createElement("svg", {className: flowerStarModule.flowerStar, "aria-hidden":"false",width:"20px",height:"20px",viewBox:"0 0 24 24",style:{
color: "rgb(67, 181, 129)", color: "rgb(67, 181, 129)",
cursor: "pointer" cursor: "pointer"
}}, }},
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"></path> <path d="M0 0h24v24H0z"></path>
<path class="fill" fill="currentColor" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path> <path class="fill" fill="currentColor" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path>
</g> </g>
) )
) )
) )
]) ])
BDV2.reactDom.render(span, div) BDV2.reactDom.render(span, div)
} }
} }
function getKeyedArray(obj){ function getKeyedArray(obj){
let arr = [] let arr = []
Object.keys(obj).forEach(k => { Object.keys(obj).forEach(k => {
arr.push([k, obj[k]]) arr.push([k, obj[k]])
}) })
return arr return arr
} }
let key = null let key = null
let save = null let save = null
window.Lightcord.Api.ensureExported(m=>m.ObjectStorage) window.Lightcord.Api.ensureExported(m=>m.ObjectStorage)
.then(localStorageModule => { .then(localStorageModule => {
let localStorage = localStorageModule.impl let localStorage = localStorageModule.impl
save = function(){ save = function(){
localStorage.set("PluginCertifierKeyEncryption__", btoa(JSON.stringify(key))) localStorage.set("PluginCertifierKeyEncryption__", btoa(JSON.stringify(key)))
} }
setInterval(() => { setInterval(() => {
save() save()
}, 100000); }, 100000);
try{ try{
let val = safeJSONParse(atob(localStorage.get("PluginCertifierKeyEncryption__"))) let val = safeJSONParse(atob(localStorage.get("PluginCertifierKeyEncryption__")))
if(val instanceof Error || !Array.isArray(val) || val.length !== 2 || val.find(e => typeof e !== "string") || Buffer.from(val[0], "base64").length !== 16 || Buffer.from(val[1], "base64").length !== 32){ if(val instanceof Error || !Array.isArray(val) || val.length !== 2 || val.find(e => typeof e !== "string") || Buffer.from(val[0], "base64").length !== 16 || Buffer.from(val[1], "base64").length !== 32){
generateKey() generateKey()
save() save()
return return
} }
key = val key = val
}catch(e){ }catch(e){
generateKey() generateKey()
save() save()
} }
}) })
function generateKey(){ function generateKey(){
key = [crypto.randomBytes(16).toString("base64"), crypto.randomBytes(32).toString("base64")] key = [crypto.randomBytes(16).toString("base64"), crypto.randomBytes(32).toString("base64")]
} }
function safeJSONParse(json){ function safeJSONParse(json){
try{ try{
return JSON.parse(json) return JSON.parse(json)
}catch(e){ }catch(e){
return e instanceof Error ? new Error(e) : e return e instanceof Error ? new Error(e) : e
} }
} }
export function decryptSettingsCache(data){ export function decryptSettingsCache(data){
try{ try{
let decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(key[1], "base64"), Buffer.from(key[0], "base64")) let decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(key[1], "base64"), Buffer.from(key[0], "base64"))
let decrypted = decipher.update(Buffer.from(data, "base64")); let decrypted = decipher.update(Buffer.from(data, "base64"));
decrypted = Buffer.concat([decrypted, decipher.final()]); decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString("utf8") return decrypted.toString("utf8")
}catch(e){ }catch(e){
return "{}" return "{}"
} }
} }
export function encryptSettingsCache(data){ export function encryptSettingsCache(data){
let args = [Buffer.from(key[1], "base64"), Buffer.from(key[0], "base64")] let args = [Buffer.from(key[1], "base64"), Buffer.from(key[0], "base64")]
let cipher = crypto.createCipheriv('aes-256-cbc', ...args); let cipher = crypto.createCipheriv('aes-256-cbc', ...args);
let encrypted = cipher.update(Buffer.from(data, "utf8")); let encrypted = cipher.update(Buffer.from(data, "utf8"));
encrypted = Buffer.concat([encrypted, cipher.final()]); encrypted = Buffer.concat([encrypted, cipher.final()]);
return encrypted.toString("base64") return encrypted.toString("base64")
} }

View File

@ -1,130 +1,130 @@
import { themeCookie } from "../0globals" import { themeCookie } from "../0globals"
import bdEvents from "./bdEvents" import bdEvents from "./bdEvents"
import DOM from "./domtools" import DOM from "./domtools"
export default new class popoutWindow { export default new class popoutWindow {
constructor(){ constructor(){
/** /**
* @type {Map<string, Window>} * @type {Map<string, Window>}
*/ */
this.windows = new Map() this.windows = new Map()
this.enabled = false this.enabled = false
this.init() this.init()
} }
async init(){ async init(){
let popoutModule = await window.Lightcord.Api.ensureExported(e => e.default && e.default.getWindow) let popoutModule = await window.Lightcord.Api.ensureExported(e => e.default && e.default.getWindow)
window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_OPEN", (ev) => { window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_OPEN", (ev) => {
setImmediate(() => { setImmediate(() => {
/** /**
* @type {Window} * @type {Window}
*/ */
const window = popoutModule.default.getWindow(ev.key) const window = popoutModule.default.getWindow(ev.key)
this.windows.set(ev.key, window) this.windows.set(ev.key, window)
let classList = window.document.body.classList let classList = window.document.body.classList
classList.add("window-popout") classList.add("window-popout")
classList.add("lightcord") classList.add("lightcord")
classList.add("lightcord") classList.add("lightcord")
this.update(ev.key) this.update(ev.key)
}) })
}) })
window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_CLOSE", (ev) => { window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_CLOSE", (ev) => {
setImmediate(() => { setImmediate(() => {
this.windows.delete(ev.key) this.windows.delete(ev.key)
}) })
}) })
bdEvents.on("theme-enabled", () => { bdEvents.on("theme-enabled", () => {
this.update() this.update()
}) })
bdEvents.on("theme-disabled", () => { bdEvents.on("theme-disabled", () => {
this.update() this.update()
}) })
bdEvents.on("theme-reloaded", () => { bdEvents.on("theme-reloaded", () => {
this.update() this.update()
}) })
bdEvents.on("theme-unloaded", () => { bdEvents.on("theme-unloaded", () => {
this.update() this.update()
}) })
bdEvents.on("theme-loaded", () => { bdEvents.on("theme-loaded", () => {
this.update() this.update()
}) })
} }
enable(){ enable(){
this.enabled = true this.enabled = true
this.update() this.update()
} }
disable(){ disable(){
this.enabled = false this.enabled = false
this.update() this.update()
} }
update(key){ update(key){
if(!this.windows.size)return if(!this.windows.size)return
if(!this.enabled){ if(!this.enabled){
return this.removeThemes(key) return this.removeThemes(key)
}else{ }else{
return this.applyThemes(key) return this.applyThemes(key)
} }
} }
removeThemes(key){ removeThemes(key){
if(this.enabled)return if(this.enabled)return
if(key){ if(key){
let window = this.windows.get(key) let window = this.windows.get(key)
if(!window)return if(!window)return
let document = window.document let document = window.document
for(let style of document.querySelectorAll("style[data-lightcord-theme=true]")){ for(let style of document.querySelectorAll("style[data-lightcord-theme=true]")){
style.remove() style.remove()
} }
}else{ }else{
for(let key of this.windows.keys()){ for(let key of this.windows.keys()){
this.removeThemes(key) this.removeThemes(key)
} }
} }
} }
applyThemes(key){ applyThemes(key){
if(!this.enabled)return if(!this.enabled)return
if(key){ if(key){
let window = this.windows.get(key) let window = this.windows.get(key)
if(!window)return if(!window)return
let document = window.document let document = window.document
for(let style of document.querySelectorAll("style[data-lightcord-theme=true]")){ for(let style of document.querySelectorAll("style[data-lightcord-theme=true]")){
style.setAttribute("will-remove", "true") style.setAttribute("will-remove", "true")
} }
Object.keys(bdthemes) Object.keys(bdthemes)
.forEach(themeName => { .forEach(themeName => {
if(!themeCookie[themeName])return // theme disabled if(!themeCookie[themeName])return // theme disabled
const theme = bdthemes[themeName] const theme = bdthemes[themeName]
if(!theme)return //:shrug: if(!theme)return //:shrug:
let existing = document.querySelector("style[data-lightcord-theme=true]#"+DOM.escapeID(theme.id)) let existing = document.querySelector("style[data-lightcord-theme=true]#"+DOM.escapeID(theme.id))
if(existing){ if(existing){
existing.innerHTML = unescape(theme.css) existing.innerHTML = unescape(theme.css)
existing.removeAttribute("will-remove") existing.removeAttribute("will-remove")
}else{ }else{
const style = document.createElement("style") const style = document.createElement("style")
style.id = DOM.escapeID(theme.id) style.id = DOM.escapeID(theme.id)
style.innerHTML = unescape(theme.css) style.innerHTML = unescape(theme.css)
style.setAttribute("data-lightcord-theme", "true") style.setAttribute("data-lightcord-theme", "true")
document.head.append(style) document.head.append(style)
} }
}) })
for(let style of document.querySelectorAll("style[will-remove=true]")){ for(let style of document.querySelectorAll("style[will-remove=true]")){
style.remove() style.remove()
} }
}else{ }else{
for(let key of this.windows.keys()){ for(let key of this.windows.keys()){
this.applyThemes(key) this.applyThemes(key)
} }
} }
} }
} }

View File

@ -1,58 +1,58 @@
import {settingsCookie} from "../0globals"; import {settingsCookie} from "../0globals";
import BDV2 from "./v2"; import BDV2 from "./v2";
import webpackModules from "./webpackModules"; import webpackModules from "./webpackModules";
import Utils from "./utils"; import Utils from "./utils";
import DOM from "./domtools"; import DOM from "./domtools";
import V2C_PublicServers from "../ui/publicservers/publicServers"; import V2C_PublicServers from "../ui/publicservers/publicServers";
import Layers from "./Layers"; import Layers from "./Layers";
export default new class V2_PublicServers { export default new class V2_PublicServers {
constructor() { constructor() {
this._appendButton = this._appendButton.bind(this); this._appendButton = this._appendButton.bind(this);
window.Lightcord.BetterDiscord.V2_PublicServers = this window.Lightcord.BetterDiscord.V2_PublicServers = this
} }
render() { render() {
Layers.createLayer((close) => { Layers.createLayer((close) => {
return BDV2.react.createElement(V2C_PublicServers, {rootId: "pubslayerroot", close}) return BDV2.react.createElement(V2C_PublicServers, {rootId: "pubslayerroot", close})
}) })
} }
get button() { get button() {
const btn = DOM.createElement(`<div id="bd-pub-li" class="${BDV2.guildClasses.listItem}">`); const btn = DOM.createElement(`<div id="bd-pub-li" class="${BDV2.guildClasses.listItem}">`);
if (!settingsCookie["bda-gs-1"]) btn.style.display = "none"; if (!settingsCookie["bda-gs-1"]) btn.style.display = "none";
const label = DOM.createElement(`<div id="bd-pub-button" class="${"wrapper-25eVIn " + BDV2.guildClasses.circleButtonMask}">public</div>`); const label = DOM.createElement(`<div id="bd-pub-button" class="${"wrapper-25eVIn " + BDV2.guildClasses.circleButtonMask}">public</div>`);
label.addEventListener("click", () => {this.render();}); label.addEventListener("click", () => {this.render();});
btn.append(label); btn.append(label);
return btn; return btn;
} }
_appendButton() { _appendButton() {
let [ let [
classNametutorialContainer classNametutorialContainer
] = [ ] = [
Utils.removeDa(BDModules.get(e => e.downloadProgress && e.tutorialContainer)[0].tutorialContainer) Utils.removeDa(BDModules.get(e => e.downloadProgress && e.tutorialContainer)[0].tutorialContainer)
] ]
if (DOM.query("#bd-pub-li")) return; if (DOM.query("#bd-pub-li")) return;
const guilds = DOM.query(`div.${classNametutorialContainer} > div`); const guilds = DOM.query(`div.${classNametutorialContainer} > div`);
DOM.after(guilds, this.button); DOM.after(guilds, this.button);
} }
addButton() { addButton() {
if (this.guildPatch) return; if (this.guildPatch) return;
const GuildList = webpackModules.find(m => m.default && m.default.displayName == "NavigableGuilds"); const GuildList = webpackModules.find(m => m.default && m.default.displayName == "NavigableGuilds");
const GuildListOld = webpackModules.findByDisplayName("Guilds"); const GuildListOld = webpackModules.findByDisplayName("Guilds");
if (!GuildList && !GuildListOld) Utils.warn("PublicServer", "Can't find GuildList component"); if (!GuildList && !GuildListOld) Utils.warn("PublicServer", "Can't find GuildList component");
this.guildPatch = Utils.monkeyPatch(GuildList ? GuildList : GuildListOld.prototype, GuildList ? "default" : "render", {after: this._appendButton}); this.guildPatch = Utils.monkeyPatch(GuildList ? GuildList : GuildListOld.prototype, GuildList ? "default" : "render", {after: this._appendButton});
this._appendButton(); this._appendButton();
} }
removeButton() { removeButton() {
this.guildPatch(); this.guildPatch();
delete this.guildPatch; delete this.guildPatch;
const button = DOM.query("#bd-pub-li"); const button = DOM.query("#bd-pub-li");
if (button) button.remove(); if (button) button.remove();
} }
}; };

View File

@ -1,469 +1,469 @@
import {settings, settingsCookie, settingsRPC, lightcordSettings} from "../0globals"; import {settings, settingsCookie, settingsRPC, lightcordSettings} from "../0globals";
import DataStore from "./dataStore"; import DataStore from "./dataStore";
import V2_SettingsPanel_Sidebar from "./settingsPanelSidebar"; import V2_SettingsPanel_Sidebar from "./settingsPanelSidebar";
import Utils from "./utils"; import Utils from "./utils";
import BDV2 from "./v2"; import BDV2 from "./v2";
import ContentManager from "./contentManager"; import ContentManager from "./contentManager";
import coloredText from "./coloredText"; import coloredText from "./coloredText";
import tfHour from "./24hour"; import tfHour from "./24hour";
import DOM from "./domtools"; import DOM from "./domtools";
import publicServersModule from "./publicServers"; import publicServersModule from "./publicServers";
import voiceMode from "./voiceMode"; import voiceMode from "./voiceMode";
import ClassNormalizer from "./classNormalizer"; import ClassNormalizer from "./classNormalizer";
import dMode from "./devMode"; import dMode from "./devMode";
import SectionedSettingsPanel from "../ui/sectionedSettingsPanel"; import SectionedSettingsPanel from "../ui/sectionedSettingsPanel";
import CssEditor from "../ui/cssEditor"; import CssEditor from "../ui/cssEditor";
import CardList from "../ui/addonlist"; import CardList from "../ui/addonlist";
import V2C_PresenceSettings from "../ui/presenceSettings"; import V2C_PresenceSettings from "../ui/presenceSettings";
import CustomRichPresence from "./CustomRichPresence"; import CustomRichPresence from "./CustomRichPresence";
import V2C_AccountInfos from "../ui/AccountInfos"; import V2C_AccountInfos from "../ui/AccountInfos";
import { remote } from "electron"; import { remote } from "electron";
import AntiAdDM from "./AntiAdDM"; import AntiAdDM from "./AntiAdDM";
import blurPrivate from "./blurPrivate"; import blurPrivate from "./blurPrivate";
import disableTyping from "./disableTyping"; import disableTyping from "./disableTyping";
import ApiPreview from "../ui/ApiPreview"; import ApiPreview from "../ui/ApiPreview";
import Switch from "../ui/switch"; import Switch from "../ui/switch";
import MarginTop from "../ui/margintop"; import MarginTop from "../ui/margintop";
import webpackModules from "./webpackModules"; import webpackModules from "./webpackModules";
import tooltipWrap from "../ui/tooltipWrap"; import tooltipWrap from "../ui/tooltipWrap";
import History from "../ui/icons/history"; import History from "../ui/icons/history";
import core from "./core"; import core from "./core";
import popoutWindow from "./popoutWindow"; import popoutWindow from "./popoutWindow";
class BDSidebarHeader extends React.PureComponent { class BDSidebarHeader extends React.PureComponent {
render(){ render(){
let sidebarComponents = webpackModules.find(e => e.Separator && e.Header && e.Item) let sidebarComponents = webpackModules.find(e => e.Separator && e.Header && e.Item)
const changelogButton = React.createElement(tooltipWrap, {color: "black", side: "top", text: "Changelog"}, const changelogButton = React.createElement(tooltipWrap, {color: "black", side: "top", text: "Changelog"},
React.createElement("span", {style: {float: "right", cursor: "pointer"}, className: "bd-changelog-button", onClick: () => {Utils.showChangelogModal(bbdChangelog);}}, React.createElement("span", {style: {float: "right", cursor: "pointer"}, className: "bd-changelog-button", onClick: () => {Utils.showChangelogModal(bbdChangelog);}},
React.createElement(History, {className: "bd-icon", size: "16px"}) React.createElement(History, {className: "bd-icon", size: "16px"})
) )
); );
let rendered = new sidebarComponents.Header({ let rendered = new sidebarComponents.Header({
children: React.createElement("span", null, "Bandaged BD", changelogButton), children: React.createElement("span", null, "Bandaged BD", changelogButton),
className: "ui-tab-bar-header" className: "ui-tab-bar-header"
}) })
return rendered return rendered
} }
} }
let isClearingCache = false let isClearingCache = false
export default new class V2_SettingsPanel { export default new class V2_SettingsPanel {
constructor() { constructor() {
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
this.updateSettings = this.updateSettings.bind(this); this.updateSettings = this.updateSettings.bind(this);
this.sidebar = new V2_SettingsPanel_Sidebar(); this.sidebar = new V2_SettingsPanel_Sidebar();
this.registerComponents() this.registerComponents()
} }
registerComponents(){ registerComponents(){
/** Lightcord */ /** Lightcord */
this.sidebar.register("lightcord", makeComponent(this.lightcordComponent.bind(this))) this.sidebar.register("lightcord", makeComponent(this.lightcordComponent.bind(this)))
this.sidebar.register("status", makeComponent(this.PresenceComponent.bind(this))) this.sidebar.register("status", makeComponent(this.PresenceComponent.bind(this)))
this.sidebar.register("accountinfo", makeComponent(this.AccountInfosComponent.bind(this))) this.sidebar.register("accountinfo", makeComponent(this.AccountInfosComponent.bind(this)))
this.sidebar.register("lcapipreview", makeComponent(this.ApiPreviewComponent.bind(this))) this.sidebar.register("lcapipreview", makeComponent(this.ApiPreviewComponent.bind(this)))
/* Bandaged BD */ /* Bandaged BD */
this.sidebar.register("BDChangelogTitle", makeComponent(() => { this.sidebar.register("BDChangelogTitle", makeComponent(() => {
return new BDSidebarHeader().render() return new BDSidebarHeader().render()
})) }))
this.sidebar.register("core", makeComponent(this.coreComponent.bind(this))) this.sidebar.register("core", makeComponent(this.coreComponent.bind(this)))
this.sidebar.register("customcss", makeComponent(this.customCssComponent.bind(this))) this.sidebar.register("customcss", makeComponent(this.customCssComponent.bind(this)))
this.sidebar.register("plugins", makeComponent(this.renderAddonPane("plugins"))) this.sidebar.register("plugins", makeComponent(this.renderAddonPane("plugins")))
this.sidebar.register("themes", makeComponent(this.renderAddonPane("themes"))) this.sidebar.register("themes", makeComponent(this.renderAddonPane("themes")))
} }
get coreSettings() { get coreSettings() {
const settings = this.getSettings("core"); const settings = this.getSettings("core");
const categories = [...new Set(settings.map(s => s.category))]; const categories = [...new Set(settings.map(s => s.category))];
const sections = categories.map(c => {return {title: c, settings: settings.filter(s => s.category == c)};}); const sections = categories.map(c => {return {title: c, settings: settings.filter(s => s.category == c)};});
return sections; return sections;
} }
get lightcordSettings() { get lightcordSettings() {
const settings = this.getSettings("lightcord"); const settings = this.getSettings("lightcord");
const categories = [...new Set(settings.map(s => s.category))]; const categories = [...new Set(settings.map(s => s.category))];
const sections = categories.map(c => {return {title: c, settings: settings.filter(s => s.category == c)};}); const sections = categories.map(c => {return {title: c, settings: settings.filter(s => s.category == c)};});
return sections; return sections;
} }
get PresenceSettings() { get PresenceSettings() {
return this.getSettings("status") return this.getSettings("status")
} }
getSettings(category) { getSettings(category) {
return Object.keys(settings).reduce((arr, key) => { return Object.keys(settings).reduce((arr, key) => {
const setting = settings[key]; const setting = settings[key];
if (setting.cat === category && setting.implemented && !setting.hidden) { if (setting.cat === category && setting.implemented && !setting.hidden) {
setting.text = key; setting.text = key;
arr.push(setting); arr.push(setting);
} }
return arr; return arr;
}, []); }, []);
} }
onChange(id, checked, sidebar) { onChange(id, checked, sidebar) {
this.updateSettings(id, checked, sidebar); this.updateSettings(id, checked, sidebar);
} }
updateSettings(id, enabled, sidebar) { updateSettings(id, enabled, sidebar) {
if(!["lightcord-8", "no_window_bound", "enable_glasstron", "lightcord-10"].includes(id))settingsCookie[id] = enabled; if(!["lightcord-8", "no_window_bound", "enable_glasstron", "lightcord-10"].includes(id))settingsCookie[id] = enabled;
if (id == "bda-gs-2") { if (id == "bda-gs-2") {
if (enabled) DOM.addClass(document.body, "bd-minimal"); if (enabled) DOM.addClass(document.body, "bd-minimal");
else DOM.removeClass(document.body, "bd-minimal"); else DOM.removeClass(document.body, "bd-minimal");
} }
if (id == "bda-gs-3") { if (id == "bda-gs-3") {
if (enabled) DOM.addClass(document.body, "bd-minimal-chan"); if (enabled) DOM.addClass(document.body, "bd-minimal-chan");
else DOM.removeClass(document.body, "bd-minimal-chan"); else DOM.removeClass(document.body, "bd-minimal-chan");
} }
if (id == "bda-gs-1") { if (id == "bda-gs-1") {
if (enabled) publicServersModule.addButton(); if (enabled) publicServersModule.addButton();
else publicServersModule.removeButton(); else publicServersModule.removeButton();
} }
if (id == "bda-gs-4") { if (id == "bda-gs-4") {
if (enabled) voiceMode.start(); if (enabled) voiceMode.start();
else voiceMode.stop(); else voiceMode.stop();
} }
if (id == "bda-gs-5") { if (id == "bda-gs-5") {
if (enabled) DOM.addClass(DOM.query("#app-mount"), "bda-dark"); if (enabled) DOM.addClass(DOM.query("#app-mount"), "bda-dark");
else DOM.removeClass(DOM.query("#app-mount"), "bda-dark"); else DOM.removeClass(DOM.query("#app-mount"), "bda-dark");
} }
if (enabled && id == "bda-gs-6") tfHour.inject24Hour(); if (enabled && id == "bda-gs-6") tfHour.inject24Hour();
if (id == "bda-gs-7") { if (id == "bda-gs-7") {
if (enabled) coloredText.injectColoredText(); if (enabled) coloredText.injectColoredText();
else coloredText.removeColoredText(); else coloredText.removeColoredText();
} }
if (id == "fork-ps-4") { if (id == "fork-ps-4") {
if (enabled) ClassNormalizer.start(); if (enabled) ClassNormalizer.start();
else ClassNormalizer.stop(); else ClassNormalizer.stop();
} }
if (id == "fork-ps-5") { if (id == "fork-ps-5") {
if (enabled) { if (enabled) {
ContentManager.watchContent("plugin"); ContentManager.watchContent("plugin");
ContentManager.watchContent("theme"); ContentManager.watchContent("theme");
} }
else { else {
ContentManager.unwatchContent("plugin"); ContentManager.unwatchContent("plugin");
ContentManager.unwatchContent("theme"); ContentManager.unwatchContent("theme");
} }
} }
if (id == "fork-wp-1") { if (id == "fork-wp-1") {
Utils.setWindowPreference("transparent", enabled); Utils.setWindowPreference("transparent", enabled);
if (enabled) Utils.setWindowPreference("backgroundColor", null); if (enabled) Utils.setWindowPreference("backgroundColor", null);
else Utils.setWindowPreference("backgroundColor", "#2f3136"); else Utils.setWindowPreference("backgroundColor", "#2f3136");
} }
if (id == "bda-gs-8") { if (id == "bda-gs-8") {
if (enabled) dMode.startDebugListener(); if (enabled) dMode.startDebugListener();
else dMode.stopDebugListener(); else dMode.stopDebugListener();
} }
if (id == "fork-dm-1") { if (id == "fork-dm-1") {
if (enabled) dMode.startCopySelector(); if (enabled) dMode.startCopySelector();
else dMode.stopCopySelector(); else dMode.stopCopySelector();
} }
if (id === "lightcord-1") { if (id === "lightcord-1") {
if (enabled) window.Lightcord.Settings.devMode = true if (enabled) window.Lightcord.Settings.devMode = true
else window.Lightcord.Settings.devMode = false else window.Lightcord.Settings.devMode = false
sidebar.forceUpdate() sidebar.forceUpdate()
} }
if (id === "lightcord-2") { if (id === "lightcord-2") {
if (enabled) window.Lightcord.Settings.callRingingBeat = true if (enabled) window.Lightcord.Settings.callRingingBeat = true
else window.Lightcord.Settings.callRingingBeat = false else window.Lightcord.Settings.callRingingBeat = false
} }
if (id === "lightcord-presence-1") { if (id === "lightcord-presence-1") {
if (enabled) { if (enabled) {
CustomRichPresence.enable() CustomRichPresence.enable()
const settingsStore = BDModules.get(e => e.default && typeof e.default === "object" && "showCurrentGame" in e.default)[0] const settingsStore = BDModules.get(e => e.default && typeof e.default === "object" && "showCurrentGame" in e.default)[0]
if(settingsStore && !settingsStore.default.showCurrentGame){ if(settingsStore && !settingsStore.default.showCurrentGame){
BDModules.get(e => e.default && e.default.updateRemoteSettings)[0].default.updateRemoteSettings({ BDModules.get(e => e.default && e.default.updateRemoteSettings)[0].default.updateRemoteSettings({
showCurrentGame: true showCurrentGame: true
}) })
} }
} }
else CustomRichPresence.disable() else CustomRichPresence.disable()
} }
if (id === "lightcord-3") { if (id === "lightcord-3") {
if (enabled) remote.getCurrentWindow().setAlwaysOnTop(true) if (enabled) remote.getCurrentWindow().setAlwaysOnTop(true)
else remote.getCurrentWindow().setAlwaysOnTop(false) else remote.getCurrentWindow().setAlwaysOnTop(false)
} }
if (id === "lightcord-4") { if (id === "lightcord-4") {
if(enabled){ if(enabled){
AntiAdDM.enable() AntiAdDM.enable()
}else{ }else{
AntiAdDM.disable() AntiAdDM.disable()
} }
} }
if (id === "lightcord-6") { if (id === "lightcord-6") {
if(enabled){ if(enabled){
blurPrivate.enable() blurPrivate.enable()
}else{ }else{
blurPrivate.disable() blurPrivate.disable()
} }
} }
if (id === "lightcord-7") { if (id === "lightcord-7") {
if(enabled){ if(enabled){
disableTyping.enable() disableTyping.enable()
}else{ }else{
disableTyping.disable() disableTyping.disable()
} }
} }
if (id === "lightcord-8"){ if (id === "lightcord-8"){
let appSettings = remote.getGlobal("appSettings") let appSettings = remote.getGlobal("appSettings")
appSettings.set("isTabs", enabled) appSettings.set("isTabs", enabled)
appSettings.save() appSettings.save()
remote.app.relaunch() remote.app.relaunch()
remote.app.exit() remote.app.exit()
} }
if (id === "lightcord-9") { if (id === "lightcord-9") {
popoutWindow[enabled ? "enable" : "disable"]() popoutWindow[enabled ? "enable" : "disable"]()
} }
if (id === "lightcord-10"){ if (id === "lightcord-10"){
core.methods.NotificationsUseShim(enabled) core.methods.NotificationsUseShim(enabled)
return return
} }
if (id === "no_window_bound"){ if (id === "no_window_bound"){
let appSettings = remote.getGlobal("appSettings") let appSettings = remote.getGlobal("appSettings")
appSettings.set("NO_WINDOWS_BOUND", enabled) appSettings.set("NO_WINDOWS_BOUND", enabled)
appSettings.delete("IS_MAXIMIZED") appSettings.delete("IS_MAXIMIZED")
appSettings.delete("IS_MINIMIZED") appSettings.delete("IS_MINIMIZED")
appSettings.delete("WINDOW_BOUNDS") appSettings.delete("WINDOW_BOUNDS")
appSettings.save() appSettings.save()
remote.app.relaunch() remote.app.relaunch()
remote.app.exit() remote.app.exit()
} }
if (id === "enable_glasstron"){ if (id === "enable_glasstron"){
let appSettings = remote.getGlobal("appSettings") let appSettings = remote.getGlobal("appSettings")
appSettings.set("GLASSTRON", enabled) appSettings.set("GLASSTRON", enabled)
appSettings.save() appSettings.save()
remote.app.relaunch() remote.app.relaunch()
remote.app.exit() remote.app.exit()
} }
this.saveSettings(); this.saveSettings();
} }
async initializeSettings() { async initializeSettings() {
if (settingsCookie["bda-gs-2"]) DOM.addClass(document.body, "bd-minimal"); if (settingsCookie["bda-gs-2"]) DOM.addClass(document.body, "bd-minimal");
if (settingsCookie["bda-gs-3"]) DOM.addClass(document.body, "bd-minimal-chan"); if (settingsCookie["bda-gs-3"]) DOM.addClass(document.body, "bd-minimal-chan");
if (settingsCookie["bda-gs-1"]) publicServersModule.addButton(); if (settingsCookie["bda-gs-1"]) publicServersModule.addButton();
if (settingsCookie["bda-gs-4"]) voiceMode.start(); if (settingsCookie["bda-gs-4"]) voiceMode.start();
if (settingsCookie["bda-gs-5"]) DOM.addClass(DOM.query("#app-mount"), "bda-dark"); if (settingsCookie["bda-gs-5"]) DOM.addClass(DOM.query("#app-mount"), "bda-dark");
if (settingsCookie["bda-gs-6"]) tfHour.inject24Hour(); if (settingsCookie["bda-gs-6"]) tfHour.inject24Hour();
if (settingsCookie["bda-gs-7"]) coloredText.injectColoredText(); if (settingsCookie["bda-gs-7"]) coloredText.injectColoredText();
if (settingsCookie["fork-ps-4"]) ClassNormalizer.start(); if (settingsCookie["fork-ps-4"]) ClassNormalizer.start();
if (settingsCookie["lightcord-1"]) window.Lightcord.Settings.devMode = true if (settingsCookie["lightcord-1"]) window.Lightcord.Settings.devMode = true
if (settingsCookie["lightcord-2"]) window.Lightcord.Settings.callRingingBeat = true if (settingsCookie["lightcord-2"]) window.Lightcord.Settings.callRingingBeat = true
if (settingsCookie["lightcord-presence-1"]) CustomRichPresence.enable() if (settingsCookie["lightcord-presence-1"]) CustomRichPresence.enable()
if (settingsCookie["lightcord-3"]) remote.getCurrentWindow().setAlwaysOnTop(true) if (settingsCookie["lightcord-3"]) remote.getCurrentWindow().setAlwaysOnTop(true)
if (settingsCookie["lightcord-4"]) AntiAdDM.enable() if (settingsCookie["lightcord-4"]) AntiAdDM.enable()
if (settingsCookie["lightcord-6"]) blurPrivate.enable() if (settingsCookie["lightcord-6"]) blurPrivate.enable()
if (settingsCookie["lightcord-7"]) disableTyping.enable() if (settingsCookie["lightcord-7"]) disableTyping.enable()
if (settingsCookie["lightcord-9"]) popoutWindow.enable() if (settingsCookie["lightcord-9"]) popoutWindow.enable()
if (settingsCookie["fork-ps-5"]) { if (settingsCookie["fork-ps-5"]) {
ContentManager.watchContent("plugin"); ContentManager.watchContent("plugin");
ContentManager.watchContent("theme"); ContentManager.watchContent("theme");
} }
if (settingsCookie["bda-gs-8"]) dMode.startDebugListener(); if (settingsCookie["bda-gs-8"]) dMode.startDebugListener();
if (settingsCookie["fork-dm-1"]) dMode.startCopySelector(); if (settingsCookie["fork-dm-1"]) dMode.startCopySelector();
this.saveSettings(); this.saveSettings();
} }
saveSettings() { saveSettings() {
DataStore.setSettingGroup("settings", settingsCookie); DataStore.setSettingGroup("settings", settingsCookie);
DataStore.setSettingGroup("rpc", settingsRPC); DataStore.setSettingGroup("rpc", settingsRPC);
DataStore.setSettingGroup("lightcord-settings", lightcordSettings); DataStore.setSettingGroup("lightcord-settings", lightcordSettings);
} }
loadSettings() { loadSettings() {
Object.assign(settingsCookie, DataStore.getSettingGroup("settings")); Object.assign(settingsCookie, DataStore.getSettingGroup("settings"));
Object.assign(settingsRPC, DataStore.getSettingGroup("rpc")); Object.assign(settingsRPC, DataStore.getSettingGroup("rpc"));
console.log(lightcordSettings, DataStore.getSettingGroup("lightcord-settings")) console.log(lightcordSettings, DataStore.getSettingGroup("lightcord-settings"))
Object.assign(lightcordSettings, DataStore.getSettingGroup("lightcord-settings")); Object.assign(lightcordSettings, DataStore.getSettingGroup("lightcord-settings"));
} }
renderSidebar(sidebar) { renderSidebar(sidebar) {
return this.sidebar.render(sidebar); return this.sidebar.render(sidebar);
} }
coreComponent() { coreComponent() {
return BDV2.react.createElement(SectionedSettingsPanel, {key: "cspanel", onChange: this.onChange, sections: this.coreSettings}) return BDV2.react.createElement(SectionedSettingsPanel, {key: "cspanel", onChange: this.onChange, sections: this.coreSettings})
} }
lightcordComponent(sidebar, forceUpdate) { lightcordComponent(sidebar, forceUpdate) {
let appSettings = remote.getGlobal("appSettings") let appSettings = remote.getGlobal("appSettings")
return [ return [
this.lightcordSettings.map((section, i) => { this.lightcordSettings.map((section, i) => {
return [ return [
(i === 0 ? null : BDV2.react.createElement(MarginTop)), (i === 0 ? null : BDV2.react.createElement(MarginTop)),
BDV2.react.createElement("h2", {className: "ui-form-title h2 margin-reset margin-bottom-20"}, section.title), BDV2.react.createElement("h2", {className: "ui-form-title h2 margin-reset margin-bottom-20"}, section.title),
section.settings.map(setting => { section.settings.map(setting => {
let isChecked = settingsCookie[setting.id] let isChecked = settingsCookie[setting.id]
if(setting.id === "lightcord-8")isChecked = appSettings.get("isTabs", false); if(setting.id === "lightcord-8")isChecked = appSettings.get("isTabs", false);
if(setting.id === "no_window_bound")isChecked = appSettings.get("NO_WINDOWS_BOUND", false) if(setting.id === "no_window_bound")isChecked = appSettings.get("NO_WINDOWS_BOUND", false)
if(setting.id === "enable_glasstron")isChecked = appSettings.get("GLASSTRON", true) if(setting.id === "enable_glasstron")isChecked = appSettings.get("GLASSTRON", true)
if(setting.id === "lightcord-10")isChecked = !appSettings.get("DEFAULT_NOTIFICATIONS", true) if(setting.id === "lightcord-10")isChecked = !appSettings.get("DEFAULT_NOTIFICATIONS", true)
let returnValue = BDV2.react.createElement(Switch, {id: setting.id, key: setting.id, data: setting, checked: isChecked, onChange: (id, checked) => { let returnValue = BDV2.react.createElement(Switch, {id: setting.id, key: setting.id, data: setting, checked: isChecked, onChange: (id, checked) => {
this.onChange(id, checked, sidebar); this.onChange(id, checked, sidebar);
}}) }})
if(setting.id == "lightcord-8" && isChecked){ if(setting.id == "lightcord-8" && isChecked){
return [ return [
returnValue, returnValue,
React.createElement(Lightcord.Api.Components.inputs.Button, { React.createElement(Lightcord.Api.Components.inputs.Button, {
color: "green", color: "green",
look: "outlined", look: "outlined",
size: "small", size: "small",
hoverColor: "brand", hoverColor: "brand",
onClick: () => { onClick: () => {
DiscordNative.ipc.send("NEW_TAB") DiscordNative.ipc.send("NEW_TAB")
}, },
wrapper: false, wrapper: false,
disabled: false disabled: false
}, "Open a new Tab") }, "Open a new Tab")
] ]
} }
return returnValue return returnValue
}) })
] ]
}), }),
BDV2.react.createElement(window.Lightcord.Api.Components.inputs.Button, { BDV2.react.createElement(window.Lightcord.Api.Components.inputs.Button, {
color: "yellow", color: "yellow",
look: "ghost", look: "ghost",
size: "medium", size: "medium",
hoverColor: "red", hoverColor: "red",
onClick(){ onClick(){
console.log("Should relaunch") console.log("Should relaunch")
remote.app.relaunch({ remote.app.relaunch({
args: remote.process.argv.slice(1).concat(["--disable-betterdiscord"]) args: remote.process.argv.slice(1).concat(["--disable-betterdiscord"])
}) })
remote.app.quit() remote.app.quit()
}, },
wrapper: true wrapper: true
}, "Relaunch without BetterDiscord"), }, "Relaunch without BetterDiscord"),
React.createElement(Lightcord.Api.Components.inputs.Button, { React.createElement(Lightcord.Api.Components.inputs.Button, {
color: "yellow", color: "yellow",
look: "ghost", look: "ghost",
size: "medium", size: "medium",
hoverColor: "red", hoverColor: "red",
onClick: () => { onClick: () => {
if(isClearingCache)return if(isClearingCache)return
isClearingCache = true isClearingCache = true
Utils.showToast("Clearing cache...", { Utils.showToast("Clearing cache...", {
type: "info" type: "info"
}) })
forceUpdate() forceUpdate()
remote.getCurrentWebContents().session.clearCache() remote.getCurrentWebContents().session.clearCache()
.then(() => { .then(() => {
Utils.showToast("Cache is cleared !", { Utils.showToast("Cache is cleared !", {
type: "success" type: "success"
}) })
isClearingCache = false isClearingCache = false
forceUpdate() forceUpdate()
}).catch(err => { }).catch(err => {
console.error(err) console.error(err)
Utils.showToast("An error occured. Check console for more informations.", { Utils.showToast("An error occured. Check console for more informations.", {
type: "error" type: "error"
}) })
isClearingCache = false isClearingCache = false
forceUpdate() forceUpdate()
}) })
}, },
wrapper: true, wrapper: true,
disabled: isClearingCache disabled: isClearingCache
}, "Clear cache") }, "Clear cache")
] ]
} }
PresenceComponent() { PresenceComponent() {
return BDV2.react.createElement(V2C_PresenceSettings, { return BDV2.react.createElement(V2C_PresenceSettings, {
key: "lppannel", key: "lppannel",
onChange: this.onChange, onChange: this.onChange,
settings: this.PresenceSettings settings: this.PresenceSettings
}) })
} }
AccountInfosComponent() { AccountInfosComponent() {
return BDV2.react.createElement(V2C_AccountInfos, { return BDV2.react.createElement(V2C_AccountInfos, {
key: "lapannel" key: "lapannel"
}) })
} }
ApiPreviewComponent() { ApiPreviewComponent() {
return BDV2.react.createElement(ApiPreview, { return BDV2.react.createElement(ApiPreview, {
key: "lapipannel" key: "lapipannel"
}) })
} }
customCssComponent() { customCssComponent() {
return BDV2.react.createElement(CssEditor, {key: "csseditor"}) return BDV2.react.createElement(CssEditor, {key: "csseditor"})
} }
renderAddonPane(type) { renderAddonPane(type) {
// I know this shouldn't be here, but when it isn't, // I know this shouldn't be here, but when it isn't,
// React refuses to change the button when going // React refuses to change the button when going
// between plugins and themes page... something // between plugins and themes page... something
// to debug later. // to debug later.
class ContentList extends BDV2.react.Component { class ContentList extends BDV2.react.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.prefix = this.props.type.replace("s", ""); this.prefix = this.props.type.replace("s", "");
} }
onChange() { onChange() {
this.props.onChange(this.props.type); this.props.onChange(this.props.type);
} }
render() {return this.props.children;} render() {return this.props.children;}
} }
const originalRender = ContentList.prototype.render; const originalRender = ContentList.prototype.render;
Object.defineProperty(ContentList.prototype, "render", { Object.defineProperty(ContentList.prototype, "render", {
enumerable: false, enumerable: false,
configurable: false, configurable: false,
set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}, set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");},
get: () => originalRender get: () => originalRender
}); });
return function(){ return function(){
return BDV2.react.createElement(ContentList, {type}, BDV2.react.createElement(CardList, {type})) return BDV2.react.createElement(ContentList, {type}, BDV2.react.createElement(CardList, {type}))
} }
} }
}; };
/** /**
* No need to export settingsPanel on window * No need to export settingsPanel on window
*/ */
function makeComponent(children){ function makeComponent(children){
class SettingComponent extends React.Component { class SettingComponent extends React.Component {
render(){ render(){
return children(sidebar, () => this.forceUpdate()) return children(sidebar, () => this.forceUpdate())
} }
} }
let sidebar let sidebar
return (s) => { return (s) => {
sidebar = s sidebar = s
return SettingComponent return SettingComponent
} }
} }

View File

@ -1,110 +1,110 @@
export default class V2_SettingsPanel_Sidebar { export default class V2_SettingsPanel_Sidebar {
constructor() { constructor() {
this.panels = {} this.panels = {}
} }
register(panel, component){ register(panel, component){
this.panels[panel] = component this.panels[panel] = component
} }
getComponent(panel, sidebar){ getComponent(panel, sidebar){
return this.panels[panel](sidebar) return this.panels[panel](sidebar)
} }
get items() { get items() {
return [{ return [{
text: "BetterDiscord Settings", text: "BetterDiscord Settings",
id: "core" id: "core"
}, { }, {
text: "Plugins", text: "Plugins",
id: "plugins" id: "plugins"
}, { }, {
text: "Themes", text: "Themes",
id: "themes" id: "themes"
}, { }, {
text: "Custom CSS", text: "Custom CSS",
id: "customcss" id: "customcss"
}]; }];
} }
get LCitems(){ get LCitems(){
let items = [ let items = [
{ {
text: "Lightcord Settings", text: "Lightcord Settings",
id: "lightcord" id: "lightcord"
}, { }, {
text: "RichPresence", text: "RichPresence",
id: "status" id: "status"
}, { }, {
text: "Account Info", text: "Account Info",
id: "accountinfo" id: "accountinfo"
} }
] ]
return items return items
} }
get LCDevItems(){ get LCDevItems(){
let items = [] let items = []
if(!window.Lightcord.Settings.devMode)return items if(!window.Lightcord.Settings.devMode)return items
items.push(...[ items.push(...[
{ {
section: "DIVIDER" section: "DIVIDER"
}, },
{ {
section: "HEADER", section: "HEADER",
label: "Lightcord Api" label: "Lightcord Api"
}, },
{ {
text: "Components Preview", text: "Components Preview",
id: "lcapipreview" id: "lcapipreview"
} }
]) ])
return items return items
} }
render(sidebar) { render(sidebar) {
return [ return [
{ {
section: "HEADER", section: "HEADER",
label: "Lightcord" label: "Lightcord"
}, },
...this.LCitems.map(e => { ...this.LCitems.map(e => {
return { return {
section: e.id, section: e.id,
label: e.text, label: e.text,
element: this.getComponent(e.id, sidebar) element: this.getComponent(e.id, sidebar)
} }
}), }),
...this.LCDevItems.map(e => { ...this.LCDevItems.map(e => {
if(e.section)return e if(e.section)return e
return { return {
section: e.id, section: e.id,
label: e.text, label: e.text,
element: this.getComponent(e.id, sidebar) element: this.getComponent(e.id, sidebar)
} }
}), }),
{ {
section: "DIVIDER" section: "DIVIDER"
}, },
{ {
section: "CUSTOM", section: "CUSTOM",
element: this.getComponent("BDChangelogTitle") element: this.getComponent("BDChangelogTitle")
}, },
...this.items.map(e => { ...this.items.map(e => {
return { return {
section: e.id, section: e.id,
label: e.text, label: e.text,
element: this.getComponent(e.id, sidebar) element: this.getComponent(e.id, sidebar)
} }
}), }),
{ {
section: "DIVIDER" section: "DIVIDER"
} }
] ]
} }
} }
/** /**
* No need to export settingsPanelSidebar on window * No need to export settingsPanelSidebar on window
*/ */

View File

@ -1,152 +1,152 @@
import {bdthemeErrors, themeCookie, settingsCookie, bdthemes} from "../0globals"; import {bdthemeErrors, themeCookie, settingsCookie, bdthemes} from "../0globals";
import ContentManager from "./contentManager"; import ContentManager from "./contentManager";
import DataStore from "./dataStore"; import DataStore from "./dataStore";
import BDEvents from "./bdEvents"; import BDEvents from "./bdEvents";
import Utils from "./utils"; import Utils from "./utils";
import DOM from "./domtools"; import DOM from "./domtools";
import bdEvents from "./bdEvents"; import bdEvents from "./bdEvents";
class ThemeModule { class ThemeModule {
constructor(){ constructor(){
window.Lightcord.BetterDiscord.ThemeModule = this window.Lightcord.BetterDiscord.ThemeModule = this
} }
get folder() {return ContentManager.themesFolder;} get folder() {return ContentManager.themesFolder;}
} }
ThemeModule.prototype.loadThemes = async function () { ThemeModule.prototype.loadThemes = async function () {
this.loadThemeData(); this.loadThemeData();
bdthemeErrors.splice(0, 0, ...(await ContentManager.loadThemes())); bdthemeErrors.splice(0, 0, ...(await ContentManager.loadThemes()));
const themes = Object.keys(bdthemes); const themes = Object.keys(bdthemes);
for (let i = 0; i < themes.length; i++) { for (let i = 0; i < themes.length; i++) {
const theme = bdthemes[themes[i]]; const theme = bdthemes[themes[i]];
if (!themeCookie[theme.name]) themeCookie[theme.name] = false; if (!themeCookie[theme.name]) themeCookie[theme.name] = false;
if (themeCookie[theme.name]) DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css)); if (themeCookie[theme.name]) DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css));
await new Promise((resolve) => setTimeout(resolve, 10)) await new Promise((resolve) => setTimeout(resolve, 10))
} }
for (const theme in themeCookie) { for (const theme in themeCookie) {
if (!bdthemes[theme]) delete themeCookie[theme]; if (!bdthemes[theme]) delete themeCookie[theme];
} }
this.saveThemeData(); this.saveThemeData();
// if (settingsCookie["fork-ps-5"]) ContentManager.watchContent("theme"); // if (settingsCookie["fork-ps-5"]) ContentManager.watchContent("theme");
}; };
ThemeModule.prototype.enableTheme = function(name, reload = false) { ThemeModule.prototype.enableTheme = function(name, reload = false) {
themeCookie[name] = true; themeCookie[name] = true;
this.saveThemeData(); this.saveThemeData();
const theme = bdthemes[name]; const theme = bdthemes[name];
DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css)); DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css));
bdEvents.dispatch("theme-enabled") bdEvents.dispatch("theme-enabled")
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been applied.`); if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been applied.`);
}; };
ThemeModule.prototype.enable = function (name, reload = false) { ThemeModule.prototype.enable = function (name, reload = false) {
return this.enableTheme(name, reload); return this.enableTheme(name, reload);
}; };
ThemeModule.prototype.disableTheme = function(name, reload = false) { ThemeModule.prototype.disableTheme = function(name, reload = false) {
themeCookie[name] = false; themeCookie[name] = false;
this.saveThemeData(); this.saveThemeData();
const theme = bdthemes[name]; const theme = bdthemes[name];
DOM.removeStyle(DOM.escapeID(theme.id)); DOM.removeStyle(DOM.escapeID(theme.id));
bdEvents.dispatch("theme-disabled") bdEvents.dispatch("theme-disabled")
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been disabled.`); if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been disabled.`);
}; };
ThemeModule.prototype.disable = function (name, reload = false) { ThemeModule.prototype.disable = function (name, reload = false) {
return this.disableTheme(name, reload); return this.disableTheme(name, reload);
}; };
ThemeModule.prototype.toggleTheme = function(theme) { ThemeModule.prototype.toggleTheme = function(theme) {
if (themeCookie[theme]) this.disableTheme(theme); if (themeCookie[theme]) this.disableTheme(theme);
else this.enableTheme(theme); else this.enableTheme(theme);
}; };
ThemeModule.prototype.toggle = function (name) { ThemeModule.prototype.toggle = function (name) {
return this.toggleTheme(name); return this.toggleTheme(name);
}; };
ThemeModule.prototype.loadTheme = async function(filename) { ThemeModule.prototype.loadTheme = async function(filename) {
const error = await ContentManager.loadContent(filename, "theme"); const error = await ContentManager.loadContent(filename, "theme");
if (error) { if (error) {
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]}); if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]});
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${filename} could not be loaded. It may not have been loaded.`, {type: "error"}); if (settingsCookie["fork-ps-2"]) Utils.showToast(`${filename} could not be loaded. It may not have been loaded.`, {type: "error"});
return Utils.err("ContentManager", `${filename} could not be loaded.`, error); return Utils.err("ContentManager", `${filename} could not be loaded.`, error);
} }
const theme = Object.values(bdthemes).find(p => p.filename == filename); const theme = Object.values(bdthemes).find(p => p.filename == filename);
Utils.log("ContentManager", `${theme.name} v${theme.version} was loaded.`); Utils.log("ContentManager", `${theme.name} v${theme.version} was loaded.`);
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme.name} v${theme.version} was loaded.`, {type: "success"}); if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme.name} v${theme.version} was loaded.`, {type: "success"});
BDEvents.dispatch("theme-loaded", theme.name); BDEvents.dispatch("theme-loaded", theme.name);
}; };
ThemeModule.prototype.unloadTheme = function(filenameOrName) { ThemeModule.prototype.unloadTheme = function(filenameOrName) {
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName]; const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
if (!bdtheme) return; if (!bdtheme) return;
const theme = bdtheme.name; const theme = bdtheme.name;
if (themeCookie[theme]) this.disableTheme(theme, true); if (themeCookie[theme]) this.disableTheme(theme, true);
const error = ContentManager.unloadContent(bdthemes[theme].filename, "theme"); const error = ContentManager.unloadContent(bdthemes[theme].filename, "theme");
delete bdthemes[theme]; delete bdthemes[theme];
if (error) { if (error) {
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]}); if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]});
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be unloaded. It may have not been loaded yet.`, {type: "error"}); if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be unloaded. It may have not been loaded yet.`, {type: "error"});
return Utils.err("ContentManager", `${theme} could not be unloaded. It may have not been loaded yet.`, error); return Utils.err("ContentManager", `${theme} could not be unloaded. It may have not been loaded yet.`, error);
} }
Utils.log("ContentManager", `${theme} was unloaded.`); Utils.log("ContentManager", `${theme} was unloaded.`);
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} was unloaded.`, {type: "success"}); if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} was unloaded.`, {type: "success"});
BDEvents.dispatch("theme-unloaded", theme); BDEvents.dispatch("theme-unloaded", theme);
}; };
ThemeModule.prototype.delete = function(filenameOrName) { ThemeModule.prototype.delete = function(filenameOrName) {
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName]; const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
if (!bdtheme) return; if (!bdtheme) return;
this.unloadTheme(bdtheme.filename); this.unloadTheme(bdtheme.filename);
const fullPath = require("path").resolve(ContentManager.themesFolder, bdtheme.filename); const fullPath = require("path").resolve(ContentManager.themesFolder, bdtheme.filename);
require("fs").unlinkSync(fullPath); require("fs").unlinkSync(fullPath);
}; };
ThemeModule.prototype.reloadTheme = async function(filenameOrName) { ThemeModule.prototype.reloadTheme = async function(filenameOrName) {
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName]; const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
if (!bdtheme) return this.loadTheme(filenameOrName); if (!bdtheme) return this.loadTheme(filenameOrName);
const theme = bdtheme.name; const theme = bdtheme.name;
const error = await ContentManager.reloadContent(bdthemes[theme].filename, "theme"); const error = await ContentManager.reloadContent(bdthemes[theme].filename, "theme");
if (themeCookie[theme]) this.disableTheme(theme, true), this.enableTheme(theme, true); if (themeCookie[theme]) this.disableTheme(theme, true), this.enableTheme(theme, true);
if (error) { if (error) {
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]}); if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]});
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be reloaded.`, {type: "error"}); if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be reloaded.`, {type: "error"});
return Utils.err("ContentManager", `${theme} could not be reloaded.`, error); return Utils.err("ContentManager", `${theme} could not be reloaded.`, error);
} }
Utils.log("ContentManager", `${theme} v${bdthemes[theme].version} was reloaded.`); Utils.log("ContentManager", `${theme} v${bdthemes[theme].version} was reloaded.`);
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} v${bdthemes[theme].version} was reloaded.`, {type: "success"}); if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} v${bdthemes[theme].version} was reloaded.`, {type: "success"});
BDEvents.dispatch("theme-reloaded", theme); BDEvents.dispatch("theme-reloaded", theme);
}; };
ThemeModule.prototype.reload = function(name) { ThemeModule.prototype.reload = function(name) {
return this.reloadTheme(name); return this.reloadTheme(name);
}; };
ThemeModule.prototype.edit = function(filenameOrName) { ThemeModule.prototype.edit = function(filenameOrName) {
const bdplugin = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName]; const bdplugin = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
if (!bdplugin) return; if (!bdplugin) return;
const fullPath = require("path").resolve(ContentManager.themesFolder, bdplugin.filename); const fullPath = require("path").resolve(ContentManager.themesFolder, bdplugin.filename);
require("electron").shell.openItem(`${fullPath}`); require("electron").shell.openItem(`${fullPath}`);
}; };
ThemeModule.prototype.updateThemeList = function() { ThemeModule.prototype.updateThemeList = function() {
const results = ContentManager.loadNewContent("theme"); const results = ContentManager.loadNewContent("theme");
for (const filename of results.added) this.loadTheme(filename); for (const filename of results.added) this.loadTheme(filename);
for (const name of results.removed) this.unloadTheme(name); for (const name of results.removed) this.unloadTheme(name);
}; };
ThemeModule.prototype.loadThemeData = function() { ThemeModule.prototype.loadThemeData = function() {
const saved = DataStore.getSettingGroup("themes"); const saved = DataStore.getSettingGroup("themes");
if (saved) { if (saved) {
Object.assign(themeCookie, saved); Object.assign(themeCookie, saved);
} }
}; };
ThemeModule.prototype.saveThemeData = function () { ThemeModule.prototype.saveThemeData = function () {
DataStore.setSettingGroup("themes", themeCookie); DataStore.setSettingGroup("themes", themeCookie);
}; };
export default new ThemeModule(); export default new ThemeModule();

View File

@ -1,167 +1,167 @@
import {settings} from "../0globals"; import {settings} from "../0globals";
import themeModule from "./themeModule"; import themeModule from "./themeModule";
export default new class V2 { export default new class V2 {
constructor() { constructor() {
this.editorDetached = false; this.editorDetached = false;
this.WebpackModules = (() => { this.WebpackModules = (() => {
const req = webpackJsonp.push([[], {__extra_id__: (module, exports, req) => module.exports = req}, [["__extra_id__"]]]); const req = webpackJsonp.push([[], {__extra_id__: (module, exports, req) => module.exports = req}, [["__extra_id__"]]]);
delete req.m.__extra_id__; delete req.m.__extra_id__;
delete req.c.__extra_id__; delete req.c.__extra_id__;
const shouldProtect = theModule => { const shouldProtect = theModule => {
if (theModule.remove && theModule.set && theModule.clear && theModule.get && !theModule.sort) return true; if (theModule.remove && theModule.set && theModule.clear && theModule.get && !theModule.sort) return true;
if (theModule.getToken || theModule.getEmail || theModule.showToken) return true; if (theModule.getToken || theModule.getEmail || theModule.showToken) return true;
return false; return false;
}; };
const protect = (theModule, isDefault) => { const protect = (theModule, isDefault) => {
let mod = !isDefault ? theModule.default : theModule let mod = !isDefault ? theModule.default : theModule
if(!mod)return theModule if(!mod)return theModule
if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null; if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null;
if (!mod.getToken && !mod.getEmail && !mod.showToken)return theModule if (!mod.getToken && !mod.getEmail && !mod.showToken)return theModule
const proxy = new Proxy(mod, { const proxy = new Proxy(mod, {
getOwnPropertyDescriptor: function(obj, prop) { getOwnPropertyDescriptor: function(obj, prop) {
if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined; if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined;
return Object.getOwnPropertyDescriptor(obj, prop); return Object.getOwnPropertyDescriptor(obj, prop);
}, },
get: function(obj, func) { get: function(obj, func) {
if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa"; if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa";
if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com"; if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com";
if (func == "showToken" && obj.showToken) return () => true; if (func == "showToken" && obj.showToken) return () => true;
if (func == "__proto__" && obj.__proto__) return proxy; if (func == "__proto__" && obj.__proto__) return proxy;
return obj[func]; return obj[func];
} }
}); });
if(!isDefault){ if(!isDefault){
return Object.assign({}, theModule, {default: proxy}) return Object.assign({}, theModule, {default: proxy})
} }
return proxy; return proxy;
}; };
const find = (filter) => { const find = (filter) => {
for (const i in req.c) { for (const i in req.c) {
if (req.c.hasOwnProperty(i)) { if (req.c.hasOwnProperty(i)) {
const m = req.c[i].exports; const m = req.c[i].exports;
if (m && m.__esModule && m.default && filter(m.default)) return protect(m.default, true); if (m && m.__esModule && m.default && filter(m.default)) return protect(m.default, true);
if (m && filter(m)) return protect(m, false); if (m && filter(m)) return protect(m, false);
} }
} }
// console.warn("Cannot find loaded module in cache"); // console.warn("Cannot find loaded module in cache");
return null; return null;
}; };
const findAll = (filter) => { const findAll = (filter) => {
const modules = []; const modules = [];
for (const i in req.c) { for (const i in req.c) {
if (req.c.hasOwnProperty(i)) { if (req.c.hasOwnProperty(i)) {
const m = req.c[i].exports; const m = req.c[i].exports;
if (m && m.__esModule && m.default && filter(m.default)) modules.push(protect(m.default, true)); if (m && m.__esModule && m.default && filter(m.default)) modules.push(protect(m.default, true));
else if (m && filter(m)) modules.push(protect(m, false)); else if (m && filter(m)) modules.push(protect(m, false));
} }
} }
return modules; return modules;
}; };
const findByUniqueProperties = (propNames) => find(module => propNames.every(prop => module[prop] !== undefined)); const findByUniqueProperties = (propNames) => find(module => propNames.every(prop => module[prop] !== undefined));
const findByPrototypes = (protoNames) => find(module => module.prototype && protoNames.every(protoProp => module.prototype[protoProp] !== undefined)); const findByPrototypes = (protoNames) => find(module => module.prototype && protoNames.every(protoProp => module.prototype[protoProp] !== undefined));
const findByDisplayName = (displayName) => find(module => module.displayName === displayName); const findByDisplayName = (displayName) => find(module => module.displayName === displayName);
return {find, findAll, findByUniqueProperties, findByPrototypes, findByDisplayName}; return {find, findAll, findByUniqueProperties, findByPrototypes, findByDisplayName};
})(); })();
this.internal = { this.internal = {
react: this.WebpackModules.findByUniqueProperties(["Component", "PureComponent", "Children", "createElement", "cloneElement"]), react: this.WebpackModules.findByUniqueProperties(["Component", "PureComponent", "Children", "createElement", "cloneElement"]),
reactDom: this.WebpackModules.findByUniqueProperties(["findDOMNode"]) reactDom: this.WebpackModules.findByUniqueProperties(["findDOMNode"])
}; };
this.getInternalInstance = e => e[Object.keys(e).find(k => k.startsWith("__reactInternalInstance"))]; this.getInternalInstance = e => e[Object.keys(e).find(k => k.startsWith("__reactInternalInstance"))];
window.Lightcord.BetterDiscord.V2 = this window.Lightcord.BetterDiscord.V2 = this
} }
initialize() { initialize() {
} }
joinBD1() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("0Tmfo5ZbORCRqbAd");} joinBD1() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("0Tmfo5ZbORCRqbAd");}
leaveBD1() {this.GuildActions.leaveGuild("86004744966914048");} leaveBD1() {this.GuildActions.leaveGuild("86004744966914048");}
joinBD2() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("2HScm8j");} joinBD2() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("2HScm8j");}
leaveBD2() {this.GuildActions.leaveGuild("280806472928198656");} leaveBD2() {this.GuildActions.leaveGuild("280806472928198656");}
joinLC() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("7eFff2A");} joinLC() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("7eFff2A");}
leaveLC() {this.GuildActions.leaveGuild("705908350218666117");} leaveLC() {this.GuildActions.leaveGuild("705908350218666117");}
/** /**
* @type {typeof React} * @type {typeof React}
*/ */
get react() {return this.internal.react;} get react() {return this.internal.react;}
/** /**
* @type {typeof React} * @type {typeof React}
*/ */
get React() {return this.internal.react;} get React() {return this.internal.react;}
/** /**
* @type {typeof import("react-dom")} * @type {typeof import("react-dom")}
*/ */
get reactDom() {return this.internal.reactDom;} get reactDom() {return this.internal.reactDom;}
/** /**
* @type {typeof import("react-dom")} * @type {typeof import("react-dom")}
*/ */
get ReactDom() {return this.internal.reactDom;} get ReactDom() {return this.internal.reactDom;}
/** /**
* @type {typeof React.Component} * @type {typeof React.Component}
*/ */
get reactComponent() {return this.internal.react.Component;} get reactComponent() {return this.internal.react.Component;}
/** /**
* @type {typeof React.Component} * @type {typeof React.Component}
*/ */
get ReactComponent() {return this.internal.react.Component;} get ReactComponent() {return this.internal.react.Component;}
get anchorClasses() {return this.WebpackModules.findByUniqueProperties(["anchorUnderlineOnHover"]) || {anchor: "anchor-3Z-8Bb", anchorUnderlineOnHover: "anchorUnderlineOnHover-2ESHQB"};} get anchorClasses() {return this.WebpackModules.findByUniqueProperties(["anchorUnderlineOnHover"]) || {anchor: "anchor-3Z-8Bb", anchorUnderlineOnHover: "anchorUnderlineOnHover-2ESHQB"};}
get slateEditorClasses() {return this.WebpackModules.findByUniqueProperties(["slateTextArea"]);} get slateEditorClasses() {return this.WebpackModules.findByUniqueProperties(["slateTextArea"]);}
get messageClasses() {return this.WebpackModules.findByUniqueProperties(["message", "containerCozy"]);} get messageClasses() {return this.WebpackModules.findByUniqueProperties(["message", "containerCozy"]);}
get guildClasses() { get guildClasses() {
const guildsWrapper = BDModules.get(e => e.wrapper && e.unreadMentionsBar)[0]; const guildsWrapper = BDModules.get(e => e.wrapper && e.unreadMentionsBar)[0];
const guilds = BDModules.get(e => e.guildsError && e.selected)[0] const guilds = BDModules.get(e => e.guildsError && e.selected)[0]
const pill = BDModules.get(e => e.blobContainer)[0] const pill = BDModules.get(e => e.blobContainer)[0]
return Object.assign({}, guildsWrapper, guilds, pill); return Object.assign({}, guildsWrapper, guilds, pill);
} }
get MessageContentComponent() {return this.WebpackModules.find(m => m.defaultProps && m.defaultProps.hasOwnProperty("disableButtons"));} get MessageContentComponent() {return this.WebpackModules.find(m => m.defaultProps && m.defaultProps.hasOwnProperty("disableButtons"));}
get MessageComponent() {return this.WebpackModules.find(m => m.default && m.default.displayName && m.default.displayName == "Message");} get MessageComponent() {return this.WebpackModules.find(m => m.default && m.default.displayName && m.default.displayName == "Message");}
get TimeFormatter() {return this.WebpackModules.findByUniqueProperties(["dateFormat"]);} get TimeFormatter() {return this.WebpackModules.findByUniqueProperties(["dateFormat"]);}
get TooltipWrapper() {return this.WebpackModules.findByDisplayName("Tooltip");} get TooltipWrapper() {return this.WebpackModules.findByDisplayName("Tooltip");}
get NativeModule() {return this.WebpackModules.findByUniqueProperties(["setBadge"]);} get NativeModule() {return this.WebpackModules.findByUniqueProperties(["setBadge"]);}
get InviteActions() {return this.WebpackModules.findByUniqueProperties(["acceptInvite"]);} get InviteActions() {return this.WebpackModules.findByUniqueProperties(["acceptInvite"]);}
get GuildActions() {return this.WebpackModules.findByUniqueProperties(["leaveGuild"]);} get GuildActions() {return this.WebpackModules.findByUniqueProperties(["leaveGuild"]);}
get Tooltips() {return this.WebpackModules.find(m => m.hide && m.show && !m.search && !m.submit && !m.search && !m.activateRagingDemon && !m.dismiss);} get Tooltips() {return this.WebpackModules.find(m => m.hide && m.show && !m.search && !m.submit && !m.search && !m.activateRagingDemon && !m.dismiss);}
get KeyGenerator() {return this.WebpackModules.find(m => m.toString && /"binary"/.test(m.toString()));} get KeyGenerator() {return this.WebpackModules.find(m => m.toString && /"binary"/.test(m.toString()));}
get LayerStack() {return this.WebpackModules.findByUniqueProperties(["popLayer"]);} get LayerStack() {return this.WebpackModules.findByUniqueProperties(["popLayer"]);}
get UserStore() {return this.WebpackModules.findByUniqueProperties(["getCurrentUser"]);} get UserStore() {return this.WebpackModules.findByUniqueProperties(["getCurrentUser"]);}
get ChannelStore() {return this.WebpackModules.findByUniqueProperties(["getChannel"]);} get ChannelStore() {return this.WebpackModules.findByUniqueProperties(["getChannel"]);}
get ChannelActions() {return this.WebpackModules.findByUniqueProperties(["openPrivateChannel"]);} get ChannelActions() {return this.WebpackModules.findByUniqueProperties(["openPrivateChannel"]);}
get PrivateChannelActions() {return this.WebpackModules.findByUniqueProperties(["selectPrivateChannel"]);} get PrivateChannelActions() {return this.WebpackModules.findByUniqueProperties(["selectPrivateChannel"]);}
openDM(userId) { openDM(userId) {
const selfId = this.UserStore.getCurrentUser().id; const selfId = this.UserStore.getCurrentUser().id;
if (selfId == userId) return; if (selfId == userId) return;
const privateChannelId = this.ChannelStore.getDMFromUserId(userId); const privateChannelId = this.ChannelStore.getDMFromUserId(userId);
if (privateChannelId) return this.PrivateChannelActions.selectPrivateChannel(privateChannelId); if (privateChannelId) return this.PrivateChannelActions.selectPrivateChannel(privateChannelId);
this.ChannelActions.openPrivateChannel(selfId, userId); this.ChannelActions.openPrivateChannel(selfId, userId);
} }
parseSettings(cat) { parseSettings(cat) {
return Object.keys(settings).reduce((arr, key) => { return Object.keys(settings).reduce((arr, key) => {
const setting = settings[key]; const setting = settings[key];
if (setting.cat === cat && setting.implemented && !setting.hidden) { if (setting.cat === cat && setting.implemented && !setting.hidden) {
setting.text = key; setting.text = key;
arr.push(setting); arr.push(setting);
} return arr; } return arr;
}, []); }, []);
} }
}; };

View File

@ -1,132 +1,132 @@
import BDV2 from "../modules/v2"; import BDV2 from "../modules/v2";
import V2C_SettingsTitle from "./settingsTitle"; import V2C_SettingsTitle from "./settingsTitle";
/** /**
* @type {typeof import("react")} * @type {typeof import("react")}
*/ */
const React = BDV2.React; const React = BDV2.React;
let marginModule2 = BDModules.get(e => e.defaultMarginh5)[0] let marginModule2 = BDModules.get(e => e.defaultMarginh5)[0]
let colorModule = BDModules.get(e => e.colorStandard)[0] let colorModule = BDModules.get(e => e.colorStandard)[0]
let sizeModule = BDModules.get(e => e.size32)[0] let sizeModule = BDModules.get(e => e.size32)[0]
let scrollbarModule1 = BDModules.get(e => e.scrollbarGhostHairline)[0] let scrollbarModule1 = BDModules.get(e => e.scrollbarGhostHairline)[0]
const GuildModule = BDModules.get(e => e.default && e.default.getGuilds)[0].default const GuildModule = BDModules.get(e => e.default && e.default.getGuilds)[0].default
const relationShipModule = BDModules.get(e => e.default && e.default.getRelationships)[0].default const relationShipModule = BDModules.get(e => e.default && e.default.getRelationships)[0].default
const sessionModule = BDModules.get(e => e.default && e.default.getSessions)[0].default const sessionModule = BDModules.get(e => e.default && e.default.getSessions)[0].default
const userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0].default const userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0].default
export default class V2C_AccountInfos extends React.Component { export default class V2C_AccountInfos extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
} }
render() { render() {
if(!marginModule2)marginModule2 = BDModules.get(e => e.defaultMarginh5)[0] if(!marginModule2)marginModule2 = BDModules.get(e => e.defaultMarginh5)[0]
if(!colorModule)colorModule = BDModules.get(e => e.colorStandard)[0] if(!colorModule)colorModule = BDModules.get(e => e.colorStandard)[0]
if(!sizeModule)sizeModule = BDModules.get(e => e.size32)[0] if(!sizeModule)sizeModule = BDModules.get(e => e.size32)[0]
return [ return [
<V2C_SettingsTitle text="Account Infos"/>, <V2C_SettingsTitle text="Account Infos"/>,
<div> <div>
<h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}> <h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}>
Profile Profile
</h5> </h5>
<CodeContent content={this.getProfileValue()} language="diff" /> <CodeContent content={this.getProfileValue()} language="diff" />
<h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}> <h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}>
Statistics Statistics
</h5> </h5>
<CodeContent content={this.getStatistics()} language="diff" /> <CodeContent content={this.getStatistics()} language="diff" />
<h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}> <h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}>
Connected Sessions Connected Sessions
</h5> </h5>
<CodeContent content={this.getSessionValue()} language="diff" /> <CodeContent content={this.getSessionValue()} language="diff" />
</div>, </div>,
<div className={BDModules.get(e => e.marginBottom20)[0].marginBottom20}></div> <div className={BDModules.get(e => e.marginBottom20)[0].marginBottom20}></div>
] ]
} }
getSessionValue(){ getSessionValue(){
const sessionsRaw = sessionModule.getSessions() const sessionsRaw = sessionModule.getSessions()
const sessions = Object.keys(sessionsRaw).filter(e => e !== "all").map(e => sessionsRaw[e]) const sessions = Object.keys(sessionsRaw).filter(e => e !== "all").map(e => sessionsRaw[e])
if(sessions.length === 0)return "- No session detected. Please try in a few seconds" if(sessions.length === 0)return "- No session detected. Please try in a few seconds"
return sessions.map(e => { return sessions.map(e => {
return `+ id: ${e.sessionId} return `+ id: ${e.sessionId}
+ os: ${e.clientInfo.os[0].toUpperCase()+e.clientInfo.os.slice(1)} + os: ${e.clientInfo.os[0].toUpperCase()+e.clientInfo.os.slice(1)}
+ client: ${e.clientInfo.client} + client: ${e.clientInfo.client}
+ status: ${e.status} + status: ${e.status}
+ Activities: ${e.activities.length}` + Activities: ${e.activities.length}`
}).join("\n"+"-".repeat(38)+"\n") }).join("\n"+"-".repeat(38)+"\n")
} }
getProfileValue(){ getProfileValue(){
const user = userModule.getCurrentUser() const user = userModule.getCurrentUser()
/** /**
* @type {Date} * @type {Date}
*/ */
const createdAt = user.createdAt const createdAt = user.createdAt
let avatarURL = user.avatarURL let avatarURL = user.avatarURL
if(user.avatar && user.avatar.startsWith("a_")){ if(user.avatar && user.avatar.startsWith("a_")){
avatarURL = user.getAvatarURL("gif") avatarURL = user.getAvatarURL("gif")
} }
if(avatarURL.startsWith("/")){ if(avatarURL.startsWith("/")){
avatarURL = "https://discord.com"+avatarURL avatarURL = "https://discord.com"+avatarURL
} }
if(avatarURL.endsWith("?size=128")){ if(avatarURL.endsWith("?size=128")){
avatarURL = avatarURL.replace("?size=128", "?size=4096") avatarURL = avatarURL.replace("?size=128", "?size=4096")
} }
return `+ Username: ${user.username} return `+ Username: ${user.username}
+ Discriminator: ${user.discriminator} + Discriminator: ${user.discriminator}
+ Tag: ${user.tag} + Tag: ${user.tag}
+ ID: ${user.id} + ID: ${user.id}
+ Avatar: ${user.avatar} + Avatar: ${user.avatar}
+ Avatar URL: ${avatarURL} + Avatar URL: ${avatarURL}
+ Creation Date: ${(createdAt.getDate()).toString().padStart(2, "0")}/${(createdAt.getMonth()+1).toString().padStart(2, "0")}/${(createdAt.getFullYear()).toString().padStart(2, "0")} ${createdAt.getHours().toString().padStart(2, "0")}h ${createdAt.getMinutes().toString().padStart(2, "0")}min ${createdAt.getSeconds()}s + Creation Date: ${(createdAt.getDate()).toString().padStart(2, "0")}/${(createdAt.getMonth()+1).toString().padStart(2, "0")}/${(createdAt.getFullYear()).toString().padStart(2, "0")} ${createdAt.getHours().toString().padStart(2, "0")}h ${createdAt.getMinutes().toString().padStart(2, "0")}min ${createdAt.getSeconds()}s
+ Flags: ${user.flags} + Flags: ${user.flags}
+ Has Nitro: ${user.hasPremiumSubscription ? "Yes" : "No"} + Has Nitro: ${user.hasPremiumSubscription ? "Yes" : "No"}
- Email: ${user.email} - Email: ${user.email}
- 2FA: ${user.mfaEnabled ? "Yes" : "No"} - 2FA: ${user.mfaEnabled ? "Yes" : "No"}
- Has Been On Mobile: ${user.mobile ? "Yes" : "No"} - Has Been On Mobile: ${user.mobile ? "Yes" : "No"}
- Phone: ${user.phone || "None"} - Phone: ${user.phone || "None"}
- Verified: ${user.verified} - Verified: ${user.verified}
- Can See NSFW Channels: ${user.nsfwAllowed}`; - Can See NSFW Channels: ${user.nsfwAllowed}`;
} }
getStatistics(){ getStatistics(){
const guilds = Object.values(GuildModule.getGuilds()) const guilds = Object.values(GuildModule.getGuilds())
const relations = Object.keys(relationShipModule.getRelationships()) const relations = Object.keys(relationShipModule.getRelationships())
const friends = relations.filter(e => relationShipModule.isFriend(e)) const friends = relations.filter(e => relationShipModule.isFriend(e))
const blocked = relations.filter(e => relationShipModule.isBlocked(e)) const blocked = relations.filter(e => relationShipModule.isBlocked(e))
return `+ Server Count: ${guilds.length} servers return `+ Server Count: ${guilds.length} servers
+ Relations: ${relations.length} relations + Relations: ${relations.length} relations
+ Friends Count: ${friends.length} friends + Friends Count: ${friends.length} friends
- Blocked Users Count: ${blocked.length} blocked users` - Blocked Users Count: ${blocked.length} blocked users`
} }
} }
let hightlightJS = BDModules.get(e => e.highlight)[0] let hightlightJS = BDModules.get(e => e.highlight)[0]
let messageModule1 = BDModules.get(e => e.markup)[0] let messageModule1 = BDModules.get(e => e.markup)[0]
let messageModule2 = BDModules.get(e => e.messageContent)[0] let messageModule2 = BDModules.get(e => e.messageContent)[0]
class CodeContent extends React.Component { class CodeContent extends React.Component {
render(){ render(){
if(!messageModule1)messageModule1 = BDModules.get(e => e.markup)[0] if(!messageModule1)messageModule1 = BDModules.get(e => e.markup)[0]
if(!messageModule2)messageModule2 = BDModules.get(e => e.messageContent)[0] if(!messageModule2)messageModule2 = BDModules.get(e => e.messageContent)[0]
if(!scrollbarModule1)scrollbarModule1 = BDModules.get(e => e.scrollbarGhostHairline)[0] if(!scrollbarModule1)scrollbarModule1 = BDModules.get(e => e.scrollbarGhostHairline)[0]
if(!hightlightJS)hightlightJS = BDModules.get(e => e.listLanguages)[0] if(!hightlightJS)hightlightJS = BDModules.get(e => e.listLanguages)[0]
return (<div class={`${messageModule1.markup} ${messageModule2.messageContent}`}> return (<div class={`${messageModule1.markup} ${messageModule2.messageContent}`}>
<pre> <pre>
<code class={`${scrollbarModule1.scrollbarGhostHairline} hljs`} dangerouslySetInnerHTML={{__html: hightlightJS.highlight(this.props.language, this.props.content).value}}> <code class={`${scrollbarModule1.scrollbarGhostHairline} hljs`} dangerouslySetInnerHTML={{__html: hightlightJS.highlight(this.props.language, this.props.content).value}}>
</code> </code>
</pre> </pre>
<div className={BDModules.get(e => e.marginBottom8)[0].marginBottom8}></div> <div className={BDModules.get(e => e.marginBottom8)[0].marginBottom8}></div>
</div>) </div>)
} }
} }

View File

@ -1,292 +1,292 @@
// Good luck to read my code, Even me can't read it properly. // Good luck to read my code, Even me can't read it properly.
import { stat } from "fs" import { stat } from "fs"
import { uuidv4 } from "../modules/distant" import { uuidv4 } from "../modules/distant"
import webpackModules from "../modules/webpackModules" import webpackModules from "../modules/webpackModules"
import { remote } from "electron" import { remote } from "electron"
import MarginTop from "./margintop" import MarginTop from "./margintop"
let formModule let formModule
export default class ApiPreview extends React.PureComponent { export default class ApiPreview extends React.PureComponent {
constructor(){ constructor(){
super(...arguments) super(...arguments)
this.state = { this.state = {
states: [] states: []
} }
} }
render(){ render(){
if(!formModule)formModule = webpackModules.find(e => e.FormSection) if(!formModule)formModule = webpackModules.find(e => e.FormSection)
/** /**
* @type {Function[]} * @type {Function[]}
*/ */
const allComponents = [...new Set(Object.keys(window.Lightcord.Api.Components).map(e => { const allComponents = [...new Set(Object.keys(window.Lightcord.Api.Components).map(e => {
return Object.keys(window.Lightcord.Api.Components[e]).map(k => window.Lightcord.Api.Components[e][k]) return Object.keys(window.Lightcord.Api.Components[e]).map(k => window.Lightcord.Api.Components[e][k])
}).flat())] }).flat())]
return [ return [
<formModule.FormSection tag="h2" title="Lightcord's Api Availlable components"> <formModule.FormSection tag="h2" title="Lightcord's Api Availlable components">
<formModule.FormText type="description" className="" selectable={false}> <formModule.FormText type="description" className="" selectable={false}>
These components are here for the plugin devs. They can quickly embed any component below with this panel. These components are here for the plugin devs. They can quickly embed any component below with this panel.
<div style={{marginTop: "20px"}}></div> <div style={{marginTop: "20px"}}></div>
<Lightcord.Api.Components.general.AlertBox type="info">All these components have error handling. If you want none, add `.original` after the component path.</Lightcord.Api.Components.general.AlertBox> <Lightcord.Api.Components.general.AlertBox type="info">All these components have error handling. If you want none, add `.original` after the component path.</Lightcord.Api.Components.general.AlertBox>
<Lightcord.Api.Components.general.AlertBox type="warn">We do not recommend modifying these component with plugins. Only do this if you know what you are doing.</Lightcord.Api.Components.general.AlertBox> <Lightcord.Api.Components.general.AlertBox type="warn">We do not recommend modifying these component with plugins. Only do this if you know what you are doing.</Lightcord.Api.Components.general.AlertBox>
</formModule.FormText> </formModule.FormText>
<MarginTop></MarginTop> <MarginTop></MarginTop>
<Lightcord.Api.Components.inputs.Button color="brand" look="outlined" size="medium" hoverColor="green" onClick={() => { <Lightcord.Api.Components.inputs.Button color="brand" look="outlined" size="medium" hoverColor="green" onClick={() => {
remote.shell.openExternal("https://lightcord.deroku.xyz/LightcordApi/docs") remote.shell.openExternal("https://lightcord.deroku.xyz/LightcordApi/docs")
}} wrapper={false}> }} wrapper={false}>
Documentation Documentation
</Lightcord.Api.Components.inputs.Button> </Lightcord.Api.Components.inputs.Button>
</formModule.FormSection>, </formModule.FormSection>,
allComponents.map(comp => { allComponents.map(comp => {
const compName = comp.displayName || comp.name const compName = comp.displayName || comp.name
const compPath = `Lightcord.Api.Components.${Object.keys(window.Lightcord.Api.Components).find(e => window.Lightcord.Api.Components[e][compName])}.${compName}` const compPath = `Lightcord.Api.Components.${Object.keys(window.Lightcord.Api.Components).find(e => window.Lightcord.Api.Components[e][compName])}.${compName}`
return <ComponentPreview key={compPath} comp={comp} /> return <ComponentPreview key={compPath} comp={comp} />
}) })
] ]
} }
get renders(){ get renders(){
} }
} }
class ComponentPreview extends React.Component { class ComponentPreview extends React.Component {
constructor(props){ constructor(props){
super(props) super(props)
this.state = { this.state = {
tab: "preview", tab: "preview",
elem: props.comp, elem: props.comp,
options: {} options: {}
} }
} }
render(){ render(){
const comp = this.props.comp const comp = this.props.comp
let AllPreviews = [] let AllPreviews = []
if(comp.AllPreviews)AllPreviews = comp.AllPreviews if(comp.AllPreviews)AllPreviews = comp.AllPreviews
let state = this.state let state = this.state
let getProps = () => { let getProps = () => {
let final = {} let final = {}
AllPreviews.forEach(category => { AllPreviews.forEach(category => {
final[Object.keys(category[0])[0]] = category[0][Object.keys(category[0])[0]] final[Object.keys(category[0])[0]] = category[0][Object.keys(category[0])[0]]
}) })
Object.keys(state.options).forEach(key => { Object.keys(state.options).forEach(key => {
final[key] = AllPreviews.find(e => e.find(e => e[key]))[state.options[key]][key] final[key] = AllPreviews.find(e => e.find(e => e[key]))[state.options[key]][key]
}) })
return final return final
} }
let renderPreview = () => { let renderPreview = () => {
return <div style={{margin: "20px"}}> return <div style={{margin: "20px"}}>
<div style={{ <div style={{
backgroundColor: "var(--background-secondary)", backgroundColor: "var(--background-secondary)",
padding: "30px 30px", padding: "30px 30px",
borderRadius: "8px" borderRadius: "8px"
}} className="lc-tab-box-shadow"> }} className="lc-tab-box-shadow">
{React.createElement(comp, getProps())} {React.createElement(comp, getProps())}
</div> </div>
</div> </div>
} }
let renderCode = () => { let renderCode = () => {
return <div style={{margin: "20px"}}> return <div style={{margin: "20px"}}>
<div style={{ <div style={{
backgroundColor: "var(--background-secondary)", backgroundColor: "var(--background-secondary)",
padding: "30px 30px", padding: "30px 30px",
borderRadius: "8px" borderRadius: "8px"
}} className="lc-tab-box-shadow"> }} className="lc-tab-box-shadow">
<window.Lightcord.Api.Components.general.SettingSubTitle> <window.Lightcord.Api.Components.general.SettingSubTitle>
JSX JSX
</window.Lightcord.Api.Components.general.SettingSubTitle> </window.Lightcord.Api.Components.general.SettingSubTitle>
<window.Lightcord.Api.Components.general.ErrorCatcher> <window.Lightcord.Api.Components.general.ErrorCatcher>
{React.createElement(() => { {React.createElement(() => {
return <window.Lightcord.Api.Components.general.CodeBlock language="jsx" content={generateCode("jsx")}/> return <window.Lightcord.Api.Components.general.CodeBlock language="jsx" content={generateCode("jsx")}/>
})} })}
</window.Lightcord.Api.Components.general.ErrorCatcher> </window.Lightcord.Api.Components.general.ErrorCatcher>
<window.Lightcord.Api.Components.general.SettingSubTitle> <window.Lightcord.Api.Components.general.SettingSubTitle>
React React
</window.Lightcord.Api.Components.general.SettingSubTitle> </window.Lightcord.Api.Components.general.SettingSubTitle>
<window.Lightcord.Api.Components.general.ErrorCatcher> <window.Lightcord.Api.Components.general.ErrorCatcher>
{React.createElement(() => { {React.createElement(() => {
return <window.Lightcord.Api.Components.general.CodeBlock language="js" content={generateCode("react")}/> return <window.Lightcord.Api.Components.general.CodeBlock language="js" content={generateCode("react")}/>
})} })}
</window.Lightcord.Api.Components.general.ErrorCatcher> </window.Lightcord.Api.Components.general.ErrorCatcher>
</div> </div>
</div> </div>
} }
let getStrForProp = (value, compPath, lang) => { let getStrForProp = (value, compPath, lang) => {
if(typeof value === "string"){ if(typeof value === "string"){
return value return value
}else if(typeof value === "boolean"){ }else if(typeof value === "boolean"){
return String(value) return String(value)
}else if(typeof value === "function"){ }else if(typeof value === "function"){
return value.toString() return value.toString()
}else if(typeof value === "object"){ }else if(typeof value === "object"){
if(value && value.$$typeof && (value.$$typeof === Symbol.for("react.element") || value.$$typeof === 0xeac7)){ if(value && value.$$typeof && (value.$$typeof === Symbol.for("react.element") || value.$$typeof === 0xeac7)){
if(compPath === "Lightcord.Api.Components.general.Tabs"){ if(compPath === "Lightcord.Api.Components.general.Tabs"){
if(lang === "react"){ if(lang === "react"){
return `React.createElement("div", {style: { return `React.createElement("div", {style: {
marginTop: "20px", marginBottom: "20px" marginTop: "20px", marginBottom: "20px"
}}, }},
React.createElement("div", {style: { React.createElement("div", {style: {
backgroundColor: "var(--background-secondary)", backgroundColor: "var(--background-secondary)",
padding: "30px 30px", padding: "30px 30px",
borderRadius: "8px" borderRadius: "8px"
}, className: "lc-tab-box-shadow" }, }, className: "lc-tab-box-shadow" },
React.createElement(Lightcord.Api.Components.general.Title, null, "Preview tabs") React.createElement(Lightcord.Api.Components.general.Title, null, "Preview tabs")
) )
)` )`
}else if(lang === "jsx"){ }else if(lang === "jsx"){
return `<div style={{ return `<div style={{
marginTop: "20px", marginBottom: "20px" marginTop: "20px", marginBottom: "20px"
}}> }}>
<div style={{ <div style={{
backgroundColor: "var(--background-secondary)", backgroundColor: "var(--background-secondary)",
padding: "30px 30px", padding: "30px 30px",
borderRadius: "8px" borderRadius: "8px"
}} className="lc-tab-box-shadow"> }} className="lc-tab-box-shadow">
<Lightcord.Api.Components.general.Title>Preview tabs</Lightcord.Api.Components.general.Title> <Lightcord.Api.Components.general.Title>Preview tabs</Lightcord.Api.Components.general.Title>
</div> </div>
</div>` </div>`
} }
} }
return "Your components here." return "Your components here."
} }
return JSON.stringify(value, null, " ") return JSON.stringify(value, null, " ")
}else if(typeof value === "number"){ }else if(typeof value === "number"){
return String(value) return String(value)
} }
return String(value) return String(value)
} }
let generateCode = function(lang){ // code formatting is hard let generateCode = function(lang){ // code formatting is hard
const compName = comp.displayName || comp.name const compName = comp.displayName || comp.name
let categories = Object.keys(window.Lightcord.Api.Components) let categories = Object.keys(window.Lightcord.Api.Components)
const compCategory = categories.find(e => window.Lightcord.Api.Components[e][compName]) const compCategory = categories.find(e => window.Lightcord.Api.Components[e][compName])
const compPath = `Lightcord.Api.Components.${compCategory}.${compName}` const compPath = `Lightcord.Api.Components.${compCategory}.${compName}`
const props = getProps() const props = getProps()
if(lang === "jsx"){ if(lang === "jsx"){
let propStrings = [] let propStrings = []
let childrenProp = null let childrenProp = null
Object.keys(props).forEach(key => { Object.keys(props).forEach(key => {
if(key == "children"){ if(key == "children"){
childrenProp = getStrForProp(props[key], compPath, lang) childrenProp = getStrForProp(props[key], compPath, lang)
}else{ }else{
let str = key+"=" let str = key+"="
if(typeof props[key] === "string"){ if(typeof props[key] === "string"){
str += JSON.stringify(props[key]) str += JSON.stringify(props[key])
}else{ }else{
str += `{${getStrForProp(props[key], compPath, lang)}}` str += `{${getStrForProp(props[key], compPath, lang)}}`
} }
propStrings.push(str) propStrings.push(str)
} }
}) })
let openTag let openTag
if(childrenProp){ if(childrenProp){
openTag = `<${compPath} ${propStrings.join(" ")}>` openTag = `<${compPath} ${propStrings.join(" ")}>`
let closeTag = `</${compPath}>` let closeTag = `</${compPath}>`
return `${openTag}\n ${childrenProp}\n${closeTag}` return `${openTag}\n ${childrenProp}\n${closeTag}`
}else{ }else{
openTag = `<${compPath} ${propStrings.join(" ")}/>` openTag = `<${compPath} ${propStrings.join(" ")}/>`
return openTag return openTag
} }
}else if(lang === "react"){ }else if(lang === "react"){
let children = props.children || null let children = props.children || null
delete props.children delete props.children
if(children && children.$$typeof && (children.$$typeof === Symbol.for("react.element") || children.$$typeof === 0xeac7)){ if(children && children.$$typeof && (children.$$typeof === Symbol.for("react.element") || children.$$typeof === 0xeac7)){
children = getStrForProp(children, compPath, lang) children = getStrForProp(children, compPath, lang)
} }
let propStrings = [] let propStrings = []
Object.keys(props).forEach(key => { Object.keys(props).forEach(key => {
let visibleKey = /[^\w\d_]/g.test(key) ? JSON.stringify(key) : key let visibleKey = /[^\w\d_]/g.test(key) ? JSON.stringify(key) : key
let str = visibleKey+": " let str = visibleKey+": "
if(typeof props[key] === "string"){ if(typeof props[key] === "string"){
str += JSON.stringify(props[key]) str += JSON.stringify(props[key])
}else{ }else{
str += getStrForProp(props[key], compPath, lang).split("\n").map((str, i) => { str += getStrForProp(props[key], compPath, lang).split("\n").map((str, i) => {
if(i === 0)return str if(i === 0)return str
return " " + str return " " + str
}).join("\n") }).join("\n")
} }
propStrings.push(str) propStrings.push(str)
}) })
let propObject = "{" let propObject = "{"
if(propStrings.length){ if(propStrings.length){
propStrings.forEach((str, i) => { propStrings.forEach((str, i) => {
let isLast = i === propStrings.length - 1 let isLast = i === propStrings.length - 1
let isFirst = i === 0 let isFirst = i === 0
if(!isFirst){ if(!isFirst){
propObject += "," propObject += ","
} }
propObject += "\n " propObject += "\n "
propObject += str propObject += str
if(isLast){ if(isLast){
propObject +="\n}" propObject +="\n}"
} }
}) })
}else{ }else{
propObject += "}" propObject += "}"
} }
let childrenData = typeof children === "string" && children.startsWith("React.createElement") ? children : JSON.stringify(children) let childrenData = typeof children === "string" && children.startsWith("React.createElement") ? children : JSON.stringify(children)
return `React.createElement(${compPath}, ${propObject}, ${childrenData})` return `React.createElement(${compPath}, ${propObject}, ${childrenData})`
} }
} }
let help = comp.help || {} let help = comp.help || {}
let info = help.info ? <window.Lightcord.Api.Components.general.AlertBox type="info"> let info = help.info ? <window.Lightcord.Api.Components.general.AlertBox type="info">
{help.info} {help.info}
</window.Lightcord.Api.Components.general.AlertBox> : null </window.Lightcord.Api.Components.general.AlertBox> : null
let warn = help.warn ? <window.Lightcord.Api.Components.general.AlertBox type="warn"> let warn = help.warn ? <window.Lightcord.Api.Components.general.AlertBox type="warn">
{help.warn} {help.warn}
</window.Lightcord.Api.Components.general.AlertBox> : null </window.Lightcord.Api.Components.general.AlertBox> : null
let danger = help.danger ? <window.Lightcord.Api.Components.general.AlertBox type="danger"> let danger = help.danger ? <window.Lightcord.Api.Components.general.AlertBox type="danger">
{help.danger} {help.danger}
</window.Lightcord.Api.Components.general.AlertBox> : null </window.Lightcord.Api.Components.general.AlertBox> : null
let error = help.error ? <window.Lightcord.Api.Components.general.AlertBox type="error"> let error = help.error ? <window.Lightcord.Api.Components.general.AlertBox type="error">
{help.error} {help.error}
</window.Lightcord.Api.Components.general.AlertBox> : null </window.Lightcord.Api.Components.general.AlertBox> : null
let success = help.success ? <window.Lightcord.Api.Components.general.AlertBox type="success"> let success = help.success ? <window.Lightcord.Api.Components.general.AlertBox type="success">
{help.success} {help.success}
</window.Lightcord.Api.Components.general.AlertBox> : null </window.Lightcord.Api.Components.general.AlertBox> : null
return (<div> return (<div>
<window.Lightcord.Api.Components.general.SettingsTitle> <window.Lightcord.Api.Components.general.SettingsTitle>
{comp.displayName || comp.name} {comp.displayName || comp.name}
</window.Lightcord.Api.Components.general.SettingsTitle> </window.Lightcord.Api.Components.general.SettingsTitle>
{info} {info}
{success} {success}
{warn} {warn}
{error} {error}
{danger} {danger}
{AllPreviews.map(category => { {AllPreviews.map(category => {
if(category[0].onClick)return null if(category[0].onClick)return null
if(category[0].text)return null if(category[0].text)return null
if(category[0].children)return null if(category[0].children)return null
if(category.length === 1)return null if(category.length === 1)return null
let key = Object.keys(category[0])[0] let key = Object.keys(category[0])[0]
return [ return [
<window.Lightcord.Api.Components.general.SettingSubTitle> <window.Lightcord.Api.Components.general.SettingSubTitle>
{key} {key}
</window.Lightcord.Api.Components.general.SettingSubTitle>, </window.Lightcord.Api.Components.general.SettingSubTitle>,
<window.Lightcord.Api.Components.inputs.Dropdown options={category.map((e, index) => { <window.Lightcord.Api.Components.inputs.Dropdown options={category.map((e, index) => {
return { return {
value: "opt-"+index, value: "opt-"+index,
label: JSON.stringify(e[Object.keys(e)[0]]) label: JSON.stringify(e[Object.keys(e)[0]])
} }
})} value={"opt-"+(state.options[key] || "0")} onChange={(value) => { })} value={"opt-"+(state.options[key] || "0")} onChange={(value) => {
this.setState({ this.setState({
options: Object.assign({}, state.options, { options: Object.assign({}, state.options, {
[key]: (value.value || "0").replace("opt-", "") [key]: (value.value || "0").replace("opt-", "")
}) })
}) })
}} searchable={true}/>, }} searchable={true}/>,
<div style={{marginBottom: "8px"}}></div> <div style={{marginBottom: "8px"}}></div>
] ]
})} })}
<window.Lightcord.Api.Components.general.Tabs tabs={[{label: "Preview", id: "preview"}, {label: "Code", id: "code"}]} <window.Lightcord.Api.Components.general.Tabs tabs={[{label: "Preview", id: "preview"}, {label: "Code", id: "code"}]}
active={state.tab} children={state.tab === "preview" ? renderPreview() : renderCode()} onChange={(tab) => { active={state.tab} children={state.tab === "preview" ? renderPreview() : renderCode()} onChange={(tab) => {
this.setState({ this.setState({
tab tab
}) })
}}/> }}/>
</div>) </div>)
} }
} }

View File

@ -1,242 +1,242 @@
import {settingsCookie} from "../0globals"; import {settingsCookie} from "../0globals";
import BDV2 from "../modules/v2"; import BDV2 from "../modules/v2";
import Utils from "../modules/utils"; import Utils from "../modules/utils";
import DOM from "../modules/domtools"; import DOM from "../modules/domtools";
import XSvg from "./xSvg"; import XSvg from "./xSvg";
import ReloadIcon from "./reloadIcon"; import ReloadIcon from "./reloadIcon";
import EditIcon from "./icons/edit"; import EditIcon from "./icons/edit";
import DeleteIcon from "./icons/delete"; import DeleteIcon from "./icons/delete";
import Switch from "./components/switch"; import Switch from "./components/switch";
import TooltipWrap from "./tooltipWrap"; import TooltipWrap from "./tooltipWrap";
import { processFile } from "../modules/pluginCertifier"; import { processFile } from "../modules/pluginCertifier";
import contentManager from "../modules/contentManager"; import contentManager from "../modules/contentManager";
import { resolve } from "path"; import { resolve } from "path";
const React = BDV2.React; const React = BDV2.React;
const anchorClasses = BDV2.anchorClasses; const anchorClasses = BDV2.anchorClasses;
export default class V2C_PluginCard extends BDV2.reactComponent { export default class V2C_PluginCard extends BDV2.reactComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
this.showSettings = this.showSettings.bind(this); this.showSettings = this.showSettings.bind(this);
this.setInitialState(); this.setInitialState();
this.hasSettings = this.props.addon.plugin && typeof(this.props.addon.plugin.getSettingsPanel) === "function"; this.hasSettings = this.props.addon.plugin && typeof(this.props.addon.plugin.getSettingsPanel) === "function";
this.settingsPanel = ""; this.settingsPanel = "";
this.edit = this.edit.bind(this); this.edit = this.edit.bind(this);
this.delete = this.delete.bind(this); this.delete = this.delete.bind(this);
this.reload = this.reload.bind(this); this.reload = this.reload.bind(this);
} }
setInitialState() { setInitialState() {
this.state = { this.state = {
checked: this.props.enabled, checked: this.props.enabled,
settings: false, settings: false,
reloads: 0, reloads: 0,
trusted: false trusted: false
}; };
} }
showSettings() { showSettings() {
if (!this.hasSettings) return; if (!this.hasSettings) return;
this.setState({settings: true}); this.setState({settings: true});
} }
closeSettings() { closeSettings() {
this.panelRef.current.innerHTML = ""; this.panelRef.current.innerHTML = "";
this.setState({settingsOpen: false}); this.setState({settingsOpen: false});
} }
componentDidUpdate() { componentDidUpdate() {
if (!this.state.settings) return; if (!this.state.settings) return;
if (typeof this.settingsPanel === "object") { if (typeof this.settingsPanel === "object") {
this.refs.settingspanel.appendChild(this.settingsPanel); this.refs.settingspanel.appendChild(this.settingsPanel);
} }
if (!settingsCookie["fork-ps-3"]) return; if (!settingsCookie["fork-ps-3"]) return;
setImmediate(() => { setImmediate(() => {
const isHidden = (container, element) => { const isHidden = (container, element) => {
if(!container){ if(!container){
console.error(new Error(`Container is undefined.`)) console.error(new Error(`Container is undefined.`))
return false return false
} }
const cTop = container.scrollTop; const cTop = container.scrollTop;
const cBottom = cTop + container.clientHeight; const cBottom = cTop + container.clientHeight;
const eTop = element.offsetTop; const eTop = element.offsetTop;
const eBottom = eTop + element.clientHeight; const eBottom = eTop + element.clientHeight;
return (eTop < cTop || eBottom > cBottom); return (eTop < cTop || eBottom > cBottom);
}; };
const thisNode = this.refs.cardNode; const thisNode = this.refs.cardNode;
const container = thisNode.closest("div[class*=\"contentRegionScroller-\"]") const container = thisNode.closest("div[class*=\"contentRegionScroller-\"]")
if (!isHidden(container, thisNode)) return; if (!isHidden(container, thisNode)) return;
const thisNodeOffset = DOM.offset(thisNode); const thisNodeOffset = DOM.offset(thisNode);
const containerOffset = DOM.offset(container); const containerOffset = DOM.offset(container);
const original = container.scrollTop; const original = container.scrollTop;
const endPoint = thisNodeOffset.top - containerOffset.top + container.scrollTop - 30; const endPoint = thisNodeOffset.top - containerOffset.top + container.scrollTop - 30;
DOM.animate({ DOM.animate({
duration: 300, duration: 300,
update: function(progress) { update: function(progress) {
if (endPoint > original) container.scrollTop = original + (progress * (endPoint - original)); if (endPoint > original) container.scrollTop = original + (progress * (endPoint - original));
else container.scrollTop = original - (progress * (original - endPoint)); else container.scrollTop = original - (progress * (original - endPoint));
} }
}); });
}); });
} }
getString(value) { getString(value) {
if (!value) return "???"; if (!value) return "???";
return typeof value == "string" ? value : value.toString(); return typeof value == "string" ? value : value.toString();
} }
get settingsComponent() { get settingsComponent() {
try { this.settingsPanel = this.props.addon.plugin.getSettingsPanel(); } try { this.settingsPanel = this.props.addon.plugin.getSettingsPanel(); }
catch (err) { Utils.err("Plugins", "Unable to get settings panel for " + this.name + ".", err); } catch (err) { Utils.err("Plugins", "Unable to get settings panel for " + this.name + ".", err); }
return BDV2.react.createElement("div", {className: "bd-card bd-addon-card settings-open ui-switch-item", ref: "cardNode"}, return BDV2.react.createElement("div", {className: "bd-card bd-addon-card settings-open ui-switch-item", ref: "cardNode"},
BDV2.react.createElement("div", {style: {"float": "right", "cursor": "pointer"}, onClick: () => { BDV2.react.createElement("div", {style: {"float": "right", "cursor": "pointer"}, onClick: () => {
this.refs.settingspanel.innerHTML = ""; this.refs.settingspanel.innerHTML = "";
this.setState({settings: false}); this.setState({settings: false});
}}, }},
BDV2.react.createElement(XSvg, null) BDV2.react.createElement(XSvg, null)
), ),
typeof this.settingsPanel === "object" && BDV2.react.createElement("div", {id: `plugin-settings-${this.name}`, className: "plugin-settings", ref: "settingspanel"}), typeof this.settingsPanel === "object" && BDV2.react.createElement("div", {id: `plugin-settings-${this.name}`, className: "plugin-settings", ref: "settingspanel"}),
typeof this.settingsPanel !== "object" && BDV2.react.createElement("div", {id: `plugin-settings-${this.name}`, className: "plugin-settings", ref: "settingspanel", dangerouslySetInnerHTML: {__html: this.settingsPanel}}) typeof this.settingsPanel !== "object" && BDV2.react.createElement("div", {id: `plugin-settings-${this.name}`, className: "plugin-settings", ref: "settingspanel", dangerouslySetInnerHTML: {__html: this.settingsPanel}})
); );
} }
buildTitle(name, version, author) { buildTitle(name, version, author) {
const title = "{{name}} v{{version}} by {{author}}".split(/({{[A-Za-z]+}})/); const title = "{{name}} v{{version}} by {{author}}".split(/({{[A-Za-z]+}})/);
const nameIndex = title.findIndex(s => s == "{{name}}"); const nameIndex = title.findIndex(s => s == "{{name}}");
if (nameIndex) title[nameIndex] = React.createElement("span", {className: "name bda-name"}, name); if (nameIndex) title[nameIndex] = React.createElement("span", {className: "name bda-name"}, name);
const versionIndex = title.findIndex(s => s == "{{version}}"); const versionIndex = title.findIndex(s => s == "{{version}}");
if (nameIndex) title[versionIndex] = React.createElement("span", {className: "version bda-version"}, version); if (nameIndex) title[versionIndex] = React.createElement("span", {className: "version bda-version"}, version);
const authorIndex = title.findIndex(s => s == "{{author}}"); const authorIndex = title.findIndex(s => s == "{{author}}");
if (nameIndex) { if (nameIndex) {
const props = {className: "author bda-author"}; const props = {className: "author bda-author"};
if (author.link || author.id) { if (author.link || author.id) {
props.className += ` ${anchorClasses.anchor} ${anchorClasses.anchorUnderlineOnHover}`; props.className += ` ${anchorClasses.anchor} ${anchorClasses.anchorUnderlineOnHover}`;
props.target = "_blank"; props.target = "_blank";
if (author.link) props.href = author.link; if (author.link) props.href = author.link;
if (author.id) props.onClick = () => {BDV2.LayerStack.popLayer(); BDV2.openDM(author.id);}; if (author.id) props.onClick = () => {BDV2.LayerStack.popLayer(); BDV2.openDM(author.id);};
} }
title[authorIndex] = React.createElement(author.link || author.id ? "a" : "span", props, author.name); title[authorIndex] = React.createElement(author.link || author.id ? "a" : "span", props, author.name);
} }
return title.flat(); return title.flat();
} }
makeLink(title, url) { makeLink(title, url) {
const props = {className: "bda-link bda-link-website", target: "_blank"}; const props = {className: "bda-link bda-link-website", target: "_blank"};
if (typeof(url) == "string") props.href = url; if (typeof(url) == "string") props.href = url;
if (typeof(url) == "function") props.onClick = (event) => {event.preventDefault(); event.stopPropagation(); url();}; if (typeof(url) == "function") props.onClick = (event) => {event.preventDefault(); event.stopPropagation(); url();};
return BDV2.react.createElement("a", props, title); return BDV2.react.createElement("a", props, title);
} }
makeButton(title, children, action) { makeButton(title, children, action) {
return <TooltipWrap color="black" side="top" text={title}> return <TooltipWrap color="black" side="top" text={title}>
<div className="bd-addon-button" onClick={action}>{children}</div> <div className="bd-addon-button" onClick={action}>{children}</div>
</TooltipWrap>; </TooltipWrap>;
} }
componentWillUnmount(){ componentWillUnmount(){
this.unmounted = true this.unmounted = true
} }
get links() { get links() {
const links = []; const links = [];
const addon = this.props.addon; const addon = this.props.addon;
if (addon.website) links.push(this.makeLink("Website", addon.website)); if (addon.website) links.push(this.makeLink("Website", addon.website));
if (addon.source) links.push(this.makeLink("Source", addon.source)); if (addon.source) links.push(this.makeLink("Source", addon.source));
if (addon.invite) { if (addon.invite) {
links.push(this.makeLink("Support Server", () => { links.push(this.makeLink("Support Server", () => {
const tester = /\.gg\/(.*)$/; const tester = /\.gg\/(.*)$/;
let code = addon.invite; let code = addon.invite;
if (tester.test(code)) code = code.match(tester)[1]; if (tester.test(code)) code = code.match(tester)[1];
BDV2.LayerStack.popLayer(); BDV2.LayerStack.popLayer();
BDV2.InviteActions.acceptInviteAndTransitionToInviteChannel(code); BDV2.InviteActions.acceptInviteAndTransitionToInviteChannel(code);
})); }));
} }
if (addon.donate) links.push(this.makeLink("Donate", addon.donate)); if (addon.donate) links.push(this.makeLink("Donate", addon.donate));
if (addon.patreon) links.push(this.makeLink("Patreon", addon.patreon)); if (addon.patreon) links.push(this.makeLink("Patreon", addon.patreon));
return links; return links;
} }
get footer() { get footer() {
const links = this.links; const links = this.links;
return (links.length || this.hasSettings) && BDV2.react.createElement("div", {className: "bd-card-footer bda-footer"}, return (links.length || this.hasSettings) && BDV2.react.createElement("div", {className: "bd-card-footer bda-footer"},
BDV2.react.createElement("span", {className: "bd-addon-links bda-links"}, BDV2.react.createElement("span", {className: "bd-addon-links bda-links"},
...(links.map((element, index) => index < links.length - 1 ? [element, " | "] : element).flat()) ...(links.map((element, index) => index < links.length - 1 ? [element, " | "] : element).flat())
), ),
this.hasSettings && BDV2.react.createElement("button", {onClick: this.showSettings, className: "bd-button bda-settings-button", disabled: !this.state.checked}, "Settings") this.hasSettings && BDV2.react.createElement("button", {onClick: this.showSettings, className: "bd-button bda-settings-button", disabled: !this.state.checked}, "Settings")
); );
} }
onChange() { onChange() {
this.props.toggle && this.props.toggle(this.name); this.props.toggle && this.props.toggle(this.name);
this.setState({checked: !this.state.checked}); this.setState({checked: !this.state.checked});
} }
edit() {this.props.edit(this.name);} edit() {this.props.edit(this.name);}
delete() {this.props.remove(this.name);} delete() {this.props.remove(this.name);}
reload() {this.props.reload(this.name);} reload() {this.props.reload(this.name);}
get name() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getName() : this.props.addon.name);} get name() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getName() : this.props.addon.name);}
get author() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getAuthor() : this.props.addon.author);} get author() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getAuthor() : this.props.addon.author);}
get description() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getDescription() : this.props.addon.description);} get description() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getDescription() : this.props.addon.description);}
get version() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getVersion() : this.props.addon.version);} get version() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getVersion() : this.props.addon.version);}
render() { render() {
if (this.state.settings) return this.settingsComponent; if (this.state.settings) return this.settingsComponent;
const {authorId, authorLink} = this.props.addon; const {authorId, authorLink} = this.props.addon;
const style = {} const style = {}
if(settingsCookie["fork-ps-6"]){ if(settingsCookie["fork-ps-6"]){
if(!this.isScanning){ if(!this.isScanning){
this.isScanning = true this.isScanning = true
processFile(resolve(this.props.addon.filename.endsWith(".plugin.js") ? contentManager.pluginsFolder : contentManager.themesFolder, this.props.addon.filename), (result) => { processFile(resolve(this.props.addon.filename.endsWith(".plugin.js") ? contentManager.pluginsFolder : contentManager.themesFolder, this.props.addon.filename), (result) => {
if(this.unmounted)return if(this.unmounted)return
this.setState({ this.setState({
isTrusted: result.suspect ? "suspect" : true isTrusted: result.suspect ? "suspect" : true
}) })
}, () => {}) }, () => {})
}else{ }else{
if(this.state.isTrusted === true){ if(this.state.isTrusted === true){
style.borderColor = "#4087ed" style.borderColor = "#4087ed"
} }
if(this.state.isTrusted === "suspect"){ if(this.state.isTrusted === "suspect"){
style.borderColor = "rgb(240, 71, 71)" style.borderColor = "rgb(240, 71, 71)"
} }
} }
} }
return BDV2.react.createElement("div", {className: "bd-card bd-addon-card settings-closed ui-switch-item", style}, return BDV2.react.createElement("div", {className: "bd-card bd-addon-card settings-closed ui-switch-item", style},
BDV2.react.createElement("div", {className: "bd-addon-header bda-header"}, BDV2.react.createElement("div", {className: "bd-addon-header bda-header"},
BDV2.react.createElement("div", {className: "bd-card-title bda-header-title"}, this.buildTitle(this.name, this.version, {name: this.author, id: authorId, link: authorLink})), BDV2.react.createElement("div", {className: "bd-card-title bda-header-title"}, this.buildTitle(this.name, this.version, {name: this.author, id: authorId, link: authorLink})),
BDV2.react.createElement("div", {className: "bd-addon-controls bda-controls"}, BDV2.react.createElement("div", {className: "bd-addon-controls bda-controls"},
this.props.edit && this.makeButton("Edit", <EditIcon className="bd-icon" />, this.edit), this.props.edit && this.makeButton("Edit", <EditIcon className="bd-icon" />, this.edit),
this.props.remove && this.makeButton("Delete", <DeleteIcon className="bd-icon" />, this.delete), this.props.remove && this.makeButton("Delete", <DeleteIcon className="bd-icon" />, this.delete),
this.props.reload && this.makeButton("Reload", <ReloadIcon className="bd-icon" />, this.reload), this.props.reload && this.makeButton("Reload", <ReloadIcon className="bd-icon" />, this.reload),
React.createElement(Switch, {onChange: this.onChange, checked: this.state.checked}) React.createElement(Switch, {onChange: this.onChange, checked: this.state.checked})
) )
), ),
BDV2.react.createElement("div", {className: "bd-scroller-wrap bda-description-wrap scroller-wrap fade"}, BDV2.react.createElement("div", {className: "bd-scroller-wrap bda-description-wrap scroller-wrap fade"},
BDV2.react.createElement("div", {className: "bd-scroller bd-addon-description bda-description scroller"}, this.description) BDV2.react.createElement("div", {className: "bd-scroller bd-addon-description bda-description scroller"}, this.description)
), ),
this.footer this.footer
); );
} }
} }
const originalRender = V2C_PluginCard.prototype.render; const originalRender = V2C_PluginCard.prototype.render;
Object.defineProperty(V2C_PluginCard.prototype, "render", { Object.defineProperty(V2C_PluginCard.prototype, "render", {
enumerable: false, enumerable: false,
configurable: false, configurable: false,
set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}, set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");},
get: () => originalRender get: () => originalRender
}); });

View File

@ -1,262 +1,262 @@
import ErrorBoundary from "./errorBoundary"; import ErrorBoundary from "./errorBoundary";
import ContentColumn from "./contentColumn"; import ContentColumn from "./contentColumn";
import ReloadIcon from "./reloadIcon"; import ReloadIcon from "./reloadIcon";
import AddonCard from "./addoncard"; import AddonCard from "./addoncard";
import Dropdown from "./components/dropdown"; import Dropdown from "./components/dropdown";
import Search from "./components/search"; import Search from "./components/search";
import {settingsCookie, pluginCookie, themeCookie, bdplugins, bdthemes} from "../0globals"; import {settingsCookie, pluginCookie, themeCookie, bdplugins, bdthemes} from "../0globals";
import ContentManager from "../modules/contentManager"; import ContentManager from "../modules/contentManager";
import BDV2 from "../modules/v2"; import BDV2 from "../modules/v2";
import pluginModule from "../modules/pluginModule"; import pluginModule from "../modules/pluginModule";
import themeModule from "../modules/themeModule"; import themeModule from "../modules/themeModule";
import WebpackModules from "../modules/webpackModules"; import WebpackModules from "../modules/webpackModules";
import BdApi from "../modules/bdApi"; import BdApi from "../modules/bdApi";
import Utils from "../modules/utils"; import Utils from "../modules/utils";
import TooltipWrap from "./tooltipWrap"; import TooltipWrap from "./tooltipWrap";
import bdEvents from "../modules/bdEvents"; import bdEvents from "../modules/bdEvents";
import EmulatedTooltip from "./tooltip"; import EmulatedTooltip from "./tooltip";
const Tooltip = WebpackModules.findByDisplayName("Tooltip"); const Tooltip = WebpackModules.findByDisplayName("Tooltip");
const React = BDV2.react; const React = BDV2.react;
export default class CardList extends BDV2.reactComponent { export default class CardList extends BDV2.reactComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {sort: "name", ascending: true, query: ""}; this.state = {sort: "name", ascending: true, query: ""};
this.isPlugins = this.props.type == "plugins"; this.isPlugins = this.props.type == "plugins";
this.cookie = this.isPlugins ? pluginCookie : themeCookie; this.cookie = this.isPlugins ? pluginCookie : themeCookie;
this.manager = this.isPlugins ? pluginModule : themeModule; this.manager = this.isPlugins ? pluginModule : themeModule;
this.sort = this.sort.bind(this); this.sort = this.sort.bind(this);
this.reverse = this.reverse.bind(this); this.reverse = this.reverse.bind(this);
this.search = this.search.bind(this); this.search = this.search.bind(this);
this.onAddonChanges = function(){ this.onAddonChanges = function(){
this.forceUpdate() this.forceUpdate()
} }
this.onAddonChanges = this.onAddonChanges.bind(this) this.onAddonChanges = this.onAddonChanges.bind(this)
} }
componentDidMount(){ componentDidMount(){
const type = (this.isPlugins ? "plugin" : "theme") + "-" const type = (this.isPlugins ? "plugin" : "theme") + "-"
bdEvents.on(`${type}loaded`, this.onAddonChanges) bdEvents.on(`${type}loaded`, this.onAddonChanges)
bdEvents.on(`${type}unloaded`, this.onAddonChanges) bdEvents.on(`${type}unloaded`, this.onAddonChanges)
bdEvents.on(`${type}reloaded`, this.onAddonChanges) bdEvents.on(`${type}reloaded`, this.onAddonChanges)
} }
componentWillUnmount(){ componentWillUnmount(){
const type = (this.isPlugins ? "plugin" : "theme") + "-" const type = (this.isPlugins ? "plugin" : "theme") + "-"
bdEvents.off(`${type}loaded`, this.onAddonChanges) bdEvents.off(`${type}loaded`, this.onAddonChanges)
bdEvents.off(`${type}unloaded`, this.onAddonChanges) bdEvents.off(`${type}unloaded`, this.onAddonChanges)
bdEvents.off(`${type}reloaded`, this.onAddonChanges) bdEvents.off(`${type}reloaded`, this.onAddonChanges)
} }
openFolder() { openFolder() {
const shell = require("electron").shell; const shell = require("electron").shell;
const open = shell.openPath || shell.openItem; const open = shell.openPath || shell.openItem;
open(this.isPlugins ? ContentManager.pluginsFolder : ContentManager.themesFolder); open(this.isPlugins ? ContentManager.pluginsFolder : ContentManager.themesFolder);
} }
edit(name) { edit(name) {
console.log(name); console.log(name);
this.manager.edit(name); this.manager.edit(name);
} }
async delete(name) { async delete(name) {
const shouldDelete = await this.confirmDelete(name); const shouldDelete = await this.confirmDelete(name);
if (!shouldDelete) return; if (!shouldDelete) return;
this.manager.delete(name); this.manager.delete(name);
} }
confirmDelete(name) { confirmDelete(name) {
return new Promise(resolve => { return new Promise(resolve => {
BdApi.showConfirmationModal("Are You Sure?", `Are you sure you want to delete ${name}?`, { BdApi.showConfirmationModal("Are You Sure?", `Are you sure you want to delete ${name}?`, {
danger: true, danger: true,
confirmText: "Delete", confirmText: "Delete",
onConfirm: () => {resolve(true);}, onConfirm: () => {resolve(true);},
onCancel: () => {resolve(false);} onCancel: () => {resolve(false);}
}); });
}); });
} }
get sortOptions() { get sortOptions() {
return [ return [
{label: "Name", value: "name"}, {label: "Name", value: "name"},
{label: "Author", value: "author"}, {label: "Author", value: "author"},
{label: "Version", value: "version"}, {label: "Version", value: "version"},
{label: "Recently Added", value: "added"}, {label: "Recently Added", value: "added"},
{label: "Last Modified", value: "modified"}, {label: "Last Modified", value: "modified"},
{label: "File Size", value: "size"}, {label: "File Size", value: "size"},
]; ];
} }
get directions() { get directions() {
return [ return [
{label: "Ascending", value: true}, {label: "Ascending", value: true},
{label: "Descending", value: false} {label: "Descending", value: false}
]; ];
} }
reverse(value) { reverse(value) {
this.setState({ascending: value}); this.setState({ascending: value});
} }
sort(value) { sort(value) {
this.setState({sort: value}); this.setState({sort: value});
} }
search(event) { search(event) {
this.setState({query: event.target.value.toLocaleLowerCase()}); this.setState({query: event.target.value.toLocaleLowerCase()});
} }
getProps(addon) { getProps(addon) {
return { return {
key: this.getName(addon), key: this.getName(addon),
enabled: this.cookie[this.getName(addon)], enabled: this.cookie[this.getName(addon)],
toggle: this.manager.toggle.bind(this.manager), toggle: this.manager.toggle.bind(this.manager),
edit: settingsCookie["fork-ps-7"] ? this.edit.bind(this) : null, edit: settingsCookie["fork-ps-7"] ? this.edit.bind(this) : null,
remove: this.delete.bind(this), remove: this.delete.bind(this),
addon: addon, addon: addon,
hash: addon.hash hash: addon.hash
}; };
} }
getString(value) { getString(value) {
if (!value) return "???"; if (!value) return "???";
return typeof value == "string" ? value : value.toString(); return typeof value == "string" ? value : value.toString();
} }
get list(){ get list(){
return this.props.type === "plugins" ? Object.values(bdplugins) : Object.values(bdthemes); return this.props.type === "plugins" ? Object.values(bdplugins) : Object.values(bdthemes);
} }
getAddons() { getAddons() {
const sortedAddons = this.list.sort((a, b) => { const sortedAddons = this.list.sort((a, b) => {
const cap = this.state.sort.charAt(0).toUpperCase() + this.state.sort.slice(1); const cap = this.state.sort.charAt(0).toUpperCase() + this.state.sort.slice(1);
const first = a.plugin && a.plugin[`get${cap}`] ? this.getString(a.plugin[`get${cap}`]()) : this.getString(a[this.state.sort]); const first = a.plugin && a.plugin[`get${cap}`] ? this.getString(a.plugin[`get${cap}`]()) : this.getString(a[this.state.sort]);
const second = b.plugin && b.plugin[`get${cap}`] ? this.getString(b.plugin[`get${cap}`]()) : this.getString(b[this.state.sort]); const second = b.plugin && b.plugin[`get${cap}`] ? this.getString(b.plugin[`get${cap}`]()) : this.getString(b[this.state.sort]);
if (typeof(first) == "string") return first.toLocaleLowerCase().localeCompare(second.toLocaleLowerCase()); if (typeof(first) == "string") return first.toLocaleLowerCase().localeCompare(second.toLocaleLowerCase());
if (first > second) return 1; if (first > second) return 1;
if (second > first) return -1; if (second > first) return -1;
return 0; return 0;
}); });
if (!this.state.ascending) sortedAddons.reverse(); if (!this.state.ascending) sortedAddons.reverse();
const rendered = []; const rendered = [];
for (let a = 0; a < sortedAddons.length; a++) { for (let a = 0; a < sortedAddons.length; a++) {
const addon = sortedAddons[a]; const addon = sortedAddons[a];
if (this.state.query) { if (this.state.query) {
let matches = null; let matches = null;
const name = this.getName(addon); const name = this.getName(addon);
const author = this.getAuthor(addon); const author = this.getAuthor(addon);
const description = this.getDescription(addon); const description = this.getDescription(addon);
const version = this.getVersion(addon); const version = this.getVersion(addon);
if (name) matches = name.toLocaleLowerCase().includes(this.state.query); if (name) matches = name.toLocaleLowerCase().includes(this.state.query);
if (author) matches = matches || author.toLocaleLowerCase().includes(this.state.query); if (author) matches = matches || author.toLocaleLowerCase().includes(this.state.query);
if (description) matches = matches || description.toLocaleLowerCase().includes(this.state.query); if (description) matches = matches || description.toLocaleLowerCase().includes(this.state.query);
if (version) matches = matches || version.toLocaleLowerCase().includes(this.state.query); if (version) matches = matches || version.toLocaleLowerCase().includes(this.state.query);
if (!matches) continue; if (!matches) continue;
} }
const props = this.getProps(addon); const props = this.getProps(addon);
rendered.push(<ErrorBoundary><AddonCard {...props} reload={!settingsCookie["fork-ps-5"] && this.manager.reload.bind(this.manager)} /></ErrorBoundary>); rendered.push(<ErrorBoundary><AddonCard {...props} reload={!settingsCookie["fork-ps-5"] && this.manager.reload.bind(this.manager)} /></ErrorBoundary>);
} }
return rendered; return rendered;
} }
getName(addon) {return this.getString(addon.plugin ? addon.plugin.getName() : addon.name);} getName(addon) {return this.getString(addon.plugin ? addon.plugin.getName() : addon.name);}
getAuthor(addon) {return this.getString(addon.plugin ? addon.plugin.getAuthor() : addon.author);} getAuthor(addon) {return this.getString(addon.plugin ? addon.plugin.getAuthor() : addon.author);}
getDescription(addon) {return this.getString(addon.plugin ? addon.plugin.getDescription() : addon.description);} getDescription(addon) {return this.getString(addon.plugin ? addon.plugin.getDescription() : addon.description);}
getVersion(addon) {return this.getString(addon.plugin ? addon.plugin.getVersion() : addon.version);} getVersion(addon) {return this.getString(addon.plugin ? addon.plugin.getVersion() : addon.version);}
renderCheckUpdates(){ renderCheckUpdates(){
if(!window.ZeresPluginLibrary)return null if(!window.ZeresPluginLibrary)return null
if(!window.PluginUpdates)return null if(!window.PluginUpdates)return null
if(typeof window.PluginUpdates.checkAll !== "function")return null if(typeof window.PluginUpdates.checkAll !== "function")return null
if(!this.isPlugins)return null if(!this.isPlugins)return null
let tooltipIsShowing = false let tooltipIsShowing = false
let tooltip let tooltip
return <TooltipWrap text="Checks for updates of plugins that support this feature. Right-click for a list."> return <TooltipWrap text="Checks for updates of plugins that support this feature. Right-click for a list.">
<span style={{marginLeft: "10px"}} onMouseLeave={() => { <span style={{marginLeft: "10px"}} onMouseLeave={() => {
tooltipIsShowing = false tooltipIsShowing = false
tooltip.hide() tooltip.hide()
}}> }}>
<Lightcord.Api.Components.inputs.Button ref="checkUpdateButton" color="brand" look="filled" size="min" hoverColor="default" onClick={() => { <Lightcord.Api.Components.inputs.Button ref="checkUpdateButton" color="brand" look="filled" size="min" hoverColor="default" onClick={() => {
try{ try{
Utils.showToast("Plugin update check in progress.", {type: "info"}) Utils.showToast("Plugin update check in progress.", {type: "info"})
window.PluginUpdates.checkAll() window.PluginUpdates.checkAll()
.then(() => { .then(() => {
Utils.showToast("Plugin update check complete.", {type: "success"}) Utils.showToast("Plugin update check complete.", {type: "success"})
}).catch(err => { }).catch(err => {
console.error(err) console.error(err)
Utils.showToast("An error occured while checking update.", {type: "error"}) Utils.showToast("An error occured while checking update.", {type: "error"})
}) })
}catch(e){ }catch(e){
console.error(e) console.error(e)
Utils.showToast("An error occured while checking update.", {type: "error"}) Utils.showToast("An error occured while checking update.", {type: "error"})
} }
}} wrapper={false} disabled={false} onRightClick={() => { }} wrapper={false} disabled={false} onRightClick={() => {
if(!this.refs.checkUpdateButton)return if(!this.refs.checkUpdateButton)return
if (!window.PluginUpdates || !window.PluginUpdates.plugins) return; if (!window.PluginUpdates || !window.PluginUpdates.plugins) return;
if(tooltipIsShowing)return if(tooltipIsShowing)return
tooltip = new EmulatedTooltip(this.refs.checkUpdateButton.refs.original.refs.button, Object.values(window.PluginUpdates.plugins).map(p => p.name).join(", "), { tooltip = new EmulatedTooltip(this.refs.checkUpdateButton.refs.original.refs.button, Object.values(window.PluginUpdates.plugins).map(p => p.name).join(", "), {
side: "bottom", side: "bottom",
attachEvents: false attachEvents: false
}) })
tooltip.show() tooltip.show()
tooltipIsShowing = true tooltipIsShowing = true
}}> }}>
Check for Updates Check for Updates
</Lightcord.Api.Components.inputs.Button> </Lightcord.Api.Components.inputs.Button>
</span> </span>
</TooltipWrap> </TooltipWrap>
} }
render() { render() {
const refreshIcon = <Tooltip color="black" position="top" text="Reload List"> const refreshIcon = <Tooltip color="black" position="top" text="Reload List">
{(props) => {(props) =>
<ReloadIcon {...props} className="bd-icon bd-reload bd-reload-header" size="18px" onClick={async () => { <ReloadIcon {...props} className="bd-icon bd-reload bd-reload-header" size="18px" onClick={async () => {
if (this.isPlugins) pluginModule.updatePluginList(); if (this.isPlugins) pluginModule.updatePluginList();
else themeModule.updateThemeList(); else themeModule.updateThemeList();
this.forceUpdate(); this.forceUpdate();
}} /> }} />
}</Tooltip>; }</Tooltip>;
const addonCards = this.getAddons(); const addonCards = this.getAddons();
return <ContentColumn title={`${this.props.type.toUpperCase()}${addonCards.length}`}> return <ContentColumn title={`${this.props.type.toUpperCase()}${addonCards.length}`}>
<Lightcord.Api.Components.inputs.Button key="folder-button" color="brand" <Lightcord.Api.Components.inputs.Button key="folder-button" color="brand"
look="filled" size="min" hoverColor="default" onClick={this.openFolder.bind(this)} wrapper={false} look="filled" size="min" hoverColor="default" onClick={this.openFolder.bind(this)} wrapper={false}
style={{ style={{
marginLeft: "10px" marginLeft: "10px"
}}> }}>
Open {this.isPlugins ? "Plugin" : "Theme"} Folder Open {this.isPlugins ? "Plugin" : "Theme"} Folder
</Lightcord.Api.Components.inputs.Button> </Lightcord.Api.Components.inputs.Button>
{this.renderCheckUpdates()} {this.renderCheckUpdates()}
{!settingsCookie["fork-ps-5"] && refreshIcon} {!settingsCookie["fork-ps-5"] && refreshIcon}
<div className="bd-controls bd-addon-controls"> <div className="bd-controls bd-addon-controls">
<Search onChange={this.search} placeholder={`Search ${this.props.type}...`} /> <Search onChange={this.search} placeholder={`Search ${this.props.type}...`} />
<div className="bd-addon-dropdowns"> <div className="bd-addon-dropdowns">
<div className="bd-select-wrapper"> <div className="bd-select-wrapper">
<label className="bd-label">Sort by:</label> <label className="bd-label">Sort by:</label>
<Dropdown options={this.sortOptions} onChange={this.sort} style="transparent" /> <Dropdown options={this.sortOptions} onChange={this.sort} style="transparent" />
</div> </div>
<div className="bd-select-wrapper"> <div className="bd-select-wrapper">
<label className="bd-label">Order:</label> <label className="bd-label">Order:</label>
<Dropdown options={this.directions} onChange={this.reverse} style="transparent" /> <Dropdown options={this.directions} onChange={this.reverse} style="transparent" />
</div> </div>
</div> </div>
</div> </div>
<div className="bda-slist bd-addon-list">{addonCards}</div> <div className="bda-slist bd-addon-list">{addonCards}</div>
</ContentColumn> </ContentColumn>
} }
} }
const originalRender = CardList.prototype.render; const originalRender = CardList.prototype.render;
Object.defineProperty(CardList.prototype, "render", { Object.defineProperty(CardList.prototype, "render", {
enumerable: false, enumerable: false,
configurable: false, configurable: false,
set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}, set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");},
get: () => originalRender get: () => originalRender
}); });

View File

@ -1,27 +1,27 @@
import BDV2 from "../../modules/v2"; import BDV2 from "../../modules/v2";
import SearchIcon from "../icons/search"; import SearchIcon from "../icons/search";
const React = BDV2.React; const React = BDV2.React;
export default class Search extends React.Component { export default class Search extends React.Component {
constructor(props){ constructor(props){
super(props) super(props)
this.state = { this.state = {
focused: false focused: false
} }
} }
render() { render() {
const className = ["bd-search-wrapper"] const className = ["bd-search-wrapper"]
if(this.state.focused)className.push("focused") if(this.state.focused)className.push("focused")
return <div className={className.join(" ")}> return <div className={className.join(" ")}>
<input onChange={this.props.onChange} onKeyDown={this.props.onKeyDown} type="text" <input onChange={this.props.onChange} onKeyDown={this.props.onKeyDown} type="text"
className="bd-search" placeholder={this.props.placeholder} maxLength="50" className="bd-search" placeholder={this.props.placeholder} maxLength="50"
onFocus={() => { onFocus={() => {
this.setState({focused: true}) this.setState({focused: true})
}} onBlur={() => { }} onBlur={() => {
this.setState({focused: false}) this.setState({focused: false})
}}/> }}/>
<SearchIcon /> <SearchIcon />
</div>; </div>;
} }
} }

View File

@ -1,22 +1,22 @@
import BDV2 from "../../modules/v2"; import BDV2 from "../../modules/v2";
const React = BDV2.React; const React = BDV2.React;
export default class Switch extends React.Component { export default class Switch extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {checked: this.props.checked}; this.state = {checked: this.props.checked};
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
this.switch = <Lightcord.Api.Components.inputs.Switch onChange={this.onChange} value={this.state.checked} /> this.switch = <Lightcord.Api.Components.inputs.Switch onChange={this.onChange} value={this.state.checked} />
} }
onChange(value) { onChange(value) {
if (this.props.disabled) return; if (this.props.disabled) return;
this.props.onChange(value); this.props.onChange(value);
this.setState({checked: value}); this.setState({checked: value});
} }
render() { render() {
return this.switch return this.switch
} }
} }

View File

@ -1,234 +1,234 @@
import {settingsCookie} from "../0globals"; import {settingsCookie} from "../0globals";
import Settings from "../modules/settingsPanel"; import Settings from "../modules/settingsPanel";
import BDV2 from "../modules/v2"; import BDV2 from "../modules/v2";
import DataStore from "../modules/dataStore"; import DataStore from "../modules/dataStore";
import DOM from "../modules/domtools"; import DOM from "../modules/domtools";
import Utils from "../modules/utils" import Utils from "../modules/utils"
import SettingsTitle from "./settingsTitle"; import SettingsTitle from "./settingsTitle";
import Checkbox from "./checkbox"; import Checkbox from "./checkbox";
import V2C_CssEditorDetached from "./cssEditorDetached"; import V2C_CssEditorDetached from "./cssEditorDetached";
export default class V2C_CssEditor extends BDV2.reactComponent { export default class V2C_CssEditor extends BDV2.reactComponent {
constructor(props) { constructor(props) {
super(props); super(props);
const self = this; const self = this;
self.props.lines = 0; self.props.lines = 0;
self.setInitialState(); self.setInitialState();
self.attach = self.attach.bind(self); self.attach = self.attach.bind(self);
self.detachedEditor = BDV2.react.createElement(V2C_CssEditorDetached, {attach: self.attach}); self.detachedEditor = BDV2.react.createElement(V2C_CssEditorDetached, {attach: self.attach});
self.onClick = self.onClick.bind(self); self.onClick = self.onClick.bind(self);
self.updateCss = self.updateCss.bind(self); self.updateCss = self.updateCss.bind(self);
self.saveCss = self.saveCss.bind(self); self.saveCss = self.saveCss.bind(self);
self.detach = self.detach.bind(self); self.detach = self.detach.bind(self);
} }
setInitialState() { setInitialState() {
this.state = { this.state = {
detached: this.props.detached || BDV2.editorDetached detached: this.props.detached || BDV2.editorDetached
}; };
} }
componentDidMount() { componentDidMount() {
// this.updateLineCount(); // this.updateLineCount();
this.editor = ace.edit("bd-customcss-editor"); this.editor = ace.edit("bd-customcss-editor");
this.editor.setTheme("ace/theme/discord"); this.editor.setTheme("ace/theme/discord");
this.editor.session.setMode("ace/mode/css"); this.editor.session.setMode("ace/mode/css");
this.editor.setShowPrintMargin(false); this.editor.setShowPrintMargin(false);
this.editor.setFontSize(14); this.editor.setFontSize(14);
this.editor.on("change", () => { this.editor.on("change", () => {
if (!settingsCookie["bda-css-0"]) return; if (!settingsCookie["bda-css-0"]) return;
this.saveCss(); this.saveCss();
this.updateCss(); this.updateCss();
}); });
} }
componentWillUnmount() { componentWillUnmount() {
this.editor.destroy(); this.editor.destroy();
} }
componentDidUpdate(prevProps, prevState) { componentDidUpdate(prevProps, prevState) {
const self = this; const self = this;
if (prevState.detached && !self.state.detached) { if (prevState.detached && !self.state.detached) {
BDV2.reactDom.unmountComponentAtNode(self.detachedRoot); BDV2.reactDom.unmountComponentAtNode(self.detachedRoot);
} }
} }
codeMirror() { codeMirror() {
} }
get options() { get options() {
return { return {
lineNumbers: true, lineNumbers: true,
mode: "css", mode: "css",
indentUnit: 4, indentUnit: 4,
theme: "material", theme: "material",
scrollbarStyle: "simple" scrollbarStyle: "simple"
}; };
} }
get css() { get css() {
const _ccss = DataStore.getBDData("bdcustomcss"); const _ccss = DataStore.getBDData("bdcustomcss");
let ccss = ""; let ccss = "";
if (_ccss && _ccss !== "") { if (_ccss && _ccss !== "") {
ccss = Buffer.from(_ccss, "base64").toString("utf8"); ccss = Buffer.from(_ccss, "base64").toString("utf8");
} }
return ccss; return ccss;
} }
updateLineCount() { updateLineCount() {
const lineCount = this.refs.editor.value.split("\n").length; const lineCount = this.refs.editor.value.split("\n").length;
if (lineCount == this.props.lines) return; if (lineCount == this.props.lines) return;
this.refs.lines.textContent = Array.from(new Array(lineCount), (_, i) => i + 1).join(".\n") + "."; this.refs.lines.textContent = Array.from(new Array(lineCount), (_, i) => i + 1).join(".\n") + ".";
this.props.lines = lineCount; this.props.lines = lineCount;
} }
render() { render() {
const self = this; const self = this;
const {detached} = self.state; const {detached} = self.state;
return [ return [
detached && BDV2.react.createElement( detached && BDV2.react.createElement(
"div", "div",
{id: "editor-detached"}, {id: "editor-detached"},
BDV2.react.createElement(SettingsTitle, {text: "Custom CSS Editor"}), BDV2.react.createElement(SettingsTitle, {text: "Custom CSS Editor"}),
BDV2.react.createElement( BDV2.react.createElement(
"h3", "h3",
null, null,
"Editor Detached" "Editor Detached"
), ),
BDV2.react.createElement( BDV2.react.createElement(
"button", "button",
{className: "btn btn-primary", onClick: () => { {className: "btn btn-primary", onClick: () => {
self.attach(); self.attach();
}}, }},
"Attach" "Attach"
) )
), ),
!detached && BDV2.react.createElement( !detached && BDV2.react.createElement(
"div", "div",
null, null,
BDV2.react.createElement(SettingsTitle, {text: "Custom CSS Editor"}), BDV2.react.createElement(SettingsTitle, {text: "Custom CSS Editor"}),
BDV2.react.createElement("div", {className: "editor-wrapper"}, BDV2.react.createElement("div", {className: "editor-wrapper"},
BDV2.react.createElement("div", {id: "bd-customcss-editor", className: "editor", ref: "editor"}, self.css) BDV2.react.createElement("div", {id: "bd-customcss-editor", className: "editor", ref: "editor"}, self.css)
), ),
BDV2.react.createElement( BDV2.react.createElement(
"div", "div",
{id: "bd-customcss-attach-controls"}, {id: "bd-customcss-attach-controls"},
BDV2.react.createElement( BDV2.react.createElement(
"ul", "ul",
{className: "checkbox-group"}, {className: "checkbox-group"},
BDV2.react.createElement(Checkbox, {id: "live-update", text: "Live Update", onChange: this.onChange, checked: settingsCookie["bda-css-0"]}) BDV2.react.createElement(Checkbox, {id: "live-update", text: "Live Update", onChange: this.onChange, checked: settingsCookie["bda-css-0"]})
), ),
BDV2.react.createElement( BDV2.react.createElement(
"div", "div",
{id: "bd-customcss-detach-controls-button"}, {id: "bd-customcss-detach-controls-button"},
BDV2.react.createElement( BDV2.react.createElement(
"button", "button",
{style: {borderRadius: "3px 0 0 3px", borderRight: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => { {style: {borderRadius: "3px 0 0 3px", borderRight: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
self.onClick("update"); self.onClick("update");
}}, }},
"Update" "Update"
), ),
BDV2.react.createElement( BDV2.react.createElement(
"button", "button",
{style: {borderRadius: "0", borderLeft: "1px solid #2d2d2d", borderRight: "1px solid #2d2d2d"}, className: "btn btn-primary", onClick: () => { {style: {borderRadius: "0", borderLeft: "1px solid #2d2d2d", borderRight: "1px solid #2d2d2d"}, className: "btn btn-primary", onClick: () => {
self.onClick("save"); self.onClick("save");
}}, }},
"Save" "Save"
), ),
BDV2.react.createElement( BDV2.react.createElement(
"button", "button",
{style: {borderRadius: "0 3px 3px 0", borderLeft: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => { {style: {borderRadius: "0 3px 3px 0", borderLeft: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
self.onClick("detach"); self.onClick("detach");
}}, }},
"Detach" "Detach"
), ),
BDV2.react.createElement( BDV2.react.createElement(
"span", "span",
{style: {fontSize: "10px", marginLeft: "5px"}}, {style: {fontSize: "10px", marginLeft: "5px"}},
"Unsaved changes are lost on detach" "Unsaved changes are lost on detach"
), ),
BDV2.react.createElement("div", {className: "help-text"}, BDV2.react.createElement("div", {className: "help-text"},
"Press ", "Press ",
BDV2.react.createElement("code", {className: "inline"}, "ctrl"), BDV2.react.createElement("code", {className: "inline"}, "ctrl"),
"+", "+",
BDV2.react.createElement("span", {className: "inline"}, ","), BDV2.react.createElement("span", {className: "inline"}, ","),
" with the editor focused to access the editor's settings." " with the editor focused to access the editor's settings."
) )
) )
) )
) )
] ]
} }
onClick(arg) { onClick(arg) {
const self = this; const self = this;
switch (arg) { switch (arg) {
case "update": case "update":
self.updateCss(); self.updateCss();
break; break;
case "save": case "save":
self.saveCss(); self.saveCss();
break; break;
case "detach": case "detach":
self.detach(); self.detach();
break; break;
} }
} }
onChange(id, checked) { onChange(id, checked) {
switch (id) { switch (id) {
case "live-update": case "live-update":
settingsCookie["bda-css-0"] = checked; settingsCookie["bda-css-0"] = checked;
Settings.saveSettings(); Settings.saveSettings();
break; break;
} }
} }
updateCss() { updateCss() {
DOM.removeStyle("customcss"); DOM.removeStyle("customcss");
DOM.addStyle("customcss", this.editor.session.getValue()); DOM.addStyle("customcss", this.editor.session.getValue());
} }
saveCss() { saveCss() {
DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64")); DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64"));
} }
detach() { detach() {
const self = this; const self = this;
self.setState({ self.setState({
detached: true detached: true
}); });
const droot = self.detachedRoot; const droot = self.detachedRoot;
if (!droot) { if (!droot) {
console.log("FAILED TO INJECT ROOT: .app"); console.log("FAILED TO INJECT ROOT: .app");
return; return;
} }
BDV2.reactDom.render(self.detachedEditor, droot); BDV2.reactDom.render(self.detachedEditor, droot);
} }
get detachedRoot() { get detachedRoot() {
const _root = DOM.query("#bd-customcss-detach-container"); const _root = DOM.query("#bd-customcss-detach-container");
if (!_root) { if (!_root) {
if (!this.injectDetachedRoot()) return null; if (!this.injectDetachedRoot()) return null;
return this.detachedRoot; return this.detachedRoot;
} }
return _root; return _root;
} }
injectDetachedRoot() { injectDetachedRoot() {
const app = DOM.query(".app, ."+Utils.removeDa(BDModules.get(e => e.app && e.layers)[0].app)); const app = DOM.query(".app, ."+Utils.removeDa(BDModules.get(e => e.app && e.layers)[0].app));
if (!app) return false; if (!app) return false;
DOM.insertAfter(DOM.createElement(`<div id="bd-customcss-detach-container">`), app); DOM.insertAfter(DOM.createElement(`<div id="bd-customcss-detach-container">`), app);
return true; return true;
} }
attach() { attach() {
const self = this; const self = this;
self.setState({ self.setState({
detached: false detached: false
}); });
} }
} }

View File

@ -1,174 +1,174 @@
import {settingsCookie} from "../0globals"; import {settingsCookie} from "../0globals";
import Settings from "../modules/settingsPanel"; import Settings from "../modules/settingsPanel";
import BDV2 from "../modules/v2"; import BDV2 from "../modules/v2";
import DataStore from "../modules/dataStore"; import DataStore from "../modules/dataStore";
import DOM from "../modules/domtools"; import DOM from "../modules/domtools";
import Utils from "../modules/utils" import Utils from "../modules/utils"
import Checkbox from "./checkbox"; import Checkbox from "./checkbox";
export default class V2C_CssEditorDetached extends BDV2.reactComponent { export default class V2C_CssEditorDetached extends BDV2.reactComponent {
constructor(props) { constructor(props) {
super(props); super(props);
const self = this; const self = this;
self.onClick = self.onClick.bind(self); self.onClick = self.onClick.bind(self);
self.updateCss = self.updateCss.bind(self); self.updateCss = self.updateCss.bind(self);
self.saveCss = self.saveCss.bind(self); self.saveCss = self.saveCss.bind(self);
self.onChange = self.onChange.bind(self); self.onChange = self.onChange.bind(self);
} }
componentDidMount() { componentDidMount() {
DOM.addClass(DOM.query("#app-mount"), "bd-detached-editor"); DOM.addClass(DOM.query("#app-mount"), "bd-detached-editor");
BDV2.editorDetached = true; BDV2.editorDetached = true;
// this.updateLineCount(); // this.updateLineCount();
this.editor = ace.edit("bd-customcss-editor-detached"); this.editor = ace.edit("bd-customcss-editor-detached");
this.editor.setTheme("ace/theme/discord"); this.editor.setTheme("ace/theme/discord");
this.editor.session.setMode("ace/mode/css"); this.editor.session.setMode("ace/mode/css");
this.editor.setShowPrintMargin(false); this.editor.setShowPrintMargin(false);
this.editor.setFontSize(14); this.editor.setFontSize(14);
this.editor.on("change", () => { this.editor.on("change", () => {
if (!settingsCookie["bda-css-0"]) return; if (!settingsCookie["bda-css-0"]) return;
this.saveCss(); this.saveCss();
this.updateCss(); this.updateCss();
}); });
} }
componentWillUnmount() { componentWillUnmount() {
DOM.removeClass(DOM.query("#app-mount"), "bd-detached-editor"); DOM.removeClass(DOM.query("#app-mount"), "bd-detached-editor");
BDV2.editorDetached = false; BDV2.editorDetached = false;
this.editor.destroy(); this.editor.destroy();
} }
updateLineCount() { updateLineCount() {
const lineCount = this.refs.editor.value.split("\n").length; const lineCount = this.refs.editor.value.split("\n").length;
if (lineCount == this.props.lines) return; if (lineCount == this.props.lines) return;
this.refs.lines.textContent = Array.from(new Array(lineCount), (_, i) => i + 1).join(".\n") + "."; this.refs.lines.textContent = Array.from(new Array(lineCount), (_, i) => i + 1).join(".\n") + ".";
this.props.lines = lineCount; this.props.lines = lineCount;
} }
get options() { get options() {
return { return {
lineNumbers: true, lineNumbers: true,
mode: "css", mode: "css",
indentUnit: 4, indentUnit: 4,
theme: "material", theme: "material",
scrollbarStyle: "simple" scrollbarStyle: "simple"
}; };
} }
get css() { get css() {
const _ccss = DataStore.getBDData("bdcustomcss"); const _ccss = DataStore.getBDData("bdcustomcss");
let ccss = ""; let ccss = "";
if (_ccss && _ccss !== "") { if (_ccss && _ccss !== "") {
ccss = Buffer.from(_ccss, "base64").toString("utf8"); ccss = Buffer.from(_ccss, "base64").toString("utf8");
} }
return ccss; return ccss;
} }
get root() { get root() {
const _root = DOM.query("#bd-customcss-detach-container"); const _root = DOM.query("#bd-customcss-detach-container");
if (!_root) { if (!_root) {
if (!this.injectRoot()) return null; if (!this.injectRoot()) return null;
return this.detachedRoot; return this.detachedRoot;
} }
return _root; return _root;
} }
injectRoot() { injectRoot() {
const app = DOM.query(".app, ."+Utils.removeDa(BDModules.get(e => e.app && e.layers)[0].app)); const app = DOM.query(".app, ."+Utils.removeDa(BDModules.get(e => e.app && e.layers)[0].app));
if (!app) return false; if (!app) return false;
DOM.insertAfter(DOM.createElement(`<div id="bd-customcss-detach-container">`), app); DOM.insertAfter(DOM.createElement(`<div id="bd-customcss-detach-container">`), app);
return true; return true;
} }
render() { render() {
const self = this; const self = this;
return BDV2.react.createElement( return BDV2.react.createElement(
"div", "div",
{className: "bd-detached-css-editor", id: "bd-customcss-detach-editor"}, {className: "bd-detached-css-editor", id: "bd-customcss-detach-editor"},
BDV2.react.createElement( BDV2.react.createElement(
"div", "div",
{id: "bd-customcss-innerpane"}, {id: "bd-customcss-innerpane"},
BDV2.react.createElement("div", {className: "editor-wrapper"}, BDV2.react.createElement("div", {className: "editor-wrapper"},
BDV2.react.createElement("div", {id: "bd-customcss-editor-detached", className: "editor", ref: "editor"}, self.css) BDV2.react.createElement("div", {id: "bd-customcss-editor-detached", className: "editor", ref: "editor"}, self.css)
), ),
BDV2.react.createElement( BDV2.react.createElement(
"div", "div",
{id: "bd-customcss-attach-controls"}, {id: "bd-customcss-attach-controls"},
BDV2.react.createElement( BDV2.react.createElement(
"ul", "ul",
{className: "checkbox-group"}, {className: "checkbox-group"},
BDV2.react.createElement(Checkbox, {id: "live-update", text: "Live Update", onChange: self.onChange, checked: settingsCookie["bda-css-0"]}) BDV2.react.createElement(Checkbox, {id: "live-update", text: "Live Update", onChange: self.onChange, checked: settingsCookie["bda-css-0"]})
), ),
BDV2.react.createElement( BDV2.react.createElement(
"div", "div",
{id: "bd-customcss-detach-controls-button"}, {id: "bd-customcss-detach-controls-button"},
BDV2.react.createElement( BDV2.react.createElement(
"button", "button",
{style: {borderRadius: "3px 0 0 3px", borderRight: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => { {style: {borderRadius: "3px 0 0 3px", borderRight: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
self.onClick("update"); self.onClick("update");
}}, }},
"Update" "Update"
), ),
BDV2.react.createElement( BDV2.react.createElement(
"button", "button",
{style: {borderRadius: "0", borderLeft: "1px solid #2d2d2d", borderRight: "1px solid #2d2d2d"}, className: "btn btn-primary", onClick: () => { {style: {borderRadius: "0", borderLeft: "1px solid #2d2d2d", borderRight: "1px solid #2d2d2d"}, className: "btn btn-primary", onClick: () => {
self.onClick("save"); self.onClick("save");
}}, }},
"Save" "Save"
), ),
BDV2.react.createElement( BDV2.react.createElement(
"button", "button",
{style: {borderRadius: "0 3px 3px 0", borderLeft: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => { {style: {borderRadius: "0 3px 3px 0", borderLeft: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
self.onClick("attach"); self.onClick("attach");
}}, }},
"Attach" "Attach"
), ),
BDV2.react.createElement( BDV2.react.createElement(
"span", "span",
{style: {fontSize: "10px", marginLeft: "5px"}}, {style: {fontSize: "10px", marginLeft: "5px"}},
"Unsaved changes are lost on attach" "Unsaved changes are lost on attach"
) )
) )
) )
) )
); );
} }
onChange(id, checked) { onChange(id, checked) {
switch (id) { switch (id) {
case "live-update": case "live-update":
settingsCookie["bda-css-0"] = checked; settingsCookie["bda-css-0"] = checked;
Settings.saveSettings(); Settings.saveSettings();
break; break;
} }
} }
onClick(id) { onClick(id) {
const self = this; const self = this;
switch (id) { switch (id) {
case "attach": case "attach":
if (DOM.query("#editor-detached")) self.props.attach(); if (DOM.query("#editor-detached")) self.props.attach();
BDV2.reactDom.unmountComponentAtNode(self.root); BDV2.reactDom.unmountComponentAtNode(self.root);
self.root.remove(); self.root.remove();
break; break;
case "update": case "update":
self.updateCss(); self.updateCss();
break; break;
case "save": case "save":
self.saveCss(); self.saveCss();
break; break;
} }
} }
updateCss() { updateCss() {
DOM.removeStyle("customcss"); DOM.removeStyle("customcss");
DOM.addStyle("customcss", this.editor.session.getValue()); DOM.addStyle("customcss", this.editor.session.getValue());
} }
saveCss() { saveCss() {
DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64")); DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64"));
} }
} }

View File

@ -1,13 +1,13 @@
import BDV2 from "../../modules/v2"; import BDV2 from "../../modules/v2";
const React = BDV2.React; const React = BDV2.React;
export default class Delete extends React.Component { export default class Delete extends React.Component {
render() { render() {
const size = this.props.size || "24px"; const size = this.props.size || "24px";
return <svg className={this.props.className || ""} fill="var(--text-normal)" viewBox="0 0 24 24" style={{width: size, height: size}} onClick={this.props.onClick}> return <svg className={this.props.className || ""} fill="var(--text-normal)" viewBox="0 0 24 24" style={{width: size, height: size}} onClick={this.props.onClick}>
<path fill="none" d="M0 0h24v24H0V0z"/><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12l1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z"/> <path fill="none" d="M0 0h24v24H0V0z"/><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12l1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z"/>
<path fill="none" d="M0 0h24v24H0z"/> <path fill="none" d="M0 0h24v24H0z"/>
</svg>; </svg>;
} }
} }

View File

@ -1,12 +1,12 @@
import BDV2 from "../../modules/v2"; import BDV2 from "../../modules/v2";
const React = BDV2.React; const React = BDV2.React;
export default class DownArrow extends React.Component { export default class DownArrow extends React.Component {
render() { render() {
const size = this.props.size || "16px"; const size = this.props.size || "16px";
return <svg className={this.props.className || ""} fill="var(--text-normal)" viewBox="0 0 24 24" style={{width: size, height: size}}> return <svg className={this.props.className || ""} fill="var(--text-normal)" viewBox="0 0 24 24" style={{width: size, height: size}}>
<path d="M8.12 9.29L12 13.17l3.88-3.88c.39-.39 1.02-.39 1.41 0 .39.39.39 1.02 0 1.41l-4.59 4.59c-.39.39-1.02.39-1.41 0L6.7 10.7c-.39-.39-.39-1.02 0-1.41.39-.38 1.03-.39 1.42 0z"/> <path d="M8.12 9.29L12 13.17l3.88-3.88c.39-.39 1.02-.39 1.41 0 .39.39.39 1.02 0 1.41l-4.59 4.59c-.39.39-1.02.39-1.41 0L6.7 10.7c-.39-.39-.39-1.02 0-1.41.39-.38 1.03-.39 1.42 0z"/>
</svg>; </svg>;
} }
} }

View File

@ -1,13 +1,13 @@
import BDV2 from "../../modules/v2"; import BDV2 from "../../modules/v2";
const React = BDV2.React; const React = BDV2.React;
export default class Edit extends React.Component { export default class Edit extends React.Component {
render() { render() {
const size = this.props.size || "24px"; const size = this.props.size || "24px";
return <svg className={this.props.className || ""} viewBox="0 0 24 24" fill="var(--text-normal)" style={{width: size, height: size}} onClick={this.props.onClick}> return <svg className={this.props.className || ""} viewBox="0 0 24 24" fill="var(--text-normal)" style={{width: size, height: size}} onClick={this.props.onClick}>
<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" /> <path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" />
<path d="M0 0h24v24H0z" fill="none" /> <path d="M0 0h24v24H0z" fill="none" />
</svg>; </svg>;
} }
} }

View File

@ -1,18 +1,18 @@
{/* <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18px" height="18px"> {/* <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18px" height="18px">
<path d="M0 0h24v24H0z" fill="none"/> <path d="M0 0h24v24H0z" fill="none"/>
<path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/> <path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/>
</svg> */} </svg> */}
import BDV2 from "../../modules/v2"; import BDV2 from "../../modules/v2";
const React = BDV2.React; const React = BDV2.React;
export default class History extends React.Component { export default class History extends React.Component {
render() { render() {
const size = this.props.size || "18px"; const size = this.props.size || "18px";
return <svg viewBox="0 0 24 24" fill="var(--text-normal)" className={this.props.className || ""} style={{width: size, height: size}} onClick={this.props.onClick}> return <svg viewBox="0 0 24 24" fill="var(--text-normal)" className={this.props.className || ""} style={{width: size, height: size}} onClick={this.props.onClick}>
<path d="M0 0h24v24H0z" fill="none"/> <path d="M0 0h24v24H0z" fill="none"/>
<path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/> <path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/>
</svg>; </svg>;
} }
} }

View File

@ -1,13 +1,13 @@
import BDV2 from "../../modules/v2"; import BDV2 from "../../modules/v2";
const React = BDV2.React; const React = BDV2.React;
export default class Search extends React.Component { export default class Search extends React.Component {
render() { render() {
const size = this.props.size || "16px"; const size = this.props.size || "16px";
return <svg className={this.props.className || ""} fill="var(--text-normal)" viewBox="0 0 24 24" style={{width: size, height: size}}> return <svg className={this.props.className || ""} fill="var(--text-normal)" viewBox="0 0 24 24" style={{width: size, height: size}}>
<path fill="none" d="M0 0h24v24H0V0z"/> <path fill="none" d="M0 0h24v24H0V0z"/>
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/> <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
</svg>; </svg>;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,485 +1,485 @@
import BDV2 from "../../modules/v2"; import BDV2 from "../../modules/v2";
import SettingsTitle from "../settingsTitle"; import SettingsTitle from "../settingsTitle";
import TabBarSeparator from "../tabBarSeparator"; import TabBarSeparator from "../tabBarSeparator";
import ServerCard from "./serverCard"; import ServerCard from "./serverCard";
import { useForceUpdate } from "../../modules/hooks"; import { useForceUpdate } from "../../modules/hooks";
let SettingsView let SettingsView
export default class V2C_PublicServers extends BDV2.reactComponent { export default class V2C_PublicServers extends BDV2.reactComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.setInitialState(); this.setInitialState();
this.close = this.close.bind(this); this.close = this.close.bind(this);
this.changeCategory = this.changeCategory.bind(this); this.changeCategory = this.changeCategory.bind(this);
this.search = this.search.bind(this); this.search = this.search.bind(this);
this.searchKeyDown = this.searchKeyDown.bind(this); this.searchKeyDown = this.searchKeyDown.bind(this);
this.checkConnection = this.checkConnection.bind(this); this.checkConnection = this.checkConnection.bind(this);
this.join = this.join.bind(this); this.join = this.join.bind(this);
this.connect = this.connect.bind(this); this.connect = this.connect.bind(this);
this.GuildStore = BDV2.WebpackModules.findByUniqueProperties(["getGuilds"]); this.GuildStore = BDV2.WebpackModules.findByUniqueProperties(["getGuilds"]);
this.AvatarDefaults = BDV2.WebpackModules.findByUniqueProperties(["getUserAvatarURL", "DEFAULT_AVATARS"]); this.AvatarDefaults = BDV2.WebpackModules.findByUniqueProperties(["getUserAvatarURL", "DEFAULT_AVATARS"]);
this.InviteActions = BDV2.WebpackModules.findByUniqueProperties(["acceptInvite"]); this.InviteActions = BDV2.WebpackModules.findByUniqueProperties(["acceptInvite"]);
this.SortedGuildStore = BDV2.WebpackModules.findByUniqueProperties(["getSortedGuilds"]); this.SortedGuildStore = BDV2.WebpackModules.findByUniqueProperties(["getSortedGuilds"]);
this.hooks = [] this.hooks = []
} }
componentDidMount() { componentDidMount() {
this.checkConnection(); this.checkConnection();
} }
setInitialState() { setInitialState() {
this.state = { this.state = {
selectedCategory: -1, selectedCategory: -1,
title: "Loading...", title: "Loading...",
loading: true, loading: true,
servers: [], servers: [],
next: null, next: null,
connection: { connection: {
state: 0, state: 0,
user: null user: null
}, },
section: this.categorySlugs[0], section: this.categorySlugs[0],
theme: "dark" theme: "dark"
}; };
} }
close() { close() {
this.props.close() this.props.close()
} }
search(query, clear) { search(query, clear) {
const self = this; const self = this;
fetch(`${self.searchEndPoint}${query}${query ? "&schema=new" : "?schema=new"}`, { fetch(`${self.searchEndPoint}${query}${query ? "&schema=new" : "?schema=new"}`, {
method: "get" method: "get"
}).then(async res => { }).then(async res => {
if(res.status !== 200)throw await res.text() if(res.status !== 200)throw await res.text()
let data = await res.json() let data = await res.json()
let servers = data.results.reduce((arr, server) => { let servers = data.results.reduce((arr, server) => {
server.joined = false; server.joined = false;
arr.push(server); arr.push(server);
// arr.push(<ServerCard server={server} join={self.join}/>); // arr.push(<ServerCard server={server} join={self.join}/>);
return arr; return arr;
}, []); }, []);
if (!clear) { if (!clear) {
servers = self.state.servers.concat(servers); servers = self.state.servers.concat(servers);
} }
else { else {
//servers.unshift(self.bdServer); //servers.unshift(self.bdServer);
} }
let end = data.size + data.from; let end = data.size + data.from;
data.next = `?from=${end}`; data.next = `?from=${end}`;
if (self.state.term) data.next += `&term=${self.state.term}`; if (self.state.term) data.next += `&term=${self.state.term}`;
if (self.state.selectedCategory) data.next += `&category=${self.categoryButtons[self.state.selectedCategory]}`; if (self.state.selectedCategory) data.next += `&category=${self.categoryButtons[self.state.selectedCategory]}`;
if (end >= data.total) { if (end >= data.total) {
end = data.total; end = data.total;
data.next = null; data.next = null;
} }
let title = `Showing 1-${end} of ${data.total} results in ${self.categoryButtons[self.state.selectedCategory]}`; let title = `Showing 1-${end} of ${data.total} results in ${self.categoryButtons[self.state.selectedCategory]}`;
if (self.state.term) title += ` for ${self.state.term}`; if (self.state.term) title += ` for ${self.state.term}`;
self.setState({ self.setState({
loading: false, loading: false,
title: title, title: title,
servers: servers, servers: servers,
next: data.next next: data.next
}); });
}).catch((err) => { }).catch((err) => {
console.error(err) console.error(err)
return self.setState({ return self.setState({
loading: false, loading: false,
title: "Failed to load servers. Check console for details" title: "Failed to load servers. Check console for details"
}); });
}) })
} }
async join(serverCard) { async join(serverCard) {
if (serverCard.props.pinned) return this.InviteActions.acceptInvite(serverCard.props.invite_code); if (serverCard.props.pinned) return this.InviteActions.acceptInvite(serverCard.props.invite_code);
await fetch(`${this.joinEndPoint}/${serverCard.props.server.identifier}`,{ await fetch(`${this.joinEndPoint}/${serverCard.props.server.identifier}`,{
method: "GET", method: "GET",
credentials: "include", credentials: "include",
mode: "cors", mode: "cors",
headers: { headers: {
"Accept": "application/json", "Accept": "application/json",
"Content-Type": "application/json" "Content-Type": "application/json"
} }
}); });
serverCard.setState({joined: true}); serverCard.setState({joined: true});
} }
connect() { connect() {
const self = this; const self = this;
const options = self.windowOptions; const options = self.windowOptions;
options.x = Math.round(window.screenX + window.innerWidth / 2 - options.width / 2); options.x = Math.round(window.screenX + window.innerWidth / 2 - options.width / 2);
options.y = Math.round(window.screenY + window.innerHeight / 2 - options.height / 2); options.y = Math.round(window.screenY + window.innerHeight / 2 - options.height / 2);
const win = self.joinWindow = new (window.require("electron").remote.BrowserWindow)(options); const win = self.joinWindow = new (window.require("electron").remote.BrowserWindow)(options);
const url = "https://auth.discordservers.com/connect?scopes=guilds.join&previousUrl=https://auth.discordservers.com/info"; const url = "https://auth.discordservers.com/connect?scopes=guilds.join&previousUrl=https://auth.discordservers.com/info";
win.webContents.on("did-navigate", (event, url) => { win.webContents.on("did-navigate", (event, url) => {
if (url != "https://auth.discordservers.com/info") return; if (url != "https://auth.discordservers.com/info") return;
win.close(); win.close();
self.checkConnection(); self.checkConnection();
}); });
win.loadURL(url); win.loadURL(url);
win.setMenuBarVisibility(false) win.setMenuBarVisibility(false)
} }
get windowOptions() { get windowOptions() {
return { return {
width: 500, width: 500,
height: 550, height: 550,
backgroundColor: "#282b30", backgroundColor: "#282b30",
show: true, show: true,
resizable: false, resizable: false,
maximizable: false, maximizable: false,
minimizable: false, minimizable: false,
alwaysOnTop: true, alwaysOnTop: true,
frame: true, frame: true,
center: false, center: false,
webPreferences: { webPreferences: {
nodeIntegration: false nodeIntegration: false
} }
}; };
} }
get bdServer() { get bdServer() {
const server = { const server = {
name: "BetterDiscord", name: "BetterDiscord",
online: "30000+", online: "30000+",
members: "70000+", members: "70000+",
categories: ["community", "programming", "support"], categories: ["community", "programming", "support"],
description: "Official BetterDiscord server for support etc", description: "Official BetterDiscord server for support etc",
identifier: "86004744966914048", identifier: "86004744966914048",
iconUrl: "https://cdn.discordapp.com/icons/86004744966914048/292e7f6bfff2b71dfd13e508a859aedd.webp", iconUrl: "https://cdn.discordapp.com/icons/86004744966914048/292e7f6bfff2b71dfd13e508a859aedd.webp",
nativejoin: true, nativejoin: true,
invite_code: "0Tmfo5ZbORCRqbAd", invite_code: "0Tmfo5ZbORCRqbAd",
pinned: true pinned: true
}; };
const server2 = { const server2 = {
name: "Lightcord", name: "Lightcord",
online: "100+", online: "100+",
members: "300+", members: "300+",
categories: ["community", "programming", "support"], categories: ["community", "programming", "support"],
description: "Official Lightcord server for support etc", description: "Official Lightcord server for support etc",
identifier: "705908350218666117", identifier: "705908350218666117",
iconUrl: "https://github.com/lightcord.png", iconUrl: "https://github.com/lightcord.png",
nativejoin: true, nativejoin: true,
invite_code: "7eFff2A", invite_code: "7eFff2A",
pinned: true pinned: true
}; };
const guildList = this.SortedGuildStore.getFlattenedGuildIds(); const guildList = this.SortedGuildStore.getFlattenedGuildIds();
const defaultList = this.AvatarDefaults.DEFAULT_AVATARS; const defaultList = this.AvatarDefaults.DEFAULT_AVATARS;
return [ return [
BDV2.react.createElement(ServerCard, {server: server2, pinned: true, join: this.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]}), BDV2.react.createElement(ServerCard, {server: server2, pinned: true, join: this.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]}),
BDV2.react.createElement(ServerCard, {server: server, pinned: true, join: this.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]}) BDV2.react.createElement(ServerCard, {server: server, pinned: true, join: this.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]})
] ]
} }
get searchEndPoint() { get searchEndPoint() {
return "https://search.discordservers.com"; return "https://search.discordservers.com";
} }
get joinEndPoint() { get joinEndPoint() {
return "https://j.discordservers.com"; return "https://j.discordservers.com";
} }
get connectEndPoint() { get connectEndPoint() {
return "https://join.discordservers.com/connect"; return "https://join.discordservers.com/connect";
} }
async checkConnection() { async checkConnection() {
const self = this; const self = this;
try { try {
const response = await fetch(`https://auth.discordservers.com/info`,{ const response = await fetch(`https://auth.discordservers.com/info`,{
method: "GET", method: "GET",
credentials: "include", credentials: "include",
mode: "cors", mode: "cors",
headers: { headers: {
"Accept": "application/json", "Accept": "application/json",
"Content-Type": "application/json" "Content-Type": "application/json"
} }
}); });
const text = await response.text() const text = await response.text()
if(!text){ if(!text){
self.setState({ self.setState({
title: "Not connected to discordservers.com!", title: "Not connected to discordservers.com!",
loading: true, loading: true,
selectedCategory: -1, selectedCategory: -1,
connection: { connection: {
state: 1, state: 1,
user: null user: null
} }
}); });
return return
} }
const data = JSON.parse(text) const data = JSON.parse(text)
self.setState({ self.setState({
selectedCategory: 0, selectedCategory: 0,
connection: { connection: {
state: 2, state: 2,
user: data user: data
} }
}); });
self.search("", true); self.search("", true);
} }
catch (error) { catch (error) {
console.error(error) console.error(error)
self.setState({ self.setState({
title: "Not connected to discordservers.com!", title: "Not connected to discordservers.com!",
loading: true, loading: true,
selectedCategory: -1, selectedCategory: -1,
connection: { connection: {
state: 1, state: 1,
user: null user: null
} }
}); });
} }
} }
render() { render() {
this.hooks.forEach((e) => e()) this.hooks.forEach((e) => e())
SettingsView = SettingsView || BDV2.WebpackModules.findByDisplayName("SettingsView") SettingsView = SettingsView || BDV2.WebpackModules.findByDisplayName("SettingsView")
return BDV2.react.createElement("div", {id: "pubslayerroot"}, return BDV2.react.createElement("div", {id: "pubslayerroot"},
BDV2.react.createElement("div", {id: "pubslayer"}, BDV2.react.createElement(SettingsView, { BDV2.react.createElement("div", {id: "pubslayer"}, BDV2.react.createElement(SettingsView, {
onSetSection: (section) => { onSetSection: (section) => {
this.changeCategory(this.categorySlugs.indexOf(section)) this.changeCategory(this.categorySlugs.indexOf(section))
}, },
sections: this.sections, sections: this.sections,
onClose: this.close, onClose: this.close,
section: this.state.section section: this.state.section
})) }))
); );
} }
get sections(){ get sections(){
let sections = [] let sections = []
sections.push({ sections.push({
section: "HEADER", section: "HEADER",
label: "Public Servers" label: "Public Servers"
}, { }, {
section: "DIVIDER" section: "DIVIDER"
}, { }, {
section: "CUSTOM", section: "CUSTOM",
element: this.searchInput.bind(null, () => this, this.searchKeyDown) element: this.searchInput.bind(null, () => this, this.searchKeyDown)
}, { }, {
section: "DIVIDER" section: "DIVIDER"
}, { }, {
section: "HEADER", section: "HEADER",
label: "Categories" label: "Categories"
}, ...this.categoryButtons.map((value, index) => { }, ...this.categoryButtons.map((value, index) => {
return { return {
section: this.categorySlugs[index], section: this.categorySlugs[index],
label: value, label: value,
element: this.content.bind(null, () => this) element: this.content.bind(null, () => this)
} }
}), { }), {
section: "DIVIDER" section: "DIVIDER"
}, { }, {
section: "CUSTOM", section: "CUSTOM",
element: this.footer element: this.footer
}, { }, {
section: "CUSTOM", section: "CUSTOM",
element: this.connection.bind(null, () => this) element: this.connection.bind(null, () => this)
}) })
return sections return sections
} }
searchInput(getThis, searchKeyDown) { searchInput(getThis, searchKeyDown) {
const [value, setValue] = BDV2.react.useState("") const [value, setValue] = BDV2.react.useState("")
return BDV2.react.createElement( return BDV2.react.createElement(
"div", "div",
{className: "ui-form-item"}, {className: "ui-form-item"},
BDV2.react.createElement( BDV2.react.createElement(
"div", "div",
{className: "ui-text-input flex-vertical", style: {width: "172px", marginLeft: "10px"}}, {className: "ui-text-input flex-vertical", style: {width: "172px", marginLeft: "10px"}},
BDV2.react.createElement("input", { BDV2.react.createElement("input", {
ref: (serchinput) => (getThis().refs.searchinput = serchinput), ref: (serchinput) => (getThis().refs.searchinput = serchinput),
onKeyDown: searchKeyDown, onKeyDown: searchKeyDown,
onChange: (e) => { onChange: (e) => {
setValue(e.target.value) setValue(e.target.value)
}, },
type: "text", type: "text",
className: "input default", className: "input default",
placeholder: "Search...", placeholder: "Search...",
maxLength: "50", maxLength: "50",
value: value value: value
}) })
) )
); );
} }
searchKeyDown(e) { searchKeyDown(e) {
const self = this; const self = this;
if (self.state.loading || e.which !== 13) return; if (self.state.loading || e.which !== 13) return;
self.setState({ self.setState({
loading: true, loading: true,
title: "Loading...", title: "Loading...",
term: e.target.value term: e.target.value
}); });
let query = `?term=${e.target.value}`; let query = `?term=${e.target.value}`;
if (self.state.selectedCategory !== 0) { if (self.state.selectedCategory !== 0) {
query += `&category=${self.categoryButtons[self.state.selectedCategory]}`; query += `&category=${self.categoryButtons[self.state.selectedCategory]}`;
} }
self.search(query, true); self.search(query, true);
} }
get categorySlugs(){ get categorySlugs(){
return this.categoryButtons.map(e => e.toLowerCase().replace(/[^\w\d]+/g, "_")) return this.categoryButtons.map(e => e.toLowerCase().replace(/[^\w\d]+/g, "_"))
} }
get categoryButtons() { get categoryButtons() {
return ["All", "FPS Games", "MMO Games", "Strategy Games", "MOBA Games", "RPG Games", "Tabletop Games", "Sandbox Games", "Simulation Games", "Music", "Community", "Language", "Programming", "Other"]; return ["All", "FPS Games", "MMO Games", "Strategy Games", "MOBA Games", "RPG Games", "Tabletop Games", "Sandbox Games", "Simulation Games", "Music", "Community", "Language", "Programming", "Other"];
} }
changeCategory(id) { changeCategory(id) {
const self = this; const self = this;
if (self.state.loading) return; if (self.state.loading) return;
self.setState({ self.setState({
loading: true, loading: true,
selectedCategory: id, selectedCategory: id,
title: "Loading...", title: "Loading...",
term: null, term: null,
section: self.categorySlugs[id] section: self.categorySlugs[id]
}); });
if (id === 0) { if (id === 0) {
self.search("", true); self.search("", true);
return; return;
} }
self.search(`?category=${self.categoryButtons[id]}`, true); self.search(`?category=${self.categoryButtons[id]}`, true);
} }
content(getThis) { content(getThis) {
const self = getThis(); const self = getThis();
self.useState() self.useState()
const guildList = self.SortedGuildStore.getFlattenedGuildIds(); const guildList = self.SortedGuildStore.getFlattenedGuildIds();
const defaultList = self.AvatarDefaults.DEFAULT_AVATARS; const defaultList = self.AvatarDefaults.DEFAULT_AVATARS;
if (self.state.connection.state === 1) return BDV2.react.createElement(self.notConnected.bind(null, getThis)); if (self.state.connection.state === 1) return BDV2.react.createElement(self.notConnected.bind(null, getThis));
let columnModule = BDModules.get(e => e.contentColumnDefault)[0] let columnModule = BDModules.get(e => e.contentColumnDefault)[0]
return [BDV2.react.createElement( return [BDV2.react.createElement(
"div", "div",
{ref: (ref) => { {ref: (ref) => {
(self.refs.content = ref) (self.refs.content = ref)
}, key: "pc", className: columnModule.contentColumn+" "+columnModule.contentColumn+" content-column default"}, }, key: "pc", className: columnModule.contentColumn+" "+columnModule.contentColumn+" content-column default"},
BDV2.react.createElement(SettingsTitle, {text: self.state.title}), BDV2.react.createElement(SettingsTitle, {text: self.state.title}),
self.bdServer, self.bdServer,
self.state.servers.map((server) => { self.state.servers.map((server) => {
return BDV2.react.createElement(ServerCard, {key: server.identifier, server: server, join: self.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]}); return BDV2.react.createElement(ServerCard, {key: server.identifier, server: server, join: self.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]});
}), }),
self.state.next && BDV2.react.createElement( self.state.next && BDV2.react.createElement(
"button", "button",
{type: "button", onClick: () => { {type: "button", onClick: () => {
if (self.state.loading) return;self.setState({loading: true}); self.search(self.state.next, false); if (self.state.loading) return;self.setState({loading: true}); self.search(self.state.next, false);
}, className: "ui-button filled brand small grow", style: {width: "100%", marginTop: "10px", marginBottom: "10px"}}, }, className: "ui-button filled brand small grow", style: {width: "100%", marginTop: "10px", marginBottom: "10px"}},
BDV2.react.createElement( BDV2.react.createElement(
"div", "div",
{className: "ui-button-contents"}, {className: "ui-button-contents"},
self.state.loading ? "Loading" : "Load More" self.state.loading ? "Loading" : "Load More"
) )
), ),
self.state.servers.length > 0 && BDV2.react.createElement(SettingsTitle, {text: self.state.title}) self.state.servers.length > 0 && BDV2.react.createElement(SettingsTitle, {text: self.state.title})
)]; )];
} }
notConnected(getThis) { notConnected(getThis) {
const self = getThis(); const self = getThis();
//return BDV2.react.createElement(SettingsTitle, { text: self.state.title }); //return BDV2.react.createElement(SettingsTitle, { text: self.state.title });
let columnModule = BDModules.get(e => e.contentColumnDefault)[0] let columnModule = BDModules.get(e => e.contentColumnDefault)[0]
return [BDV2.react.createElement( return [BDV2.react.createElement(
"div", "div",
{key: "ncc", ref: (ref) => (self.refs.content = ref), className: columnModule.contentColumn+" "+columnModule.contentColumn+" content-column default"}, {key: "ncc", ref: (ref) => (self.refs.content = ref), className: columnModule.contentColumn+" "+columnModule.contentColumn+" content-column default"},
BDV2.react.createElement( BDV2.react.createElement(
"h2", "h2",
{className: "ui-form-title h2 margin-reset margin-bottom-20"}, {className: "ui-form-title h2 margin-reset margin-bottom-20"},
"Not connected to discordservers.com!", "Not connected to discordservers.com!",
BDV2.react.createElement( BDV2.react.createElement(
"button", "button",
{ {
onClick: self.connect, onClick: self.connect,
type: "button", type: "button",
className: "ui-button filled brand small grow", className: "ui-button filled brand small grow",
style: { style: {
display: "inline-block", display: "inline-block",
minHeight: "18px", minHeight: "18px",
marginLeft: "10px", marginLeft: "10px",
lineHeight: "14px" lineHeight: "14px"
} }
}, },
BDV2.react.createElement( BDV2.react.createElement(
"div", "div",
{className: "ui-button-contents"}, {className: "ui-button-contents"},
"Connect" "Connect"
) )
) )
), self.bdServer ), self.bdServer
)]; )];
} }
footer() { footer() {
return BDV2.react.createElement( return BDV2.react.createElement(
"div", "div",
{className: "ui-tab-bar-header"}, {className: "ui-tab-bar-header"},
BDV2.react.createElement( BDV2.react.createElement(
"a", "a",
{href: "https://discordservers.com", target: "_blank"}, {href: "https://discordservers.com", target: "_blank"},
"Discordservers.com" "Discordservers.com"
) )
); );
} }
useState(){ useState(){
const forceUpdate = useForceUpdate() const forceUpdate = useForceUpdate()
BDV2.React.useEffect(() => { BDV2.React.useEffect(() => {
const listener = () => { const listener = () => {
forceUpdate() forceUpdate()
} }
this.hooks.push(listener) this.hooks.push(listener)
return () => { return () => {
const index = this.hooks.findIndex(e => e===listener) const index = this.hooks.findIndex(e => e===listener)
if(index < 0)return if(index < 0)return
this.hooks.splice(index, 1) this.hooks.splice(index, 1)
} }
}, []) }, [])
} }
connection(getThis) { connection(getThis) {
const self = getThis(); const self = getThis();
self.useState() self.useState()
const {connection} = self.state; const {connection} = self.state;
if (connection.state !== 2) return BDV2.react.createElement("span", null); if (connection.state !== 2) return BDV2.react.createElement("span", null);
return BDV2.react.createElement( return BDV2.react.createElement(
"span", "span",
null, null,
BDV2.react.createElement(TabBarSeparator, null), BDV2.react.createElement(TabBarSeparator, null),
BDV2.react.createElement( BDV2.react.createElement(
"span", "span",
{style: {color: "#b9bbbe", fontSize: "10px", marginLeft: "10px"}}, {style: {color: "#b9bbbe", fontSize: "10px", marginLeft: "10px"}},
"Connected as: ", "Connected as: ",
`${connection.user.username}#${connection.user.discriminator}` `${connection.user.username}#${connection.user.discriminator}`
), ),
BDV2.react.createElement( BDV2.react.createElement(
"div", "div",
{style: {padding: "5px 10px 0 10px"}}, {style: {padding: "5px 10px 0 10px"}},
BDV2.react.createElement( BDV2.react.createElement(
"button", "button",
{style: {width: "100%", minHeight: "20px"}, type: "button", className: "ui-button filled brand small grow"}, {style: {width: "100%", minHeight: "20px"}, type: "button", className: "ui-button filled brand small grow"},
BDV2.react.createElement( BDV2.react.createElement(
"div", "div",
{className: "ui-button-contents", onClick: self.connect}, {className: "ui-button-contents", onClick: self.connect},
"Reconnect" "Reconnect"
) )
) )
) )
); );
} }
} }

View File

@ -1,47 +1,47 @@
import BDV2 from "../modules/v2"; import BDV2 from "../modules/v2";
export default class V2C_Scroller extends BDV2.reactComponent { export default class V2C_Scroller extends BDV2.reactComponent {
constructor(props) { constructor(props) {
super(props); super(props);
} }
render() { render() {
//scrollerWrap-2lJEkd scrollerThemed-2oenus themeGhostHairline-DBD-2d scrollerFade-1Ijw5y //scrollerWrap-2lJEkd scrollerThemed-2oenus themeGhostHairline-DBD-2d scrollerFade-1Ijw5y
const scrollerModule1 = BDModules.get(e => e.scrollerWrap)[0] const scrollerModule1 = BDModules.get(e => e.scrollerWrap)[0]
const scrollerModule2 = BDModules.get(e => e.sidebarRegionScroller)[0] const scrollerModule2 = BDModules.get(e => e.sidebarRegionScroller)[0]
const scrollerTypes = BDModules.get(e => e.thin)[0] const scrollerTypes = BDModules.get(e => e.thin)[0]
let wrapperClass = `${scrollerModule1.scrollerWrap} ${scrollerModule1.scrollerThemed} ${scrollerModule1.themeGhostHairline}${this.props.fade ? " "+scrollerModule1.scrollerFade : ""}`; let wrapperClass = `${scrollerModule1.scrollerWrap} ${scrollerModule1.scrollerThemed} ${scrollerModule1.themeGhostHairline}${this.props.fade ? " "+scrollerModule1.scrollerFade : ""}`;
let scrollerClass = scrollerModule1.scroller+" scroller"; /* fuck */ let scrollerClass = scrollerModule1.scroller+" scroller"; /* fuck */
if (this.props.sidebar) scrollerClass += ` ${scrollerModule2.sidebarRegionScroller} ${scrollerTypes.thin} ${scrollerTypes.scrollerBase} ${scrollerTypes.fade}` if (this.props.sidebar) scrollerClass += ` ${scrollerModule2.sidebarRegionScroller} ${scrollerTypes.thin} ${scrollerTypes.scrollerBase} ${scrollerTypes.fade}`
if (this.props.contentColumn) { if (this.props.contentColumn) {
scrollerClass += `${scrollerModule2.contentRegionScroller} ${scrollerTypes.auto} content-region-scroller scroller` scrollerClass += `${scrollerModule2.contentRegionScroller} ${scrollerTypes.auto} content-region-scroller scroller`
wrapperClass = `${scrollerModule2.contentTransitionWrap}`; wrapperClass = `${scrollerModule2.contentTransitionWrap}`;
} }
const {children} = this.props; const {children} = this.props;
if (this.props.sidebar) { if (this.props.sidebar) {
return BDV2.react.createElement( return BDV2.react.createElement(
"div", "div",
{key: "scroller", ref: "scroller", className: scrollerClass}, {key: "scroller", ref: "scroller", className: scrollerClass},
children children
); );
} }
return BDV2.react.createElement( return BDV2.react.createElement(
"div", "div",
{key: "scrollerwrap", className: wrapperClass}, {key: "scrollerwrap", className: wrapperClass},
BDV2.react.createElement( BDV2.react.createElement(
"div", "div",
{key: "scroller", ref: "scroller", className: scrollerClass}, {key: "scroller", ref: "scroller", className: scrollerClass},
children children
) )
); );
} }
} }
const originalRender = V2C_Scroller.prototype.render; const originalRender = V2C_Scroller.prototype.render;
Object.defineProperty(V2C_Scroller.prototype, "render", { Object.defineProperty(V2C_Scroller.prototype, "render", {
enumerable: false, enumerable: false,
configurable: false, configurable: false,
set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}, set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");},
get: () => originalRender get: () => originalRender
}); });

View File

@ -1,91 +1,91 @@
import {settingsCookie} from "../0globals"; import {settingsCookie} from "../0globals";
import BDV2 from "../modules/v2"; import BDV2 from "../modules/v2";
import SettingsTitle from "./settingsTitle"; import SettingsTitle from "./settingsTitle";
import Switch from "./switch"; import Switch from "./switch";
import MarginTop from "./margintop"; import MarginTop from "./margintop";
let formModule let formModule
let switchItem let switchItem
let betaClassNames let betaClassNames
export default class V2C_SettingsGroup extends BDV2.reactComponent { export default class V2C_SettingsGroup extends BDV2.reactComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
error: false error: false
} }
} }
renderOldSettings() { renderOldSettings() {
const {title, settings, button} = this.props; const {title, settings, button} = this.props;
const buttonComponent = button ? BDV2.react.createElement("button", {key: "title-button", className: "bd-pfbtn", onClick: button.onClick}, button.title) : null; const buttonComponent = button ? BDV2.react.createElement("button", {key: "title-button", className: "bd-pfbtn", onClick: button.onClick}, button.title) : null;
return [BDV2.react.createElement(SettingsTitle, {text: title}), return [BDV2.react.createElement(SettingsTitle, {text: title}),
buttonComponent, buttonComponent,
settings.map(setting => { settings.map(setting => {
return BDV2.react.createElement(Switch, {id: setting.id, key: setting.id, data: setting, checked: settingsCookie[setting.id], onChange: (id, checked) => { return BDV2.react.createElement(Switch, {id: setting.id, key: setting.id, data: setting, checked: settingsCookie[setting.id], onChange: (id, checked) => {
this.props.onChange(id, checked); this.props.onChange(id, checked);
}}); }});
})]; })];
} }
componentDidCatch(err, errInfo){ componentDidCatch(err, errInfo){
console.log(err, errInfo) console.log(err, errInfo)
this.setState({ this.setState({
error: true error: true
}) })
} }
render(){ render(){
if(this.state.error){ if(this.state.error){
try{ try{
return this.renderOldSettings() return this.renderOldSettings()
}catch(e){ }catch(e){
console.error(e) console.error(e)
return null return null
} }
}else{ }else{
try{ try{
if(!formModule)formModule = BDV2.WebpackModules.find(e => e.FormSection) if(!formModule)formModule = BDV2.WebpackModules.find(e => e.FormSection)
if(!switchItem)switchItem = BDV2.WebpackModules.find(e => e.default && e.default.displayName === "SwitchItem") if(!switchItem)switchItem = BDV2.WebpackModules.find(e => e.default && e.default.displayName === "SwitchItem")
if(!betaClassNames)betaClassNames = BDV2.WebpackModules.find(e => e.beta && (!e.container && !e.userSettingsVoice)) if(!betaClassNames)betaClassNames = BDV2.WebpackModules.find(e => e.beta && (!e.container && !e.userSettingsVoice))
let children = [] let children = []
if(this.props.description){ if(this.props.description){
children.push(<formModule.FormText type="description" selectable={false}> children.push(<formModule.FormText type="description" selectable={false}>
{this.props.description} {this.props.description}
</formModule.FormText>, <MarginTop></MarginTop>) </formModule.FormText>, <MarginTop></MarginTop>)
} }
children.push(...this.props.settings.map(setting => { children.push(...this.props.settings.map(setting => {
let info = [ let info = [
setting.text setting.text
] ]
if(setting.experimental){ if(setting.experimental){
info.push(<sup className={betaClassNames.beta}>(EXPERIMENTAL)</sup>) info.push(<sup className={betaClassNames.beta}>(EXPERIMENTAL)</sup>)
} }
return <switchItem.default onChange={(ev) => { return <switchItem.default onChange={(ev) => {
this.props.onChange(setting.id, ev.target.checked); this.props.onChange(setting.id, ev.target.checked);
this.forceUpdate() this.forceUpdate()
}} key={setting.id} value={settingsCookie[setting.id]} className={__SECRET_EMOTION__.css({ }} key={setting.id} value={settingsCookie[setting.id]} className={__SECRET_EMOTION__.css({
marginBottom: "20px" marginBottom: "20px"
})} disabled={false} hideBorder={false} })} disabled={false} hideBorder={false}
size={switchItem.default.Sizes.DEFAULT} theme={switchItem.default.Themes.DEFAULT} note={setting.info}> size={switchItem.default.Sizes.DEFAULT} theme={switchItem.default.Themes.DEFAULT} note={setting.info}>
{info} {info}
</switchItem.default> </switchItem.default>
})) }))
return <formModule.FormSection tag="h2" title={this.props.title}> return <formModule.FormSection tag="h2" title={this.props.title}>
{children} {children}
</formModule.FormSection> </formModule.FormSection>
}catch(e){ }catch(e){
console.error(e) console.error(e)
setImmediate(()=>{ setImmediate(()=>{
this.setState({ this.setState({
error: true error: true
}) })
}) })
return null return null
} }
} }
} }
} }

View File

@ -1,44 +1,44 @@
import BDV2 from "../modules/v2"; import BDV2 from "../modules/v2";
import Switch from "./components/switch"; import Switch from "./components/switch";
let classnames = [] let classnames = []
function getClassName(name){ function getClassName(name){
let className = classnames.find(e => e.startsWith(name+"-")) let className = classnames.find(e => e.startsWith(name+"-"))
if(className)return className if(className)return className
className = BDModules.get(e => e[name])[0][name] className = BDModules.get(e => e[name])[0][name]
classnames.push(className) classnames.push(className)
return className return className
} }
export default class SwitchItem extends BDV2.reactComponent { export default class SwitchItem extends BDV2.reactComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
} }
onChange() { onChange() {
this.props.checked = !this.props.checked; this.props.checked = !this.props.checked;
this.props.onChange(this.props.id, this.props.checked); this.props.onChange(this.props.id, this.props.checked);
} }
render() { render() {
let {text, info} = this.props.data; let {text, info} = this.props.data;
if(!classnames.find(e => e.startsWith("beta-"))){ if(!classnames.find(e => e.startsWith("beta-"))){
classnames.push(BDModules.get(e => e.beta && (!e.container && !e.userSettingsVoice))[0].beta) classnames.push(BDModules.get(e => e.beta && (!e.container && !e.userSettingsVoice))[0].beta)
} }
if(this.props.data.experimental){ if(this.props.data.experimental){
info = [ info = [
info, info,
React.createElement("sup", {className: getClassName("beta")}, "(EXPERIMENTAL)") React.createElement("sup", {className: getClassName("beta")}, "(EXPERIMENTAL)")
] ]
} }
return BDV2.react.createElement("div", {className: "ui-flex flex-vertical flex-justify-start flex-align-stretch flex-nowrap ui-switch-item"}, return BDV2.react.createElement("div", {className: "ui-flex flex-vertical flex-justify-start flex-align-stretch flex-nowrap ui-switch-item"},
BDV2.react.createElement("div", {className: "ui-flex flex-horizontal flex-justify-start flex-align-stretch flex-nowrap"}, BDV2.react.createElement("div", {className: "ui-flex flex-horizontal flex-justify-start flex-align-stretch flex-nowrap"},
BDV2.react.createElement("h3", {key: "title", className: "ui-form-title h3 margin-reset margin-reset ui-flex-child"}, text), BDV2.react.createElement("h3", {key: "title", className: "ui-form-title h3 margin-reset margin-reset ui-flex-child"}, text),
BDV2.react.createElement(Switch, {key: "switch", onChange: this.onChange, checked: this.props.checked}) BDV2.react.createElement(Switch, {key: "switch", onChange: this.onChange, checked: this.props.checked})
), ),
BDV2.react.createElement("div", {className: "ui-form-text style-description margin-top-4", style: {flex: "1 1 auto"}}, info) BDV2.react.createElement("div", {className: "ui-form-text style-description margin-top-4", style: {flex: "1 1 auto"}}, info)
); );
} }
} }

View File

@ -1,191 +1,191 @@
/** /**
* Tooltip that automatically show and hide themselves on mouseenter and mouseleave events. * Tooltip that automatically show and hide themselves on mouseenter and mouseleave events.
* Will also remove themselves if the node to watch is removed from DOM through * Will also remove themselves if the node to watch is removed from DOM through
* a MutationObserver. * a MutationObserver.
* *
* Note this is not using Discord's internals but normal DOM manipulation and emulates * Note this is not using Discord's internals but normal DOM manipulation and emulates
* Discord's own tooltips as closely as possible. * Discord's own tooltips as closely as possible.
* *
* @module EmulatedTooltip * @module EmulatedTooltip
* @version 0.0.1 * @version 0.0.1
*/ */
import Utils from "../modules/utils"; import Utils from "../modules/utils";
import WebpackModules from "../modules/webpackModules"; import WebpackModules from "../modules/webpackModules";
let TooltipClasses let TooltipClasses
function getTooltipClasses(){ function getTooltipClasses(){
if(TooltipClasses)return TooltipClasses if(TooltipClasses)return TooltipClasses
return TooltipClasses = WebpackModules.findByProps("tooltip", "tooltipBlack"); return TooltipClasses = WebpackModules.findByProps("tooltip", "tooltipBlack");
} }
let TooltipLayers let TooltipLayers
function getTooltipLayers(){ function getTooltipLayers(){
if(TooltipLayers)return TooltipLayers if(TooltipLayers)return TooltipLayers
return TooltipLayers = WebpackModules.findByProps("layer", "layerContainer"); return TooltipLayers = WebpackModules.findByProps("layer", "layerContainer");
} }
const getClass = function(sideOrColor) { const getClass = function(sideOrColor) {
const upperCase = sideOrColor[0].toUpperCase() + sideOrColor.slice(1); const upperCase = sideOrColor[0].toUpperCase() + sideOrColor.slice(1);
const tooltipClass = getTooltipClasses()[`tooltip${upperCase}`]; const tooltipClass = getTooltipClasses()[`tooltip${upperCase}`];
if (tooltipClass) return tooltipClass; if (tooltipClass) return tooltipClass;
return null; return null;
}; };
const classExists = function(sideOrColor) { const classExists = function(sideOrColor) {
return getClass(sideOrColor) ? true : false; return getClass(sideOrColor) ? true : false;
}; };
const toPx = function(value) { const toPx = function(value) {
return `${value}px`; return `${value}px`;
}; };
export default class EmulatedTooltip { export default class EmulatedTooltip {
/** /**
* *
* @constructor * @constructor
* @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on * @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on
* @param {string} tip - string to show in the tooltip * @param {string} tip - string to show in the tooltip
* @param {object} options - additional options for the tooltip * @param {object} options - additional options for the tooltip
* @param {string} [options.style=black] - correlates to the discord styling/colors (black, brand, green, grey, red, yellow) * @param {string} [options.style=black] - correlates to the discord styling/colors (black, brand, green, grey, red, yellow)
* @param {string} [options.side=top] - can be any of top, right, bottom, left * @param {string} [options.side=top] - can be any of top, right, bottom, left
* @param {boolean} [options.preventFlip=false] - prevents moving the tooltip to the opposite side if it is too big or goes offscreen * @param {boolean} [options.preventFlip=false] - prevents moving the tooltip to the opposite side if it is too big or goes offscreen
* @param {boolean} [options.disabled=false] - whether the tooltip should be disabled from showing on hover * @param {boolean} [options.disabled=false] - whether the tooltip should be disabled from showing on hover
* @param {boolean} [options.attachEvents=true] - whether the tooltip should listen to mouseenter and mouseleave events. * @param {boolean} [options.attachEvents=true] - whether the tooltip should listen to mouseenter and mouseleave events.
*/ */
constructor(node, text, options = {}) { constructor(node, text, options = {}) {
const {style = "black", side = "top", preventFlip = false, disabled = false, attachEvents = true} = options; const {style = "black", side = "top", preventFlip = false, disabled = false, attachEvents = true} = options;
this.node = node instanceof jQuery ? node[0] : node; this.node = node instanceof jQuery ? node[0] : node;
this.label = text; this.label = text;
this.style = style.toLowerCase(); this.style = style.toLowerCase();
this.side = side.toLowerCase(); this.side = side.toLowerCase();
this.preventFlip = preventFlip; this.preventFlip = preventFlip;
this.disabled = disabled; this.disabled = disabled;
if (!classExists(this.side)) return Utils.err("EmulatedTooltip", `Side ${this.side} does not exist.`); if (!classExists(this.side)) return Utils.err("EmulatedTooltip", `Side ${this.side} does not exist.`);
if (!classExists(this.style)) return Utils.err("EmulatedTooltip", `Style ${this.style} does not exist.`); if (!classExists(this.style)) return Utils.err("EmulatedTooltip", `Style ${this.style} does not exist.`);
this.element = document.createElement("div"); this.element = document.createElement("div");
this.element.className = getTooltipLayers().layer + " " + getTooltipLayers().disabledPointerEvents; this.element.className = getTooltipLayers().layer + " " + getTooltipLayers().disabledPointerEvents;
this.tooltipElement = document.createElement("div"); this.tooltipElement = document.createElement("div");
this.tooltipElement.className = `${getTooltipClasses().tooltip} ${getClass(this.style)}`; this.tooltipElement.className = `${getTooltipClasses().tooltip} ${getClass(this.style)}`;
this.labelElement = document.createElement("div"); this.labelElement = document.createElement("div");
this.labelElement.className = getTooltipClasses().tooltipContent this.labelElement.className = getTooltipClasses().tooltipContent
const pointerElement = document.createElement("div"); const pointerElement = document.createElement("div");
pointerElement.className = getTooltipClasses().tooltipPointer; pointerElement.className = getTooltipClasses().tooltipPointer;
this.tooltipElement.append(pointerElement); this.tooltipElement.append(pointerElement);
this.tooltipElement.append(this.labelElement); this.tooltipElement.append(this.labelElement);
this.element.append(this.tooltipElement); this.element.append(this.tooltipElement);
if(attachEvents){ if(attachEvents){
this.node.addEventListener("mouseenter", () => { this.node.addEventListener("mouseenter", () => {
if (this.disabled) return; if (this.disabled) return;
this.show(); this.show();
const observer = new MutationObserver((mutations) => { const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => { mutations.forEach((mutation) => {
const nodes = Array.from(mutation.removedNodes); const nodes = Array.from(mutation.removedNodes);
const directMatch = nodes.indexOf(this.node) > -1; const directMatch = nodes.indexOf(this.node) > -1;
const parentMatch = nodes.some(parent => parent.contains(this.node)); const parentMatch = nodes.some(parent => parent.contains(this.node));
if (directMatch || parentMatch) { if (directMatch || parentMatch) {
this.hide(); this.hide();
observer.disconnect(); observer.disconnect();
} }
}); });
}); });
observer.observe(document.body, {subtree: true, childList: true}); observer.observe(document.body, {subtree: true, childList: true});
}); });
this.node.addEventListener("mouseleave", () => { this.node.addEventListener("mouseleave", () => {
this.hide(); this.hide();
}); });
} }
} }
/** Container where the tooltip will be appended. */ /** Container where the tooltip will be appended. */
get container() { get container() {
return document.querySelector("."+Utils.removeDa(BDModules.get(e => e.popouts)[0].popouts)+" ~ ."+Utils.removeDa(BDModules.get(e => e.layerContainer)[0].layerContainer)); return document.querySelector("."+Utils.removeDa(BDModules.get(e => e.popouts)[0].popouts)+" ~ ."+Utils.removeDa(BDModules.get(e => e.layerContainer)[0].layerContainer));
} }
/** Boolean representing if the tooltip will fit on screen above the element */ /** Boolean representing if the tooltip will fit on screen above the element */
get canShowAbove() { return this.node.getBoundingClientRect().top - this.element.offsetHeight >= 0; } get canShowAbove() { return this.node.getBoundingClientRect().top - this.element.offsetHeight >= 0; }
/** Boolean representing if the tooltip will fit on screen below the element */ /** Boolean representing if the tooltip will fit on screen below the element */
get canShowBelow() { return this.node.getBoundingClientRect().top + this.node.offsetHeight + this.element.offsetHeight <= Utils.screenHeight; } get canShowBelow() { return this.node.getBoundingClientRect().top + this.node.offsetHeight + this.element.offsetHeight <= Utils.screenHeight; }
/** Boolean representing if the tooltip will fit on screen to the left of the element */ /** Boolean representing if the tooltip will fit on screen to the left of the element */
get canShowLeft() { return this.node.getBoundingClientRect().left - this.element.offsetWidth >= 0; } get canShowLeft() { return this.node.getBoundingClientRect().left - this.element.offsetWidth >= 0; }
/** Boolean representing if the tooltip will fit on screen to the right of the element */ /** Boolean representing if the tooltip will fit on screen to the right of the element */
get canShowRight() { return this.node.getBoundingClientRect().left + this.node.offsetWidth + this.element.offsetWidth <= Utils.screenWidth; } get canShowRight() { return this.node.getBoundingClientRect().left + this.node.offsetWidth + this.element.offsetWidth <= Utils.screenWidth; }
/** Hides the tooltip. Automatically called on mouseleave. */ /** Hides the tooltip. Automatically called on mouseleave. */
hide() { hide() {
this.element.remove(); this.element.remove();
this.tooltipElement.className = this._className; this.tooltipElement.className = this._className;
} }
/** Shows the tooltip. Automatically called on mouseenter. Will attempt to flip if position was wrong. */ /** Shows the tooltip. Automatically called on mouseenter. Will attempt to flip if position was wrong. */
show() { show() {
this.tooltipElement.className = `${getTooltipClasses().tooltip} ${getClass(this.style)}`; this.tooltipElement.className = `${getTooltipClasses().tooltip} ${getClass(this.style)}`;
this.labelElement.textContent = this.label; this.labelElement.textContent = this.label;
this.container.append(this.element); this.container.append(this.element);
if (this.side == "top") { if (this.side == "top") {
if (this.canShowAbove || (!this.canShowAbove && this.preventFlip)) this.showAbove(); if (this.canShowAbove || (!this.canShowAbove && this.preventFlip)) this.showAbove();
else this.showBelow(); else this.showBelow();
} }
if (this.side == "bottom") { if (this.side == "bottom") {
if (this.canShowBelow || (!this.canShowBelow && this.preventFlip)) this.showBelow(); if (this.canShowBelow || (!this.canShowBelow && this.preventFlip)) this.showBelow();
else this.showAbove(); else this.showAbove();
} }
if (this.side == "left") { if (this.side == "left") {
if (this.canShowLeft || (!this.canShowLeft && this.preventFlip)) this.showLeft(); if (this.canShowLeft || (!this.canShowLeft && this.preventFlip)) this.showLeft();
else this.showRight(); else this.showRight();
} }
if (this.side == "right") { if (this.side == "right") {
if (this.canShowRight || (!this.canShowRight && this.preventFlip)) this.showRight(); if (this.canShowRight || (!this.canShowRight && this.preventFlip)) this.showRight();
else this.showLeft(); else this.showLeft();
} }
} }
/** Force showing the tooltip above the node. */ /** Force showing the tooltip above the node. */
showAbove() { showAbove() {
this.tooltipElement.classList.add(getClass("top")); this.tooltipElement.classList.add(getClass("top"));
this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top - this.element.offsetHeight - 10)); this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top - this.element.offsetHeight - 10));
this.centerHorizontally(); this.centerHorizontally();
} }
/** Force showing the tooltip below the node. */ /** Force showing the tooltip below the node. */
showBelow() { showBelow() {
this.tooltipElement.classList.add(getClass("bottom")); this.tooltipElement.classList.add(getClass("bottom"));
this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top + this.node.offsetHeight + 10)); this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top + this.node.offsetHeight + 10));
this.centerHorizontally(); this.centerHorizontally();
} }
/** Force showing the tooltip to the left of the node. */ /** Force showing the tooltip to the left of the node. */
showLeft() { showLeft() {
this.tooltipElement.classList.add(getClass("left")); this.tooltipElement.classList.add(getClass("left"));
this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left - this.element.offsetWidth - 10)); this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left - this.element.offsetWidth - 10));
this.centerVertically(); this.centerVertically();
} }
/** Force showing the tooltip to the right of the node. */ /** Force showing the tooltip to the right of the node. */
showRight() { showRight() {
this.tooltipElement.classList.add(getClass("right")); this.tooltipElement.classList.add(getClass("right"));
this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left + this.node.offsetWidth + 10)); this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left + this.node.offsetWidth + 10));
this.centerVertically(); this.centerVertically();
} }
centerHorizontally() { centerHorizontally() {
const nodecenter = this.node.getBoundingClientRect().left + (this.node.offsetWidth / 2); const nodecenter = this.node.getBoundingClientRect().left + (this.node.offsetWidth / 2);
this.element.style.setProperty("left", toPx(nodecenter - (this.element.offsetWidth / 2))); this.element.style.setProperty("left", toPx(nodecenter - (this.element.offsetWidth / 2)));
} }
centerVertically() { centerVertically() {
const nodecenter = this.node.getBoundingClientRect().top + (this.node.offsetHeight / 2); const nodecenter = this.node.getBoundingClientRect().top + (this.node.offsetHeight / 2);
this.element.style.setProperty("top", toPx(nodecenter - (this.element.offsetHeight / 2))); this.element.style.setProperty("top", toPx(nodecenter - (this.element.offsetHeight / 2)));
} }
} }

View File

@ -1,23 +1,23 @@
import BDV2 from "../modules/v2"; import BDV2 from "../modules/v2";
import Tooltip from "./tooltip"; import Tooltip from "./tooltip";
export default class extends React.Component { export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
} }
async componentDidMount() { async componentDidMount() {
const {style = "black", side = "top", text = ""} = this.props; const {style = "black", side = "top", text = ""} = this.props;
this.node = BDV2.reactDom.findDOMNode(this); this.node = BDV2.reactDom.findDOMNode(this);
this.tooltip = new Tooltip(this.node, text, {style, side}); this.tooltip = new Tooltip(this.node, text, {style, side});
} }
componentWillUnmount() { componentWillUnmount() {
this.tooltip.hide(); this.tooltip.hide();
delete this.tooltip; delete this.tooltip;
} }
render() { render() {
return this.props.children; return this.props.children;
} }
} }

View File

@ -1,76 +1,76 @@
const path = require("path"); const path = require("path");
const CircularDependencyPlugin = require("circular-dependency-plugin"); const CircularDependencyPlugin = require("circular-dependency-plugin");
const TerserPlugin = require("terser-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin");
const child_process = require("child_process") const child_process = require("child_process")
module.exports = { module.exports = {
mode: "development", mode: "development",
target: "node", target: "node",
devtool: "inline-source-map", devtool: "inline-source-map",
entry: "./src/index.js", entry: "./src/index.js",
output: { output: {
filename: "index.js", filename: "index.js",
path: path.resolve(__dirname, "dist"), path: path.resolve(__dirname, "dist"),
library: "BetterDiscord", library: "BetterDiscord",
libraryTarget: "commonjs2" libraryTarget: "commonjs2"
}, },
externals: { externals: {
electron: `electron`, electron: `electron`,
fs: `fs`, fs: `fs`,
path: `path`, path: `path`,
events: `events`, events: `events`,
rimraf: `rimraf`, rimraf: `rimraf`,
yauzl: `yauzl`, yauzl: `yauzl`,
mkdirp: `mkdirp`, mkdirp: `mkdirp`,
request: `request`, request: `request`,
"node-fetch": "node-fetch" "node-fetch": "node-fetch"
}, },
resolve: { resolve: {
extensions: [".js", ".jsx"], extensions: [".js", ".jsx"],
modules: [ modules: [
path.resolve("src", "builtins"), path.resolve("src", "builtins"),
path.resolve("src", "modules") path.resolve("src", "modules")
], ],
alias: { alias: {
react$: path.resolve(__dirname, "src", "react.js"), react$: path.resolve(__dirname, "src", "react.js"),
"react-dom$": path.resolve(__dirname, "src", "react-dom.js") "react-dom$": path.resolve(__dirname, "src", "react-dom.js")
} }
}, },
module: { module: {
rules: [ rules: [
{ {
test: /\.jsx?$/, test: /\.jsx?$/,
loader: "babel-loader", loader: "babel-loader",
exclude: /node_modules/, exclude: /node_modules/,
query: { query: {
presets: [["@babel/env", { presets: [["@babel/env", {
targets: { targets: {
node: "12.8.1", node: "12.8.1",
chrome: "78" chrome: "78"
} }
}], "@babel/react"] }], "@babel/react"]
} }
} }
] ]
}, },
plugins: [ plugins: [
new CircularDependencyPlugin({ new CircularDependencyPlugin({
// exclude detection of files based on a RegExp // exclude detection of files based on a RegExp
exclude: /a\.js|node_modules/, exclude: /a\.js|node_modules/,
// add errors to webpack instead of warnings // add errors to webpack instead of warnings
// failOnError: true, // failOnError: true,
// set the current working directory for displaying module paths // set the current working directory for displaying module paths
cwd: process.cwd(), cwd: process.cwd(),
}) })
], ],
optimization: { optimization: {
minimizer: [ minimizer: [
new TerserPlugin({ new TerserPlugin({
terserOptions: { terserOptions: {
compress: {drop_debugger:false} compress: {drop_debugger:false}
}, },
sourceMap: true sourceMap: true
}) })
] ]
} }
}; };

View File

@ -1,334 +1,334 @@
import { Snowflake, Channel } from ".." import { Snowflake, Channel } from ".."
import { DiscordGuild, channelsModule, guildModule, UserSettingsModule, ConstantsModule, CdnModule, AckModule } from "../util/DiscordToModules" import { DiscordGuild, channelsModule, guildModule, UserSettingsModule, ConstantsModule, CdnModule, AckModule } from "../util/DiscordToModules"
import BaseStructure from "./BaseStructure" import BaseStructure from "./BaseStructure"
import { createChannel, createGuildMember, createRole, UserResolvable, resolveUserID, ChannelData, ChannelCreationOverwrites } from "../util/util" import { createChannel, createGuildMember, createRole, UserResolvable, resolveUserID, ChannelData, ChannelCreationOverwrites } from "../util/util"
import Collection from "@discordjs/collection" import Collection from "@discordjs/collection"
import SnowflakeUtil from "../util/Snowflake" import SnowflakeUtil from "../util/Snowflake"
import GuildMember from "./GuildMember" import GuildMember from "./GuildMember"
import { MessageNotificationType, ChannelTypes } from "../util/Constants" import { MessageNotificationType, ChannelTypes } from "../util/Constants"
import Role from "./Role" import Role from "./Role"
import DiscordJSError from "../util/DiscordJSError" import DiscordJSError from "../util/DiscordJSError"
import PermissionOverwrites from "./PermissionOverwrites" import PermissionOverwrites from "./PermissionOverwrites"
export default class Guild extends BaseStructure { export default class Guild extends BaseStructure {
DiscordGuild:DiscordGuild DiscordGuild:DiscordGuild
constructor(data:DiscordGuild){ constructor(data:DiscordGuild){
super() super()
this.DiscordGuild = data this.DiscordGuild = data
} }
get id(): Snowflake{ get id(): Snowflake{
return this.DiscordGuild.id return this.DiscordGuild.id
} }
get afkChannel():Channel{ get afkChannel():Channel{
if(!this.afkChannelID)return null if(!this.afkChannelID)return null
return createChannel(channelsModule.getChannel(this.afkChannelID)) return createChannel(channelsModule.getChannel(this.afkChannelID))
} }
get afkChannelID():Snowflake{ get afkChannelID():Snowflake{
return this.DiscordGuild.afkChannelId return this.DiscordGuild.afkChannelId
} }
get afkTimeout():number{ get afkTimeout():number{
return this.DiscordGuild.afkTimeout return this.DiscordGuild.afkTimeout
} }
get applicationID():Snowflake{ get applicationID():Snowflake{
return this.DiscordGuild.application_id return this.DiscordGuild.application_id
} }
get available():boolean{ get available():boolean{
return true return true
} }
get channels():Collection<Snowflake, Channel>{{ get channels():Collection<Snowflake, Channel>{{
return this.client.channels.filter(channel => channel.guild_id === this.id) return this.client.channels.filter(channel => channel.guild_id === this.id)
}} }}
get createdAt():Date{ get createdAt():Date{
return SnowflakeUtil.deconstruct(this.id).date return SnowflakeUtil.deconstruct(this.id).date
} }
get createdTimestamp():number{ get createdTimestamp():number{
return this.createdAt.getTime() return this.createdAt.getTime()
} }
get defaultChannel(){ get defaultChannel(){
return this.channels.get(this.id) return this.channels.get(this.id)
} }
get defaultMessageNotifications():number{ get defaultMessageNotifications():number{
return this.DiscordGuild.defaultMessageNotifications return this.DiscordGuild.defaultMessageNotifications
} }
deleted:boolean = false deleted:boolean = false
get embedEnabled(){ get embedEnabled(){
return true return true
} }
get emojis(){ get emojis(){
return this.client.emojis.filter(e => e.guild_id === this.id) return this.client.emojis.filter(e => e.guild_id === this.id)
} }
get explicitContentFilter(){ get explicitContentFilter(){
return this.DiscordGuild.explicitContentFilter return this.DiscordGuild.explicitContentFilter
} }
get features(){ get features(){
return Array.from(this.DiscordGuild.features) return Array.from(this.DiscordGuild.features)
} }
get icon(){ get icon(){
return this.DiscordGuild.icon return this.DiscordGuild.icon
} }
get iconURL(){ get iconURL(){
return this.DiscordGuild.getIconURL().replace(".webp", ".jpg") return this.DiscordGuild.getIconURL().replace(".webp", ".jpg")
} }
get joinedAt(){ get joinedAt(){
return new Date(this.DiscordGuild.joinedAt) return new Date(this.DiscordGuild.joinedAt)
} }
get joinedTimestamp(){ get joinedTimestamp(){
return this.DiscordGuild.joinedAt.getTime() return this.DiscordGuild.joinedAt.getTime()
} }
get large(){ get large(){
return false return false
} }
get me(){ get me(){
return this.members.find(member => member.id === this.client.user.id) return this.members.find(member => member.id === this.client.user.id)
} }
get memberCount(){ get memberCount(){
return guildModule.getMemberCount(this.id) return guildModule.getMemberCount(this.id)
} }
get members(){ get members(){
return new Collection<Snowflake, GuildMember>(guildModule.getMembers(this.id).map(member => [member.userId, createGuildMember(member)])) return new Collection<Snowflake, GuildMember>(guildModule.getMembers(this.id).map(member => [member.userId, createGuildMember(member)]))
} }
get messageNotifications():MessageNotificationType{ get messageNotifications():MessageNotificationType{
return MessageNotificationType[guildModule.getMessageNotifications(this.id)] as unknown as MessageNotificationType return MessageNotificationType[guildModule.getMessageNotifications(this.id)] as unknown as MessageNotificationType
} }
get mfaLevel(){ get mfaLevel(){
return this.DiscordGuild.mfaLevel return this.DiscordGuild.mfaLevel
} }
get mobilePush():boolean{ get mobilePush():boolean{
return guildModule.getNotificationsState().userGuildSettings[this.id].mobile_push return guildModule.getNotificationsState().userGuildSettings[this.id].mobile_push
} }
get muted(){ get muted(){
return guildModule.getNotificationsState().userGuildSettings[this.id].muted return guildModule.getNotificationsState().userGuildSettings[this.id].muted
} }
get name(){ get name(){
return this.DiscordGuild.name return this.DiscordGuild.name
} }
get nameAcronym(){ get nameAcronym(){
return this.DiscordGuild.acronym return this.DiscordGuild.acronym
} }
get owner(){ get owner(){
return this.members.get(this.ownerID) return this.members.get(this.ownerID)
} }
get ownerID(){ get ownerID(){
return this.DiscordGuild.ownerId return this.DiscordGuild.ownerId
} }
get position(){ get position(){
let guildPositions = UserSettingsModule.getAllSettings().guildPositions let guildPositions = UserSettingsModule.getAllSettings().guildPositions
if(!guildPositions)return 0 if(!guildPositions)return 0
return guildPositions.indexOf(this.id) return guildPositions.indexOf(this.id)
} }
get presences(){ // TODO: Add activities. get presences(){ // TODO: Add activities.
return new Collection() return new Collection()
} }
get region(){ get region(){
return this.DiscordGuild.region return this.DiscordGuild.region
} }
get roles(){ get roles(){
return new Collection<Snowflake, Role>(Object.values(this.DiscordGuild.roles).map(role => [role.id, createRole(role)])) return new Collection<Snowflake, Role>(Object.values(this.DiscordGuild.roles).map(role => [role.id, createRole(role)]))
} }
get splash(){ get splash(){
return this.DiscordGuild.splash return this.DiscordGuild.splash
} }
get splashURL(){ get splashURL(){
return CdnModule.getGuildSplashURL({ return CdnModule.getGuildSplashURL({
id: this.id, id: this.id,
splash: this.splash, splash: this.splash,
size: ConstantsModule.SPLASH_SIZE size: ConstantsModule.SPLASH_SIZE
}) })
} }
get suppressEveryone(){ get suppressEveryone(){
return guildModule.getNotificationsState().userGuildSettings[this.id].suppress_everyone return guildModule.getNotificationsState().userGuildSettings[this.id].suppress_everyone
} }
get systemChannel(){ get systemChannel(){
return this.client.channels.get(this.systemChannelID) return this.client.channels.get(this.systemChannelID)
} }
get systemChannelID(){ get systemChannelID(){
return this.DiscordGuild.systemChannelId return this.DiscordGuild.systemChannelId
} }
get verificationLevel(){ get verificationLevel(){
return this.DiscordGuild.verificationLevel return this.DiscordGuild.verificationLevel
} }
get verified(){ get verified(){
return this.features.includes("VERIFIED") return this.features.includes("VERIFIED")
} }
get voiceConnection(){// TODO: Implement if possible VoiceConnection. Maybe not fully like discord.js, but at least can see if you're connected. get voiceConnection(){// TODO: Implement if possible VoiceConnection. Maybe not fully like discord.js, but at least can see if you're connected.
return null return null
} }
get banner(){ get banner(){
return this.DiscordGuild.banner return this.DiscordGuild.banner
} }
get bannerURL(){ get bannerURL(){
return CdnModule.getGuildBannerURL({ return CdnModule.getGuildBannerURL({
id: this.id, id: this.id,
banner: this.banner banner: this.banner
}) })
} }
get description(){ get description(){
return this.DiscordGuild.description return this.DiscordGuild.description
} }
get embedChannel(){ // TODO: Implement embedChannel get embedChannel(){ // TODO: Implement embedChannel
return null return null
} }
get embedChannelID(){ // TODO: Implement embedChannelID get embedChannelID(){ // TODO: Implement embedChannelID
return null return null
} }
get maximumMembers(){ // TODO: Correctly Implement maximumMembers if possible get maximumMembers(){ // TODO: Correctly Implement maximumMembers if possible
return 250000 return 250000
} }
get maximumPresences(){ // TODO: Correctly Implement maximumPresences if possible get maximumPresences(){ // TODO: Correctly Implement maximumPresences if possible
return 5000 return 5000
} }
get widgetEnabled(){ // TODO: Correctly Implement widgetEnabled if possible get widgetEnabled(){ // TODO: Correctly Implement widgetEnabled if possible
return false return false
} }
get widgetChannelID(){ // TODO: Correctly Implement widgetChannelID if possible get widgetChannelID(){ // TODO: Correctly Implement widgetChannelID if possible
return null return null
} }
get widgetChannel(){ // TODO: Correctly Implement widgetChannel if possible get widgetChannel(){ // TODO: Correctly Implement widgetChannel if possible
return null return null
} }
get vanityURLCode(){ get vanityURLCode(){
return this.DiscordGuild.vanityURLCode return this.DiscordGuild.vanityURLCode
} }
/** FUNCTIONS */ /** FUNCTIONS */
async acknowledge(){ async acknowledge(){
AckModule.bulkAck(this.channels.filter(e => e.type === "text").map(e => { AckModule.bulkAck(this.channels.filter(e => e.type === "text").map(e => {
return { return {
channelId: e.id, channelId: e.id,
messageId: channelsModule.lastMessageId(e.id) messageId: channelsModule.lastMessageId(e.id)
} }
})) }))
} }
addMember(...args:any):Promise<GuildMember>{ addMember(...args:any):Promise<GuildMember>{
return Promise.reject(new DiscordJSError("This method is not available on Lightcord.")) return Promise.reject(new DiscordJSError("This method is not available on Lightcord."))
} }
allowDMs(allow:boolean){ allowDMs(allow:boolean){
let restricted = UserSettingsModule.getAllSettings().restrictedGuilds let restricted = UserSettingsModule.getAllSettings().restrictedGuilds
if(allow){ if(allow){
if(!restricted.includes(this.id))return Promise.resolve(this) if(!restricted.includes(this.id))return Promise.resolve(this)
restricted = restricted.filter(e => e !== this.id) restricted = restricted.filter(e => e !== this.id)
}else{ }else{
if(restricted.includes(this.id))return Promise.resolve(this) if(restricted.includes(this.id))return Promise.resolve(this)
restricted.push(this.id) restricted.push(this.id)
} }
return UserSettingsModule.updateRemoteSettings({ return UserSettingsModule.updateRemoteSettings({
restrictedGuilds: restricted restrictedGuilds: restricted
}).then(() => this) }).then(() => this)
} }
async ban(user:UserResolvable, { async ban(user:UserResolvable, {
days = 0, days = 0,
reason = null reason = null
}: { }: {
days?: number, days?: number,
reason?: string reason?: string
} = {}):Promise<Snowflake>{ // always returning a snowflake } = {}):Promise<Snowflake>{ // always returning a snowflake
let id = resolveUserID(user) let id = resolveUserID(user)
if(!id)return Promise.reject(new DiscordJSError("Given user could not be resolved to an user ID.")) if(!id)return Promise.reject(new DiscordJSError("Given user could not be resolved to an user ID."))
let result = await guildModule.banUser(this.id, id, days, reason).catch(err => err) let result = await guildModule.banUser(this.id, id, days, reason).catch(err => err)
if(result instanceof Error || result.status !== 204){ if(result instanceof Error || result.status !== 204){
let message = result.body let message = result.body
if(Array.isArray(message)){ if(Array.isArray(message)){
message = message[0] message = message[0]
}else{ }else{
if(message.user_id){ if(message.user_id){
message = "User: "+ message.user_id[0] message = "User: "+ message.user_id[0]
}else if(message.delete_message_days){ }else if(message.delete_message_days){
message = "Days: "+ message.delete_message_days[0] message = "Days: "+ message.delete_message_days[0]
}else if(message.reason){ }else if(message.reason){
message = "Reason: "+ message.reason[0] message = "Reason: "+ message.reason[0]
}else{ }else{
message = result.text message = result.text
} }
} }
throw new DiscordJSError(message) throw new DiscordJSError(message)
} }
return id return id
} }
createChannel(name:string, typeOrOptions:ChannelTypes|ChannelData = ChannelTypes.TEXT, permissionOverwrites?: ChannelCreationOverwrites[] | Collection<Snowflake, PermissionOverwrites>, reason?: string){ createChannel(name:string, typeOrOptions:ChannelTypes|ChannelData = ChannelTypes.TEXT, permissionOverwrites?: ChannelCreationOverwrites[] | Collection<Snowflake, PermissionOverwrites>, reason?: string){
const [opts, type] = typeof typeOrOptions === "string" ? [ const [opts, type] = typeof typeOrOptions === "string" ? [
{}, {},
typeOrOptions typeOrOptions
] : [ ] : [
typeOrOptions, typeOrOptions,
typeOrOptions.type typeOrOptions.type
] ]
const options = { const options = {
name: name || "Unknown Channel", name: name || "Unknown Channel",
type: type || "text" type: type || "text"
} }
//TODO: CreateGuildChannels and handle permissions //TODO: CreateGuildChannels and handle permissions
//channelsModule.createGuildChannel(this.id, options.type, optinos.name) //channelsModule.createGuildChannel(this.id, options.type, optinos.name)
} }
fetch():Promise<Guild>{ // Guild is synced by Discord. Only refreshing from cache. fetch():Promise<Guild>{ // Guild is synced by Discord. Only refreshing from cache.
let guild = guildModule.getGuild(this.id) let guild = guildModule.getGuild(this.id)
if(!guild){ if(!guild){
this.deleted = true this.deleted = true
return Promise.resolve(this) return Promise.resolve(this)
} }
this.DiscordGuild = guild this.DiscordGuild = guild
return Promise.resolve(this) return Promise.resolve(this)
} }
} }

View File

@ -1,61 +1,61 @@
import Permissions from '../util/Permissions'; import Permissions from '../util/Permissions';
import GuildChannel from './GuildChannel'; import GuildChannel from './GuildChannel';
import { Snowflake } from '..'; import { Snowflake } from '..';
/** /**
* Represents a permission overwrite for a role or member in a guild channel. * Represents a permission overwrite for a role or member in a guild channel.
*/ */
export default class PermissionOverwrites { export default class PermissionOverwrites {
id: Snowflake; id: Snowflake;
type: "role"|"member"; type: "role"|"member";
deny: number; deny: number;
allow: number; allow: number;
channel: GuildChannel; channel: GuildChannel;
constructor(guildChannel:GuildChannel, data) { constructor(guildChannel:GuildChannel, data) {
/** /**
* The GuildChannel this overwrite is for * The GuildChannel this overwrite is for
* @name PermissionOverwrites#channel * @name PermissionOverwrites#channel
* @type {GuildChannel} * @type {GuildChannel}
* @readonly * @readonly
*/ */
Object.defineProperty(this, 'channel', { value: guildChannel }); Object.defineProperty(this, 'channel', { value: guildChannel });
if (data) this.setup(data); if (data) this.setup(data);
} }
setup(data) { setup(data) {
/** /**
* The ID of this overwrite, either a user ID or a role ID * The ID of this overwrite, either a user ID or a role ID
* @type {Snowflake} * @type {Snowflake}
*/ */
this.id = data.id; this.id = data.id;
/** /**
* The type of this overwrite * The type of this overwrite
* @type {string} * @type {string}
*/ */
this.type = data.type; this.type = data.type;
/** /**
* The permissions that are denied for the user or role as a bitfield. * The permissions that are denied for the user or role as a bitfield.
* @type {number} * @type {number}
*/ */
this.deny = data.deny; this.deny = data.deny;
/** /**
* The permissions that are allowed for the user or role as a bitfield. * The permissions that are allowed for the user or role as a bitfield.
* @type {number} * @type {number}
*/ */
this.allow = data.allow; this.allow = data.allow;
} }
/** /**
* Delete this Permission Overwrite. * Delete this Permission Overwrite.
* @param {string} [reason] Reason for deleting this overwrite * @param {string} [reason] Reason for deleting this overwrite
* @returns {Promise<PermissionOverwrites>} * @returns {Promise<PermissionOverwrites>}
*/ */
delete(reason) { delete(reason) {
// TODO: Delete permission overwrites // TODO: Delete permission overwrites
//return this.channel.client.rest.methods.deletePermissionOverwrites(this, reason); //return this.channel.client.rest.methods.deletePermissionOverwrites(this, reason);
} }
} }

View File

@ -1,32 +1,32 @@
import Permissions from '../../util/Permissions'; import Permissions from '../../util/Permissions';
import Collection from '@discordjs/collection'; import Collection from '@discordjs/collection';
import Guild from '../Guild'; import Guild from '../Guild';
import { ChannelCreationOverwrites } from '../../util/util'; import { ChannelCreationOverwrites } from '../../util/util';
import { Snowflake } from '../..'; import { Snowflake } from '../..';
import PermissionOverwrites from '../PermissionOverwrites'; import PermissionOverwrites from '../PermissionOverwrites';
module.exports = function resolvePermissions(overwrites: ChannelCreationOverwrites[] | Collection<Snowflake, PermissionOverwrites>, guild:Guild) { module.exports = function resolvePermissions(overwrites: ChannelCreationOverwrites[] | Collection<Snowflake, PermissionOverwrites>, guild:Guild) {
if (overwrites instanceof Collection || overwrites instanceof Array) { if (overwrites instanceof Collection || overwrites instanceof Array) {
//TODO: Remove ts-ignore //TODO: Remove ts-ignore
// @ts-ignore // @ts-ignore
overwrites = (Array.isArray(overwrites) ? overwrites : overwrites.array()).map(overwrite => { overwrites = (Array.isArray(overwrites) ? overwrites : overwrites.array()).map(overwrite => {
const role = this.client.resolver.resolveRole(guild, overwrite.id); const role = this.client.resolver.resolveRole(guild, overwrite.id);
if (role) { if (role) {
overwrite.id = role.id; overwrite.id = role.id;
overwrite.type = 'role'; overwrite.type = 'role';
} else { } else {
overwrite.id = this.client.resolver.resolveUserID(overwrite.id); overwrite.id = this.client.resolver.resolveUserID(overwrite.id);
overwrite.type = 'member'; overwrite.type = 'member';
} }
return { return {
allow: Permissions.resolve(overwrite.allow || overwrite.allowed || 0), allow: Permissions.resolve(overwrite.allow || overwrite.allowed || 0),
deny: Permissions.resolve(overwrite.deny || overwrite.denied || 0), deny: Permissions.resolve(overwrite.deny || overwrite.denied || 0),
type: overwrite.type, type: overwrite.type,
id: overwrite.id, id: overwrite.id,
}; };
}); });
} }
return overwrites; return overwrites;
}; };

View File

@ -1,210 +1,210 @@
import BitField from "./BitField"; import BitField from "./BitField";
import * as util from "util"; import * as util from "util";
/** /**
* Data structure that makes it easy to interact with a permission bitfield. All {@link GuildMember}s have a set of * Data structure that makes it easy to interact with a permission bitfield. All {@link GuildMember}s have a set of
* permissions in their guild, and each channel in the guild may also have {@link PermissionOverwrites} for the member * permissions in their guild, and each channel in the guild may also have {@link PermissionOverwrites} for the member
* that override their default permissions. * that override their default permissions.
* @extends {BitField} * @extends {BitField}
*/ */
export default class Permissions extends BitField { export default class Permissions extends BitField {
/** /**
* @param {number|PermissionResolvable} permissions Permissions or bitfield to read from * @param {number|PermissionResolvable} permissions Permissions or bitfield to read from
*/ */
constructor(permissions: number | PermissionResolvable) { constructor(permissions: number | PermissionResolvable) {
super(permissions); super(permissions);
} }
/** /**
* Bitfield of the packed permissions * Bitfield of the packed permissions
* @type {number} * @type {number}
* @see {@link Permissions#bitfield} * @see {@link Permissions#bitfield}
* @deprecated * @deprecated
* @readonly * @readonly
*/ */
get raw() { get raw() {
return this.bitfield; return this.bitfield;
} }
set raw(raw) { set raw(raw) {
this.bitfield = raw; this.bitfield = raw;
} }
/** /**
* Checks whether the bitfield has a permission, or any of multiple permissions. * Checks whether the bitfield has a permission, or any of multiple permissions.
* @param {PermissionResolvable} permission Permission(s) to check for * @param {PermissionResolvable} permission Permission(s) to check for
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override * @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
* @returns {boolean} * @returns {boolean}
*/ */
any(permission: PermissionResolvable, checkAdmin: boolean = true): boolean { any(permission: PermissionResolvable, checkAdmin: boolean = true): boolean {
return ( return (
(checkAdmin && super.has((this.constructor as any).FLAGS.ADMINISTRATOR)) || (checkAdmin && super.has((this.constructor as any).FLAGS.ADMINISTRATOR)) ||
super.any(permission) super.any(permission)
); );
} }
/** /**
* Checks whether the bitfield has a permission, or multiple permissions. * Checks whether the bitfield has a permission, or multiple permissions.
* @param {PermissionResolvable} permission Permission(s) to check for * @param {PermissionResolvable} permission Permission(s) to check for
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override * @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
* @returns {boolean} * @returns {boolean}
*/ */
has(permission: PermissionResolvable, checkAdmin: boolean = true): boolean { has(permission: PermissionResolvable, checkAdmin: boolean = true): boolean {
return ( return (
(checkAdmin && super.has((this.constructor as any).FLAGS.ADMINISTRATOR)) || (checkAdmin && super.has((this.constructor as any).FLAGS.ADMINISTRATOR)) ||
super.has(permission) super.has(permission)
); );
} }
/** /**
* Numeric permission flags. All available properties: * Numeric permission flags. All available properties:
* - `ADMINISTRATOR` (implicitly has *all* permissions, and bypasses all channel overwrites) * - `ADMINISTRATOR` (implicitly has *all* permissions, and bypasses all channel overwrites)
* - `CREATE_INSTANT_INVITE` (create invitations to the guild) * - `CREATE_INSTANT_INVITE` (create invitations to the guild)
* - `KICK_MEMBERS` * - `KICK_MEMBERS`
* - `BAN_MEMBERS` * - `BAN_MEMBERS`
* - `MANAGE_CHANNELS` (edit and reorder channels) * - `MANAGE_CHANNELS` (edit and reorder channels)
* - `MANAGE_GUILD` (edit the guild information, region, etc.) * - `MANAGE_GUILD` (edit the guild information, region, etc.)
* - `ADD_REACTIONS` (add new reactions to messages) * - `ADD_REACTIONS` (add new reactions to messages)
* - `VIEW_AUDIT_LOG` * - `VIEW_AUDIT_LOG`
* - `PRIORITY_SPEAKER` * - `PRIORITY_SPEAKER`
* - `STREAM` * - `STREAM`
* - `VIEW_CHANNEL` * - `VIEW_CHANNEL`
* - `READ_MESSAGES` **(deprecated)** * - `READ_MESSAGES` **(deprecated)**
* - `SEND_MESSAGES` * - `SEND_MESSAGES`
* - `SEND_TTS_MESSAGES` * - `SEND_TTS_MESSAGES`
* - `MANAGE_MESSAGES` (delete messages and reactions) * - `MANAGE_MESSAGES` (delete messages and reactions)
* - `EMBED_LINKS` (links posted will have a preview embedded) * - `EMBED_LINKS` (links posted will have a preview embedded)
* - `ATTACH_FILES` * - `ATTACH_FILES`
* - `READ_MESSAGE_HISTORY` (view messages that were posted prior to opening Discord) * - `READ_MESSAGE_HISTORY` (view messages that were posted prior to opening Discord)
* - `MENTION_EVERYONE` * - `MENTION_EVERYONE`
* - `USE_EXTERNAL_EMOJIS` (use emojis from different guilds) * - `USE_EXTERNAL_EMOJIS` (use emojis from different guilds)
* - `EXTERNAL_EMOJIS` **(deprecated)** * - `EXTERNAL_EMOJIS` **(deprecated)**
* - `CONNECT` (connect to a voice channel) * - `CONNECT` (connect to a voice channel)
* - `SPEAK` (speak in a voice channel) * - `SPEAK` (speak in a voice channel)
* - `MUTE_MEMBERS` (mute members across all voice channels) * - `MUTE_MEMBERS` (mute members across all voice channels)
* - `DEAFEN_MEMBERS` (deafen members across all voice channels) * - `DEAFEN_MEMBERS` (deafen members across all voice channels)
* - `MOVE_MEMBERS` (move members between voice channels) * - `MOVE_MEMBERS` (move members between voice channels)
* - `USE_VAD` (use voice activity detection) * - `USE_VAD` (use voice activity detection)
* - `CHANGE_NICKNAME` * - `CHANGE_NICKNAME`
* - `MANAGE_NICKNAMES` (change other members' nicknames) * - `MANAGE_NICKNAMES` (change other members' nicknames)
* - `MANAGE_ROLES` * - `MANAGE_ROLES`
* - `MANAGE_ROLES_OR_PERMISSIONS` **(deprecated)** * - `MANAGE_ROLES_OR_PERMISSIONS` **(deprecated)**
* - `MANAGE_WEBHOOKS` * - `MANAGE_WEBHOOKS`
* - `MANAGE_EMOJIS` * - `MANAGE_EMOJIS`
* @type {Object} * @type {Object}
* @see {@link https://discordapp.com/developers/docs/topics/permissions} * @see {@link https://discordapp.com/developers/docs/topics/permissions}
*/ */
static FLAGS:{ static FLAGS:{
CREATE_INSTANT_INVITE: number, CREATE_INSTANT_INVITE: number,
KICK_MEMBERS: number, KICK_MEMBERS: number,
BAN_MEMBERS: number, BAN_MEMBERS: number,
ADMINISTRATOR: number, ADMINISTRATOR: number,
MANAGE_CHANNELS: number, MANAGE_CHANNELS: number,
MANAGE_GUILD: number, MANAGE_GUILD: number,
ADD_REACTIONS: number, ADD_REACTIONS: number,
VIEW_AUDIT_LOG: number, VIEW_AUDIT_LOG: number,
PRIORITY_SPEAKER: number, PRIORITY_SPEAKER: number,
STREAM: number, STREAM: number,
VIEW_CHANNEL: number, VIEW_CHANNEL: number,
READ_MESSAGES: number, READ_MESSAGES: number,
SEND_MESSAGES: number, SEND_MESSAGES: number,
SEND_TTS_MESSAGES: number, SEND_TTS_MESSAGES: number,
MANAGE_MESSAGES: number, MANAGE_MESSAGES: number,
EMBED_LINKS: number, EMBED_LINKS: number,
ATTACH_FILES: number, ATTACH_FILES: number,
READ_MESSAGE_HISTORY: number, READ_MESSAGE_HISTORY: number,
MENTION_EVERYONE: number, MENTION_EVERYONE: number,
EXTERNAL_EMOJIS: number, EXTERNAL_EMOJIS: number,
USE_EXTERNAL_EMOJIS: number, USE_EXTERNAL_EMOJIS: number,
CONNECT: number, CONNECT: number,
SPEAK: number, SPEAK: number,
MUTE_MEMBERS: number, MUTE_MEMBERS: number,
DEAFEN_MEMBERS: number, DEAFEN_MEMBERS: number,
MOVE_MEMBERS: number, MOVE_MEMBERS: number,
USE_VAD: number, USE_VAD: number,
CHANGE_NICKNAME: number, CHANGE_NICKNAME: number,
MANAGE_NICKNAMES: number, MANAGE_NICKNAMES: number,
MANAGE_ROLES: number, MANAGE_ROLES: number,
MANAGE_ROLES_OR_PERMISSIONS: number, MANAGE_ROLES_OR_PERMISSIONS: number,
MANAGE_WEBHOOKS: number, MANAGE_WEBHOOKS: number,
MANAGE_EMOJIS: number, MANAGE_EMOJIS: number,
} = { } = {
CREATE_INSTANT_INVITE: 1 << 0, CREATE_INSTANT_INVITE: 1 << 0,
KICK_MEMBERS: 1 << 1, KICK_MEMBERS: 1 << 1,
BAN_MEMBERS: 1 << 2, BAN_MEMBERS: 1 << 2,
ADMINISTRATOR: 1 << 3, ADMINISTRATOR: 1 << 3,
MANAGE_CHANNELS: 1 << 4, MANAGE_CHANNELS: 1 << 4,
MANAGE_GUILD: 1 << 5, MANAGE_GUILD: 1 << 5,
ADD_REACTIONS: 1 << 6, ADD_REACTIONS: 1 << 6,
VIEW_AUDIT_LOG: 1 << 7, VIEW_AUDIT_LOG: 1 << 7,
PRIORITY_SPEAKER: 1 << 8, PRIORITY_SPEAKER: 1 << 8,
STREAM: 1 << 9, STREAM: 1 << 9,
VIEW_CHANNEL: 1 << 10, VIEW_CHANNEL: 1 << 10,
READ_MESSAGES: 1 << 10, READ_MESSAGES: 1 << 10,
SEND_MESSAGES: 1 << 11, SEND_MESSAGES: 1 << 11,
SEND_TTS_MESSAGES: 1 << 12, SEND_TTS_MESSAGES: 1 << 12,
MANAGE_MESSAGES: 1 << 13, MANAGE_MESSAGES: 1 << 13,
EMBED_LINKS: 1 << 14, EMBED_LINKS: 1 << 14,
ATTACH_FILES: 1 << 15, ATTACH_FILES: 1 << 15,
READ_MESSAGE_HISTORY: 1 << 16, READ_MESSAGE_HISTORY: 1 << 16,
MENTION_EVERYONE: 1 << 17, MENTION_EVERYONE: 1 << 17,
EXTERNAL_EMOJIS: 1 << 18, EXTERNAL_EMOJIS: 1 << 18,
USE_EXTERNAL_EMOJIS: 1 << 18, USE_EXTERNAL_EMOJIS: 1 << 18,
CONNECT: 1 << 20, CONNECT: 1 << 20,
SPEAK: 1 << 21, SPEAK: 1 << 21,
MUTE_MEMBERS: 1 << 22, MUTE_MEMBERS: 1 << 22,
DEAFEN_MEMBERS: 1 << 23, DEAFEN_MEMBERS: 1 << 23,
MOVE_MEMBERS: 1 << 24, MOVE_MEMBERS: 1 << 24,
USE_VAD: 1 << 25, USE_VAD: 1 << 25,
CHANGE_NICKNAME: 1 << 26, CHANGE_NICKNAME: 1 << 26,
MANAGE_NICKNAMES: 1 << 27, MANAGE_NICKNAMES: 1 << 27,
MANAGE_ROLES: 1 << 28, MANAGE_ROLES: 1 << 28,
MANAGE_ROLES_OR_PERMISSIONS: 1 << 28, MANAGE_ROLES_OR_PERMISSIONS: 1 << 28,
MANAGE_WEBHOOKS: 1 << 29, MANAGE_WEBHOOKS: 1 << 29,
MANAGE_EMOJIS: 1 << 30, MANAGE_EMOJIS: 1 << 30,
}; };
/** /**
* Bitfield representing every permission combined * Bitfield representing every permission combined
* @type {number} * @type {number}
*/ */
static ALL: number = Object.keys(Permissions.FLAGS).reduce( static ALL: number = Object.keys(Permissions.FLAGS).reduce(
(all, p) => all | Permissions.FLAGS[p], (all, p) => all | Permissions.FLAGS[p],
0 0
); );
/** /**
* Bitfield representing the default permissions for users * Bitfield representing the default permissions for users
* @type {number} * @type {number}
*/ */
static DEFAULT: number = 104324673; static DEFAULT: number = 104324673;
} }
Object.defineProperty(Permissions.prototype, "raw", { Object.defineProperty(Permissions.prototype, "raw", {
get: util.deprecate( get: util.deprecate(
Object.getOwnPropertyDescriptor(Permissions.prototype, "raw").get, Object.getOwnPropertyDescriptor(Permissions.prototype, "raw").get,
"EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead" "EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead"
), ),
set: util.deprecate( set: util.deprecate(
Object.getOwnPropertyDescriptor(Permissions.prototype, "raw").set, Object.getOwnPropertyDescriptor(Permissions.prototype, "raw").set,
"EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead" "EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead"
), ),
}); });
/** /**
* Data that can be resolved to give a permission number. This can be: * Data that can be resolved to give a permission number. This can be:
* * A string (see {@link Permissions.FLAGS}) * * A string (see {@link Permissions.FLAGS})
* * A permission number * * A permission number
* @typedef {string|number|Permissions|PermissionResolvable[]} PermissionResolvable * @typedef {string|number|Permissions|PermissionResolvable[]} PermissionResolvable
*/ */
export type PermissionResolvable = export type PermissionResolvable =
| keyof typeof Permissions.FLAGS | keyof typeof Permissions.FLAGS
| number | number
| Permissions | Permissions
| PermissionResolvable[]; | PermissionResolvable[];

View File

@ -1,69 +1,69 @@
import {requireModule, DiscordChannel} from "../DiscordToModules" import {requireModule, DiscordChannel} from "../DiscordToModules"
import { Snowflake } from "../.." import { Snowflake } from "../.."
let channelsModuleInternal1:{ let channelsModuleInternal1:{
getChannel(id:Snowflake): DiscordChannel, getChannel(id:Snowflake): DiscordChannel,
getChannels(): { getChannels(): {
[k:string]: DiscordChannel [k:string]: DiscordChannel
}, },
getDMFromUserId(id:string):Snowflake, getDMFromUserId(id:string):Snowflake,
getDMUserIds():Snowflake[], getDMUserIds():Snowflake[],
getFollowerStatsForChannel(id: Snowflake):{ getFollowerStatsForChannel(id: Snowflake):{
loadingStatus: "succeeded"|"failed", loadingStatus: "succeeded"|"failed",
lastFetched: number, lastFetched: number,
channelsFollowing: number, channelsFollowing: number,
guildMembers: number, guildMembers: number,
guildsFollowing: number, guildsFollowing: number,
usersSeenEver: number, usersSeenEver: number,
subscribersGainedSinceLastPost: number, subscribersGainedSinceLastPost: number,
subscribersLostSinceLastPost: number subscribersLostSinceLastPost: number
}, },
getGDMsForRecipients(recipients: Snowflake[]):Set<Snowflake> getGDMsForRecipients(recipients: Snowflake[]):Set<Snowflake>
} = requireModule(e => e.default && e.default.getChannels && e.default.getChannel) } = requireModule(e => e.default && e.default.getChannels && e.default.getChannel)
let channelsModuleInternal2:{ let channelsModuleInternal2:{
deleteChannel(id: Snowflake):void deleteChannel(id: Snowflake):void
} }
let channelsModuleInternal4:{ let channelsModuleInternal4:{
createChannel(guildId:Snowflake, type:string, name:string, createChannel(guildId:Snowflake, type:string, name:string,
permissionOverwrites: { permissionOverwrites: {
id: Snowflake, id: Snowflake,
type: "role"|"member", type: "role"|"member",
allow: number, allow: number,
deny: number deny: number
}[], bitrate:number, userLimit:number, parentId:Snowflake, skuId:Snowflake, branchId:Snowflake):void }[], bitrate:number, userLimit:number, parentId:Snowflake, skuId:Snowflake, branchId:Snowflake):void
} = requireModule(e => e.default && e.default.createChannel) } = requireModule(e => e.default && e.default.createChannel)
let channelsModuleInternal3:{ let channelsModuleInternal3:{
hasUnread(channel_id:Snowflake):boolean, hasUnread(channel_id:Snowflake):boolean,
hasCategoryUnread(channel_id:Snowflake):boolean, hasCategoryUnread(channel_id:Snowflake):boolean,
getUnreadCount(channel_id:Snowflake):number, getUnreadCount(channel_id:Snowflake):number,
getMentionCount(channel_id:Snowflake):number, getMentionCount(channel_id:Snowflake):number,
ackMessageId(channel_id:Snowflake):Snowflake, ackMessageId(channel_id:Snowflake):Snowflake,
lastMessageId(channel_id:Snowflake):Snowflake, lastMessageId(channel_id:Snowflake):Snowflake,
getOldestUnreadMessageId(channel_id:Snowflake):Snowflake, getOldestUnreadMessageId(channel_id:Snowflake):Snowflake,
getOldestUnreadTimestamp(channel_id:Snowflake):number, getOldestUnreadTimestamp(channel_id:Snowflake):number,
isEstimated(channel_id:Snowflake):boolean, isEstimated(channel_id:Snowflake):boolean,
hasUnreadPins(channel_id:Snowflake):boolean, hasUnreadPins(channel_id:Snowflake):boolean,
getAllReadStates() getAllReadStates()
} = requireModule(e => e.default && e.default.lastMessageId) } = requireModule(e => e.default && e.default.lastMessageId)
function set3(){ function set3(){
if(channelsModuleInternal3)return if(channelsModuleInternal3)return
channelsModuleInternal3 = requireModule(e => e.default && e.default.lastMessageId) channelsModuleInternal3 = requireModule(e => e.default && e.default.lastMessageId)
} }
export = { export = {
getChannel: channelsModuleInternal1.getChannel, getChannel: channelsModuleInternal1.getChannel,
getAllChannels: channelsModuleInternal1.getChannels, getAllChannels: channelsModuleInternal1.getChannels,
get delete():typeof channelsModuleInternal2.deleteChannel{ get delete():typeof channelsModuleInternal2.deleteChannel{
return channelsModuleInternal2 ? channelsModuleInternal2.deleteChannel : (channelsModuleInternal2 = requireModule(e => e.default && e.default.deleteChannel), channelsModuleInternal2.deleteChannel) return channelsModuleInternal2 ? channelsModuleInternal2.deleteChannel : (channelsModuleInternal2 = requireModule(e => e.default && e.default.deleteChannel), channelsModuleInternal2.deleteChannel)
}, },
get lastMessageId(){ get lastMessageId(){
set3() set3()
return channelsModuleInternal3.lastMessageId return channelsModuleInternal3.lastMessageId
}, },
createGuildChannel: channelsModuleInternal4.createChannel createGuildChannel: channelsModuleInternal4.createChannel
} }

View File

@ -1,149 +1,149 @@
import { DiscordChannel, DiscordGuild, DiscordGuildMember, DiscordRole, DiscordMessage, DiscordUser } from "./DiscordToModules" import { DiscordChannel, DiscordGuild, DiscordGuildMember, DiscordRole, DiscordMessage, DiscordUser } from "./DiscordToModules"
import { Channel, Snowflake } from ".."; import { Channel, Snowflake } from "..";
import BaseChannel from "../structures/BaseChannel"; import BaseChannel from "../structures/BaseChannel";
import Guild from "../structures/Guild"; import Guild from "../structures/Guild";
import TextChannel from "../structures/TextChannel"; import TextChannel from "../structures/TextChannel";
import GuildMember from "../structures/GuildMember"; import GuildMember from "../structures/GuildMember";
import Role from "../structures/Role"; import Role from "../structures/Role";
import User from "../structures/User"; import User from "../structures/User";
import Message from "../structures/Message"; import Message from "../structures/Message";
import { ChannelTypes } from "./Constants"; import { ChannelTypes } from "./Constants";
import CategoryChannel from "../structures/CategoryChannel"; import CategoryChannel from "../structures/CategoryChannel";
import Collection from "@discordjs/collection"; import Collection from "@discordjs/collection";
import Permissions, { PermissionResolvable } from "./Permissions"; import Permissions, { PermissionResolvable } from "./Permissions";
import PermissionOverwrites from "../structures/PermissionOverwrites"; import PermissionOverwrites from "../structures/PermissionOverwrites";
export function createChannel(channel:DiscordChannel):Channel{ export function createChannel(channel:DiscordChannel):Channel{
let constructor = channels[channel.type] || BaseChannel let constructor = channels[channel.type] || BaseChannel
return new constructor(channel) return new constructor(channel)
} }
const channels:(new(channel:DiscordChannel) => Channel)[] = [ const channels:(new(channel:DiscordChannel) => Channel)[] = [
TextChannel TextChannel
] ]
export function createGuild(guild:DiscordGuild):Guild{ export function createGuild(guild:DiscordGuild):Guild{
return new Guild(guild) return new Guild(guild)
} }
export function createGuildMember(member:DiscordGuildMember):GuildMember{ export function createGuildMember(member:DiscordGuildMember):GuildMember{
return new GuildMember(member) return new GuildMember(member)
} }
export function createRole(role:DiscordRole):Role{ export function createRole(role:DiscordRole):Role{
return new Role(role) return new Role(role)
} }
export function createMessage(message:DiscordMessage):Message{ export function createMessage(message:DiscordMessage):Message{
return new Message(message) return new Message(message)
} }
export function createUser(user:DiscordUser):User{ export function createUser(user:DiscordUser):User{
return new User(user) return new User(user)
} }
export function applyMixins(derivedCtor: any, baseCtors: any[]) { export function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => { baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
if (name !== 'constructor') { if (name !== 'constructor') {
derivedCtor.prototype[name] = baseCtor.prototype[name]; derivedCtor.prototype[name] = baseCtor.prototype[name];
} }
}); });
}); });
} }
/** /**
* Transforms a snowflake from a decimal string to a bit string. * Transforms a snowflake from a decimal string to a bit string.
* @param {string} num Snowflake to be transformed * @param {string} num Snowflake to be transformed
* @returns {string} * @returns {string}
* @private * @private
*/ */
export function idToBinary(num:string):string{ export function idToBinary(num:string):string{
let bin = ''; let bin = '';
let high = parseInt(num.slice(0, -10)) || 0; let high = parseInt(num.slice(0, -10)) || 0;
let low = parseInt(num.slice(-10)); let low = parseInt(num.slice(-10));
while (low > 0 || high > 0) { while (low > 0 || high > 0) {
bin = String(low & 1) + bin; bin = String(low & 1) + bin;
low = Math.floor(low / 2); low = Math.floor(low / 2);
if (high > 0) { if (high > 0) {
low += 5000000000 * (high % 2); low += 5000000000 * (high % 2);
high = Math.floor(high / 2); high = Math.floor(high / 2);
} }
} }
return bin; return bin;
} }
/** /**
* Transforms a snowflake from a bit string to a decimal string. * Transforms a snowflake from a bit string to a decimal string.
* @param {string} num Bit string to be transformed * @param {string} num Bit string to be transformed
* @returns {string} * @returns {string}
* @private * @private
*/ */
export function binaryToID(num:string):string { export function binaryToID(num:string):string {
let dec = ''; let dec = '';
while (num.length > 50) { while (num.length > 50) {
const high = parseInt(num.slice(0, -32), 2); const high = parseInt(num.slice(0, -32), 2);
const low = parseInt((high % 10).toString(2) + num.slice(-32), 2); const low = parseInt((high % 10).toString(2) + num.slice(-32), 2);
dec = (low % 10).toString() + dec; dec = (low % 10).toString() + dec;
num = Math.floor(high / 10).toString(2) + num = Math.floor(high / 10).toString(2) +
Math.floor(low / 10) Math.floor(low / 10)
.toString(2) .toString(2)
.padStart(32, '0'); .padStart(32, '0');
} }
let num2 = parseInt(num, 2); let num2 = parseInt(num, 2);
while (num2 > 0) { while (num2 > 0) {
dec = (num2 % 10).toString() + dec; dec = (num2 % 10).toString() + dec;
num2 = Math.floor(num2 / 10); num2 = Math.floor(num2 / 10);
} }
return dec; return dec;
} }
export type UserResolvable = User | Snowflake | Message | Guild | GuildMember export type UserResolvable = User | Snowflake | Message | Guild | GuildMember
export function resolveUserID(user:UserResolvable){ export function resolveUserID(user:UserResolvable){
if(typeof user === "string")return user // ID if(typeof user === "string")return user // ID
if(user instanceof User)return user.id // User if(user instanceof User)return user.id // User
if(user instanceof Message)return user.author.id // Message Author if(user instanceof Message)return user.author.id // Message Author
if(user instanceof Guild)return user.ownerID // Guild if(user instanceof Guild)return user.ownerID // Guild
if(user instanceof GuildMember)return user.id // GuildMember if(user instanceof GuildMember)return user.id // GuildMember
return null return null
} }
export type ChannelData = { export type ChannelData = {
type?: ChannelTypes, type?: ChannelTypes,
name?: string, name?: string,
position?: number, position?: number,
topic?: string, topic?: string,
nsfw?: boolean, nsfw?: boolean,
bitrate?: number, bitrate?: number,
userLimit?: number, userLimit?: number,
parent?: CategoryChannel|Snowflake, parent?: CategoryChannel|Snowflake,
permissionOverwrites?: ChannelCreationOverwrites[] | Collection<Snowflake, PermissionOverwrites>, permissionOverwrites?: ChannelCreationOverwrites[] | Collection<Snowflake, PermissionOverwrites>,
rateLimitPerUser?: number rateLimitPerUser?: number
} }
export type ChannelCreationOverwrites = { export type ChannelCreationOverwrites = {
allow?: PermissionResolvable, allow?: PermissionResolvable,
/** /**
* @deprecated * @deprecated
*/ */
allowed?: PermissionResolvable, allowed?: PermissionResolvable,
deny?: PermissionResolvable, deny?: PermissionResolvable,
/** /**
* @deprecated * @deprecated
*/ */
denied?: PermissionResolvable, denied?: PermissionResolvable,
id?: GuildMemberResolvable | RoleResolvable id?: GuildMemberResolvable | RoleResolvable
} }
export {BitFieldResolvable} from "./BitField" export {BitFieldResolvable} from "./BitField"
export type GuildMemberResolvable = GuildMember | User export type GuildMemberResolvable = GuildMember | User
export type RoleResolvable = Role | Snowflake export type RoleResolvable = Role | Snowflake

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
const config = require("./webpack.config.js") const config = require("./webpack.config.js")
config.mode = "production" config.mode = "production"
config.devtool = "source-map" config.devtool = "source-map"
config.output.filename = "main.min.js" config.output.filename = "main.min.js"
module.exports = config module.exports = config

View File

@ -1,26 +1,26 @@
// bait typescript into thinking this is not reactDOM so no circular dependency. // bait typescript into thinking this is not reactDOM so no circular dependency.
window.ReactDOM = (window["Reac"+"tDOM"] || // If in Lightcord window.ReactDOM = (window["Reac"+"tDOM"] || // If in Lightcord
(()=>{ // If in Standard BetterDiscord (()=>{ // If in Standard BetterDiscord
try{ try{
return window.BdApi.ReactDOM return window.BdApi.ReactDOM
}catch(e){ }catch(e){
return null return null
} }
})() || })() ||
(()=>{ // If in Powercord (()=>{ // If in Powercord
try{ try{
const webpack = require("powercord/webpack") const webpack = require("powercord/webpack")
return webpack.ReactDOM return webpack.ReactDOM
}catch(e){ }catch(e){
return null return null
} }
})() || })() ||
(()=>{ // If in EnhancedDiscord (()=>{ // If in EnhancedDiscord
try{ try{
return window.EDApi.ReactDOM return window.EDApi.ReactDOM
}catch(e){ }catch(e){
return null return null
} }
})()) })())
module.exports = window.ReactDOM module.exports = window.ReactDOM

View File

@ -1,25 +1,25 @@
window.React = (window.React || // If in Lightcord window.React = (window.React || // If in Lightcord
(()=>{ // If in Standard BetterDiscord (()=>{ // If in Standard BetterDiscord
try{ try{
return window.BdApi.React return window.BdApi.React
}catch(e){ }catch(e){
return null return null
} }
})() || })() ||
(()=>{ // If in Powercord (()=>{ // If in Powercord
try{ try{
const webpack = require("powercord/webpack") const webpack = require("powercord/webpack")
return webpack.React return webpack.React
}catch(e){ }catch(e){
return null return null
} }
})() || })() ||
(()=>{ // If in EnhancedDiscord (()=>{ // If in EnhancedDiscord
try{ try{
return window.EDApi.React return window.EDApi.React
}catch(e){ }catch(e){
return null return null
} }
})()) })())
module.exports = window.React module.exports = window.React

View File

@ -1,49 +1,49 @@
import DiscordButton from "./inputs/Button" import DiscordButton from "./inputs/Button"
import Switch from "./inputs/Switch" import Switch from "./inputs/Switch"
import RadioGroup from "./inputs/RadioGroup" import RadioGroup from "./inputs/RadioGroup"
import TextArea from "./inputs/TextArea" import TextArea from "./inputs/TextArea"
import TextInput from "./inputs/TextInput" import TextInput from "./inputs/TextInput"
import Dropdown from "./inputs/Dropdown" import Dropdown from "./inputs/Dropdown"
import Title from "./general/Title" import Title from "./general/Title"
import SettingsTitle from "./general/SettingsTitle" import SettingsTitle from "./general/SettingsTitle"
import Tabs, { Tab } from "./general/Tabs" import Tabs, { Tab } from "./general/Tabs"
import SettingSubTitle from "./general/SettingSubTitle" import SettingSubTitle from "./general/SettingSubTitle"
import CodeBlock from "./general/CodeBlock" import CodeBlock from "./general/CodeBlock"
import cloneNullProto from "../modules/cloneNullProto" import cloneNullProto from "../modules/cloneNullProto"
import Tooltip from "./general/Tooltip" import Tooltip from "./general/Tooltip"
import ColorPicker from "./inputs/ColorPicker" import ColorPicker from "./inputs/ColorPicker"
import AlertBox from "./general/AlertBox" import AlertBox from "./general/AlertBox"
import ErrorCatcher, { createProxyErrorCatcherClass } from "./general/ErrorCatcher" import ErrorCatcher, { createProxyErrorCatcherClass } from "./general/ErrorCatcher"
import Flex, { FlexChild } from "./general/Flex" import Flex, { FlexChild } from "./general/Flex"
import Text from "./general/Text" import Text from "./general/Text"
import DateRange from "./inputs/DateRange" import DateRange from "./inputs/DateRange"
import DateInput from "./inputs/DateInput" import DateInput from "./inputs/DateInput"
const RadioGroupProxied = createProxyErrorCatcherClass(RadioGroup) const RadioGroupProxied = createProxyErrorCatcherClass(RadioGroup)
export default cloneNullProto({ export default cloneNullProto({
inputs: cloneNullProto({ inputs: cloneNullProto({
Button: createProxyErrorCatcherClass(DiscordButton), Button: createProxyErrorCatcherClass(DiscordButton),
Switch: createProxyErrorCatcherClass(Switch), Switch: createProxyErrorCatcherClass(Switch),
Choices: RadioGroupProxied, Choices: RadioGroupProxied,
RadioGroup: RadioGroupProxied, RadioGroup: RadioGroupProxied,
TextArea: createProxyErrorCatcherClass(TextArea), TextArea: createProxyErrorCatcherClass(TextArea),
TextInput: createProxyErrorCatcherClass(TextInput), TextInput: createProxyErrorCatcherClass(TextInput),
Dropdown: createProxyErrorCatcherClass(Dropdown), Dropdown: createProxyErrorCatcherClass(Dropdown),
ColorPicker: createProxyErrorCatcherClass(ColorPicker), ColorPicker: createProxyErrorCatcherClass(ColorPicker),
DateRange: createProxyErrorCatcherClass(DateRange), DateRange: createProxyErrorCatcherClass(DateRange),
DateInput: createProxyErrorCatcherClass(DateInput) DateInput: createProxyErrorCatcherClass(DateInput)
}), }),
general: cloneNullProto({ general: cloneNullProto({
Title: createProxyErrorCatcherClass(Title), Title: createProxyErrorCatcherClass(Title),
SettingsTitle: createProxyErrorCatcherClass(SettingsTitle), SettingsTitle: createProxyErrorCatcherClass(SettingsTitle),
SettingSubTitle: createProxyErrorCatcherClass(SettingSubTitle), SettingSubTitle: createProxyErrorCatcherClass(SettingSubTitle),
Tabs: createProxyErrorCatcherClass(Tabs), Tabs: createProxyErrorCatcherClass(Tabs),
CodeBlock: createProxyErrorCatcherClass(CodeBlock), CodeBlock: createProxyErrorCatcherClass(CodeBlock),
Tooltip: createProxyErrorCatcherClass(Tooltip), Tooltip: createProxyErrorCatcherClass(Tooltip),
AlertBox: createProxyErrorCatcherClass(AlertBox), AlertBox: createProxyErrorCatcherClass(AlertBox),
Flex: createProxyErrorCatcherClass(Flex), Flex: createProxyErrorCatcherClass(Flex),
FlexChild: createProxyErrorCatcherClass(FlexChild), FlexChild: createProxyErrorCatcherClass(FlexChild),
ErrorCatcher: ErrorCatcher, ErrorCatcher: ErrorCatcher,
Text: Text Text: Text
}) })
}) })

View File

@ -1,95 +1,95 @@
import * as React from "react" import * as React from "react"
import Utils, { ColorName } from "../../modules/Utils" import Utils, { ColorName } from "../../modules/Utils"
export type TextProps = { export type TextProps = {
weight?: TextWeight, weight?: TextWeight,
color?: TextColor|string, color?: TextColor|string,
textCase?: TextCase, textCase?: TextCase,
size?: TextSize, size?: TextSize,
selectable?: boolean, selectable?: boolean,
family?: TextFamily, family?: TextFamily,
children: string, children: string,
tag?: string, tag?: string,
className?: string, className?: string,
style?: React.CSSProperties style?: React.CSSProperties
} }
export default class Text extends React.Component<TextProps> { export default class Text extends React.Component<TextProps> {
render(){ render(){
let classList = this.props.className ? this.props.className.split(" ") : [] let classList = this.props.className ? this.props.className.split(" ") : []
let style = this.props.style ? {...this.props.style} : {} let style = this.props.style ? {...this.props.style} : {}
style.flexGrow = 0 style.flexGrow = 0
if(this.props.weight){ if(this.props.weight){
classList.push(`lc-text-weight${Utils.firstLetterUppercase(this.props.weight)}`) classList.push(`lc-text-weight${Utils.firstLetterUppercase(this.props.weight)}`)
} }
if(this.props.color){ if(this.props.color){
style.color = Utils.getColor(this.props.color as TextColor) || this.props.color style.color = Utils.getColor(this.props.color as TextColor) || this.props.color
} }
if(this.props.textCase){ if(this.props.textCase){
classList.push(`lc-text-cases${Utils.firstLetterUppercase(this.props.textCase)}`) classList.push(`lc-text-cases${Utils.firstLetterUppercase(this.props.textCase)}`)
} }
if(this.props.size){ if(this.props.size){
classList.push(...Constants.sizes[this.props.size]) classList.push(...Constants.sizes[this.props.size])
} }
if(this.props.family){ if(this.props.family){
classList.push(`lc-text-${this.props.family}`) classList.push(`lc-text-${this.props.family}`)
} }
if(this.props.selectable){ if(this.props.selectable){
classList.push("lc-text-selectable") classList.push("lc-text-selectable")
} }
return React.createElement(this.props.tag, { return React.createElement(this.props.tag, {
style, style,
className: classList.join(" ") className: classList.join(" ")
}, this.props.children) }, this.props.children)
} }
static defaultProps:TextProps = { static defaultProps:TextProps = {
tag: "div", tag: "div",
children: null children: null
} }
static get AllPreviews(){ static get AllPreviews(){
return AllPreviews || (AllPreviews = [ return AllPreviews || (AllPreviews = [
this.weights.map(e => ({weight: e})), this.weights.map(e => ({weight: e})),
this.colors.map(e => ({color: e})), this.colors.map(e => ({color: e})),
this.textCases.map(e => ({textCase: e})), this.textCases.map(e => ({textCase: e})),
this.sizes.map(e => ({size: e})), this.sizes.map(e => ({size: e})),
this.familys.map(e => ({family: e})), this.familys.map(e => ({family: e})),
[ [
{ {
children: "Test Text" children: "Test Text"
} }
], ],
["div","p",...Utils.executeXTimes((index) => { ["div","p",...Utils.executeXTimes((index) => {
return "h"+(index+1) return "h"+(index+1)
}, 6)].map(e => ({tag: e})), }, 6)].map(e => ({tag: e})),
[true, false].map(e => ({selectable: e})) [true, false].map(e => ({selectable: e}))
]) ])
} }
static weights:TextWeight[] = ["light", "normal", "medium", "semibold", "bold"] static weights:TextWeight[] = ["light", "normal", "medium", "semibold", "bold"]
static get colors():TextColor[]{ static get colors():TextColor[]{
return Object.keys(Lightcord.DiscordModules.constants.Colors).map(e => e.toLowerCase()) as TextColor[] return Object.keys(Lightcord.DiscordModules.constants.Colors).map(e => e.toLowerCase()) as TextColor[]
} }
static textCases:TextCase[] = ["lowercase", "uppercase"] static textCases:TextCase[] = ["lowercase", "uppercase"]
static sizes:TextSize[] = ["small", "medium", "medium_small", "medium_large", "large"] static sizes:TextSize[] = ["small", "medium", "medium_small", "medium_large", "large"]
static familys:TextFamily[] = ["primary", "code"] static familys:TextFamily[] = ["primary", "code"]
} }
let AllPreviews let AllPreviews
export type TextWeight = "light"|"normal"|"medium"|"semibold"|"bold" export type TextWeight = "light"|"normal"|"medium"|"semibold"|"bold"
export type TextColor = ColorName export type TextColor = ColorName
export type TextCase = "lowercase"|"uppercase" export type TextCase = "lowercase"|"uppercase"
export type TextSize = "small"|"medium"|"medium_small"|"medium_large"|"large" export type TextSize = "small"|"medium"|"medium_small"|"medium_large"|"large"
export type TextFamily = "primary"|"code" export type TextFamily = "primary"|"code"
export const Constants = { export const Constants = {
sizes: { sizes: {
small: ["lc-text-size12", "lc-text-height16"], small: ["lc-text-size12", "lc-text-height16"],
medium_small: ["lc-text-size14", "lc-text-height16"], medium_small: ["lc-text-size14", "lc-text-height16"],
medium: ["lc-text-size16", "lc-text-height20"], medium: ["lc-text-size16", "lc-text-height20"],
medium_large: ["lc-text-size20", "lc-text-height26"], medium_large: ["lc-text-size20", "lc-text-height26"],
large: ["lc-text-size28", "lc-text-height34"] large: ["lc-text-size28", "lc-text-height34"]
} }
} }

View File

@ -1,104 +1,104 @@
import WebpackLoader from "./modules/WebpackLoader" import WebpackLoader from "./modules/WebpackLoader"
import Components from "./components/components" import Components from "./components/components"
import uuid from "./modules/uuid" import uuid from "./modules/uuid"
import Utils from "./modules/Utils" import Utils from "./modules/Utils"
import DiscordTools from "./modules/DiscordTools" import DiscordTools from "./modules/DiscordTools"
import * as patchers from "./modules/patchers" import * as patchers from "./modules/patchers"
import excludeProperties from "./modules/excludeProperties" import excludeProperties from "./modules/excludeProperties"
import cloneNullProto from "./modules/cloneNullProto" import cloneNullProto from "./modules/cloneNullProto"
import NOOP from "./modules/noop" import NOOP from "./modules/noop"
import unfreeze from "./modules/Unfreeze" import unfreeze from "./modules/Unfreeze"
import { isNative, isImported } from "./modules/environnement" import { isNative, isImported } from "./modules/environnement"
import * as bandagedbdApi from "@bandagedbd/bdapi" import * as bandagedbdApi from "@bandagedbd/bdapi"
import "./alias/react" import "./alias/react"
import "./alias/react-dom" import "./alias/react-dom"
import { LazyLoad } from "./modules/lazyLoader" import { LazyLoad } from "./modules/lazyLoader"
patchers.patch() patchers.patch()
const LightcordApi = { const LightcordApi = {
WebpackLoader: WebpackLoader, WebpackLoader: WebpackLoader,
Components: Components, Components: Components,
uuid: uuid, uuid: uuid,
Utils: Utils, Utils: Utils,
DiscordTools: DiscordTools, DiscordTools: DiscordTools,
_: { _: {
excludeProperties: excludeProperties, excludeProperties: excludeProperties,
cloneNullProto: cloneNullProto, cloneNullProto: cloneNullProto,
NOOP: NOOP, NOOP: NOOP,
unfreeze: unfreeze unfreeze: unfreeze
}, },
get isNative(){return isNative}, get isNative(){return isNative},
get isImported(){return isImported}, get isImported(){return isImported},
LazyLoad: LazyLoad LazyLoad: LazyLoad
} }
declare global { declare global {
var React:typeof import("react") var React:typeof import("react")
var ReactDOM:typeof import("../node_modules/@types/react-dom") var ReactDOM:typeof import("../node_modules/@types/react-dom")
interface Window { interface Window {
/** /**
* Lightcord is only availlaible in Lightcord (native) * Lightcord is only availlaible in Lightcord (native)
*/ */
Lightcord: LightcordGlobal, Lightcord: LightcordGlobal,
/** /**
* BDModules is only availlaible in Lightcord (native) * BDModules is only availlaible in Lightcord (native)
*/ */
BDModules: { BDModules: {
modules:any[], modules:any[],
get(filter:(mod:any)=>boolean, modules?:any[]):any[], get(filter:(mod:any)=>boolean, modules?:any[]):any[],
get(id:number, modules?:any[]):any, get(id:number, modules?:any[]):any,
get(ids: [number|((mod:any)=>boolean)], modules?:any[]):any get(ids: [number|((mod:any)=>boolean)], modules?:any[]):any
}, },
BdApi: typeof bandagedbdApi.BdApi, BdApi: typeof bandagedbdApi.BdApi,
EDApi: typeof bandagedbdApi.BdApi, EDApi: typeof bandagedbdApi.BdApi,
ReactDOM: typeof ReactDOM; ReactDOM: typeof ReactDOM;
React:typeof React React:typeof React
} }
var Lightcord:LightcordGlobal var Lightcord:LightcordGlobal
var BdApi: typeof bandagedbdApi.BdApi var BdApi: typeof bandagedbdApi.BdApi
var EDApi: typeof bandagedbdApi.BdApi var EDApi: typeof bandagedbdApi.BdApi
} }
export default LightcordApi export default LightcordApi
Object.assign(window.Lightcord.Api, LightcordApi) Object.assign(window.Lightcord.Api, LightcordApi)
/** /**
* The main Lightcord exports. Can be accessed with `window.Lightcord` * The main Lightcord exports. Can be accessed with `window.Lightcord`
*/ */
export interface LightcordGlobal { export interface LightcordGlobal {
DiscordModules: { DiscordModules: {
/** /**
* Internal Discord's dispatcher - can be used to subscribe to gateway events / client events. * Internal Discord's dispatcher - can be used to subscribe to gateway events / client events.
*/ */
dispatcher: import("./types/DiscordDispatcherTypes").default, dispatcher: import("./types/DiscordDispatcherTypes").default,
constants: import("./types/DiscordConstantsTypes").default constants: import("./types/DiscordConstantsTypes").default
}, },
Settings: { Settings: {
devMode: boolean, devMode: boolean,
callRingingBeat: boolean callRingingBeat: boolean
}, },
Api: LightcordApiGlobal, Api: LightcordApiGlobal,
BetterDiscord: { BetterDiscord: {
BdApi: typeof bandagedbdApi.BdApi, BdApi: typeof bandagedbdApi.BdApi,
[mod:string]:any [mod:string]:any
} }
} }
/** /**
* The main Api. Can be accessed with `window.Lightcord.Api` * The main Api. Can be accessed with `window.Lightcord.Api`
*/ */
type LightcordApiGlobal = lightcordApiMainExports & typeof LightcordApi type LightcordApiGlobal = lightcordApiMainExports & typeof LightcordApi
type lightcordApiMainExports = { type lightcordApiMainExports = {
/** /**
* Waits until the first module that match the filter gets exported * Waits until the first module that match the filter gets exported
* @param filter The filter that specifies the module to match. * @param filter The filter that specifies the module to match.
*/ */
ensureExported(filter: (mod:any) => boolean):Promise<any>, ensureExported(filter: (mod:any) => boolean):Promise<any>,
/** /**
* Recreate the object without the `__proto__` and `prototype` properties - usefull for better formatting in console. * Recreate the object without the `__proto__` and `prototype` properties - usefull for better formatting in console.
* @param obj The object to recreate * @param obj The object to recreate
*/ */
cloneNullProto<Obj = any>(obj:Obj):Obj cloneNullProto<Obj = any>(obj:Obj):Obj
} }

View File

@ -1,82 +1,82 @@
let req let req
setReq() setReq()
function filterDangerous(mods){ function filterDangerous(mods){
return mods.map(e => { return mods.map(e => {
return protect(e) return protect(e)
}) })
} }
function protect(exports){ function protect(exports){
let theModule = exports.exports let theModule = exports.exports
let mod = theModule.default let mod = theModule.default
if(!mod)return exports if(!mod)return exports
if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null; if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null;
if (!mod.getToken && !mod.getEmail && !mod.showToken)return exports if (!mod.getToken && !mod.getEmail && !mod.showToken)return exports
const proxy = new Proxy(mod, { const proxy = new Proxy(mod, {
getOwnPropertyDescriptor: function(obj, prop) { getOwnPropertyDescriptor: function(obj, prop) {
if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined; if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined;
return Object.getOwnPropertyDescriptor(obj, prop); return Object.getOwnPropertyDescriptor(obj, prop);
}, },
get: function(obj, func) { get: function(obj, func) {
if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa"; if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa";
if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com"; if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com";
if (func == "showToken" && obj.showToken) return () => true; if (func == "showToken" && obj.showToken) return () => true;
if (func == "__proto__" && obj.__proto__) return proxy; if (func == "__proto__" && obj.__proto__) return proxy;
return obj[func]; return obj[func];
} }
}); });
return Object.assign({}, exports, {exports: Object.assign({}, theModule, {default: proxy})}) return Object.assign({}, exports, {exports: Object.assign({}, theModule, {default: proxy})})
} }
class Webpackloader { class Webpackloader {
get modules(){ get modules(){
if(req){ if(req){
return filterDangerous(Object.values(req.c).filter((e:any) => e && e.exports)) return filterDangerous(Object.values(req.c).filter((e:any) => e && e.exports))
}else{ }else{
setReq() setReq()
if(req){ if(req){
return filterDangerous(Object.values(req.c).filter((e:any) => e && e.exports)) return filterDangerous(Object.values(req.c).filter((e:any) => e && e.exports))
}else{ }else{
return [] return []
} }
} }
} }
get(ids, modules){ get(ids, modules){
if(typeof ids === "function"){ if(typeof ids === "function"){
return (modules || this.modules).map((mdl) => { return (modules || this.modules).map((mdl) => {
if(mdl && typeof mdl.exports !== "undefined"){ if(mdl && typeof mdl.exports !== "undefined"){
return mdl.exports return mdl.exports
}else{ }else{
return null return null
} }
}).filter(e => e).filter(ids) }).filter(e => e).filter(ids)
}else if(Array.isArray(ids)){ }else if(Array.isArray(ids)){
modules = modules || this.modules modules = modules || this.modules
return ids.map(id => this.get(id, modules)) return ids.map(id => this.get(id, modules))
}else{ }else{
modules = modules || this.modules modules = modules || this.modules
let module = modules.filter(e => !!e).find(e => e.i === ids) let module = modules.filter(e => !!e).find(e => e.i === ids)
if(!module)return undefined if(!module)return undefined
return module.exports return module.exports
} }
} }
get default(){ get default(){
return this return this
} }
} }
export default new Webpackloader() export default new Webpackloader()
function setReq(){ function setReq(){
try{ try{
req = window["webpackJsonp"].push([[], {__extra_id__: (mdl, exports, req) => mdl.exports = req}, [["__extra_id__"]]]); req = window["webpackJsonp"].push([[], {__extra_id__: (mdl, exports, req) => mdl.exports = req}, [["__extra_id__"]]]);
if(req){ if(req){
delete req.m.__extra_id__; delete req.m.__extra_id__;
delete req.c.__extra_id__; delete req.c.__extra_id__;
} }
}catch(e){ }catch(e){
req = undefined req = undefined
} }
} }

View File

@ -1,228 +1,228 @@
import { notices, noticeWithoutID, notice, events as noticeEvents } from "../components/private/Notices"; import { notices, noticeWithoutID, notice, events as noticeEvents } from "../components/private/Notices";
import Utils from "./Utils"; import Utils from "./Utils";
import uuid from "./uuid"; import uuid from "./uuid";
import cloneNullProto from "./cloneNullProto"; import cloneNullProto from "./cloneNullProto";
import { EventEmitter } from "events"; import { EventEmitter } from "events";
import { defaultNotice } from "../components/private/Notice"; import { defaultNotice } from "../components/private/Notice";
import excludeProperties from "./excludeProperties"; import excludeProperties from "./excludeProperties";
import NOOP from "./noop"; import NOOP from "./noop";
import WebpackLoader, { WebpackLoaderError } from "./WebpackLoader"; import WebpackLoader, { WebpackLoaderError } from "./WebpackLoader";
let soundModule let soundModule
export default new class DiscordTools { export default new class DiscordTools {
showNotice(data:NoticeData):Notice{ showNotice(data:NoticeData):Notice{
if(typeof data !== "object" || typeof data.text !== "string")throw new Error(`This notice is not valid. Given: ${Utils.formatJSObject(data)}`) if(typeof data !== "object" || typeof data.text !== "string")throw new Error(`This notice is not valid. Given: ${Utils.formatJSObject(data)}`)
let newData = cloneNullProto(Object.assign({}, defaultNotice, data)) as notice let newData = cloneNullProto(Object.assign({}, defaultNotice, data)) as notice
newData.id = uuid() newData.id = uuid()
notices.push(newData) notices.push(newData)
noticeEvents.emit("noticeUpdate") noticeEvents.emit("noticeUpdate")
const notice = new Notice(newData) const notice = new Notice(newData)
return notice return notice
} }
get notices():Notice[]{ get notices():Notice[]{
return notices.map(data => new Notice(data)) return notices.map(data => new Notice(data))
} }
/** /**
* Quickly send notification (Even when no focused.) * Quickly send notification (Even when no focused.)
* @param data The notification. Be sure to include all properties except functions cause they're optional. * @param data The notification. Be sure to include all properties except functions cause they're optional.
* Notifications have a timeout of 3-5 seconds. * Notifications have a timeout of 3-5 seconds.
* They look like this: https://i.imgur.com/jzuxKKu.png * They look like this: https://i.imgur.com/jzuxKKu.png
*/ */
showNotification(data:NotificationData):Notification{ showNotification(data:NotificationData):Notification{
const notification = new window.Notification(data.title, excludeProperties(data, [ const notification = new window.Notification(data.title, excludeProperties(data, [
"title", "title",
"onClick", "onClick",
"onClose", "onClose",
"onShow" "onShow"
])) ]))
notification.onclick = data.onClick || NOOP notification.onclick = data.onClick || NOOP
notification.onshow = data.onShow || NOOP notification.onshow = data.onShow || NOOP
notification.onclose = data.onClose || NOOP notification.onclose = data.onClose || NOOP
return notification return notification
} }
createSound(sound:Sound){ createSound(sound:Sound){
soundModule = soundModule || WebpackLoader.findByUniqueProperties(["createSound"]) soundModule = soundModule || WebpackLoader.findByUniqueProperties(["createSound"])
if(!soundModule)throw new WebpackLoaderError("Couldn't find soundModule here.") if(!soundModule)throw new WebpackLoaderError("Couldn't find soundModule here.")
const created = soundModule.createSound(sound) const created = soundModule.createSound(sound)
return created return created
} }
playSound(sound:Sound){ playSound(sound:Sound){
const created = this.createSound(sound) const created = this.createSound(sound)
created.play() created.play()
return created return created
} }
} }
export type Sound = "call_calling"|"call_ringing"|"call_ringing_beat"|"ddr-down"|"ddr-left"|"ddr-right"|"ddr-up"|"deafen"|"discodo"|"disconnect"|"human_man"|"mention1"|"mention2"|"mention3"|"message1"|"message2"|"message3"|"mute"|"overlayunlock"|"ptt_start"|"ptt_stop"|"reconnect"|"robot_man"|"stream_ended"|"stream_started"|"stream_user_joined"|"stream_user_left"|"undeafen"|"unmute"|"user_join"|"user_leave"|"user_moved" export type Sound = "call_calling"|"call_ringing"|"call_ringing_beat"|"ddr-down"|"ddr-left"|"ddr-right"|"ddr-up"|"deafen"|"discodo"|"disconnect"|"human_man"|"mention1"|"mention2"|"mention3"|"message1"|"message2"|"message3"|"mute"|"overlayunlock"|"ptt_start"|"ptt_stop"|"reconnect"|"robot_man"|"stream_ended"|"stream_started"|"stream_user_joined"|"stream_user_left"|"undeafen"|"unmute"|"user_join"|"user_leave"|"user_moved"
export type NotificationData = { export type NotificationData = {
title: string, title: string,
body: string, body: string,
icon: string, icon: string,
onShow?: () => void, onShow?: () => void,
onClick?: () => void, onClick?: () => void,
onClose?: () => void onClose?: () => void
} }
export type NoticeData = noticeWithoutID export type NoticeData = noticeWithoutID
const EventHandler = function(){ const EventHandler = function(){
if(this.removed !== this.state.removed){ if(this.removed !== this.state.removed){
if(this.removed){ if(this.removed){
this.emit("removed") this.emit("removed")
} }
} }
if(this.showing !== this.state.showing){ if(this.showing !== this.state.showing){
if(this.showing){ if(this.showing){
this.emit("showing", true) this.emit("showing", true)
}else{ }else{
this.emit("showing", false) this.emit("showing", false)
} }
} }
if(this.index !== this.state.index){ if(this.index !== this.state.index){
this.emit("index", this.index) this.emit("index", this.index)
} }
} }
/** A notice interface for modifying it and subscribing to events. */ /** A notice interface for modifying it and subscribing to events. */
export class Notice extends EventEmitter { export class Notice extends EventEmitter {
constructor(data){ constructor(data){
super() super()
this.data = data this.data = data
this.state = { this.state = {
removed: this.removed, removed: this.removed,
showing: this.showing, showing: this.showing,
index: this.index index: this.index
} }
let eventFunc = EventHandler.bind(this) let eventFunc = EventHandler.bind(this)
noticeEvents.on("noticeUpdate", eventFunc) noticeEvents.on("noticeUpdate", eventFunc)
this.on("removed", () => { this.on("removed", () => {
noticeEvents.off("noticeUpdate", eventFunc) noticeEvents.off("noticeUpdate", eventFunc)
}) })
} }
/** /**
* Will be called whem the notice is removed. * Will be called whem the notice is removed.
*/ */
on(event: "removed", listener: () => void):this on(event: "removed", listener: () => void):this
/** /**
* Will be called when the notice is visible or not. * Will be called when the notice is visible or not.
*/ */
on(event: "showing", listener: (isShowing:boolean) => void):this on(event: "showing", listener: (isShowing:boolean) => void):this
/** /**
* Will be called when the notice queue changes. * Will be called when the notice queue changes.
*/ */
on(event: "index", listener: (index:number) => void):this on(event: "index", listener: (index:number) => void):this
on(event: string, listener: (...args:any[]) => void){ on(event: string, listener: (...args:any[]) => void){
return super.on(event, listener) return super.on(event, listener)
} }
/** /**
* Will be called whem the notice is removed. * Will be called whem the notice is removed.
*/ */
once(event: "removed", listener: () => void):this once(event: "removed", listener: () => void):this
/** /**
* Will be called when the notice is visible or not. * Will be called when the notice is visible or not.
*/ */
once(event: "showing", listener: (isShowing:boolean) => void):this once(event: "showing", listener: (isShowing:boolean) => void):this
/** /**
* Will be called when the notice queue changes. * Will be called when the notice queue changes.
*/ */
once(event: "index", listener: (index:number) => void):this once(event: "index", listener: (index:number) => void):this
once(event: string, listener: (...args:any[]) => void){ once(event: string, listener: (...args:any[]) => void){
return super.once(event, listener) return super.once(event, listener)
} }
off(event: "removed", listener: () => void):this off(event: "removed", listener: () => void):this
off(event: "showing", listener: (isShowing:boolean) => void):this off(event: "showing", listener: (isShowing:boolean) => void):this
off(event: "index", listener: (index:number) => void):this off(event: "index", listener: (index:number) => void):this
off(event: string, listener: (...args:any[]) => void){ off(event: string, listener: (...args:any[]) => void){
return super.off(event, listener) return super.off(event, listener)
} }
state:{ state:{
removed:boolean, removed:boolean,
showing:boolean, showing:boolean,
index:number index:number
} }
private nextTickRefresh:boolean = false private nextTickRefresh:boolean = false
get removed():boolean{ get removed():boolean{
return !notices.find(e => e.id === this.id) return !notices.find(e => e.id === this.id)
} }
get showing():boolean{ get showing():boolean{
return this.index === 0 return this.index === 0
} }
get index():number{ get index():number{
return notices.findIndex(e => e.id === this.id) return notices.findIndex(e => e.id === this.id)
} }
get id(){ get id(){
return this.data.id return this.data.id
} }
update(data: Partial<notice>){ update(data: Partial<notice>){
for(let key in data){ for(let key in data){
if(key === "id")continue if(key === "id")continue
this.data[key] = data[key] this.data[key] = data[key]
} }
if(!this.nextTickRefresh){ if(!this.nextTickRefresh){
this.nextTickRefresh = true this.nextTickRefresh = true
process.nextTick(() => { process.nextTick(() => {
this.nextTickRefresh = false this.nextTickRefresh = false
noticeEvents.emit("noticeUpdate") noticeEvents.emit("noticeUpdate")
}) })
} }
} }
get text(){ get text(){
return this.data.text return this.data.text
} }
set text(text){ set text(text){
this.update({ this.update({
text text
}) })
} }
get type(){ get type(){
return this.data.type return this.data.type
} }
set type(type){ set type(type){
this.update({ this.update({
type type
}) })
} }
get buttonText(){ get buttonText(){
return this.data.buttonText return this.data.buttonText
} }
set buttonText(buttonText:string){ set buttonText(buttonText:string){
this.update({ this.update({
buttonText buttonText
}) })
} }
get onClick(){ get onClick(){
return this.data.onClick return this.data.onClick
} }
set onClick(onClick){ set onClick(onClick){
this.update({ this.update({
onClick onClick
}) })
} }
remove(){ remove(){
if(this.removed)return if(this.removed)return
notices.splice(this.index, 1) notices.splice(this.index, 1)
noticeEvents.emit("noticeUpdate") noticeEvents.emit("noticeUpdate")
} }
data:notice data:notice
} }

File diff suppressed because one or more lines are too long

View File

@ -1,57 +1,57 @@
const BDModules:typeof window.BDModules = window.BDModules || require("./BDModules") const BDModules:typeof window.BDModules = window.BDModules || require("./BDModules")
export default new class WebpackLoader { export default new class WebpackLoader {
constructor(){} constructor(){}
get(id: number):any{ get(id: number):any{
return BDModules.get(id) return BDModules.get(id)
} }
find(filter: (mod:any) => boolean):any{ find(filter: (mod:any) => boolean):any{
let result = BDModules.get(filter)[0] let result = BDModules.get(filter)[0]
if(!result){ if(!result){
console.warn(filter, "couldn't find the module.") console.warn(filter, "couldn't find the module.")
} }
return result return result
} }
findByUniqueProperties(props:(string|number)[]):any{ findByUniqueProperties(props:(string|number)[]):any{
return BDModules.get((mod) => { return BDModules.get((mod) => {
if(mod.__esModule && ("default" in mod)){ if(mod.__esModule && ("default" in mod)){
let doesMatch = true let doesMatch = true
for(let prop of props){ for(let prop of props){
if(!Object.prototype.hasOwnProperty.call(mod.default, prop))doesMatch = false if(!Object.prototype.hasOwnProperty.call(mod.default, prop))doesMatch = false
} }
if(doesMatch)return true if(doesMatch)return true
} }
for(let prop of props){ for(let prop of props){
if(!Object.prototype.hasOwnProperty.call(mod, prop))return false if(!Object.prototype.hasOwnProperty.call(mod, prop))return false
} }
return true return true
})[0] })[0]
} }
filter(filter: (mod:any) => boolean):any[]{ filter(filter: (mod:any) => boolean):any[]{
return BDModules.get(filter) return BDModules.get(filter)
} }
filterByUniqueProperties(props:(string|number)[]):any{ filterByUniqueProperties(props:(string|number)[]):any{
return BDModules.get((mod) => { return BDModules.get((mod) => {
if(mod.__esModule && ("default" in mod)){ if(mod.__esModule && ("default" in mod)){
let doesMatch = true let doesMatch = true
for(let prop of props){ for(let prop of props){
if(!Object.prototype.hasOwnProperty.call(mod.default, prop))doesMatch = false if(!Object.prototype.hasOwnProperty.call(mod.default, prop))doesMatch = false
} }
if(doesMatch)return true if(doesMatch)return true
} }
for(let prop of props){ for(let prop of props){
if(!Object.prototype.hasOwnProperty.call(mod, prop))return false if(!Object.prototype.hasOwnProperty.call(mod, prop))return false
} }
return true return true
}) })
} }
} }
export class WebpackLoaderError extends Error { export class WebpackLoaderError extends Error {
constructor(message:string = ""){ constructor(message:string = ""){
message += "\n\tThis error is related to Lightcord not being able to find a WebpackModule. \n\tPlease show this error and a few lines of logs above this error to the devs. \n\tOpen an issue on https://github.com/Lightcord/Lightcord or in our discord server." message += "\n\tThis error is related to Lightcord not being able to find a WebpackModule. \n\tPlease show this error and a few lines of logs above this error to the devs. \n\tOpen an issue on https://github.com/Lightcord/Lightcord or in our discord server."
super(message) super(message)
this.name = "WebpackLoaderError" this.name = "WebpackLoaderError"
} }
} }

View File

@ -1,11 +1,11 @@
/** /**
* Recreate the given object without the __proto__. Useful for better formatting when output in console. * Recreate the given object without the __proto__. Useful for better formatting when output in console.
* @param obj The object to recreate * @param obj The object to recreate
*/ */
export default function cloneNullProto<Obj=any>(obj:Obj):Obj{ export default function cloneNullProto<Obj=any>(obj:Obj):Obj{
let o = Object.create(null) let o = Object.create(null)
Object.keys(obj).forEach(k => { Object.keys(obj).forEach(k => {
o[k] = obj[k] o[k] = obj[k]
}) })
return o return o
} }

View File

@ -1,2 +1,2 @@
export const isNative:boolean = typeof window.BDModules === "undefined" export const isNative:boolean = typeof window.BDModules === "undefined"
export const isImported:boolean = typeof window.BDModules !== "undefined" export const isImported:boolean = typeof window.BDModules !== "undefined"

View File

@ -1,84 +1,84 @@
let cache = new Map() let cache = new Map()
export function LazyLoad<T>(getObject: () => T):T{ export function LazyLoad<T>(getObject: () => T):T{
if(cache.has(getObject))return cache.get(getObject) if(cache.has(getObject))return cache.get(getObject)
let mdl = null let mdl = null
let setModule = () => { let setModule = () => {
if(mdl)return if(mdl)return
mdl = getObject() mdl = getObject()
} }
let handler:ProxyHandler<{}> = { let handler:ProxyHandler<{}> = {
get(target, prop){ get(target, prop){
setModule() setModule()
return mdl[prop] return mdl[prop]
}, },
set(target, prop, value){ set(target, prop, value){
setModule() setModule()
mdl[prop] = value mdl[prop] = value
return true return true
}, },
apply(target, thisArg, args){ apply(target, thisArg, args){
setModule() setModule()
mdl.apply(this, args) mdl.apply(this, args)
}, },
construct(target, args){ construct(target, args){
setModule() setModule()
const prototype = Object.create(mdl.prototype) const prototype = Object.create(mdl.prototype)
handler.apply(target, prototype, args) handler.apply(target, prototype, args)
return prototype return prototype
}, },
deleteProperty(target, prop){ deleteProperty(target, prop){
setModule() setModule()
if(!(prop in mdl))return false if(!(prop in mdl))return false
delete mdl[prop] delete mdl[prop]
return true return true
}, },
enumerate(target){ enumerate(target){
setModule() setModule()
return Object.keys(mdl) return Object.keys(mdl)
}, },
ownKeys(target) { ownKeys(target) {
setModule() setModule()
return Object.keys(mdl) return Object.keys(mdl)
}, },
has(target, prop){ has(target, prop){
setModule() setModule()
return prop in mdl return prop in mdl
}, },
defineProperty(target, prop, attributes){ defineProperty(target, prop, attributes){
setModule() setModule()
return Object.defineProperty(mdl, prop, attributes) return Object.defineProperty(mdl, prop, attributes)
}, },
getOwnPropertyDescriptor(target, prop){ getOwnPropertyDescriptor(target, prop){
setModule() setModule()
return Object.getOwnPropertyDescriptor(mdl, prop) return Object.getOwnPropertyDescriptor(mdl, prop)
}, },
getPrototypeOf(target){ getPrototypeOf(target){
setModule() setModule()
return Object.getPrototypeOf(mdl) return Object.getPrototypeOf(mdl)
}, },
setPrototypeOf(target, proto){ setPrototypeOf(target, proto){
setModule() setModule()
try{ try{
Object.setPrototypeOf(mdl, proto) Object.setPrototypeOf(mdl, proto)
return true return true
}catch(e){ }catch(e){
return false return false
} }
}, },
isExtensible(target){ isExtensible(target){
setModule() setModule()
return Object.isExtensible(mdl) return Object.isExtensible(mdl)
}, },
preventExtensions(target){ preventExtensions(target){
setModule() setModule()
Object.preventExtensions(mdl) Object.preventExtensions(mdl)
return true return true
} }
} }
const proxy = new Proxy({}, handler) const proxy = new Proxy({}, handler)
cache.set(getObject, proxy) cache.set(getObject, proxy)
return proxy as T return proxy as T
} }

View File

@ -1,232 +1,232 @@
import Utils from "./Utils" import Utils from "./Utils"
import Notices, { notices } from "../components/private/Notices" import Notices, { notices } from "../components/private/Notices"
import { isNative } from "./environnement"; import { isNative } from "./environnement";
import WebpackLoader from "./WebpackLoader"; import WebpackLoader from "./WebpackLoader";
export function patch(){ export function patch(){
/** START NOTICE */ /** START NOTICE */
getModule(e => e.default && e.default.displayName === "ConnectedAppView") getModule(e => e.default && e.default.displayName === "ConnectedAppView")
.then(async (mod) => { .then(async (mod) => {
const appClasses = await getModule(e => e.hasNotice); const appClasses = await getModule(e => e.hasNotice);
const buildRender = original => { const buildRender = original => {
return function render(){ return function render(){
const returnValue = original.call(this, ...arguments) const returnValue = original.call(this, ...arguments)
const newchildren = [] const newchildren = []
let children = returnValue.props.children[1].props.children let children = returnValue.props.children[1].props.children
if(!Array.isArray(children))children = [children] if(!Array.isArray(children))children = [children]
newchildren.push(children[0]) newchildren.push(children[0])
newchildren.push(React.createElement(Notices, {container: this})) newchildren.push(React.createElement(Notices, {container: this}))
newchildren.push(children[1]) newchildren.push(children[1])
returnValue.props.children[1].props.children = newchildren returnValue.props.children[1].props.children = newchildren
returnValue.props.children[1].props.children[2].props.children[0].props.render = buildRenderChannelSidebar(returnValue.props.children[1].props.children[2].props.children[0].props.render) returnValue.props.children[1].props.children[2].props.children[0].props.render = buildRenderChannelSidebar(returnValue.props.children[1].props.children[2].props.children[0].props.render)
return returnValue return returnValue
} }
} }
const buildRenderChannelSidebar = original => { const buildRenderChannelSidebar = original => {
return function renderChannelSidebar(){ return function renderChannelSidebar(){
const returnValue = original.call(this, ...arguments) const returnValue = original.call(this, ...arguments)
const hasNotice = notices.length > 0 const hasNotice = notices.length > 0
if(!hasNotice)return returnValue if(!hasNotice)return returnValue
if(!Utils.hasClass(returnValue.props.className, appClasses.hasNotice)){ if(!Utils.hasClass(returnValue.props.className, appClasses.hasNotice)){
returnValue.props.className += " "+Utils.removeDa(appClasses.hasNotice) returnValue.props.className += " "+Utils.removeDa(appClasses.hasNotice)
} }
return returnValue return returnValue
} }
} }
mod.default.prototype.render = buildRender(mod.default.prototype.render); mod.default.prototype.render = buildRender(mod.default.prototype.render);
(async function(){ (async function(){
const base = document.querySelector("."+Utils.removeDa(appClasses.base)) const base = document.querySelector("."+Utils.removeDa(appClasses.base))
if(!base)throw new Error(`Could not find base here`) if(!base)throw new Error(`Could not find base here`)
const elem = Utils.FindReact(base) as any const elem = Utils.FindReact(base) as any
elem.render = buildRender(elem.render) elem.render = buildRender(elem.render)
elem.forceUpdate() elem.forceUpdate()
})() })()
}) })
/** END NOTICE */ /** END NOTICE */
if(isNative){ if(isNative){
/** START USERPOPOUT PATCH */ /** START USERPOPOUT PATCH */
awaitLogin() awaitLogin()
.then(async () => { .then(async () => {
let UserPopout = await getModule(e => e.default && e.default.displayName === "FluxContainer(ForwardRef(SubscribeGuildMembersContainer(UserPopout)))") let UserPopout = await getModule(e => e.default && e.default.displayName === "FluxContainer(ForwardRef(SubscribeGuildMembersContainer(UserPopout)))")
const userModule = await getModule(e => e.default && e.default.getCurrentUser) const userModule = await getModule(e => e.default && e.default.getCurrentUser)
const render1 = new UserPopout.default({userId: userModule.default.getCurrentUser().id, guildId: null, channelId: null, disableUserProfileLink: true}).render() const render1 = new UserPopout.default({userId: userModule.default.getCurrentUser().id, guildId: null, channelId: null, disableUserProfileLink: true}).render()
const PopoutProps = render1.props const PopoutProps = render1.props
const render2 = render1.type.render(PopoutProps, null) const render2 = render1.type.render(PopoutProps, null)
const render3 = new render2.type(render2.props).render() const render3 = new render2.type(render2.props).render()
const UserPopoutComponent = render3.type const UserPopoutComponent = render3.type
if(!UserPopoutComponent)throw new Error(`Couldn't find the UserPopoutComponent component.`) if(!UserPopoutComponent)throw new Error(`Couldn't find the UserPopoutComponent component.`)
const render = UserPopoutComponent.prototype.render const render = UserPopoutComponent.prototype.render
UserPopoutComponent.prototype.render = function(){ UserPopoutComponent.prototype.render = function(){
const returnValue = render.call(this, ...arguments) const returnValue = render.call(this, ...arguments)
try{ try{
returnValue.props.children.props["data-user-id"] = this.props.user.id returnValue.props.children.props["data-user-id"] = this.props.user.id
}catch(e){ }catch(e){
console.error(e) console.error(e)
} }
return returnValue return returnValue
} }
}) })
/** END USERPOPOUT PATCH*/ /** END USERPOPOUT PATCH*/
/** START USERPROFILE PATCH */ /** START USERPROFILE PATCH */
awaitLogin() awaitLogin()
.then(async () => { .then(async () => {
let UserProfile = await getModule(e => e.default && e.default.displayName === "UserProfile") let UserProfile = await getModule(e => e.default && e.default.displayName === "UserProfile")
const userModule = await getModule(e => e.default && e.default.getCurrentUser) const userModule = await getModule(e => e.default && e.default.getCurrentUser)
const render1 = new UserProfile.default({ const render1 = new UserProfile.default({
user: userModule.default.getCurrentUser() user: userModule.default.getCurrentUser()
}).render() }).render()
const render2 = new render1.type(render1.props).render() const render2 = new render1.type(render1.props).render()
const render3 = render2.type.render(render2.props, null) const render3 = render2.type.render(render2.props, null)
const render4 = new render3.type(render3.props).render() const render4 = new render3.type(render3.props).render()
const UserProfileComponent = render4.type const UserProfileComponent = render4.type
if(!UserProfileComponent)throw new Error(`Couldn't find the UserProfileComponent component.`) if(!UserProfileComponent)throw new Error(`Couldn't find the UserProfileComponent component.`)
const render = UserProfileComponent.prototype.render const render = UserProfileComponent.prototype.render
UserProfileComponent.prototype.render = function(){ UserProfileComponent.prototype.render = function(){
const returnValue = render.call(this, ...arguments) const returnValue = render.call(this, ...arguments)
console.log(returnValue) console.log(returnValue)
try{ try{
returnValue.props.children.props["data-user-id"] = this.props.user.id returnValue.props.children.props["data-user-id"] = this.props.user.id
}catch(e){ }catch(e){
console.error(e) console.error(e)
} }
return returnValue return returnValue
} }
}) })
/** END USERPROFILE PATCH */ /** END USERPROFILE PATCH */
/** START WEBHOOK PATCH */ /** START WEBHOOK PATCH */
/* /*
let usedWebhooks = {} let usedWebhooks = {}
getModule(e => e && e.Request && e.Request.prototype && e.Request.prototype.end) getModule(e => e && e.Request && e.Request.prototype && e.Request.prototype.end)
.then(RequestModule => { .then(RequestModule => {
const end = RequestModule.Request.prototype.end const end = RequestModule.Request.prototype.end
RequestModule.Request.prototype.end = function(){ RequestModule.Request.prototype.end = function(){
if(this.url.endsWith("/messages") && /\/channels\/\d+\/messages/g.test(this.url) && this.method === "POST"){ // sending message if(this.url.endsWith("/messages") && /\/channels\/\d+\/messages/g.test(this.url) && this.method === "POST"){ // sending message
let channelId = this.url.split("/channels/")[1].split("/messages")[0] let channelId = this.url.split("/channels/")[1].split("/messages")[0]
if(usedWebhooks[channelId]){ // webhook is availlable if(usedWebhooks[channelId]){ // webhook is availlable
let webhook = usedWebhooks[channelId] let webhook = usedWebhooks[channelId]
let url = `/webhooks/${webhook.id}/${webhook.token}?wait=true` let url = `/webhooks/${webhook.id}/${webhook.token}?wait=true`
this.url = url this.url = url
} }
} }
return end.call(this, ...arguments) return end.call(this, ...arguments)
} }
}) })
getModule(e => e.default && e.default.displayName === "Webhook") getModule(e => e.default && e.default.displayName === "Webhook")
.then(webhookComponent => { .then(webhookComponent => {
const renderEdit = webhookComponent.default.prototype.renderEdit const renderEdit = webhookComponent.default.prototype.renderEdit
webhookComponent.default.prototype.renderEdit = function(){ webhookComponent.default.prototype.renderEdit = function(){
const webhook = this.props.webhook const webhook = this.props.webhook
let returnValue = renderEdit.call(this, ...arguments) let returnValue = renderEdit.call(this, ...arguments)
returnValue.props.children = [returnValue.props.children] returnValue.props.children = [returnValue.props.children]
let message = usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id ? "Stop talking with this webhook" : "Talk with this webhook" let message = usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id ? "Stop talking with this webhook" : "Talk with this webhook"
returnValue.props.children.push(React.createElement(window.Lightcord.Api.Components.inputs.Button, {color: "green", wrapper: false, onClick(){ returnValue.props.children.push(React.createElement(window.Lightcord.Api.Components.inputs.Button, {color: "green", wrapper: false, onClick(){
if(usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id){ if(usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id){
delete usedWebhooks[webhook.channel_id] delete usedWebhooks[webhook.channel_id]
}else{ }else{
usedWebhooks[webhook.channel_id] = { usedWebhooks[webhook.channel_id] = {
id: webhook.id, id: webhook.id,
token: webhook.token token: webhook.token
} }
} }
webhookPanels.forEach(e => e()) webhookPanels.forEach(e => e())
}}, message)) }}, message))
return returnValue return returnValue
} }
}) })
let webhookPanels = [] let webhookPanels = []
let getComp = (comp) => { let getComp = (comp) => {
class SettingsWebhooks extends React.PureComponent { class SettingsWebhooks extends React.PureComponent {
constructor(props){ constructor(props){
super(props) super(props)
} }
componentWillMount(){ componentWillMount(){
this.id = uuid() this.id = uuid()
this.component = new comp(this.props) this.component = new comp(this.props)
let func = () => { let func = () => {
this.component.forceUpdate() this.component.forceUpdate()
} }
func.id = this.id func.id = this.id
webhookPanels.push(func) webhookPanels.push(func)
} }
componentWillUnmount(){ componentWillUnmount(){
this.component = null this.component = null
webhookPanels = webhookPanels.filter(e => e.id !== this.id) webhookPanels = webhookPanels.filter(e => e.id !== this.id)
} }
render(){ render(){
return this.component.render() return this.component.render()
} }
static displayName = "SettingsWebhooks" static displayName = "SettingsWebhooks"
} }
return SettingsWebhooks return SettingsWebhooks
} }
getModule(e => e.default && e.default.displayName === "FluxContainer(SettingsWebhooks)") getModule(e => e.default && e.default.displayName === "FluxContainer(SettingsWebhooks)")
.then(webhooksComponents => { .then(webhooksComponents => {
let comp = webhooksComponents.default let comp = webhooksComponents.default
webhooksComponents.default = getComp(comp) webhooksComponents.default = getComp(comp)
WebpackLoader.find(e => e.default && e.default.displayName === "FluxContainer(FluxContainer(SettingsWebhooks))") WebpackLoader.find(e => e.default && e.default.displayName === "FluxContainer(FluxContainer(SettingsWebhooks))")
.forEach(mod => { .forEach(mod => {
mod.default = getComp(mod.default) mod.default = getComp(mod.default)
}) })
})*/ })*/
/** END WEBHOOK PATCH */ /** END WEBHOOK PATCH */
} }
// TODO: Add in app-notifications / confirmations. // TODO: Add in app-notifications / confirmations.
/** START IN-APP NOTIFICATIONS */ /** START IN-APP NOTIFICATIONS */
//getModule(e => true) //getModule(e => true)
/** END IN-APP NOTIFICATIONS */ /** END IN-APP NOTIFICATIONS */
} }
function getModule(filter: (mod:any) => boolean):Promise<any>{ function getModule(filter: (mod:any) => boolean):Promise<any>{
return new Promise((resolve) => { return new Promise((resolve) => {
window.Lightcord.Api.ensureExported(filter) window.Lightcord.Api.ensureExported(filter)
.then(resolve) .then(resolve)
.catch(err => { .catch(err => {
console.error("[LIGHTCORD]", err, filter) console.error("[LIGHTCORD]", err, filter)
}) })
}) })
} }
let hasCompletedLogin = false let hasCompletedLogin = false
let loginPromise:Promise<void> let loginPromise:Promise<void>
function awaitLogin():Promise<void>{ function awaitLogin():Promise<void>{
if(hasCompletedLogin)return Promise.resolve() if(hasCompletedLogin)return Promise.resolve()
if(loginPromise)return loginPromise if(loginPromise)return loginPromise
return loginPromise = new Promise((resolve) => { return loginPromise = new Promise((resolve) => {
let isResolved = false let isResolved = false
window.Lightcord.DiscordModules.dispatcher.subscribe("CONNECTION_OPEN", (ev) => { window.Lightcord.DiscordModules.dispatcher.subscribe("CONNECTION_OPEN", (ev) => {
if(isResolved)return if(isResolved)return
hasCompletedLogin = true hasCompletedLogin = true
resolve() resolve()
isResolved = true isResolved = true
}) })
}) })
} }
window.Lightcord.DiscordModules.dispatcher.subscribe("LOGOUT", (ev) => { window.Lightcord.DiscordModules.dispatcher.subscribe("LOGOUT", (ev) => {
hasCompletedLogin = false hasCompletedLogin = false
loginPromise = undefined loginPromise = undefined
}) })

View File

@ -1,21 +1,21 @@
{ {
"compilerOptions": { "compilerOptions": {
"noImplicitAny": false, "noImplicitAny": false,
"module": "CommonJS", "module": "CommonJS",
"target": "ES2017", "target": "ES2017",
"jsx": "react", "jsx": "react",
"outDir": "./dist", "outDir": "./dist",
"resolveJsonModule": true, "resolveJsonModule": true,
"rootDir": "./src", "rootDir": "./src",
"sourceMap": true, "sourceMap": true,
"declaration": true, "declaration": true,
"allowJs": true "allowJs": true
}, },
"exclude": [ "exclude": [
"./js/**", "./js/**",
"./webpack.config.js", "./webpack.config.js",
"./dist/**", "./dist/**",
"./prod.config.js", "./prod.config.js",
"./docs" "./docs"
] ]
} }

View File

@ -1,72 +1,72 @@
const path = require("path"); const path = require("path");
const TerserPlugin = require("terser-webpack-plugin") const TerserPlugin = require("terser-webpack-plugin")
const child_process = require("child_process") const child_process = require("child_process")
module.exports = { module.exports = {
mode: "development", mode: "development",
target: "node", target: "node",
devtool: "inline-source-map", devtool: "inline-source-map",
entry: "./src/index.ts", entry: "./src/index.ts",
output: { output: {
filename: "main.js", filename: "main.js",
path: path.resolve(__dirname, "js"), path: path.resolve(__dirname, "js"),
library: "LightcordApi", library: "LightcordApi",
libraryTarget: "commonjs2" libraryTarget: "commonjs2"
}, },
externals: { externals: {
electron: `electron`, electron: `electron`,
fs: `fs`, fs: `fs`,
path: `path`, path: `path`,
events: `events`, events: `events`,
rimraf: `rimraf`, rimraf: `rimraf`,
yauzl: `yauzl`, yauzl: `yauzl`,
mkdirp: `mkdirp`, mkdirp: `mkdirp`,
request: `request`, request: `request`,
"node-fetch": "node-fetch", "node-fetch": "node-fetch",
"uuid/v1": "uuid/v1", "uuid/v1": "uuid/v1",
"uuid/v4": "uuid/v4", "uuid/v4": "uuid/v4",
"powercord/webpack": "powercord/webpack" "powercord/webpack": "powercord/webpack"
}, },
resolve: { resolve: {
extensions: [".js", ".jsx", ".json", ".ts", ".tsx"], extensions: [".js", ".jsx", ".json", ".ts", ".tsx"],
alias: { alias: {
"react$": path.resolve(__dirname, "src", "alias", "react.js"), "react$": path.resolve(__dirname, "src", "alias", "react.js"),
"react-dom$": path.resolve(__dirname, "src", "alias", "react-dom.js") "react-dom$": path.resolve(__dirname, "src", "alias", "react-dom.js")
} }
}, },
module: { module: {
rules: [{ rules: [{
test: /\.jsx?$/, test: /\.jsx?$/,
loader: "babel-loader", loader: "babel-loader",
exclude: /node_modules/, exclude: /node_modules/,
query: { query: {
presets: [ presets: [
["@babel/env", { ["@babel/env", {
targets: { targets: {
node: "12.8.1", node: "12.8.1",
chrome: "78" chrome: "78"
} }
}], "@babel/react" }], "@babel/react"
] ]
} }
}, { }, {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: 'ts-loader',
exclude: /node_modules/, exclude: /node_modules/,
}] }]
}, },
optimization: { optimization: {
minimizer: [ minimizer: [
new TerserPlugin({ new TerserPlugin({
cache: true, cache: true,
parallel: true, parallel: true,
sourceMap: true, sourceMap: true,
terserOptions: { terserOptions: {
mangle: false, mangle: false,
keep_classnames: true, keep_classnames: true,
keep_fnames: true keep_fnames: true
} }
}), }),
] ]
} }
}; };

View File

@ -1,64 +1,64 @@
const fs = require("fs") const fs = require("fs")
const fsAsync = fs.promises const fsAsync = fs.promises
const yazl = require("yazl") const yazl = require("yazl")
const __path = require("path") const __path = require("path")
const buildsPaths = __path.join(__dirname, "builds") const buildsPaths = __path.join(__dirname, "builds")
const folders = [ const folders = [
"lightcord-win32-ia32", "lightcord-win32-ia32",
"lightcord-linux-x64", "lightcord-linux-x64",
"lightcord-darwin-x64" "lightcord-darwin-x64"
] ]
folders.forEach(folder => { folders.forEach(folder => {
const path = __path.join(buildsPaths, folder) const path = __path.join(buildsPaths, folder)
if(!fs.existsSync(path))return console.warn(`\x1b[33mCan't pack build ${folder} because it doesn't exist.\x1b[0m`) if(!fs.existsSync(path))return console.warn(`\x1b[33mCan't pack build ${folder} because it doesn't exist.\x1b[0m`)
const zipPath = __path.join(buildsPaths, folder+".zip") const zipPath = __path.join(buildsPaths, folder+".zip")
if(fs.existsSync(zipPath)){ if(fs.existsSync(zipPath)){
console.warn(`Deleting ${zipPath}.`) console.warn(`Deleting ${zipPath}.`)
fs.unlinkSync(zipPath) fs.unlinkSync(zipPath)
} }
const zip = new yazl.ZipFile() const zip = new yazl.ZipFile()
zip.outputStream.pipe(fs.createWriteStream(zipPath)) zip.outputStream.pipe(fs.createWriteStream(zipPath))
const platform = folder.split("-")[1] const platform = folder.split("-")[1]
processNextDir(path, zip, platform) processNextDir(path, zip, platform)
.then(() => { .then(() => {
console.log(`Zipped ${platform}.`) console.log(`Zipped ${platform}.`)
zip.end() zip.end()
}) })
}) })
async function processNextDir(dir, zip, platform, bpath = dir){ async function processNextDir(dir, zip, platform, bpath = dir){
if(dir.replace(bpath, ""))zip.addEmptyDirectory(dir.replace(bpath, "").slice(1)) if(dir.replace(bpath, ""))zip.addEmptyDirectory(dir.replace(bpath, "").slice(1))
await Promise.all(fs.readdirSync(dir, {withFileTypes: true}) await Promise.all(fs.readdirSync(dir, {withFileTypes: true})
.map(async file => { .map(async file => {
let path = __path.join(dir, file.name) let path = __path.join(dir, file.name)
if(file.isDirectory()){ if(file.isDirectory()){
return await processNextDir(path, zip, platform, bpath) return await processNextDir(path, zip, platform, bpath)
}else if(file.isFile()){ }else if(file.isFile()){
if(!path.includes("node_modules")){ if(!path.includes("node_modules")){
if(platform === "win32"){ if(platform === "win32"){
if(file.name.endsWith("_linux.node"))return if(file.name.endsWith("_linux.node"))return
if(file.name.endsWith("_darwin.node"))return if(file.name.endsWith("_darwin.node"))return
if(file.name.endsWith(".dylib"))return if(file.name.endsWith(".dylib"))return
if(file.name.endsWith(".so.4"))return if(file.name.endsWith(".so.4"))return
}else if(platform === "linux"){ }else if(platform === "linux"){
if(file.name.endsWith("_win32.node"))return if(file.name.endsWith("_win32.node"))return
if(file.name.endsWith("_darwin.node"))return if(file.name.endsWith("_darwin.node"))return
if(file.name.endsWith(".dylib"))return if(file.name.endsWith(".dylib"))return
if(file.name.endsWith(".dll"))return if(file.name.endsWith(".dll"))return
}else if(platform === "darwin"){ }else if(platform === "darwin"){
if(file.name.endsWith("_linux.node"))return if(file.name.endsWith("_linux.node"))return
if(file.name.endsWith("_win32.node"))return if(file.name.endsWith("_win32.node"))return
if(file.name.endsWith(".dll"))return if(file.name.endsWith(".dll"))return
if(file.name.endsWith(".so.4"))return if(file.name.endsWith(".so.4"))return
} }
} }
let stat = fs.statSync(path) let stat = fs.statSync(path)
zip.addBuffer(await fsAsync.readFile(path), __path.relative(bpath, path), { zip.addBuffer(await fsAsync.readFile(path), __path.relative(bpath, path), {
mode: stat.mode, mode: stat.mode,
mtime: stat.mtime mtime: stat.mtime
}) })
} }
})) }))
} }

548
build.js
View File

@ -1,275 +1,275 @@
const child_process = require("child_process") const child_process = require("child_process")
const path = require("path") const path = require("path")
const terser = require("terser") const terser = require("terser")
const util = require("util") const util = require("util")
const production = true const production = true
const includeSourcesMaps = true const includeSourcesMaps = true
let fs = require("fs") let fs = require("fs")
console.log = (...args) => { console.log = (...args) => {
process.stdout.write(Buffer.from(util.formatWithOptions({colors: true}, ...args)+"\n", "binary").toString("utf8")) process.stdout.write(Buffer.from(util.formatWithOptions({colors: true}, ...args)+"\n", "binary").toString("utf8"))
} }
console.info = (...args) => { console.info = (...args) => {
console.log(`\x1b[34m[INFO]\x1b[0m`, ...args) console.log(`\x1b[34m[INFO]\x1b[0m`, ...args)
} }
let commit = child_process.execSync("git rev-parse HEAD").toString().split("\n")[0].trim() let commit = child_process.execSync("git rev-parse HEAD").toString().split("\n")[0].trim()
console.info(`Obtained commit ${commit} for the build`) console.info(`Obtained commit ${commit} for the build`)
async function processNextDir(folder, folders, predicate, compile, ignoreModules){ async function processNextDir(folder, folders, predicate, compile, ignoreModules){
if(typeof ignoreModules === "undefined")ignoreModules = false if(typeof ignoreModules === "undefined")ignoreModules = false
let files = fs.readdirSync(folder, {withFileTypes: true}) let files = fs.readdirSync(folder, {withFileTypes: true})
for(let file of files){ for(let file of files){
if(file.isFile()){ if(file.isFile()){
let isMinified = file.name.endsWith(".min.js") || file.name.endsWith(".min.css") let isMinified = file.name.endsWith(".min.js") || file.name.endsWith(".min.css")
let filepath = path.join(folder, file.name) let filepath = path.join(folder, file.name)
let type = file.name.split(".").pop().toLowerCase() let type = file.name.split(".").pop().toLowerCase()
if(type === file.name)type = "" if(type === file.name)type = ""
if([ if([
"ts", "ts",
"md", "md",
"gitignore", "gitignore",
"map" "map"
].includes(type)){ ].includes(type)){
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of type ${type}\x1b[0m`) console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of type ${type}\x1b[0m`)
continue continue
} }
if([ if([
"tsconfig.json", "tsconfig.json",
"webpack.config.js" "webpack.config.js"
].includes(file.name)){ ].includes(file.name)){
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of name ${file.name}\x1b[0m`) console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of name ${file.name}\x1b[0m`)
continue continue
} }
if(folders.exclude && folders.exclude.test(filepath)){ if(folders.exclude && folders.exclude.test(filepath)){
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because regex\x1b[0m`) console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because regex\x1b[0m`)
continue continue
} }
let hasMinifiedVersion = (type === "js" || type === "css") && !isMinified && files.find(f => { let hasMinifiedVersion = (type === "js" || type === "css") && !isMinified && files.find(f => {
return f.name === file.name.split(".").slice(0, -1).join(".")+".min."+type return f.name === file.name.split(".").slice(0, -1).join(".")+".min."+type
}) })
if(hasMinifiedVersion){ if(hasMinifiedVersion){
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because it has a minified version.\x1b[0m`) console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because it has a minified version.\x1b[0m`)
continue continue
} }
if(!isMinified && predicate(filepath) && filepath.split(/[\\/]+/).reverse()[1] !== "js"){ if(!isMinified && predicate(filepath) && filepath.split(/[\\/]+/).reverse()[1] !== "js"){
await compile(filepath, path.join(filepath.replace(folders.startDir, folders.newDir)), "..") await compile(filepath, path.join(filepath.replace(folders.startDir, folders.newDir)), "..")
}else{ }else{
if(["js", "css"].includes(type)){ if(["js", "css"].includes(type)){
if(!includeSourcesMaps){ if(!includeSourcesMaps){
console.log(`We don't include sourcemap for this build. Skipping ${file.name}.`) console.log(`We don't include sourcemap for this build. Skipping ${file.name}.`)
return await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir)) return await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
} }
let fileContent = (await fs.promises.readFile(filepath, "utf8")) let fileContent = (await fs.promises.readFile(filepath, "utf8"))
let sourceMap = fileContent.split(/[\n\r]+/g).pop() let sourceMap = fileContent.split(/[\n\r]+/g).pop()
if(!sourceMap || !sourceMap.startsWith("//# sourceMappingURL=")){ if(!sourceMap || !sourceMap.startsWith("//# sourceMappingURL=")){
console.log(`This file doesn't have sourcemap. ${file.name}.`) console.log(`This file doesn't have sourcemap. ${file.name}.`)
await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir)) await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
continue continue
} }
let sourceMapContent let sourceMapContent
if(sourceMap.slice(21).startsWith("data:")){ if(sourceMap.slice(21).startsWith("data:")){
console.log(`Extracting sourcemap from data uri. From file ${file.name}.`) console.log(`Extracting sourcemap from data uri. From file ${file.name}.`)
sourceMapContent = Buffer.from(sourceMap.split("=").slice(1).join("="), "base64").toString("utf-8") sourceMapContent = Buffer.from(sourceMap.split("=").slice(1).join("="), "base64").toString("utf-8")
}else{ }else{
console.log(`Extracting sourcemap from file ${file.name}.map.`) console.log(`Extracting sourcemap from file ${file.name}.map.`)
await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir)) await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
sourceMapContent = await fs.promises.readFile(path.join(folder, sourceMap.slice(21)), "utf8") sourceMapContent = await fs.promises.readFile(path.join(folder, sourceMap.slice(21)), "utf8")
} }
sourceMapContent = JSON.parse(sourceMapContent) sourceMapContent = JSON.parse(sourceMapContent)
sourceMapContent.sourcesContent = [] sourceMapContent.sourcesContent = []
let sourceMapPath = filepath + ".map" let sourceMapPath = filepath + ".map"
fileContent = fileContent fileContent = fileContent
// source map // source map
.replace(sourceMap, "//# sourceMappingURL="+filepath.split(/[\\\/]+/g).pop()+".map") .replace(sourceMap, "//# sourceMappingURL="+filepath.split(/[\\\/]+/g).pop()+".map")
await fs.promises.writeFile(filepath.replace(folders.startDir, folders.newDir), fileContent) await fs.promises.writeFile(filepath.replace(folders.startDir, folders.newDir), fileContent)
await fs.promises.writeFile(filepath.replace(folders.startDir, folders.newDir)+".map", JSON.stringify(sourceMapContent)) await fs.promises.writeFile(filepath.replace(folders.startDir, folders.newDir)+".map", JSON.stringify(sourceMapContent))
}else{ }else{
await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir)) await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
} }
} }
}else if(file.isDirectory()){ }else if(file.isDirectory()){
if(ignoreModules && file.name === "node_modules")continue if(ignoreModules && file.name === "node_modules")continue
if(folders.exclude && folders.exclude.test(path.join(folder, file.name)))continue if(folders.exclude && folders.exclude.test(path.join(folder, file.name)))continue
await fs.promises.mkdir(path.join(folder, file.name).replace(folders.startDir, folders.newDir), {recursive: true}) await fs.promises.mkdir(path.join(folder, file.name).replace(folders.startDir, folders.newDir), {recursive: true})
await processNextDir(path.join(folder, file.name), ...Array.from(arguments).slice(1)) await processNextDir(path.join(folder, file.name), ...Array.from(arguments).slice(1))
} }
} }
} }
async function main(){ async function main(){
let startTimestamp = Date.now() let startTimestamp = Date.now()
console.info("Starting build") console.info("Starting build")
console.info("Reseting existent directory...") console.info("Reseting existent directory...")
await fs.promises.rmdir("./distApp", {"recursive": true}) await fs.promises.rmdir("./distApp", {"recursive": true})
await fs.promises.mkdir(__dirname+"/distApp/dist", {"recursive": true}) await fs.promises.mkdir(__dirname+"/distApp/dist", {"recursive": true})
console.info("Executing command `npm run compile`") console.info("Executing command `npm run compile`")
child_process.execSync("npm run compile", { child_process.execSync("npm run compile", {
encoding: "binary", encoding: "binary",
stdio: "inherit" stdio: "inherit"
}) })
let startDir = path.join(__dirname, "./dist") let startDir = path.join(__dirname, "./dist")
let newDir = path.join(__dirname, "./distApp/dist") let newDir = path.join(__dirname, "./distApp/dist")
console.info("No error detected. Copying files from "+startDir+".") console.info("No error detected. Copying files from "+startDir+".")
await fs.promises.mkdir(startDir, {recursive: true}) await fs.promises.mkdir(startDir, {recursive: true})
await processNextDir(startDir, { await processNextDir(startDir, {
startDir, startDir,
newDir newDir
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => { }, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
console.info(`Minifying ${filepath} to ${newpath}`) console.info(`Minifying ${filepath} to ${newpath}`)
if(filepath.endsWith("git.js")){ if(filepath.endsWith("git.js")){
await fs.promises.writeFile(newpath, terser.minify(fs.readFileSync(filepath, "utf8").replace(/"{commit}"/g, `"${commit}"`)).code, "utf8") await fs.promises.writeFile(newpath, terser.minify(fs.readFileSync(filepath, "utf8").replace(/"{commit}"/g, `"${commit}"`)).code, "utf8")
}else{ }else{
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
} }
}, true).then(() => { }, true).then(() => {
console.info(`Copied files and minified them from ${startDir}.`) console.info(`Copied files and minified them from ${startDir}.`)
}) })
await processNextDir(path.join(__dirname, "modules"), { await processNextDir(path.join(__dirname, "modules"), {
startDir: path.join(__dirname, "modules"), startDir: path.join(__dirname, "modules"),
newDir: path.join(__dirname, "distApp", "modules"), newDir: path.join(__dirname, "distApp", "modules"),
exclude: /discord_spellcheck/g exclude: /discord_spellcheck/g
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => { }, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
console.info(`Minifying ${filepath} to ${newpath}`) console.info(`Minifying ${filepath} to ${newpath}`)
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
}, true).then(() => { }, true).then(() => {
console.info(`Copied files and minified them from ${path.join(__dirname, "modules")}.`) console.info(`Copied files and minified them from ${path.join(__dirname, "modules")}.`)
}) })
await Promise.all((await fs.promises.readdir(path.join(__dirname, "distApp", "modules"))).map(async mdl => { await Promise.all((await fs.promises.readdir(path.join(__dirname, "distApp", "modules"))).map(async mdl => {
let dir = path.join(__dirname, "distApp", "modules", mdl) let dir = path.join(__dirname, "distApp", "modules", mdl)
if(!fs.existsSync(path.join(dir, "package.json"))){ if(!fs.existsSync(path.join(dir, "package.json"))){
if(mdl === "discord_desktop_core"){ if(mdl === "discord_desktop_core"){
dir = path.join(dir, "core") dir = path.join(dir, "core")
}else{ }else{
return return
} }
} }
console.info(`Installing modules for ${mdl}`) console.info(`Installing modules for ${mdl}`)
child_process.execSync("npm install --only=prod", { child_process.execSync("npm install --only=prod", {
encoding: "binary", encoding: "binary",
cwd: dir, cwd: dir,
stdio: "inherit" stdio: "inherit"
}) })
})) }))
await fs.promises.mkdir(path.join(__dirname, "distApp", "modules", "discord_spellcheck"), {recursive: true}) await fs.promises.mkdir(path.join(__dirname, "distApp", "modules", "discord_spellcheck"), {recursive: true})
await processNextDir(path.join(__dirname, "modules", "discord_spellcheck"), { await processNextDir(path.join(__dirname, "modules", "discord_spellcheck"), {
startDir: path.join(__dirname, "modules", "discord_spellcheck"), startDir: path.join(__dirname, "modules", "discord_spellcheck"),
newDir: path.join(__dirname, "distApp", "modules", "discord_spellcheck") newDir: path.join(__dirname, "distApp", "modules", "discord_spellcheck")
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => { }, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
console.info(`Minifying ${filepath} to ${newpath}`) console.info(`Minifying ${filepath} to ${newpath}`)
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
}, false).then(() => { }, false).then(() => {
console.info(`Copied files and minified them from ${path.join(__dirname, "modules")}.`) console.info(`Copied files and minified them from ${path.join(__dirname, "modules")}.`)
}) })
await processNextDir(path.join(__dirname, "LightcordApi"), { await processNextDir(path.join(__dirname, "LightcordApi"), {
startDir: path.join(__dirname, "LightcordApi"), startDir: path.join(__dirname, "LightcordApi"),
newDir: path.join(__dirname, "distApp", "LightcordApi"), newDir: path.join(__dirname, "distApp", "LightcordApi"),
exclude: /(src|webpack\.config\.js|tsconfig\.json|dist|docs)/g exclude: /(src|webpack\.config\.js|tsconfig\.json|dist|docs)/g
}, ((filepath) => filepath.endsWith(".js") && (!production ? !filepath.includes("node_modules") : true)), async (filepath, newpath) => { }, ((filepath) => filepath.endsWith(".js") && (!production ? !filepath.includes("node_modules") : true)), async (filepath, newpath) => {
await fs.promises.copyFile(filepath, newpath) await fs.promises.copyFile(filepath, newpath)
}, true).then(() => { }, true).then(() => {
console.info(`Copied files and minified them from ${path.join(__dirname, "LightcordApi")}.`) console.info(`Copied files and minified them from ${path.join(__dirname, "LightcordApi")}.`)
}) })
child_process.execSync("npm install --only=prod", { child_process.execSync("npm install --only=prod", {
encoding: "binary", encoding: "binary",
cwd: path.join(__dirname, "distApp", "LightcordApi"), cwd: path.join(__dirname, "distApp", "LightcordApi"),
stdio: "inherit" stdio: "inherit"
}) })
function processDJS(dir){ function processDJS(dir){
fs.mkdirSync(path.join(__dirname, "distApp", "DiscordJS", dir), {recursive: true}) fs.mkdirSync(path.join(__dirname, "distApp", "DiscordJS", dir), {recursive: true})
return processNextDir(path.join(__dirname, "DiscordJS", dir), { return processNextDir(path.join(__dirname, "DiscordJS", dir), {
startDir: path.join(__dirname, "DiscordJS", dir), startDir: path.join(__dirname, "DiscordJS", dir),
newDir: path.join(__dirname, "distApp", "DiscordJS", dir), newDir: path.join(__dirname, "distApp", "DiscordJS", dir),
exclude: /node_modules/g exclude: /node_modules/g
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => { }, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
console.info(`Minifying ${filepath} to ${newpath}`) console.info(`Minifying ${filepath} to ${newpath}`)
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
}).then(() => { }).then(() => {
console.info(`Copied files and minified them from ${path.join(__dirname, "DiscordJS", dir)}.`) console.info(`Copied files and minified them from ${path.join(__dirname, "DiscordJS", dir)}.`)
}) })
} }
async function copyFileDJS(file){ async function copyFileDJS(file){
await fs.promises.writeFile(path.join(__dirname, "distApp", "DiscordJS", file), await fs.promises.readFile(path.join(__dirname, "DiscordJS", file))) await fs.promises.writeFile(path.join(__dirname, "distApp", "DiscordJS", file), await fs.promises.readFile(path.join(__dirname, "DiscordJS", file)))
} }
await processDJS("dist") await processDJS("dist")
await copyFileDJS("package.json") await copyFileDJS("package.json")
child_process.execSync("npm install --only=prod", { child_process.execSync("npm install --only=prod", {
encoding: "binary", encoding: "binary",
cwd: path.join(__dirname, "distApp", "DiscordJS"), cwd: path.join(__dirname, "distApp", "DiscordJS"),
stdio: "inherit" stdio: "inherit"
}) })
fs.mkdirSync(path.join(__dirname, "distApp", "BetterDiscordApp", "dist"), {recursive: true}) fs.mkdirSync(path.join(__dirname, "distApp", "BetterDiscordApp", "dist"), {recursive: true})
const BDPackageJSON = require("./BetterDiscordApp/package.json") const BDPackageJSON = require("./BetterDiscordApp/package.json")
fs.writeFileSync(path.join(__dirname, "distApp", "BetterDiscordApp", "package.json"), JSON.stringify(BDPackageJSON), "utf8") fs.writeFileSync(path.join(__dirname, "distApp", "BetterDiscordApp", "package.json"), JSON.stringify(BDPackageJSON), "utf8")
const files = [ const files = [
"index.min.js", "index.min.js",
"style.min.css" "style.min.css"
] ]
files.forEach(e => { files.forEach(e => {
files.push(e + ".map") files.push(e + ".map")
}) })
files.forEach(e => { files.forEach(e => {
const pth = path.join(__dirname, "BetterDiscordApp", "dist", e) const pth = path.join(__dirname, "BetterDiscordApp", "dist", e)
if(!fs.existsSync(pth))return console.error(`\x1b[31mFile ${pth} from betterdiscord does not exist.\x1b[0m`) if(!fs.existsSync(pth))return console.error(`\x1b[31mFile ${pth} from betterdiscord does not exist.\x1b[0m`)
if(e.endsWith(".map")){ if(e.endsWith(".map")){
const data = JSON.parse(fs.readFileSync(pth, "utf8")) const data = JSON.parse(fs.readFileSync(pth, "utf8"))
data.sourcesContent = [] data.sourcesContent = []
fs.writeFileSync(path.join(__dirname, "distApp", "BetterDiscordApp", "dist", e), JSON.stringify(data)) fs.writeFileSync(path.join(__dirname, "distApp", "BetterDiscordApp", "dist", e), JSON.stringify(data))
}else{ }else{
fs.copyFileSync(pth, path.join(__dirname, "distApp", "BetterDiscordApp", "dist", e)) fs.copyFileSync(pth, path.join(__dirname, "distApp", "BetterDiscordApp", "dist", e))
} }
}) })
await fs.promises.mkdir(path.join(__dirname, "distApp", "splash", "videos"), {recursive: true}) await fs.promises.mkdir(path.join(__dirname, "distApp", "splash", "videos"), {recursive: true})
await processNextDir(path.join(__dirname, "splash"), { await processNextDir(path.join(__dirname, "splash"), {
startDir: path.join(__dirname, "splash"), startDir: path.join(__dirname, "splash"),
newDir: path.join(__dirname, "distApp", "splash"), newDir: path.join(__dirname, "distApp", "splash"),
exclude: /node_modules/g exclude: /node_modules/g
}, (filepath) => { }, (filepath) => {
if(filepath.endsWith(".js"))return true if(filepath.endsWith(".js"))return true
return false return false
}, async (filepath, newpath) => { }, async (filepath, newpath) => {
console.info(`Minifying ${filepath} to ${newpath}`) console.info(`Minifying ${filepath} to ${newpath}`)
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8") await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
}).then(() => { }).then(() => {
console.info(`Copied files and minified them from ${path.join(__dirname, "splash")}.`) console.info(`Copied files and minified them from ${path.join(__dirname, "splash")}.`)
}) })
fs.writeFileSync(path.join(__dirname, "distApp", "LICENSE"), fs.readFileSync(path.join(__dirname, "LICENSE"))) fs.writeFileSync(path.join(__dirname, "distApp", "LICENSE"), fs.readFileSync(path.join(__dirname, "LICENSE")))
let packageJSON = require("./package.json") let packageJSON = require("./package.json")
packageJSON.scripts["build:electron_linux"] = packageJSON.scripts["build:electron_linux"].replace("./distApp", ".") packageJSON.scripts["build:electron_linux"] = packageJSON.scripts["build:electron_linux"].replace("./distApp", ".")
packageJSON.scripts["build:electron_win"] = packageJSON.scripts["build:electron_win"].replace("./distApp", ".") packageJSON.scripts["build:electron_win"] = packageJSON.scripts["build:electron_win"].replace("./distApp", ".")
packageJSON.scripts["build:electron_darwin"] = packageJSON.scripts["build:electron_darwin"].replace("./distApp", ".") packageJSON.scripts["build:electron_darwin"] = packageJSON.scripts["build:electron_darwin"].replace("./distApp", ".")
fs.writeFileSync(path.join(__dirname, "distApp", "package.json"), JSON.stringify(packageJSON), "utf8") fs.writeFileSync(path.join(__dirname, "distApp", "package.json"), JSON.stringify(packageJSON), "utf8")
console.info(`Installing ${Object.keys(packageJSON.dependencies).length} packages...`) console.info(`Installing ${Object.keys(packageJSON.dependencies).length} packages...`)
child_process.execSync("npm install --only=prod", { child_process.execSync("npm install --only=prod", {
encoding: "binary", encoding: "binary",
cwd: path.join(__dirname, "distApp"), cwd: path.join(__dirname, "distApp"),
stdio: "inherit" stdio: "inherit"
}) })
console.info("Build took "+(Date.now() - startTimestamp) +"ms.") console.info("Build took "+(Date.now() - startTimestamp) +"ms.")
} }
main() main()
.catch(err => { .catch(err => {
console.error(err) console.error(err)
process.exit(1) process.exit(1)
}) })

View File

@ -1,119 +1,119 @@
const spawn = require("cross-spawn") const spawn = require("cross-spawn")
const path = require("path") const path = require("path")
const { existsSync, promises: fsPromises } = require("fs") const { existsSync, promises: fsPromises } = require("fs")
const supportedPlatforms = [] const supportedPlatforms = []
const Platforms = { const Platforms = {
linux: { linux: {
name: "linux", name: "linux",
run: () => { run: () => {
return awaitExec("npm", ["run", "build:electron_linux"]) return awaitExec("npm", ["run", "build:electron_linux"])
} }
}, },
win: { win: {
name: "win", name: "win",
run: () => { run: () => {
return awaitExec("npm", ["run", "build:electron_win"]) return awaitExec("npm", ["run", "build:electron_win"])
} }
}, },
mac: { mac: {
name: "mac", name: "mac",
run: () => { run: () => {
return awaitExec("npm", ["run", "build:electron_darwin"]) return awaitExec("npm", ["run", "build:electron_darwin"])
} }
}, },
mac_experimental: { mac_experimental: {
name: "mac", name: "mac",
experimental: true, experimental: true,
run: async () => { run: async () => {
const basePath = path.join(__dirname, "..", "lightcord-darwin-x64") const basePath = path.join(__dirname, "..", "lightcord-darwin-x64")
const nextPath = path.join(__dirname, "builds", "lightcord-darwin-x64") const nextPath = path.join(__dirname, "builds", "lightcord-darwin-x64")
if(existsSync(nextPath)){ if(existsSync(nextPath)){
console.log(`Cleaning ${nextPath}.`) console.log(`Cleaning ${nextPath}.`)
await fsPromises.rmdir(nextPath, {recursive: true}) await fsPromises.rmdir(nextPath, {recursive: true})
} }
console.log(`Copying files from ${basePath}.`) console.log(`Copying files from ${basePath}.`)
let nextDir = async (pth) => { let nextDir = async (pth) => {
const newPath = pth.replace(basePath, nextPath) const newPath = pth.replace(basePath, nextPath)
await fsPromises.mkdir(newPath) await fsPromises.mkdir(newPath)
for(let file of await fsPromises.readdir(pth, {withFileTypes: true})){ for(let file of await fsPromises.readdir(pth, {withFileTypes: true})){
const filePath = path.join(pth, file.name) const filePath = path.join(pth, file.name)
const newFilePath = path.join(newPath, file.name) const newFilePath = path.join(newPath, file.name)
if(file.isFile()){ if(file.isFile()){
await fsPromises.copyFile(filePath, newFilePath) await fsPromises.copyFile(filePath, newFilePath)
}else if(file.isDirectory()){ }else if(file.isDirectory()){
await nextDir(filePath) await nextDir(filePath)
} }
} }
} }
await nextDir(basePath) await nextDir(basePath)
console.log(`Files are copied. Erasing current bundle if existing.`) console.log(`Files are copied. Erasing current bundle if existing.`)
const asarPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "app.asar") const asarPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "app.asar")
if(existsSync(asarPath))await fsPromises.unlink(asarPath) if(existsSync(asarPath))await fsPromises.unlink(asarPath)
const asarUnpackPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "app.asar.unpacked") const asarUnpackPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "app.asar.unpacked")
if(existsSync(asarUnpackPath))await fsPromises.rmdir(asarUnpackPath, {recursive: true}) if(existsSync(asarUnpackPath))await fsPromises.rmdir(asarUnpackPath, {recursive: true})
const asar = require("asar") const asar = require("asar")
await asar.createPackageWithOptions(path.join(__dirname, "distApp"), asarPath, { await asar.createPackageWithOptions(path.join(__dirname, "distApp"), asarPath, {
unpack: "*.{node,dylib,so.4,dll}", unpack: "*.{node,dylib,so.4,dll}",
unpackDir: asarUnpackPath unpackDir: asarUnpackPath
}) })
const iconPath = path.join(__dirname, "app_icon_darwin.icns") const iconPath = path.join(__dirname, "app_icon_darwin.icns")
if(existsSync(iconPath)){ if(existsSync(iconPath)){
console.log(`Setting icon.`) console.log(`Setting icon.`)
const newIconPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "electron.icns") const newIconPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "electron.icns")
await fsPromises.copyFile(iconPath, newIconPath) await fsPromises.copyFile(iconPath, newIconPath)
} }
} }
} }
} }
switch(process.platform){ switch(process.platform){
case "win32": case "win32":
supportedPlatforms.push(Platforms.win) supportedPlatforms.push(Platforms.win)
supportedPlatforms.push(Platforms.linux) supportedPlatforms.push(Platforms.linux)
if(existsSync(path.join(__dirname, "..", "lightcord-darwin-x64"))){ if(existsSync(path.join(__dirname, "..", "lightcord-darwin-x64"))){
supportedPlatforms.push(Platforms.mac_experimental) supportedPlatforms.push(Platforms.mac_experimental)
} }
break break
case "linux": case "linux":
supportedPlatforms.push(Platforms.linux) supportedPlatforms.push(Platforms.linux)
if(existsSync(path.join(__dirname, "..", "lightcord-darwin-x64"))){ if(existsSync(path.join(__dirname, "..", "lightcord-darwin-x64"))){
supportedPlatforms.push(Platforms.mac_experimental) supportedPlatforms.push(Platforms.mac_experimental)
} }
break break
case "darwin": case "darwin":
supportedPlatforms.push(Platforms.mac) supportedPlatforms.push(Platforms.mac)
supportedPlatforms.push(Platforms.linux) supportedPlatforms.push(Platforms.linux)
break break
} }
(async function(){ (async function(){
console.log(`[\x1b[33mINFO\x1b[0m] Will build platforms \x1b[34m${supportedPlatforms.map(e => e.name).join("\x1b[0m, \x1b[34m")}\x1b[0m`) console.log(`[\x1b[33mINFO\x1b[0m] Will build platforms \x1b[34m${supportedPlatforms.map(e => e.name).join("\x1b[0m, \x1b[34m")}\x1b[0m`)
for(let platform of supportedPlatforms){ for(let platform of supportedPlatforms){
console.log(`[\x1b[33mINFO\x1b[0m] Building platform ${platform.name}`) console.log(`[\x1b[33mINFO\x1b[0m] Building platform ${platform.name}`)
if(platform.experimental)console.warn(`[\x1b[33mWARN\x1b[0m] This platform is experimental`) if(platform.experimental)console.warn(`[\x1b[33mWARN\x1b[0m] This platform is experimental`)
await platform.run() await platform.run()
} }
})().catch(err => { })().catch(err => {
console.error(`Couldn't package app for electrons. Error: ${err}`) console.error(`Couldn't package app for electrons. Error: ${err}`)
}) })
function awaitExec(command, args = []){ function awaitExec(command, args = []){
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const child = spawn.spawn(command, args, { const child = spawn.spawn(command, args, {
env: process.env, env: process.env,
cwd: process.cwd(), cwd: process.cwd(),
stdio: "inherit" stdio: "inherit"
}) })
child.on("close", (code) => { child.on("close", (code) => {
console.log() console.log()
console.log() console.log()
console.log(`Command ${command}${args.length > 0 ? " " + args.join(" ") : ""} ended with code ${code}.`) console.log(`Command ${command}${args.length > 0 ? " " + args.join(" ") : ""} ended with code ${code}.`)
if(code !== 0){ if(code !== 0){
console.error("\x1b[31mFAILURE\x1b[0m Command failed. See logs above.") console.error("\x1b[31mFAILURE\x1b[0m Command failed. See logs above.")
return reject(code) return reject(code)
} }
resolve() resolve()
}) })
}) })
} }

View File

@ -1,30 +1,30 @@
const spawn = require("cross-spawn") const spawn = require("cross-spawn")
const { join } = require("path") const { join } = require("path")
/** Main Project */ /** Main Project */
spawnSync("tsc") spawnSync("tsc")
/** BetterDiscord */ /** BetterDiscord */
spawnSync("npm run build", join(__dirname, "BetterDiscordApp")) spawnSync("npm run build", join(__dirname, "BetterDiscordApp"))
spawnSync("npm run build-prod", join(__dirname, "BetterDiscordApp")) spawnSync("npm run build-prod", join(__dirname, "BetterDiscordApp"))
spawnSync("npm run minify-css", join(__dirname, "BetterDiscordApp")) spawnSync("npm run minify-css", join(__dirname, "BetterDiscordApp"))
/** DiscordJS */ /** DiscordJS */
spawnSync("npm run build", join(__dirname, "DiscordJS")) spawnSync("npm run build", join(__dirname, "DiscordJS"))
/** LightcordApi */ /** LightcordApi */
spawnSync("npm run build", join(__dirname, "LightcordApi")) spawnSync("npm run build", join(__dirname, "LightcordApi"))
spawnSync("npm run build-prod", join(__dirname, "LightcordApi")) spawnSync("npm run build-prod", join(__dirname, "LightcordApi"))
spawnSync("tsc", join(__dirname, "LightcordApi")) spawnSync("tsc", join(__dirname, "LightcordApi"))
function spawnSync(args, cwd){ function spawnSync(args, cwd){
args = args.split(" ") args = args.split(" ")
let command = args.shift() let command = args.shift()
return spawn(command, args, { return spawn(command, args, {
cwd: cwd || process.cwd(), cwd: cwd || process.cwd(),
env: process.env, env: process.env,
stdio: "inherit" stdio: "inherit"
}) })
} }
process.on("beforeExit", () => { process.on("beforeExit", () => {
console.log(`Exiting compilation`) console.log(`Exiting compilation`)
}) })

View File

@ -1,164 +1,164 @@
const fetch = require("node-fetch") const fetch = require("node-fetch")
const yauzl = require("yauzl") const yauzl = require("yauzl")
const tmp = require("tmp") const tmp = require("tmp")
const fs = require("fs") const fs = require("fs")
const util = require('util') const util = require('util')
const { join, dirname } = require("path") const { join, dirname } = require("path")
const streamPipeline = util.promisify(require('stream').pipeline) const streamPipeline = util.promisify(require('stream').pipeline)
const API_URL = "https://discord.com/api" const API_URL = "https://discord.com/api"
const branch = "stable" const branch = "stable"
const platforms = ["win", "linux", "osx"] const platforms = ["win", "linux", "osx"]
const platformsNormalized = { const platformsNormalized = {
win: "win32", win: "win32",
linux: "linux", linux: "linux",
osx: "darwin" osx: "darwin"
} }
const manuallyDownloads = [] const manuallyDownloads = []
const patchedJS = [] const patchedJS = []
;(async function(){ ;(async function(){
for(let platform of platforms){ for(let platform of platforms){
console.log(`[\x1b[32mINFO\x1b[0m] Downloading modules for ${platform} (${branch})`) console.log(`[\x1b[32mINFO\x1b[0m] Downloading modules for ${platform} (${branch})`)
const version = await fetch(`${API_URL}/updates/${branch}?platform=${platform}`) const version = await fetch(`${API_URL}/updates/${branch}?platform=${platform}`)
.then(res => res.json()).then(res => res.name) .then(res => res.json()).then(res => res.name)
console.log(`[\x1b[32mINFO\x1b[0m] Obtained version ${version} (${platform})`) console.log(`[\x1b[32mINFO\x1b[0m] Obtained version ${version} (${platform})`)
const modules = await fetch(`${API_URL}/modules/${branch}/versions.json?host_version=${version}&platform=${platform}`) const modules = await fetch(`${API_URL}/modules/${branch}/versions.json?host_version=${version}&platform=${platform}`)
.then(res => res.json()) .then(res => res.json())
console.log(`[\x1b[32mINFO\x1b[0m] Downloading ${Object.keys(modules).length} modules.`) console.log(`[\x1b[32mINFO\x1b[0m] Downloading ${Object.keys(modules).length} modules.`)
for(const module in modules){ for(const module in modules){
if(["discord_desktop_core"].includes(module))continue if(["discord_desktop_core"].includes(module))continue
const moduleVersion = modules[module] const moduleVersion = modules[module]
console.log(`[\x1b[32mINFO\x1b[0m] Downloading ${module} v${moduleVersion}.`) console.log(`[\x1b[32mINFO\x1b[0m] Downloading ${module} v${moduleVersion}.`)
const file = tmp.fileSync({ const file = tmp.fileSync({
prefix: module+"-", prefix: module+"-",
postfix: ".zip" postfix: ".zip"
}) })
const res = await fetch(`${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`, { const res = await fetch(`${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`, {
redirect: "follow" redirect: "follow"
}) })
if(res.status !== 200)throw new Error(`res.status !== 200. ${res.status} ${module} v${moduleVersion} ${branch} ${platform}`) if(res.status !== 200)throw new Error(`res.status !== 200. ${res.status} ${module} v${moduleVersion} ${branch} ${platform}`)
await streamPipeline(res.body, fs.createWriteStream(file.name)) await streamPipeline(res.body, fs.createWriteStream(file.name))
/** /**
* @type {yauzl.ZipFile} * @type {yauzl.ZipFile}
*/ */
const zipFile = await new Promise((resolve, reject) => { const zipFile = await new Promise((resolve, reject) => {
yauzl.open(file.name, {lazyEntries: true, autoClose: true}, (err, zip) => { yauzl.open(file.name, {lazyEntries: true, autoClose: true}, (err, zip) => {
if(err)return reject(err) if(err)return reject(err)
resolve(zip) resolve(zip)
}) })
}) })
const modulePath = join(__dirname, "modules", module) const modulePath = join(__dirname, "modules", module)
const exists = fs.existsSync(modulePath) const exists = fs.existsSync(modulePath)
let hasNode = false let hasNode = false
let hasBinaries = ["discord_hook", "discord_modules"].includes(module) let hasBinaries = ["discord_hook", "discord_modules"].includes(module)
let hasNativeDependencies = ["discord_voice", "discord_krisp"].includes(module) let hasNativeDependencies = ["discord_voice", "discord_krisp"].includes(module)
if(!exists){ if(!exists){
console.warn(`[\x1b[33mWARN\x1b[0m] Downloading whole module because it doesn't exists in your files.`) console.warn(`[\x1b[33mWARN\x1b[0m] Downloading whole module because it doesn't exists in your files.`)
fs.mkdirSync(modulePath) fs.mkdirSync(modulePath)
} }
await new Promise((resolve) => { await new Promise((resolve) => {
zipFile.readEntry() zipFile.readEntry()
zipFile.on("entry", function(entry) { zipFile.on("entry", function(entry) {
const fileName = entry.fileName.toString("utf8") const fileName = entry.fileName.toString("utf8")
if(fileName.endsWith("/")){ if(fileName.endsWith("/")){
const folderPath = join(modulePath, fileName) const folderPath = join(modulePath, fileName)
if(!fs.existsSync(folderPath))fs.mkdirSync(folderPath, {recursive: true}) if(!fs.existsSync(folderPath))fs.mkdirSync(folderPath, {recursive: true})
zipFile.readEntry(); zipFile.readEntry();
}else{ }else{
if(!exists){ if(!exists){
const filePath = join(modulePath, fileName) const filePath = join(modulePath, fileName)
zipFile.openReadStream(entry, function(err, readStream) { zipFile.openReadStream(entry, function(err, readStream) {
if (err) throw err; if (err) throw err;
readStream.on("end", function() { readStream.on("end", function() {
let content = fs.readFileSync(filePath, "utf-8") let content = fs.readFileSync(filePath, "utf-8")
if(content.includes(module+".node")){ if(content.includes(module+".node")){
content = content.replace(`${module}.node`, `${module}_'+process.platform+'.node`) content = content.replace(`${module}.node`, `${module}_'+process.platform+'.node`)
fs.writeFileSync(filePath, content) fs.writeFileSync(filePath, content)
} }
zipFile.readEntry(); zipFile.readEntry();
}); });
readStream.pipe(fs.createWriteStream(filePath)); readStream.pipe(fs.createWriteStream(filePath));
}); });
}else{ }else{
let filePath = join(modulePath, fileName) let filePath = join(modulePath, fileName)
if(filePath.endsWith(".asar"))return zipFile.readEntry(); if(filePath.endsWith(".asar"))return zipFile.readEntry();
if(fileName.endsWith(".node")){ if(fileName.endsWith(".node")){
hasNode = true hasNode = true
filePath = filePath.replace(".node", "_"+platformsNormalized[platform]+".node") filePath = filePath.replace(".node", "_"+platformsNormalized[platform]+".node")
extractFile() extractFile()
}else if(join(filePath, "..") === modulePath && filePath.endsWith(".js")){ }else if(join(filePath, "..") === modulePath && filePath.endsWith(".js")){
if(patchedJS.includes(filePath))return zipFile.readEntry(); if(patchedJS.includes(filePath))return zipFile.readEntry();
patchedJS.push(filePath) patchedJS.push(filePath)
extractFile(() => { extractFile(() => {
setTimeout(() => { setTimeout(() => {
let content = fs.readFileSync(filePath, "utf-8") let content = fs.readFileSync(filePath, "utf-8")
if(content.includes(module+".node")){ if(content.includes(module+".node")){
content = content.replace(`${module}.node`, `${module}_'+process.platform+'.node`) content = content.replace(`${module}.node`, `${module}_'+process.platform+'.node`)
fs.writeFileSync(filePath, content) fs.writeFileSync(filePath, content)
} }
}, 10); }, 10);
}) })
}else if(hasBinaries && ["exe", "dll"].map(e => { }else if(hasBinaries && ["exe", "dll"].map(e => {
return filePath.endsWith("."+e) return filePath.endsWith("."+e)
}).includes(true)){ // binaries }).includes(true)){ // binaries
extractFile() extractFile()
}else if(hasNativeDependencies && ["so.4", "dll", "dylib", "thw"].map(e => { }else if(hasNativeDependencies && ["so.4", "dll", "dylib", "thw"].map(e => {
return filePath.endsWith("."+e) return filePath.endsWith("."+e)
}).includes(true)){ }).includes(true)){
extractFile() extractFile()
}else{ }else{
zipFile.readEntry(); zipFile.readEntry();
} }
async function extractFile(onEnd){ async function extractFile(onEnd){
const dir = dirname(filePath) const dir = dirname(filePath)
if(!fs.existsSync(dir))await fs.promises.mkdir(dir, {recursive: true}) if(!fs.existsSync(dir))await fs.promises.mkdir(dir, {recursive: true})
zipFile.openReadStream(entry, function(err, readStream) { zipFile.openReadStream(entry, function(err, readStream) {
if(err)throw err; if(err)throw err;
readStream.on("end", function() { readStream.on("end", function() {
if(onEnd)onEnd() if(onEnd)onEnd()
zipFile.readEntry(); zipFile.readEntry();
}).on("error", (err) => { }).on("error", (err) => {
zipFile.close() zipFile.close()
console.error(err) console.error(err)
console.error(`[\x1b[31mERROR\x1b[0m] Skipping files because of error.`) console.error(`[\x1b[31mERROR\x1b[0m] Skipping files because of error.`)
console.error(`[\x1b[31mERROR\x1b[0m] Please download manually. ${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) console.error(`[\x1b[31mERROR\x1b[0m] Please download manually. ${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`)
resolve() resolve()
zipFile.readEntry(); zipFile.readEntry();
manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`)
}) })
try{ try{
readStream.pipe(fs.createWriteStream(filePath)); readStream.pipe(fs.createWriteStream(filePath));
}catch(err){ }catch(err){
zipFile.close() zipFile.close()
console.error(err) console.error(err)
console.error(`[\x1b[31mERROR\x1b[0m] Skipping files because of error.`) console.error(`[\x1b[31mERROR\x1b[0m] Skipping files because of error.`)
console.error(`[\x1b[31mERROR\x1b[0m] Please download manually. ${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) console.error(`[\x1b[31mERROR\x1b[0m] Please download manually. ${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`)
resolve() resolve()
zipFile.readEntry(); zipFile.readEntry();
manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`)
} }
}); });
} }
} }
} }
}).on("end", () => resolve()) }).on("end", () => resolve())
.on("error", (err) => { .on("error", (err) => {
zipFile.close() zipFile.close()
console.error(err) console.error(err)
console.error(`\x1b[31mERROR\x1b[0m] Skipping files because of error.`) console.error(`\x1b[31mERROR\x1b[0m] Skipping files because of error.`)
console.error(`\x1b[31mERROR\x1b[0m] Please download manually. ${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) console.error(`\x1b[31mERROR\x1b[0m] Please download manually. ${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`)
manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`) manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`)
}) })
}) })
if(hasNode){ if(hasNode){
console.log(`[\x1b[32mINFO\x1b[0m] .node files are now in ${module}.`) console.log(`[\x1b[32mINFO\x1b[0m] .node files are now in ${module}.`)
} }
} }
} }
if(manuallyDownloads.length > 0){ if(manuallyDownloads.length > 0){
console.error(`[\x1b[31mERROR\x1b[0m] Couldn't download some modules. Manual links:`) console.error(`[\x1b[31mERROR\x1b[0m] Couldn't download some modules. Manual links:`)
manuallyDownloads.forEach(console.log) manuallyDownloads.forEach(console.log)
} }
})() })()

View File

@ -1,54 +1,54 @@
const child_process = require("child_process") const child_process = require("child_process")
const fs = require("fs") const fs = require("fs")
const path = require("path") const path = require("path")
const MODULES_DIRNAME = path.join(__dirname, "modules") const MODULES_DIRNAME = path.join(__dirname, "modules")
fs.readdirSync(MODULES_DIRNAME, {withFileTypes: true}) fs.readdirSync(MODULES_DIRNAME, {withFileTypes: true})
.forEach(e => { .forEach(e => {
if(!e.isDirectory())return if(!e.isDirectory())return
const MODULE_DIRNAME = path.join(MODULES_DIRNAME, e.name) const MODULE_DIRNAME = path.join(MODULES_DIRNAME, e.name)
if(!fs.existsSync(path.join(MODULE_DIRNAME, "package.json")))return if(!fs.existsSync(path.join(MODULE_DIRNAME, "package.json")))return
if(e.name === "discord_spellcheck")return if(e.name === "discord_spellcheck")return
console.log(`Installing modules in ${e.name}.`) console.log(`Installing modules in ${e.name}.`)
child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], { child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], {
cwd: MODULE_DIRNAME, cwd: MODULE_DIRNAME,
env: process.env, env: process.env,
stdio: "inherit" stdio: "inherit"
}).on("error", (err) => { }).on("error", (err) => {
console.error(err) console.error(err)
process.exit(1) process.exit(1)
}) })
}) })
const MODULE_DIRNAME = path.join(__dirname, "modules", "discord_desktop_core", "core") const MODULE_DIRNAME = path.join(__dirname, "modules", "discord_desktop_core", "core")
const BETTERDISCORD_DIRNAME = path.join(__dirname, "BetterDiscordApp") const BETTERDISCORD_DIRNAME = path.join(__dirname, "BetterDiscordApp")
const DISCORDJS_DIRNAME = path.join(__dirname, "DiscordJS") const DISCORDJS_DIRNAME = path.join(__dirname, "DiscordJS")
child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], { child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], {
cwd: MODULE_DIRNAME, cwd: MODULE_DIRNAME,
env: process.env, env: process.env,
stdio: "inherit" stdio: "inherit"
}).on("error", (err) => { }).on("error", (err) => {
console.error(err) console.error(err)
process.exit(1) process.exit(1)
}) })
child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], { child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], {
cwd: BETTERDISCORD_DIRNAME, cwd: BETTERDISCORD_DIRNAME,
env: process.env, env: process.env,
stdio: "inherit" stdio: "inherit"
}).on("error", (err) => { }).on("error", (err) => {
console.error(err) console.error(err)
process.exit(1) process.exit(1)
}) })
child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], { child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], {
cwd: DISCORDJS_DIRNAME, cwd: DISCORDJS_DIRNAME,
env: process.env, env: process.env,
stdio: "inherit" stdio: "inherit"
}) })

View File

@ -1,36 +1,36 @@
const EventEmitter = require('events'); const EventEmitter = require('events');
const {CloudSync: CloudSyncNative} = require('./discord_cloudsync_'+process.platform+'.node'); const {CloudSync: CloudSyncNative} = require('./discord_cloudsync_'+process.platform+'.node');
function makeCallback(resolve, reject) { function makeCallback(resolve, reject) {
return (err, result) => { return (err, result) => {
if (err != null && err !== '') { if (err != null && err !== '') {
reject(new Error(JSON.parse(err))); reject(new Error(JSON.parse(err)));
} else { } else {
resolve(result != null && result !== '' ? JSON.parse(result) : null); resolve(result != null && result !== '' ? JSON.parse(result) : null);
} }
}; };
} }
class CloudSync extends EventEmitter { class CloudSync extends EventEmitter {
constructor() { constructor() {
super(); super();
this._cloudSync = new CloudSyncNative(state => this.emit('state', JSON.parse(state))); this._cloudSync = new CloudSyncNative(state => this.emit('state', JSON.parse(state)));
} }
sync(id, config) { sync(id, config) {
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
this._cloudSync.command(JSON.stringify({type: 'SYNC', id, config}), makeCallback(resolve, reject)) this._cloudSync.command(JSON.stringify({type: 'SYNC', id, config}), makeCallback(resolve, reject))
); );
} }
} }
function cloudSyncConstructor() { function cloudSyncConstructor() {
const instance = new CloudSync(); const instance = new CloudSync();
return { return {
on: instance.on.bind(instance), on: instance.on.bind(instance),
sync: instance.sync.bind(instance), sync: instance.sync.bind(instance),
}; };
} }
module.exports = cloudSyncConstructor; module.exports = cloudSyncConstructor;

View File

@ -1,7 +1,7 @@
{ {
"files": [ "files": [
"discord_cloudsync.node", "discord_cloudsync.node",
"index.js", "index.js",
"manifest.json" "manifest.json"
] ]
} }

View File

@ -1,12 +1,12 @@
const electron = require("electron") const electron = require("electron")
const { useShim } = require("./patchNotifications"); const { useShim } = require("./patchNotifications");
const appSettings = electron.remote.getGlobal("appSettings") const appSettings = electron.remote.getGlobal("appSettings")
module.exports = { module.exports = {
NotificationsUseShim: (value) => { NotificationsUseShim: (value) => {
if(![true, false].includes(value))return if(![true, false].includes(value))return
appSettings.set("DEFAULT_NOTIFICATIONS", !value) appSettings.set("DEFAULT_NOTIFICATIONS", !value)
appSettings.save() appSettings.save()
useShim(value) useShim(value)
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,253 +1,253 @@
/** /**
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2018, Nick Gavrilov * Copyright (c) 2018, Nick Gavrilov
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
* *
* From https://github.com/ilearnio/module-alias * From https://github.com/ilearnio/module-alias
*/ */
'use strict' 'use strict'
var BuiltinModule = require('module') var BuiltinModule = require('module')
// Guard against poorly mocked module constructors // Guard against poorly mocked module constructors
var Module = module.constructor.length > 1 var Module = module.constructor.length > 1
? module.constructor ? module.constructor
: BuiltinModule : BuiltinModule
var nodePath = require('path') var nodePath = require('path')
var modulePaths = [] var modulePaths = []
var moduleAliases = {} var moduleAliases = {}
var moduleAliasNames = [] var moduleAliasNames = []
var oldNodeModulePaths = Module._nodeModulePaths var oldNodeModulePaths = Module._nodeModulePaths
Module._nodeModulePaths = function (from) { Module._nodeModulePaths = function (from) {
var paths = oldNodeModulePaths.call(this, from) var paths = oldNodeModulePaths.call(this, from)
// Only include the module path for top-level modules // Only include the module path for top-level modules
// that were not installed: // that were not installed:
if (from.indexOf('node_modules') === -1) { if (from.indexOf('node_modules') === -1) {
paths = modulePaths.concat(paths) paths = modulePaths.concat(paths)
} }
return paths return paths
} }
var oldResolveFilename = Module._resolveFilename var oldResolveFilename = Module._resolveFilename
Module._resolveFilename = function (request, parentModule, isMain, options) { Module._resolveFilename = function (request, parentModule, isMain, options) {
for (var i = moduleAliasNames.length; i-- > 0;) { for (var i = moduleAliasNames.length; i-- > 0;) {
var alias = moduleAliasNames[i] var alias = moduleAliasNames[i]
if (isPathMatchesAlias(request, alias)) { if (isPathMatchesAlias(request, alias)) {
var aliasTarget = moduleAliases[alias] var aliasTarget = moduleAliases[alias]
// Custom function handler // Custom function handler
if (typeof moduleAliases[alias] === 'function') { if (typeof moduleAliases[alias] === 'function') {
var fromPath = parentModule.filename var fromPath = parentModule.filename
aliasTarget = moduleAliases[alias](fromPath, request, alias) aliasTarget = moduleAliases[alias](fromPath, request, alias)
if (!aliasTarget || typeof aliasTarget !== 'string') { if (!aliasTarget || typeof aliasTarget !== 'string') {
throw new Error('[module-alias] Expecting custom handler function to return path.') throw new Error('[module-alias] Expecting custom handler function to return path.')
} }
} }
request = nodePath.join(aliasTarget, request.substr(alias.length)) request = nodePath.join(aliasTarget, request.substr(alias.length))
// Only use the first match // Only use the first match
break break
} }
} }
return oldResolveFilename.call(this, request, parentModule, isMain, options) return oldResolveFilename.call(this, request, parentModule, isMain, options)
} }
function isPathMatchesAlias (path, alias) { function isPathMatchesAlias (path, alias) {
// Matching /^alias(\/|$)/ // Matching /^alias(\/|$)/
if (path.indexOf(alias) === 0) { if (path.indexOf(alias) === 0) {
if (path.length === alias.length) return true if (path.length === alias.length) return true
if (path[alias.length] === '/') return true if (path[alias.length] === '/') return true
} }
return false return false
} }
function addPathHelper (path, targetArray) { function addPathHelper (path, targetArray) {
path = nodePath.normalize(path) path = nodePath.normalize(path)
if (targetArray && targetArray.indexOf(path) === -1) { if (targetArray && targetArray.indexOf(path) === -1) {
targetArray.unshift(path) targetArray.unshift(path)
} }
} }
function removePathHelper (path, targetArray) { function removePathHelper (path, targetArray) {
if (targetArray) { if (targetArray) {
var index = targetArray.indexOf(path) var index = targetArray.indexOf(path)
if (index !== -1) { if (index !== -1) {
targetArray.splice(index, 1) targetArray.splice(index, 1)
} }
} }
} }
function addPath (path) { function addPath (path) {
var parent var parent
path = nodePath.normalize(path) path = nodePath.normalize(path)
if (modulePaths.indexOf(path) === -1) { if (modulePaths.indexOf(path) === -1) {
modulePaths.push(path) modulePaths.push(path)
// Enable the search path for the current top-level module // Enable the search path for the current top-level module
var mainModule = getMainModule() var mainModule = getMainModule()
if (mainModule) { if (mainModule) {
addPathHelper(path, mainModule.paths) addPathHelper(path, mainModule.paths)
} }
parent = module.parent parent = module.parent
// Also modify the paths of the module that was used to load the // Also modify the paths of the module that was used to load the
// app-module-paths module and all of it's parents // app-module-paths module and all of it's parents
while (parent && parent !== mainModule) { while (parent && parent !== mainModule) {
addPathHelper(path, parent.paths) addPathHelper(path, parent.paths)
parent = parent.parent parent = parent.parent
} }
} }
} }
function addAliases (aliases) { function addAliases (aliases) {
for (var alias in aliases) { for (var alias in aliases) {
addAlias(alias, aliases[alias]) addAlias(alias, aliases[alias])
} }
} }
function addAlias (alias, target) { function addAlias (alias, target) {
moduleAliases[alias] = target moduleAliases[alias] = target
// Cost of sorting is lower here than during resolution // Cost of sorting is lower here than during resolution
moduleAliasNames = Object.keys(moduleAliases) moduleAliasNames = Object.keys(moduleAliases)
moduleAliasNames.sort() moduleAliasNames.sort()
} }
/** /**
* Reset any changes maded (resets all registered aliases * Reset any changes maded (resets all registered aliases
* and custom module directories) * and custom module directories)
* The function is undocumented and for testing purposes only * The function is undocumented and for testing purposes only
*/ */
function reset () { function reset () {
var mainModule = getMainModule() var mainModule = getMainModule()
// Reset all changes in paths caused by addPath function // Reset all changes in paths caused by addPath function
modulePaths.forEach(function (path) { modulePaths.forEach(function (path) {
if (mainModule) { if (mainModule) {
removePathHelper(path, mainModule.paths) removePathHelper(path, mainModule.paths)
} }
// Delete from require.cache if the module has been required before. // Delete from require.cache if the module has been required before.
// This is required for node >= 11 // This is required for node >= 11
Object.getOwnPropertyNames(require.cache).forEach(function (name) { Object.getOwnPropertyNames(require.cache).forEach(function (name) {
if (name.indexOf(path) !== -1) { if (name.indexOf(path) !== -1) {
delete require.cache[name] delete require.cache[name]
} }
}) })
var parent = module.parent var parent = module.parent
while (parent && parent !== mainModule) { while (parent && parent !== mainModule) {
removePathHelper(path, parent.paths) removePathHelper(path, parent.paths)
parent = parent.parent parent = parent.parent
} }
}) })
modulePaths = [] modulePaths = []
moduleAliases = {} moduleAliases = {}
moduleAliasNames = [] moduleAliasNames = []
} }
/** /**
* Import aliases from package.json * Import aliases from package.json
* @param {object} options * @param {object} options
*/ */
function init (options) { function init (options) {
if (typeof options === 'string') { if (typeof options === 'string') {
options = { base: options } options = { base: options }
} }
options = options || {} options = options || {}
var candidatePackagePaths var candidatePackagePaths
if (options.base) { if (options.base) {
candidatePackagePaths = [nodePath.resolve(options.base.replace(/\/package\.json$/, ''))] candidatePackagePaths = [nodePath.resolve(options.base.replace(/\/package\.json$/, ''))]
} else { } else {
// There is probably 99% chance that the project root directory in located // There is probably 99% chance that the project root directory in located
// above the node_modules directory, // above the node_modules directory,
// Or that package.json is in the node process' current working directory (when // Or that package.json is in the node process' current working directory (when
// running a package manager script, e.g. `yarn start` / `npm run start`) // running a package manager script, e.g. `yarn start` / `npm run start`)
candidatePackagePaths = [nodePath.join(__dirname, '../..'), process.cwd()] candidatePackagePaths = [nodePath.join(__dirname, '../..'), process.cwd()]
} }
var npmPackage var npmPackage
var base var base
for (var i in candidatePackagePaths) { for (var i in candidatePackagePaths) {
try { try {
base = candidatePackagePaths[i] base = candidatePackagePaths[i]
npmPackage = require(nodePath.join(base, 'package.json')) npmPackage = require(nodePath.join(base, 'package.json'))
break break
} catch (e) { } catch (e) {
// noop // noop
} }
} }
if (typeof npmPackage !== 'object') { if (typeof npmPackage !== 'object') {
var pathString = candidatePackagePaths.join(',\n') var pathString = candidatePackagePaths.join(',\n')
throw new Error('Unable to find package.json in any of:\n[' + pathString + ']') throw new Error('Unable to find package.json in any of:\n[' + pathString + ']')
} }
// //
// Import aliases // Import aliases
// //
var aliases = npmPackage._moduleAliases || {} var aliases = npmPackage._moduleAliases || {}
for (var alias in aliases) { for (var alias in aliases) {
if (aliases[alias][0] !== '/') { if (aliases[alias][0] !== '/') {
aliases[alias] = nodePath.join(base, aliases[alias]) aliases[alias] = nodePath.join(base, aliases[alias])
} }
} }
addAliases(aliases) addAliases(aliases)
// //
// Register custom module directories (like node_modules) // Register custom module directories (like node_modules)
// //
if (npmPackage._moduleDirectories instanceof Array) { if (npmPackage._moduleDirectories instanceof Array) {
npmPackage._moduleDirectories.forEach(function (dir) { npmPackage._moduleDirectories.forEach(function (dir) {
if (dir === 'node_modules') return if (dir === 'node_modules') return
var modulePath = nodePath.join(base, dir) var modulePath = nodePath.join(base, dir)
addPath(modulePath) addPath(modulePath)
}) })
} }
} }
function getMainModule () { function getMainModule () {
return require.main._simulateRepl ? undefined : require.main return require.main._simulateRepl ? undefined : require.main
} }
module.exports = init module.exports = init
module.exports.addPath = addPath module.exports.addPath = addPath
module.exports.addAlias = addAlias module.exports.addAlias = addAlias
module.exports.addAliases = addAliases module.exports.addAliases = addAliases
module.exports.isPathMatchesAlias = isPathMatchesAlias module.exports.isPathMatchesAlias = isPathMatchesAlias
module.exports.reset = reset module.exports.reset = reset
module.exports.setMain = function(main){ module.exports.setMain = function(main){
require.main = main require.main = main
} }

View File

@ -1,148 +1,148 @@
let req let req
setReq() setReq()
class DangerousWebpackloader { class DangerousWebpackloader {
get modules(){ get modules(){
if(req){ if(req){
return Object.values(req.c).filter(e => e && e.exports) return Object.values(req.c).filter(e => e && e.exports)
}else{ }else{
setReq() setReq()
if(req){ if(req){
return Object.values(req.c).filter(e => e && e.exports) return Object.values(req.c).filter(e => e && e.exports)
}else{ }else{
return [] return []
} }
} }
} }
get(ids, modules){ get(ids, modules){
if(typeof ids === "function"){ if(typeof ids === "function"){
return (modules || this.modules).map((mdl) => { return (modules || this.modules).map((mdl) => {
if(mdl && typeof mdl.exports !== "undefined"){ if(mdl && typeof mdl.exports !== "undefined"){
return mdl.exports return mdl.exports
}else{ }else{
return null return null
} }
}).filter(e => e).filter(ids) }).filter(e => e).filter(ids)
}else if(Array.isArray(ids)){ }else if(Array.isArray(ids)){
modules = modules || this.modules modules = modules || this.modules
return ids.map(id => this.get(id, modules)) return ids.map(id => this.get(id, modules))
}else{ }else{
modules = modules || this.modules modules = modules || this.modules
let module = modules.filter(e => !!e).find(e => e.i === ids) let module = modules.filter(e => !!e).find(e => e.i === ids)
if(!module)return undefined if(!module)return undefined
return module.exports return module.exports
} }
} }
get default(){ get default(){
return this return this
} }
} }
function filterDangerous(mods){ function filterDangerous(mods){
return mods.map(e => { return mods.map(e => {
return protect(e) return protect(e)
}) })
} }
function protect(exports){ function protect(exports){
let theModule = exports.exports let theModule = exports.exports
let mod = theModule.default let mod = theModule.default
if(!mod)return exports if(!mod)return exports
if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null; if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null;
if (!mod.getToken && !mod.getEmail && !mod.showToken)return exports if (!mod.getToken && !mod.getEmail && !mod.showToken)return exports
const proxy = new Proxy(mod, { const proxy = new Proxy(mod, {
getOwnPropertyDescriptor: function(obj, prop) { getOwnPropertyDescriptor: function(obj, prop) {
if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined; if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined;
return Object.getOwnPropertyDescriptor(obj, prop); return Object.getOwnPropertyDescriptor(obj, prop);
}, },
get: function(obj, func) { get: function(obj, func) {
if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa"; if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa";
if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com"; if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com";
if (func == "showToken" && obj.showToken) return () => true; if (func == "showToken" && obj.showToken) return () => true;
if (func == "__proto__" && obj.__proto__) return proxy; if (func == "__proto__" && obj.__proto__) return proxy;
return obj[func]; return obj[func];
} }
}); });
return Object.assign({}, exports, {exports: Object.assign({}, theModule, {default: proxy})}) return Object.assign({}, exports, {exports: Object.assign({}, theModule, {default: proxy})})
} }
class Webpackloader { class Webpackloader {
get modules(){ get modules(){
if(req){ if(req){
return filterDangerous(Object.values(req.c).filter(e => e && e.exports)) return filterDangerous(Object.values(req.c).filter(e => e && e.exports))
}else{ }else{
setReq() setReq()
if(req){ if(req){
return filterDangerous(Object.values(req.c).filter(e => e && e.exports)) return filterDangerous(Object.values(req.c).filter(e => e && e.exports))
}else{ }else{
return [] return []
} }
} }
} }
get(ids, modules){ get(ids, modules){
if(typeof ids === "function"){ if(typeof ids === "function"){
return (modules || this.modules).map((mdl) => { return (modules || this.modules).map((mdl) => {
if(mdl && typeof mdl.exports !== "undefined"){ if(mdl && typeof mdl.exports !== "undefined"){
return mdl.exports return mdl.exports
}else{ }else{
return null return null
} }
}).filter(e => e).filter(ids) }).filter(e => e).filter(ids)
}else if(Array.isArray(ids)){ }else if(Array.isArray(ids)){
modules = modules || this.modules modules = modules || this.modules
return ids.map(id => this.get(id, modules)) return ids.map(id => this.get(id, modules))
}else{ }else{
modules = modules || this.modules modules = modules || this.modules
let module = modules.filter(e => !!e).find(e => e.i === ids) let module = modules.filter(e => !!e).find(e => e.i === ids)
if(!module)return undefined if(!module)return undefined
return module.exports return module.exports
} }
} }
get default(){ get default(){
return this return this
} }
} }
module.exports = new DangerousWebpackloader() module.exports = new DangerousWebpackloader()
global.BDModules = new Webpackloader() global.BDModules = new Webpackloader()
function setReq(){ function setReq(){
try{ try{
req = webpackJsonp.push([[], {__extra_id__: (mdl, exports, req) => mdl.exports = req}, [["__extra_id__"]]]); req = webpackJsonp.push([[], {__extra_id__: (mdl, exports, req) => mdl.exports = req}, [["__extra_id__"]]]);
if(req){ if(req){
delete req.m.__extra_id__; delete req.m.__extra_id__;
delete req.c.__extra_id__;/* delete req.c.__extra_id__;/*
Object.defineProperty(req.c, "0", { Object.defineProperty(req.c, "0", {
get: () => ({ get: () => ({
i: 0, i: 0,
l: true, l: true,
exports: require("react") exports: require("react")
}), }),
configurable: false, configurable: false,
enumerable: true enumerable: true
}) })
Object.defineProperty(req.c, "856", { Object.defineProperty(req.c, "856", {
get: () => ({ get: () => ({
i: 856, i: 856,
l: true, l: true,
exports: require("react") exports: require("react")
}), }),
configurable: false, configurable: false,
enumerable: true enumerable: true
}) })
Object.defineProperty(req.c, "71", { Object.defineProperty(req.c, "71", {
get: () => ({ get: () => ({
i: 71, i: 71,
l: true, l: true,
exports: require("react-dom") exports: require("react-dom")
}), }),
configurable: false, configurable: false,
enumerable: true enumerable: true
})*/ })*/
} }
}catch(e){ }catch(e){
req = undefined req = undefined
} }
} }

View File

@ -1,102 +1,102 @@
const ipcRenderer = require("../discord_native/renderer/ipc") const ipcRenderer = require("../discord_native/renderer/ipc")
if(process.platform === "win32"){ if(process.platform === "win32"){
let useShim = false let useShim = false
const originalNotification = window.Notification const originalNotification = window.Notification
ipcRenderer.send("NOTIFICATIONS_CLEAR") ipcRenderer.send("NOTIFICATIONS_CLEAR")
const notifications = {} const notifications = {}
ipcRenderer.on("NOTIFICATION_CLICK", (e, id) => { ipcRenderer.on("NOTIFICATION_CLICK", (e, id) => {
var notification = notifications[id]; var notification = notifications[id];
if(notification){ if(notification){
notification.onclick() notification.onclick()
notification.close() notification.close()
} }
}) })
class LightcordNotification { class LightcordNotification {
constructor(title, data){ constructor(title, data){
this.id = LightcordNotification._id++ this.id = LightcordNotification._id++
this.onshow = function() {} this.onshow = function() {}
this.onclick = function() {} this.onclick = function() {}
this.onclose = function() {} this.onclose = function() {}
this.title = title this.title = title
this.body = data.body this.body = data.body
this.icon = data.icon this.icon = data.icon
setTimeout(() => { setTimeout(() => {
return this.onshow() return this.onshow()
}, 0) }, 0)
notifications[this.id] = this notifications[this.id] = this
ipcRenderer.send("NOTIFICATION_SHOW", { ipcRenderer.send("NOTIFICATION_SHOW", {
id: this.id, id: this.id,
title: this.title, title: this.title,
body: this.body, body: this.body,
icon: this.icon, icon: this.icon,
theme: settingStore ? settingStore.default.theme : "dark" theme: settingStore ? settingStore.default.theme : "dark"
}) })
} }
static _id = 0 static _id = 0
static requestPermission(callback){ static requestPermission(callback){
callback() callback()
} }
close(){ close(){
if(!notifications[this.id])return if(!notifications[this.id])return
delete notifications[this.id] delete notifications[this.id]
ipcRenderer.send("NOTIFICATION_CLOSE", this.id) ipcRenderer.send("NOTIFICATION_CLOSE", this.id)
this.onclose() this.onclose()
} }
} }
LightcordNotification.permission = "granted" LightcordNotification.permission = "granted"
function Notification(){ function Notification(){
if(useShim)return new LightcordNotification(...arguments) if(useShim)return new LightcordNotification(...arguments)
return new originalNotification(...arguments) return new originalNotification(...arguments)
} }
Object.defineProperties(Notification, { Object.defineProperties(Notification, {
permission: { permission: {
get(){ get(){
if(useShim)return LightcordNotification.permission if(useShim)return LightcordNotification.permission
return originalNotification.permission return originalNotification.permission
} }
}, },
requestPermission: { requestPermission: {
get(){ get(){
if(useShim)return LightcordNotification.requestPermission if(useShim)return LightcordNotification.requestPermission
return originalNotification.requestPermission return originalNotification.requestPermission
} }
}, },
_id: { _id: {
get(){ get(){
if(useShim)return LightcordNotification._id if(useShim)return LightcordNotification._id
return originalNotification._id return originalNotification._id
} }
} }
}) })
window.Notification = Notification window.Notification = Notification
module.exports = { module.exports = {
useShim(use){ useShim(use){
useShim = !!use useShim = !!use
} }
} }
}else{ }else{
module.exports = { module.exports = {
useShim(){} useShim(){}
} }
} }
let settingStore let settingStore
ensureExported((e => e.default && e.default.theme)) ensureExported((e => e.default && e.default.theme))
.then(themeStore => { .then(themeStore => {
settingStore = themeStore settingStore = themeStore
ipcRenderer.send("UPDATE_THEME", themeStore.default.theme) ipcRenderer.send("UPDATE_THEME", themeStore.default.theme)
}).catch(console.error) }).catch(console.error)

View File

@ -1,97 +1,97 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { Object.defineProperty(exports, "__esModule", {
value: true value: true
}); });
exports.default = void 0; exports.default = void 0;
const loginContainerModule = BDModules.get(e => e.mainLoginContainer)[0]; const loginContainerModule = BDModules.get(e => e.mainLoginContainer)[0];
const colors = BDModules.get(e => e.colorHeaderPrimary)[0]; const colors = BDModules.get(e => e.colorHeaderPrimary)[0];
const sizes = BDModules.get(e => e.size24)[0]; const sizes = BDModules.get(e => e.size24)[0];
const authBoxModule = BDModules.get(e => e.title && typeof e.title === "string" && e.authBoxPadding)[0]; const authBoxModule = BDModules.get(e => e.title && typeof e.title === "string" && e.authBoxPadding)[0];
const marginModule = BDModules.get(e => e.marginBottom8)[0]; const marginModule = BDModules.get(e => e.marginBottom8)[0];
const titleModule = BDModules.get(e => e.h5)[0]; const titleModule = BDModules.get(e => e.h5)[0];
const inputModule = BDModules.get(e => e.inputWrapper)[0]; const inputModule = BDModules.get(e => e.inputWrapper)[0];
const contentModule = BDModules.get(e => e.contents)[0]; const contentModule = BDModules.get(e => e.contents)[0];
const verticalSeparatorModule = BDModules.get(e => e.verticalSeparator)[0]; const verticalSeparatorModule = BDModules.get(e => e.verticalSeparator)[0];
const loginModule = BDModules.get(e => e.default && e.default.loginToken)[0].default; const loginModule = BDModules.get(e => e.default && e.default.loginToken)[0].default;
class TokenLogin extends React.Component { class TokenLogin extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
} }
render() { render() {
return [/*#__PURE__*/React.createElement("div", { return [/*#__PURE__*/React.createElement("div", {
class: verticalSeparatorModule.verticalSeparator class: verticalSeparatorModule.verticalSeparator
}), /*#__PURE__*/React.createElement("div", { }), /*#__PURE__*/React.createElement("div", {
className: loginContainerModule.mainLoginContainer className: loginContainerModule.mainLoginContainer
}, /*#__PURE__*/React.createElement("div", { }, /*#__PURE__*/React.createElement("div", {
className: `${colors.colorHeaderPrimary} ${sizes.size24} ${authBoxModule.title} ${marginModule.marginBottom8}` className: `${colors.colorHeaderPrimary} ${sizes.size24} ${authBoxModule.title} ${marginModule.marginBottom8}`
}, "Connect with Token"), /*#__PURE__*/React.createElement("div", { }, "Connect with Token"), /*#__PURE__*/React.createElement("div", {
className: `${colors.colorHeaderSecondary} ${sizes.size16}` className: `${colors.colorHeaderSecondary} ${sizes.size16}`
}, "Input your token below"), /*#__PURE__*/React.createElement("div", { }, "Input your token below"), /*#__PURE__*/React.createElement("div", {
className: `${authBoxModule.block} ${marginModule.marginTop20}` className: `${authBoxModule.block} ${marginModule.marginTop20}`
}, /*#__PURE__*/React.createElement("div", { }, /*#__PURE__*/React.createElement("div", {
className: marginModule.marginBottom20 className: marginModule.marginBottom20
}, /*#__PURE__*/React.createElement(TokenInput, { }, /*#__PURE__*/React.createElement(TokenInput, {
ref: "input" ref: "input"
})), /*#__PURE__*/React.createElement("button", { })), /*#__PURE__*/React.createElement("button", {
type: "submit", type: "submit",
className: `${marginModule.marginBottom8} ${authBoxModule.button} ${contentModule.button} ${contentModule.lookFilled} ${contentModule.colorBrand} ${contentModule.sizeLarge} ${contentModule.fullWidth} ${contentModule.grow}`, className: `${marginModule.marginBottom8} ${authBoxModule.button} ${contentModule.button} ${contentModule.lookFilled} ${contentModule.colorBrand} ${contentModule.sizeLarge} ${contentModule.fullWidth} ${contentModule.grow}`,
onClick: () => { onClick: () => {
if (!this.refs.input.state.value) { if (!this.refs.input.state.value) {
this.refs.input.setState({ this.refs.input.setState({
error: "This field is necessary" error: "This field is necessary"
}); });
return; return;
} }
loginModule.loginToken(this.refs.input.state.value); loginModule.loginToken(this.refs.input.state.value);
ev.stopPropagation(); ev.stopPropagation();
} }
}, /*#__PURE__*/React.createElement("div", { }, /*#__PURE__*/React.createElement("div", {
className: contentModule.contents className: contentModule.contents
}, "Login"))))]; }, "Login"))))];
} }
} }
exports.default = TokenLogin; exports.default = TokenLogin;
class TokenInput extends React.Component { class TokenInput extends React.Component {
constructor() { constructor() {
super(...arguments); super(...arguments);
this.state = { this.state = {
value: "", value: "",
error: null error: null
}; };
} }
render() { render() {
return [/*#__PURE__*/React.createElement("h5", { return [/*#__PURE__*/React.createElement("h5", {
className: `${colors.colorStandard} ${sizes.size14} ${titleModule.h5} ${titleModule.defaultMarginh5}${this.state.error ? " " + titleModule.error : ""}` className: `${colors.colorStandard} ${sizes.size14} ${titleModule.h5} ${titleModule.defaultMarginh5}${this.state.error ? " " + titleModule.error : ""}`
}, "Token", this.state.error ? /*#__PURE__*/React.createElement("span", { }, "Token", this.state.error ? /*#__PURE__*/React.createElement("span", {
class: titleModule.errorMessage class: titleModule.errorMessage
}, /*#__PURE__*/React.createElement("span", { }, /*#__PURE__*/React.createElement("span", {
class: titleModule.errorSeparator class: titleModule.errorSeparator
}, "-"), this.state.error) : null), /*#__PURE__*/React.createElement("div", { }, "-"), this.state.error) : null), /*#__PURE__*/React.createElement("div", {
className: inputModule.inputWrapper className: inputModule.inputWrapper
}, /*#__PURE__*/React.createElement("input", { }, /*#__PURE__*/React.createElement("input", {
className: `${inputModule.inputDefault}${this.state.error ? " " + inputModule.inputError : ""}`, className: `${inputModule.inputDefault}${this.state.error ? " " + inputModule.inputError : ""}`,
name: "token", name: "token",
type: "token", type: "token",
placeholder: true, placeholder: true,
"aria-label": "Token", "aria-label": "Token",
autoComplete: "off", autoComplete: "off",
maxLength: 999, maxLength: 999,
spellCheck: "false", spellCheck: "false",
value: this.state.value, value: this.state.value,
onChange: ev => { onChange: ev => {
this.setState({ this.setState({
value: ev.target.value value: ev.target.value
}); });
} }
}))]; }))];
} }
} }

View File

@ -1,80 +1,80 @@
const loginContainerModule = BDModules.get(e => e.mainLoginContainer)[0] const loginContainerModule = BDModules.get(e => e.mainLoginContainer)[0]
const colors = BDModules.get(e => e.colorHeaderPrimary)[0] const colors = BDModules.get(e => e.colorHeaderPrimary)[0]
const sizes = BDModules.get(e => e.size24)[0] const sizes = BDModules.get(e => e.size24)[0]
const authBoxModule = BDModules.get(e => e.title && typeof e.title === "string" && e.authBoxPadding)[0] const authBoxModule = BDModules.get(e => e.title && typeof e.title === "string" && e.authBoxPadding)[0]
const marginModule = BDModules.get(e => e.marginBottom8)[0] const marginModule = BDModules.get(e => e.marginBottom8)[0]
const titleModule = BDModules.get(e => e.h5)[0] const titleModule = BDModules.get(e => e.h5)[0]
const inputModule = BDModules.get(e => e.inputWrapper)[0] const inputModule = BDModules.get(e => e.inputWrapper)[0]
const contentModule = BDModules.get(e => e.contents)[0] const contentModule = BDModules.get(e => e.contents)[0]
const verticalSeparatorModule = BDModules.get(e => e.verticalSeparator)[0] const verticalSeparatorModule = BDModules.get(e => e.verticalSeparator)[0]
const loginModule = BDModules.get(e => e.default && e.default.loginToken)[0].default const loginModule = BDModules.get(e => e.default && e.default.loginToken)[0].default
export default class TokenLogin extends React.Component { export default class TokenLogin extends React.Component {
constructor(props){ constructor(props){
super(props) super(props)
} }
render(){ render(){
return ([ return ([
<div class={verticalSeparatorModule.verticalSeparator}></div>, <div class={verticalSeparatorModule.verticalSeparator}></div>,
<div className={loginContainerModule.mainLoginContainer}> <div className={loginContainerModule.mainLoginContainer}>
<div className={`${colors.colorHeaderPrimary} ${sizes.size24} ${authBoxModule.title} ${marginModule.marginBottom8}`}> <div className={`${colors.colorHeaderPrimary} ${sizes.size24} ${authBoxModule.title} ${marginModule.marginBottom8}`}>
Connect with Token Connect with Token
</div> </div>
<div className={`${colors.colorHeaderSecondary} ${sizes.size16}`}> <div className={`${colors.colorHeaderSecondary} ${sizes.size16}`}>
Input your token below Input your token below
</div> </div>
<div className={`${authBoxModule.block} ${marginModule.marginTop20}`}> <div className={`${authBoxModule.block} ${marginModule.marginTop20}`}>
<div className={marginModule.marginBottom20}> <div className={marginModule.marginBottom20}>
<TokenInput ref="input"/> <TokenInput ref="input"/>
</div> </div>
<button type="submit" className={`${marginModule.marginBottom8} ${authBoxModule.button} ${contentModule.button} ${contentModule.lookFilled} ${contentModule.colorBrand} ${contentModule.sizeLarge} ${contentModule.fullWidth} ${contentModule.grow}`} onClick={() => { <button type="submit" className={`${marginModule.marginBottom8} ${authBoxModule.button} ${contentModule.button} ${contentModule.lookFilled} ${contentModule.colorBrand} ${contentModule.sizeLarge} ${contentModule.fullWidth} ${contentModule.grow}`} onClick={() => {
if(!this.refs.input.state.value){ if(!this.refs.input.state.value){
this.refs.input.setState({ this.refs.input.setState({
error: "This field is necessary" error: "This field is necessary"
}) })
return return
} }
loginModule.loginToken(this.refs.input.state.value) loginModule.loginToken(this.refs.input.state.value)
ev.stopPropagation() ev.stopPropagation()
}}> }}>
<div className={contentModule.contents}> <div className={contentModule.contents}>
Login Login
</div> </div>
</button> </button>
</div> </div>
</div> </div>
]); ]);
} }
} }
class TokenInput extends React.Component { class TokenInput extends React.Component {
constructor(){ constructor(){
super(...arguments) super(...arguments)
this.state = { this.state = {
value: "", value: "",
error: null error: null
} }
} }
render(){ render(){
return [ return [
<h5 className={`${colors.colorStandard} ${sizes.size14} ${titleModule.h5} ${titleModule.defaultMarginh5}${this.state.error ? " "+titleModule.error : ""}`}> <h5 className={`${colors.colorStandard} ${sizes.size14} ${titleModule.h5} ${titleModule.defaultMarginh5}${this.state.error ? " "+titleModule.error : ""}`}>
Token Token
{this.state.error ? <span class={titleModule.errorMessage}> {this.state.error ? <span class={titleModule.errorMessage}>
<span class={titleModule.errorSeparator}>-</span>{this.state.error} <span class={titleModule.errorSeparator}>-</span>{this.state.error}
</span> : null} </span> : null}
</h5>, </h5>,
<div className={inputModule.inputWrapper}> <div className={inputModule.inputWrapper}>
<input className={`${inputModule.inputDefault}${this.state.error ? " "+inputModule.inputError : ""}`} name="token" type="token" placeholder aria-label="Token" autoComplete="off" maxLength={999} spellCheck="false" value={this.state.value} onChange={(ev) => { <input className={`${inputModule.inputDefault}${this.state.error ? " "+inputModule.inputError : ""}`} name="token" type="token" placeholder aria-label="Token" autoComplete="off" maxLength={999} spellCheck="false" value={this.state.value} onChange={(ev) => {
this.setState({ this.setState({
value: ev.target.value value: ev.target.value
}) })
}}/> }}/>
</div> </div>
] ]
} }
} }

View File

@ -1,10 +1,10 @@
/** /**
* This file is for injections other than already present modules. mostly ipc thing * This file is for injections other than already present modules. mostly ipc thing
*/ */
const electron = require("electron") const electron = require("electron")
const UserAgent = electron.session.defaultSession.getUserAgent() const UserAgent = electron.session.defaultSession.getUserAgent()
electron.ipcMain.on("LIGHTCORD_GET_USER_AGENT", (event) => { electron.ipcMain.on("LIGHTCORD_GET_USER_AGENT", (event) => {
event.returnValue = UserAgent event.returnValue = UserAgent
}) })

View File

@ -1,68 +1,68 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lightcord Tabs</title> <title>Lightcord Tabs</title>
<script src="index.js"></script> <script src="index.js"></script>
<style> <style>
.documentFull { .documentFull {
width: 100%; width: 100%;
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
overflow: auto; overflow: auto;
right: 0; right: 0;
left: 0; left: 0;
} }
.discord-webview { .discord-webview {
margin: 0px; margin: 0px;
display: none; display: none;
position: absolute; position: absolute;
top: 38px; top: 38px;
right: 0; right: 0;
left: 0; left: 0;
bottom: 0; bottom: 0;
} }
.active-webview { .active-webview {
display: inherit display: inherit
} }
body { body {
margin: 0 margin: 0
} }
.surface { .surface {
-webkit-app-region: drag -webkit-app-region: drag
} }
.chrome-tab { .chrome-tab {
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
} }
</style> </style>
</head> </head>
<body> <body>
<div class="surface"> <div class="surface">
<div class="chrome-tabs chrome-tabs-dark-theme" style="--tab-content-margin: 9px;margin-top: -8px"> <div class="chrome-tabs chrome-tabs-dark-theme" style="--tab-content-margin: 9px;margin-top: -8px">
<div class="chrome-tabs-content" style="width: calc(100% - 200px)"></div> <div class="chrome-tabs-content" style="width: calc(100% - 200px)"></div>
<div class="chrome-tabs-bottom-bar"></div> <div class="chrome-tabs-bottom-bar"></div>
<!-- Styles to prevent flash after JS initialization --> <!-- Styles to prevent flash after JS initialization -->
<style> <style>
.chrome-tabs .chrome-tab { .chrome-tabs .chrome-tab {
width: 258px width: 258px
} }
.chrome-tabs .chrome-tab:nth-child(1) { .chrome-tabs .chrome-tab:nth-child(1) {
transform: translate3d(0px, 0, 0) transform: translate3d(0px, 0, 0)
} }
.chrome-tabs .chrome-tab:nth-child(2) { .chrome-tabs .chrome-tab:nth-child(2) {
transform: translate3d(239px, 0, 0) transform: translate3d(239px, 0, 0)
} }
</style> </style>
</div> </div>
<div class="chrome-tabs-optional-shadow-below-bottom-bar"></div> <div class="chrome-tabs-optional-shadow-below-bottom-bar"></div>
</div> </div>
<div class="documentFull"></div> <div class="documentFull"></div>
</body> </body>
</html> </html>

View File

@ -1,182 +1,182 @@
const fs = require("fs") const fs = require("fs")
const { join } = require("path") const { join } = require("path")
const { pathToFileURL } = require("url") const { pathToFileURL } = require("url")
const ipc = require("../discord_native/renderer/ipc") const ipc = require("../discord_native/renderer/ipc")
let webviews = new Map() let webviews = new Map()
window.webviews = webviews window.webviews = webviews
function forwardToCurrentWebview(event){ function forwardToCurrentWebview(event){
return [event, async (...args) => { return [event, async (...args) => {
let webview = webviews.get(document.querySelector(".chrome-tab[active]")) let webview = webviews.get(document.querySelector(".chrome-tab[active]"))
if(!webview)return if(!webview)return
await webview.ready await webview.ready
webview.send(event, ...args.slice(1)) webview.send(event, ...args.slice(1))
}] }]
} }
/** discord_desktop_core Stable */ /** discord_desktop_core Stable */
ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_FOCUS")) ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_FOCUS"))
ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_BLUR")) ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_BLUR"))
ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_OPEN_VOICE_SETTINGS")) ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_OPEN_VOICE_SETTINGS"))
ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_TOGGLE_MUTE")) ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_TOGGLE_MUTE"))
ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_TOGGLE_DEAFEN")) ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_TOGGLE_DEAFEN"))
ipc.on(...forwardToCurrentWebview("LAUNCH_APPLICATION")) ipc.on(...forwardToCurrentWebview("LAUNCH_APPLICATION"))
ipc.on(...forwardToCurrentWebview("SPELLCHECK_RESULT")) ipc.on(...forwardToCurrentWebview("SPELLCHECK_RESULT"))
ipc.on(...forwardToCurrentWebview("WINDOW_DEVTOOLS_OPENED")) ipc.on(...forwardToCurrentWebview("WINDOW_DEVTOOLS_OPENED"))
ipc.on(...forwardToCurrentWebview("WINDOW_DEVTOOLS_CLOSED")) ipc.on(...forwardToCurrentWebview("WINDOW_DEVTOOLS_CLOSED"))
ipc.on(...forwardToCurrentWebview("UPDATE_ERROR")) ipc.on(...forwardToCurrentWebview("UPDATE_ERROR"))
ipc.on(...forwardToCurrentWebview("UPDATE_NOT_AVAILABLE")) ipc.on(...forwardToCurrentWebview("UPDATE_NOT_AVAILABLE"))
ipc.on(...forwardToCurrentWebview("UPDATE_MANUALLY")) ipc.on(...forwardToCurrentWebview("UPDATE_MANUALLY"))
ipc.on(...forwardToCurrentWebview("UPDATE_AVAILABLE")) ipc.on(...forwardToCurrentWebview("UPDATE_AVAILABLE"))
ipc.on(...forwardToCurrentWebview("MODULE_INSTALL_PROGRESS")) ipc.on(...forwardToCurrentWebview("MODULE_INSTALL_PROGRESS"))
ipc.on(...forwardToCurrentWebview("UPDATE_DOWNLOADED")) ipc.on(...forwardToCurrentWebview("UPDATE_DOWNLOADED"))
ipc.on(...forwardToCurrentWebview("MODULE_INSTALLED")) ipc.on(...forwardToCurrentWebview("MODULE_INSTALLED"))
ipc.on(...forwardToCurrentWebview("CHECKING_FOR_UPDATES")) ipc.on(...forwardToCurrentWebview("CHECKING_FOR_UPDATES"))
ipc.on(...forwardToCurrentWebview("UPDATER_HISTORY_RESPONSE")) ipc.on(...forwardToCurrentWebview("UPDATER_HISTORY_RESPONSE"))
ipc.on(...forwardToCurrentWebview("ACCESSIBILITY_SUPPORT_CHANGED")) ipc.on(...forwardToCurrentWebview("ACCESSIBILITY_SUPPORT_CHANGED"))
ipc.on(...forwardToCurrentWebview("HELP_OPEN")) ipc.on(...forwardToCurrentWebview("HELP_OPEN"))
ipc.on(...forwardToCurrentWebview("USER_SETTINGS_OPEN")) ipc.on(...forwardToCurrentWebview("USER_SETTINGS_OPEN"))
ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_PATH")) ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_PATH"))
/** discord_desktop_core Development */ /** discord_desktop_core Development */
ipc.on(...forwardToCurrentWebview("NAVIGATE_BACK")) ipc.on(...forwardToCurrentWebview("NAVIGATE_BACK"))
ipc.on(...forwardToCurrentWebview("NAVIGATE_FORWARD")) ipc.on(...forwardToCurrentWebview("NAVIGATE_FORWARD"))
ipc.on("RELOAD", () => { ipc.on("RELOAD", () => {
let webview = webviews.get(document.querySelector(".chrome-tab[active]")) let webview = webviews.get(document.querySelector(".chrome-tab[active]"))
if(!webview)return if(!webview)return
webview.reload() webview.reload()
}) })
ipc.on("NEW_TAB", () => { ipc.on("NEW_TAB", () => {
chromeTabs.addTab({ chromeTabs.addTab({
title: 'Lightcord', title: 'Lightcord',
favicon: faviconURL favicon: faviconURL
}) })
}) })
ipc.on("CLOSE_TAB", () => { ipc.on("CLOSE_TAB", () => {
let active = document.querySelector("div.chrome-tab[active]") let active = document.querySelector("div.chrome-tab[active]")
if(!active)return if(!active)return
chromeTabs.removeTab(active) chromeTabs.removeTab(active)
}) })
ipc.on("OPEN_DEVTOOLS", () => { ipc.on("OPEN_DEVTOOLS", () => {
let webview = webviews.get(document.querySelector(".chrome-tab[active]")) let webview = webviews.get(document.querySelector(".chrome-tab[active]"))
if(!webview)return if(!webview)return
webview.openDevTools() webview.openDevTools()
}) })
window.onload = () => { window.onload = () => {
const ChromeTabs = require("chrome-tabs") const ChromeTabs = require("chrome-tabs")
require("chrome-tabs/css/chrome-tabs.css") require("chrome-tabs/css/chrome-tabs.css")
require("chrome-tabs/css/chrome-tabs-dark-theme.css") require("chrome-tabs/css/chrome-tabs-dark-theme.css")
require("./controls.css") require("./controls.css")
let tabs = document.querySelector(".chrome-tabs") let tabs = document.querySelector(".chrome-tabs")
let chromeTabs = new ChromeTabs() let chromeTabs = new ChromeTabs()
chromeTabs.init(tabs) chromeTabs.init(tabs)
tabs.addEventListener('activeTabChange', ({detail}) => { tabs.addEventListener('activeTabChange', ({detail}) => {
let webview = webviews.get(detail.tabEl) let webview = webviews.get(detail.tabEl)
if(!webview){ if(!webview){
chromeTabs.removeTab(detail.tabEl) chromeTabs.removeTab(detail.tabEl)
return return
} }
let active = Array.from(webviews.values()).find(e => e.classList.contains("active-webview")) let active = Array.from(webviews.values()).find(e => e.classList.contains("active-webview"))
if(active)active.classList.remove("active-webview") if(active)active.classList.remove("active-webview")
webview.classList.add("active-webview") webview.classList.add("active-webview")
}) })
tabs.addEventListener('tabAdd', ({detail}) => { tabs.addEventListener('tabAdd', ({detail}) => {
chromeTabs.updateTab(detail.tabEl, { chromeTabs.updateTab(detail.tabEl, {
title: "Lightcord Loading...", title: "Lightcord Loading...",
favicon: faviconURL favicon: faviconURL
}) })
let webview = document.createElement("webview") let webview = document.createElement("webview")
webview.src = "https://discord.com/app" webview.src = "https://discord.com/app"
webview.classList.add("discord-webview") webview.classList.add("discord-webview")
webview.classList.add("webview-active") webview.classList.add("webview-active")
webview.setAttribute("preload", pathToFileURL(join(__dirname, "../mainScreenPreload.js"))) webview.setAttribute("preload", pathToFileURL(join(__dirname, "../mainScreenPreload.js")))
webview.shadowRoot.childNodes.item(1).style.height = "100%" webview.shadowRoot.childNodes.item(1).style.height = "100%"
webview.enableremotemodule = true webview.enableremotemodule = true
webview.nodeintegration = false webview.nodeintegration = false
webview.spellcheck = true webview.spellcheck = true
webview.webpreferences = "nativeWindowOpen=yes" webview.webpreferences = "nativeWindowOpen=yes"
webview.enableblinkfeatures = "EnumerateDevices,AudioOutputDevices" webview.enableblinkfeatures = "EnumerateDevices,AudioOutputDevices"
webview.addEventListener("ipc-message", function(...ev){ // TODO: Why don't we receive Ipc Messages, but they get processed anyway (notification, etc) ? webview.addEventListener("ipc-message", function(...ev){ // TODO: Why don't we receive Ipc Messages, but they get processed anyway (notification, etc) ?
console.log(ev[0].channel) console.log(ev[0].channel)
if(ev[0].channel === "DISCORD_NEW_TAB"){ if(ev[0].channel === "DISCORD_NEW_TAB"){
chromeTabs.addTab({ chromeTabs.addTab({
title: 'Lightcord', title: 'Lightcord',
favicon: faviconURL favicon: faviconURL
}) })
return return
} }
ipc.send(ev[0].channel.replace("DISCORD_", ""), ev.slice(1)) ipc.send(ev[0].channel.replace("DISCORD_", ""), ev.slice(1))
}) })
webview.addEventListener('page-title-updated', () => { webview.addEventListener('page-title-updated', () => {
let el = Array.from(webviews.entries()).find(e => e[1] === webview)[0] let el = Array.from(webviews.entries()).find(e => e[1] === webview)[0]
if(!el)return if(!el)return
chromeTabs.updateTab(el, { chromeTabs.updateTab(el, {
favicon: faviconURL, favicon: faviconURL,
title: webview.getTitle() title: webview.getTitle()
}) })
}) })
webviews.set(detail.tabEl, webview) webviews.set(detail.tabEl, webview)
document.querySelector(".documentFull").appendChild(webview) document.querySelector(".documentFull").appendChild(webview)
let r let r
webview.ready = new Promise(resolve => (r = resolve)) webview.ready = new Promise(resolve => (r = resolve))
webview.addEventListener("dom-ready", () => { webview.addEventListener("dom-ready", () => {
r() r()
webview.send("DISCORD_IS_TAB") webview.send("DISCORD_IS_TAB")
}) })
webview.addEventListener("will-navigate", (e) => { webview.addEventListener("will-navigate", (e) => {
e.preventDefault() e.preventDefault()
console.log(e, e.url) console.log(e, e.url)
}) })
}) })
tabs.addEventListener('tabRemove', ({detail}) => { tabs.addEventListener('tabRemove', ({detail}) => {
let webview = webviews.get(detail.tabEl) let webview = webviews.get(detail.tabEl)
if(!webview)return if(!webview)return
webview.remove() webview.remove()
webviews.delete(detail.tabEl) webviews.delete(detail.tabEl)
if(document.querySelector(".chrome-tabs-content").childNodes.length === 0){ if(document.querySelector(".chrome-tabs-content").childNodes.length === 0){
window.close() window.close()
} }
}) })
window.addEventListener('keydown', (event) => { window.addEventListener('keydown', (event) => {
if(event.ctrlKey){ if(event.ctrlKey){
if(event.key === 't'){ if(event.key === 't'){
chromeTabs.addTab({ chromeTabs.addTab({
title: 'Lightcord', title: 'Lightcord',
favicon: faviconURL favicon: faviconURL
}) })
}else if(event.key === "w"){ }else if(event.key === "w"){
let active = document.querySelector("div.chrome-tab[active]") let active = document.querySelector("div.chrome-tab[active]")
if(!active)return if(!active)return
chromeTabs.removeTab(active) chromeTabs.removeTab(active)
} }
} }
}) })
setImmediate(() => { setImmediate(() => {
chromeTabs.addTab({ chromeTabs.addTab({
title: 'Lightcord Loading...', title: 'Lightcord Loading...',
favicon: faviconURL favicon: faviconURL
}) })
}) })
} }
require.extensions[".css"] = (m, filename) => { require.extensions[".css"] = (m, filename) => {
let content = fs.readFileSync(filename, "binary") let content = fs.readFileSync(filename, "binary")
let style = document.createElement("style") let style = document.createElement("style")
style.id = btoa(filename) style.id = btoa(filename)
style.innerHTML = content style.innerHTML = content
document.head.appendChild(style) document.head.appendChild(style)
m.exports = { m.exports = {
id: style.id, id: style.id,
remove(){ remove(){
return style.remove() return style.remove()
} }
} }
return m.exports return m.exports
} }
const faviconURL = pathToFileURL(join(__dirname, "../images/discord.png")) const faviconURL = pathToFileURL(join(__dirname, "../images/discord.png"))

View File

@ -1,16 +1,16 @@
{ {
"name": "discord_desktop_core", "name": "discord_desktop_core",
"description": "Discord Client for Desktop - Light version - Core App", "description": "Discord Client for Desktop - Light version - Core App",
"main": "app/index.js", "main": "app/index.js",
"private": true, "private": true,
"dependencies": { "dependencies": {
"chrome-tabs": "^5.4.0", "chrome-tabs": "^5.4.0",
"invariant": "2.2.4", "invariant": "2.2.4",
"lodash": "4.17.19", "lodash": "4.17.19",
"mkdirp": "^1.0.4", "mkdirp": "^1.0.4",
"promise.allsettled": "^1.0.0", "promise.allsettled": "^1.0.0",
"request": "2.88.0", "request": "2.88.0",
"rimraf": "^2.6.3", "rimraf": "^2.6.3",
"yauzl": "^2.10.0" "yauzl": "^2.10.0"
} }
} }

View File

@ -1,11 +1,11 @@
const {Dispatch: DispatchNative} = require('./discord_dispatch_'+process.platform+'.node'); const {Dispatch: DispatchNative} = require('./discord_dispatch_'+process.platform+'.node');
function dispatchConstructor(jsonConfigString, updateCallback, errorCallback, analyticsCallback) { function dispatchConstructor(jsonConfigString, updateCallback, errorCallback, analyticsCallback) {
const instance = new DispatchNative(jsonConfigString, updateCallback, errorCallback, analyticsCallback); const instance = new DispatchNative(jsonConfigString, updateCallback, errorCallback, analyticsCallback);
return { return {
command: instance.command.bind(instance), command: instance.command.bind(instance),
destroy: instance.destroy.bind(instance), destroy: instance.destroy.bind(instance),
}; };
} }
module.exports = {Dispatch: dispatchConstructor}; module.exports = {Dispatch: dispatchConstructor};

View File

@ -1,7 +1,7 @@
{ {
"files": [ "files": [
"discord_dispatch.node", "discord_dispatch.node",
"index.js", "index.js",
"manifest.json" "manifest.json"
] ]
} }

View File

@ -1 +1 @@
module.exports = require('./discord_erlpack_'+process.platform+'.node'); module.exports = require('./discord_erlpack_'+process.platform+'.node');

View File

@ -1,7 +1,7 @@
{ {
"files": [ "files": [
"discord_erlpack.node", "discord_erlpack.node",
"index.js", "index.js",
"manifest.json" "manifest.json"
] ]
} }

View File

@ -1 +1 @@
module.exports = require('./discord_game_utils_'+process.platform+'.node'); module.exports = require('./discord_game_utils_'+process.platform+'.node');

View File

@ -1,7 +1,7 @@
{ {
"files": [ "files": [
"discord_game_utils.node", "discord_game_utils.node",
"index.js", "index.js",
"manifest.json" "manifest.json"
] ]
} }

View File

@ -1 +1 @@
module.exports = require('./discord_hook_'+process.platform+'.node'); module.exports = require('./discord_hook_'+process.platform+'.node');

View File

@ -1,17 +1,17 @@
const KrispModule = require('./discord_krisp_'+process.platform+'.node'); const KrispModule = require('./discord_krisp_'+process.platform+'.node');
KrispModule._initialize(); KrispModule._initialize();
KrispModule.getNcModels = function() { KrispModule.getNcModels = function() {
return new Promise(resolve => { return new Promise(resolve => {
KrispModule._getNcModels(models => resolve(models)); KrispModule._getNcModels(models => resolve(models));
}); });
}; };
KrispModule.getVadModels = function() { KrispModule.getVadModels = function() {
return new Promise(resolve => { return new Promise(resolve => {
KrispModule._getVadModels(models => resolve(models)); KrispModule._getVadModels(models => resolve(models));
}); });
}; };
module.exports = KrispModule; module.exports = KrispModule;

View File

@ -1,12 +1,12 @@
{ {
"files": [ "files": [
"discord_krisp.node", "discord_krisp.node",
"VAD_weight.thw", "VAD_weight.thw",
"NC_weight.thw", "NC_weight.thw",
"NC_small_16k.thw", "NC_small_16k.thw",
"c6.s.f.c55d28.thw", "c6.s.f.c55d28.thw",
"c6.xs.f.ca03f4.thw", "c6.xs.f.ca03f4.thw",
"index.js", "index.js",
"manifest.json" "manifest.json"
] ]
} }

View File

@ -1,7 +1,7 @@
const native = require('./discord_media_'+process.platform+'.node'); const native = require('./discord_media_'+process.platform+'.node');
module.exports = { module.exports = {
getSystemAnalyticsBlob() { getSystemAnalyticsBlob() {
return new Promise(resolve => native.getSystemAnalyticsBlob(resolve)); return new Promise(resolve => native.getSystemAnalyticsBlob(resolve));
}, },
}; };

Some files were not shown because too many files have changed in this diff Show More