Fix line endings
This commit is contained in:
parent
d1b6592a3d
commit
3daf362a4e
|
@ -1,12 +1,12 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
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
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
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
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt 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']
|
File diff suppressed because it is too large
Load Diff
|
@ -1,37 +1,37 @@
|
|||
const CleanCSS = require("clean-css")
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
|
||||
function minify(){
|
||||
let start = Date.now()
|
||||
console.log(`\x1b[33mMinifying...\x1b[0m`)
|
||||
const css = fs.readFileSync(path.join(__dirname, "./src/styles/index.css"), "utf-8")
|
||||
fs.writeFileSync(path.join(__dirname, "./dist/style.css"), css)
|
||||
const output = new CleanCSS({
|
||||
sourceMap: true
|
||||
}).minify(css)
|
||||
if(output.errors.length > 0){
|
||||
console.error("\x1b[31m"+output.errors.join("\n")+"\x1b[0m")
|
||||
}
|
||||
if(output.warnings.length > 0){
|
||||
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.map"), output.sourceMap)
|
||||
console.log(`\x1b[32mMinified in ${(Date.now() - start)}ms. Minified by ${Math.floor(output.stats.efficiency*100)}%\x1b[0m`)
|
||||
}
|
||||
module.exports.minify = minify
|
||||
|
||||
if(require.main === module){
|
||||
if(process.argv.includes("--watch")){
|
||||
console.log("\x1b[32mStarting watch mode.\x1b[0m")
|
||||
minify()
|
||||
console.log()
|
||||
fs.watchFile(path.join(__dirname, "./src/styles/index.css"), () => {
|
||||
minify()
|
||||
console.log()
|
||||
})
|
||||
}else{
|
||||
minify()
|
||||
}
|
||||
const CleanCSS = require("clean-css")
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
|
||||
function minify(){
|
||||
let start = Date.now()
|
||||
console.log(`\x1b[33mMinifying...\x1b[0m`)
|
||||
const css = fs.readFileSync(path.join(__dirname, "./src/styles/index.css"), "utf-8")
|
||||
fs.writeFileSync(path.join(__dirname, "./dist/style.css"), css)
|
||||
const output = new CleanCSS({
|
||||
sourceMap: true
|
||||
}).minify(css)
|
||||
if(output.errors.length > 0){
|
||||
console.error("\x1b[31m"+output.errors.join("\n")+"\x1b[0m")
|
||||
}
|
||||
if(output.warnings.length > 0){
|
||||
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.map"), output.sourceMap)
|
||||
console.log(`\x1b[32mMinified in ${(Date.now() - start)}ms. Minified by ${Math.floor(output.stats.efficiency*100)}%\x1b[0m`)
|
||||
}
|
||||
module.exports.minify = minify
|
||||
|
||||
if(require.main === module){
|
||||
if(process.argv.includes("--watch")){
|
||||
console.log("\x1b[32mStarting watch mode.\x1b[0m")
|
||||
minify()
|
||||
console.log()
|
||||
fs.watchFile(path.join(__dirname, "./src/styles/index.css"), () => {
|
||||
minify()
|
||||
console.log()
|
||||
})
|
||||
}else{
|
||||
minify()
|
||||
}
|
||||
}
|
|
@ -1,37 +1,37 @@
|
|||
{
|
||||
"name": "bandagedbd",
|
||||
"version": "0.3.5",
|
||||
"description": "Enhances Discord adding functionality and themes.",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"build": "webpack --progress --colors",
|
||||
"watch": "webpack --progress --colors --watch",
|
||||
"build-prod": "webpack --progress --colors --config prod.config.js",
|
||||
"watch-prod": "webpack --progress --colors --watch --config prod.config.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"minify-css": "node minify-css.js",
|
||||
"watch-css": "node minify-css.js --watch",
|
||||
"deploy": "npm run build-prod && npm run minify-css"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rauenzi/BetterDiscordApp.git"
|
||||
},
|
||||
"author": "rauenzi",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/rauenzi/BetterDiscordApp/issues"
|
||||
},
|
||||
"homepage": "https://github.com/rauenzi/BetterDiscordApp#readme",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.5",
|
||||
"@babel/preset-env": "^7.10.4",
|
||||
"@babel/preset-react": "^7.10.4",
|
||||
"@babel/register": "^7.10.5",
|
||||
"babel-loader": "^8.1.0",
|
||||
"circular-dependency-plugin": "^5.2.0",
|
||||
"clean-css": "^4.2.3",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-cli": "^3.3.12"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "bandagedbd",
|
||||
"version": "0.3.5",
|
||||
"description": "Enhances Discord adding functionality and themes.",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"build": "webpack --progress --colors",
|
||||
"watch": "webpack --progress --colors --watch",
|
||||
"build-prod": "webpack --progress --colors --config prod.config.js",
|
||||
"watch-prod": "webpack --progress --colors --watch --config prod.config.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"minify-css": "node minify-css.js",
|
||||
"watch-css": "node minify-css.js --watch",
|
||||
"deploy": "npm run build-prod && npm run minify-css"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rauenzi/BetterDiscordApp.git"
|
||||
},
|
||||
"author": "rauenzi",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/rauenzi/BetterDiscordApp/issues"
|
||||
},
|
||||
"homepage": "https://github.com/rauenzi/BetterDiscordApp#readme",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.5",
|
||||
"@babel/preset-env": "^7.10.4",
|
||||
"@babel/preset-react": "^7.10.4",
|
||||
"@babel/register": "^7.10.5",
|
||||
"babel-loader": "^8.1.0",
|
||||
"circular-dependency-plugin": "^5.2.0",
|
||||
"clean-css": "^4.2.3",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-cli": "^3.3.12"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const config = require("./webpack.config.js")
|
||||
|
||||
config.mode = "production"
|
||||
config.devtool = "source-map"
|
||||
config.output.filename = "index.min.js"
|
||||
|
||||
const config = require("./webpack.config.js")
|
||||
|
||||
config.mode = "production"
|
||||
config.devtool = "source-map"
|
||||
config.output.filename = "index.min.js"
|
||||
|
||||
module.exports = config
|
|
@ -1,143 +1,143 @@
|
|||
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 minSupportedVersion = "0.3.0";
|
||||
export const bbdVersion = "0.3.5";
|
||||
export const bbdChangelog = {
|
||||
description: "Big things are coming.",
|
||||
changes: [
|
||||
{
|
||||
title: "Bug Fixes",
|
||||
type: "fixed",
|
||||
items: [
|
||||
"Some fixes related to showing modals in the `BdApi`.",
|
||||
"Fixed the open folder buttons for plugins and themes"
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const settings = {
|
||||
"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"},
|
||||
"BetterDiscord Blue": {id: "bda-gs-b", info: "Replace Discord blue with BD Blue", implemented: false, hidden: false, cat: "core"},
|
||||
|
||||
/* Core */
|
||||
/* ====== */
|
||||
"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"},
|
||||
"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"},
|
||||
"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"},
|
||||
"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"},
|
||||
"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 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"},
|
||||
"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"},
|
||||
"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"},
|
||||
"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 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"},
|
||||
|
||||
/** 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"},
|
||||
"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"},
|
||||
"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"},
|
||||
"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},
|
||||
"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 */
|
||||
"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"},
|
||||
"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 */
|
||||
"Enable": {id: "lightcord-presence-1", info: "Enable RichPresence below.", implemented: true, hidden: false, cat: "status"}
|
||||
};
|
||||
|
||||
export const defaultCookie = {
|
||||
"bda-gs-1": true,
|
||||
"bda-gs-2": false,
|
||||
"bda-gs-3": false,
|
||||
"bda-gs-4": false,
|
||||
"bda-gs-5": true,
|
||||
"bda-gs-6": false,
|
||||
"bda-gs-7": false,
|
||||
"bda-gs-8": false,
|
||||
"bda-es-0": true,
|
||||
"bda-es-1": true,
|
||||
"bda-es-2": true,
|
||||
"bda-es-4": false,
|
||||
"bda-es-6": true,
|
||||
"bda-es-7": true,
|
||||
"bda-gs-b": false,
|
||||
"bda-es-8": true,
|
||||
"bda-dc-0": false,
|
||||
"bda-css-0": false,
|
||||
"bda-css-1": false,
|
||||
"bda-es-9": true,
|
||||
"fork-dm-1": false,
|
||||
"fork-ps-1": true,
|
||||
"fork-ps-2": true,
|
||||
"fork-ps-3": true,
|
||||
"fork-ps-4": true,
|
||||
"fork-ps-5": true,
|
||||
"fork-ps-6": true,
|
||||
"fork-ps-7": false,
|
||||
"fork-es-2": false,
|
||||
"fork-es-3": true,
|
||||
"fork-wp-1": false,
|
||||
"fork-wp-2": false,
|
||||
"fork-beta": true,
|
||||
"lightcord-1": false,
|
||||
"lightcord-2": true,
|
||||
"lightcord-presence-1": false,
|
||||
"lightcord-3": false,
|
||||
"lightcord-4": false,
|
||||
"lightcord-5": true,
|
||||
"lightcord-6": true,
|
||||
"lightcord-7": false,
|
||||
"lightcord-8": false,
|
||||
"lightcord-9": false,
|
||||
"lightcord-10": false,
|
||||
"no_window_bound": false,
|
||||
};
|
||||
|
||||
|
||||
export const settingsCookie = {};
|
||||
|
||||
export const settingsRPC = {};
|
||||
export const defaultRPC = {
|
||||
"name": "Lightcord",
|
||||
"application_id": "711416957718757418",
|
||||
"state": "Lightcord Client",
|
||||
"details": "Browsing Discord",
|
||||
"timestamps.start": Date.now(),
|
||||
"assets.small": null,
|
||||
"assets.large": "712323821037682791"
|
||||
}
|
||||
|
||||
export const lightcordSettings = {};
|
||||
export const defaultLightcordSettings = {};
|
||||
|
||||
export const bdpluginErrors = [];
|
||||
export const bdthemeErrors = []; // define for backwards compatibility
|
||||
|
||||
export const bdConfig = Object.create(BetterDiscordConfig);
|
||||
|
||||
export const bdthemes = {};
|
||||
export const bdplugins = {};
|
||||
|
||||
export const pluginCookie = {};
|
||||
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 minSupportedVersion = "0.3.0";
|
||||
export const bbdVersion = "0.3.5";
|
||||
export const bbdChangelog = {
|
||||
description: "Big things are coming.",
|
||||
changes: [
|
||||
{
|
||||
title: "Bug Fixes",
|
||||
type: "fixed",
|
||||
items: [
|
||||
"Some fixes related to showing modals in the `BdApi`.",
|
||||
"Fixed the open folder buttons for plugins and themes"
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const settings = {
|
||||
"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"},
|
||||
"BetterDiscord Blue": {id: "bda-gs-b", info: "Replace Discord blue with BD Blue", implemented: false, hidden: false, cat: "core"},
|
||||
|
||||
/* Core */
|
||||
/* ====== */
|
||||
"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"},
|
||||
"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"},
|
||||
"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"},
|
||||
"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"},
|
||||
"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 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"},
|
||||
"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"},
|
||||
"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"},
|
||||
"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 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"},
|
||||
|
||||
/** 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"},
|
||||
"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"},
|
||||
"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"},
|
||||
"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},
|
||||
"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 */
|
||||
"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"},
|
||||
"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 */
|
||||
"Enable": {id: "lightcord-presence-1", info: "Enable RichPresence below.", implemented: true, hidden: false, cat: "status"}
|
||||
};
|
||||
|
||||
export const defaultCookie = {
|
||||
"bda-gs-1": true,
|
||||
"bda-gs-2": false,
|
||||
"bda-gs-3": false,
|
||||
"bda-gs-4": false,
|
||||
"bda-gs-5": true,
|
||||
"bda-gs-6": false,
|
||||
"bda-gs-7": false,
|
||||
"bda-gs-8": false,
|
||||
"bda-es-0": true,
|
||||
"bda-es-1": true,
|
||||
"bda-es-2": true,
|
||||
"bda-es-4": false,
|
||||
"bda-es-6": true,
|
||||
"bda-es-7": true,
|
||||
"bda-gs-b": false,
|
||||
"bda-es-8": true,
|
||||
"bda-dc-0": false,
|
||||
"bda-css-0": false,
|
||||
"bda-css-1": false,
|
||||
"bda-es-9": true,
|
||||
"fork-dm-1": false,
|
||||
"fork-ps-1": true,
|
||||
"fork-ps-2": true,
|
||||
"fork-ps-3": true,
|
||||
"fork-ps-4": true,
|
||||
"fork-ps-5": true,
|
||||
"fork-ps-6": true,
|
||||
"fork-ps-7": false,
|
||||
"fork-es-2": false,
|
||||
"fork-es-3": true,
|
||||
"fork-wp-1": false,
|
||||
"fork-wp-2": false,
|
||||
"fork-beta": true,
|
||||
"lightcord-1": false,
|
||||
"lightcord-2": true,
|
||||
"lightcord-presence-1": false,
|
||||
"lightcord-3": false,
|
||||
"lightcord-4": false,
|
||||
"lightcord-5": true,
|
||||
"lightcord-6": true,
|
||||
"lightcord-7": false,
|
||||
"lightcord-8": false,
|
||||
"lightcord-9": false,
|
||||
"lightcord-10": false,
|
||||
"no_window_bound": false,
|
||||
};
|
||||
|
||||
|
||||
export const settingsCookie = {};
|
||||
|
||||
export const settingsRPC = {};
|
||||
export const defaultRPC = {
|
||||
"name": "Lightcord",
|
||||
"application_id": "711416957718757418",
|
||||
"state": "Lightcord Client",
|
||||
"details": "Browsing Discord",
|
||||
"timestamps.start": Date.now(),
|
||||
"assets.small": null,
|
||||
"assets.large": "712323821037682791"
|
||||
}
|
||||
|
||||
export const lightcordSettings = {};
|
||||
export const defaultLightcordSettings = {};
|
||||
|
||||
export const bdpluginErrors = [];
|
||||
export const bdthemeErrors = []; // define for backwards compatibility
|
||||
|
||||
export const bdConfig = Object.create(BetterDiscordConfig);
|
||||
|
||||
export const bdthemes = {};
|
||||
export const bdplugins = {};
|
||||
|
||||
export const pluginCookie = {};
|
||||
export const themeCookie = {};
|
|
@ -1,59 +1,59 @@
|
|||
import localStorageFix from "./localStorageFix";
|
||||
import loadingIcon from "./loadingIcon";
|
||||
localStorageFix();
|
||||
loadingIcon();
|
||||
|
||||
const deprecateGlobal = (key, value) => {
|
||||
Object.defineProperty(window, key, {
|
||||
get() {
|
||||
Utils.warn("Deprecated Global", `"${key}" will be removed in future versions. Please only use BdApi.`);
|
||||
return value;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
import * as Globals from "./0globals";
|
||||
|
||||
const globalKeys = Object.keys(Globals);
|
||||
for (const key of globalKeys) deprecateGlobal(key, Globals[key]);
|
||||
|
||||
|
||||
import BdApi from "./modules/bdApi";
|
||||
import BDV2 from "./modules/v2";
|
||||
import pluginModule from "./modules/pluginModule";
|
||||
import themeModule from "./modules/themeModule";
|
||||
import Utils from "./modules/utils";
|
||||
import BDEvents from "./modules/bdEvents";
|
||||
import settingsPanel from "./modules/settingsPanel";
|
||||
import DataStore from "./modules/dataStore";
|
||||
import ContentManager from "./modules/contentManager";
|
||||
import ClassNormalizer from "./modules/classNormalizer";
|
||||
|
||||
deprecateGlobal("BDV2", BDV2);
|
||||
deprecateGlobal("pluginModule", pluginModule);
|
||||
deprecateGlobal("themeModule", themeModule);
|
||||
deprecateGlobal("Utils", Utils);
|
||||
deprecateGlobal("BDEvents", BDEvents);
|
||||
deprecateGlobal("settingsPanel", settingsPanel);
|
||||
deprecateGlobal("DataStore", DataStore);
|
||||
deprecateGlobal("ContentManager", ContentManager);
|
||||
deprecateGlobal("ClassNormalizer", ClassNormalizer);
|
||||
|
||||
window.BdApi = BdApi;
|
||||
|
||||
import Core from "./modules/core";
|
||||
deprecateGlobal("mainCore", Core);
|
||||
|
||||
// TODO: Change Init mode and stop using CoreWrapper.
|
||||
export default class CoreWrapper {
|
||||
constructor(bdConfig, methods) {
|
||||
Core.setConfig(bdConfig);
|
||||
Core.setMethods(methods);
|
||||
}
|
||||
|
||||
init() {
|
||||
// deprecateGlobal("mainCore", this.mainCore);
|
||||
Core.init();
|
||||
}
|
||||
import localStorageFix from "./localStorageFix";
|
||||
import loadingIcon from "./loadingIcon";
|
||||
localStorageFix();
|
||||
loadingIcon();
|
||||
|
||||
const deprecateGlobal = (key, value) => {
|
||||
Object.defineProperty(window, key, {
|
||||
get() {
|
||||
Utils.warn("Deprecated Global", `"${key}" will be removed in future versions. Please only use BdApi.`);
|
||||
return value;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
import * as Globals from "./0globals";
|
||||
|
||||
const globalKeys = Object.keys(Globals);
|
||||
for (const key of globalKeys) deprecateGlobal(key, Globals[key]);
|
||||
|
||||
|
||||
import BdApi from "./modules/bdApi";
|
||||
import BDV2 from "./modules/v2";
|
||||
import pluginModule from "./modules/pluginModule";
|
||||
import themeModule from "./modules/themeModule";
|
||||
import Utils from "./modules/utils";
|
||||
import BDEvents from "./modules/bdEvents";
|
||||
import settingsPanel from "./modules/settingsPanel";
|
||||
import DataStore from "./modules/dataStore";
|
||||
import ContentManager from "./modules/contentManager";
|
||||
import ClassNormalizer from "./modules/classNormalizer";
|
||||
|
||||
deprecateGlobal("BDV2", BDV2);
|
||||
deprecateGlobal("pluginModule", pluginModule);
|
||||
deprecateGlobal("themeModule", themeModule);
|
||||
deprecateGlobal("Utils", Utils);
|
||||
deprecateGlobal("BDEvents", BDEvents);
|
||||
deprecateGlobal("settingsPanel", settingsPanel);
|
||||
deprecateGlobal("DataStore", DataStore);
|
||||
deprecateGlobal("ContentManager", ContentManager);
|
||||
deprecateGlobal("ClassNormalizer", ClassNormalizer);
|
||||
|
||||
window.BdApi = BdApi;
|
||||
|
||||
import Core from "./modules/core";
|
||||
deprecateGlobal("mainCore", Core);
|
||||
|
||||
// TODO: Change Init mode and stop using CoreWrapper.
|
||||
export default class CoreWrapper {
|
||||
constructor(bdConfig, methods) {
|
||||
Core.setConfig(bdConfig);
|
||||
Core.setMethods(methods);
|
||||
}
|
||||
|
||||
init() {
|
||||
// deprecateGlobal("mainCore", this.mainCore);
|
||||
Core.init();
|
||||
}
|
||||
}
|
|
@ -1,83 +1,83 @@
|
|||
import Utils from "./utils"
|
||||
|
||||
const dispatcher = window.Lightcord.DiscordModules.dispatcher
|
||||
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 DMModule = BDModules.get(e => e.default && e.default.closePrivateChannel)[0]
|
||||
|
||||
const blocked = {}
|
||||
|
||||
let userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0]
|
||||
function getCurrentUser(){
|
||||
if(userModule)return userModule.default.getCurrentUser()
|
||||
return null
|
||||
}
|
||||
|
||||
export default new class AntiBotDM {
|
||||
constructor(){
|
||||
this.antiDM = this.antiDM.bind(this)
|
||||
this.enabled = false
|
||||
}
|
||||
|
||||
enable(){
|
||||
if(this.enabled)return
|
||||
this.enabled = true
|
||||
|
||||
dispatcher.subscribe("MESSAGE_CREATE", this.antiDM)
|
||||
}
|
||||
|
||||
disable(){
|
||||
if(!this.enabled)return
|
||||
this.enabled = false
|
||||
|
||||
dispatcher.unsubscribe("MESSAGE_CREATE", this.antiDM)
|
||||
}
|
||||
|
||||
antiDM(ev){
|
||||
if(!ev.message.author.bot)return
|
||||
if(ev.message.author.id === getCurrentUser().id)
|
||||
if(ev.message.guild_id)return
|
||||
|
||||
const channel = ChannelModule.getChannel(ev.message.channel_id)
|
||||
if(!channel)return // might be broken
|
||||
|
||||
if(channel.type !== 1)return
|
||||
|
||||
if(blocked[ev.message.author.id])return // If the user unblock the bot, Don't block it again.
|
||||
|
||||
if(scanMessage(ev.message)){
|
||||
blocked[ev.message.author.id] = true
|
||||
Utils.showToast(`[AdBlock]: Blocked ${ev.message.author.username}#${ev.message.author.discriminator}`, {
|
||||
"type": "warning"
|
||||
})
|
||||
if(!relationShipModule)relationShipModule = BDModules.get(e => e.default && e.default.addRelationship)[0]
|
||||
relationShipModule.default.addRelationship(ev.message.author.id, {
|
||||
location: "ContextMenu"
|
||||
}, 2)
|
||||
if(!DMModule)DMModule = BDModules.get(e => e.default && e.default.closePrivateChannel)[0]
|
||||
DMModule.default.closePrivateChannel(channel.id, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scanMessage(message){
|
||||
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
|
||||
|
||||
return false
|
||||
}
|
||||
function EmbedsContains(message, search){
|
||||
let embeds = message.embeds || []
|
||||
if(embeds.length === 0)return false
|
||||
return embeds.map(embed => {
|
||||
if(embed.type !== "rich")return false
|
||||
if((embed.title || "").includes(search))return true
|
||||
if((embed.description || "").includes(search))return true
|
||||
if(((embed.footer || "") && embed.footer.text || "").includes(search))return true
|
||||
if((embed.fields || []).map(e => {
|
||||
return e.value.includes(search) || e.name.includes(search)
|
||||
}).includes(true))return true
|
||||
return false
|
||||
|
||||
}).includes(true)
|
||||
import Utils from "./utils"
|
||||
|
||||
const dispatcher = window.Lightcord.DiscordModules.dispatcher
|
||||
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 DMModule = BDModules.get(e => e.default && e.default.closePrivateChannel)[0]
|
||||
|
||||
const blocked = {}
|
||||
|
||||
let userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0]
|
||||
function getCurrentUser(){
|
||||
if(userModule)return userModule.default.getCurrentUser()
|
||||
return null
|
||||
}
|
||||
|
||||
export default new class AntiBotDM {
|
||||
constructor(){
|
||||
this.antiDM = this.antiDM.bind(this)
|
||||
this.enabled = false
|
||||
}
|
||||
|
||||
enable(){
|
||||
if(this.enabled)return
|
||||
this.enabled = true
|
||||
|
||||
dispatcher.subscribe("MESSAGE_CREATE", this.antiDM)
|
||||
}
|
||||
|
||||
disable(){
|
||||
if(!this.enabled)return
|
||||
this.enabled = false
|
||||
|
||||
dispatcher.unsubscribe("MESSAGE_CREATE", this.antiDM)
|
||||
}
|
||||
|
||||
antiDM(ev){
|
||||
if(!ev.message.author.bot)return
|
||||
if(ev.message.author.id === getCurrentUser().id)
|
||||
if(ev.message.guild_id)return
|
||||
|
||||
const channel = ChannelModule.getChannel(ev.message.channel_id)
|
||||
if(!channel)return // might be broken
|
||||
|
||||
if(channel.type !== 1)return
|
||||
|
||||
if(blocked[ev.message.author.id])return // If the user unblock the bot, Don't block it again.
|
||||
|
||||
if(scanMessage(ev.message)){
|
||||
blocked[ev.message.author.id] = true
|
||||
Utils.showToast(`[AdBlock]: Blocked ${ev.message.author.username}#${ev.message.author.discriminator}`, {
|
||||
"type": "warning"
|
||||
})
|
||||
if(!relationShipModule)relationShipModule = BDModules.get(e => e.default && e.default.addRelationship)[0]
|
||||
relationShipModule.default.addRelationship(ev.message.author.id, {
|
||||
location: "ContextMenu"
|
||||
}, 2)
|
||||
if(!DMModule)DMModule = BDModules.get(e => e.default && e.default.closePrivateChannel)[0]
|
||||
DMModule.default.closePrivateChannel(channel.id, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scanMessage(message){
|
||||
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
|
||||
|
||||
return false
|
||||
}
|
||||
function EmbedsContains(message, search){
|
||||
let embeds = message.embeds || []
|
||||
if(embeds.length === 0)return false
|
||||
return embeds.map(embed => {
|
||||
if(embed.type !== "rich")return false
|
||||
if((embed.title || "").includes(search))return true
|
||||
if((embed.description || "").includes(search))return true
|
||||
if(((embed.footer || "") && embed.footer.text || "").includes(search))return true
|
||||
if((embed.fields || []).map(e => {
|
||||
return e.value.includes(search) || e.name.includes(search)
|
||||
}).includes(true))return true
|
||||
return false
|
||||
|
||||
}).includes(true)
|
||||
}
|
|
@ -1,166 +1,166 @@
|
|||
import Utils from "./utils";
|
||||
import v2 from "./v2";
|
||||
import { uuidv4 } from "./distant";
|
||||
import { timingSafeEqual } from "crypto";
|
||||
|
||||
export default new class Layers {
|
||||
constructor(){
|
||||
this.layers = []
|
||||
window.Lightcord.BetterDiscord.Layers = this
|
||||
}
|
||||
|
||||
get isInjected(){
|
||||
return !!document.querySelector("div.Layers.injected")
|
||||
}
|
||||
|
||||
inject(){
|
||||
if(this.isInjected)return
|
||||
let [
|
||||
classNameLayers
|
||||
] = [
|
||||
Utils.removeDa(v2.WebpackModules.find(e => e.layers && e.layer).layers)
|
||||
]
|
||||
const layers = document.querySelector("."+classNameLayers);
|
||||
if (!layers) return false;
|
||||
const ReactInstance = Lightcord.Api.Utils.FindReact(layers, 1)
|
||||
if(!ReactInstance)return layers
|
||||
this.ReactInstance = ReactInstance
|
||||
let render = ReactInstance.render
|
||||
ReactInstance.render = () => {
|
||||
let returnValue = render.call(ReactInstance)
|
||||
let classList = returnValue.props.children[1].props.className.split(" ")
|
||||
classList.push("Layers")
|
||||
classList.push("injected")
|
||||
returnValue.props.children[1].props.className = classList.join(" ")
|
||||
|
||||
this.layers.forEach(layer => {
|
||||
returnValue.props.children[1].props.children.push(layer[1])
|
||||
})
|
||||
|
||||
return returnValue
|
||||
}
|
||||
ReactInstance.forceUpdate()
|
||||
}
|
||||
|
||||
createLayer(children, props = {}){
|
||||
let id = uuidv4()
|
||||
let layer = null
|
||||
this.layers.push([id, <Layer {...props} getLayer={lay => {
|
||||
layer = lay
|
||||
}} key={id} id={id} remove={() => {
|
||||
let index = this.layers.findIndex(e => e[0] === id)
|
||||
if(index === -1)return
|
||||
this.layers.splice(index, 1)
|
||||
layer = null
|
||||
if(this.ReactInstance)this.ReactInstance.forceUpdate()
|
||||
this.inject()
|
||||
}}>{children}</Layer>])
|
||||
if(this.ReactInstance)this.ReactInstance.forceUpdate()
|
||||
this.inject()
|
||||
return {
|
||||
remove: () => {
|
||||
if(!layer){
|
||||
let index = this.layers.findIndex(e => e[0] === id)
|
||||
if(index === -1)return
|
||||
this.layers.splice(index, 1)
|
||||
layer = null
|
||||
if(this.ReactInstance)this.ReactInstance.forceUpdate()
|
||||
this.inject()
|
||||
}else{
|
||||
layer.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let LayerModules
|
||||
export class Layer extends React.Component {
|
||||
constructor(){
|
||||
super(...arguments)
|
||||
this.state = {
|
||||
animation: new this.modules[0].default.Value(0)
|
||||
}
|
||||
this.props.getLayer(this)
|
||||
this.keydown = this.keydown.bind(this)
|
||||
}
|
||||
|
||||
keydown(e){
|
||||
if (e.which === 27) {
|
||||
this.remove()
|
||||
}
|
||||
}
|
||||
|
||||
get modules(){
|
||||
return LayerModules || (LayerModules = [
|
||||
v2.WebpackModules.find(e => e.default && e.default.Value && e.default.div)
|
||||
])
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
window.removeEventListener("keydown", this.keydown)
|
||||
}
|
||||
|
||||
componentDidMount(ev){
|
||||
window.addEventListener("keydown", this.keydown)
|
||||
}
|
||||
|
||||
remove(){
|
||||
this.props.remove()
|
||||
}
|
||||
|
||||
componentWillEnter(ev){
|
||||
this.modules[0].default.timing(this.state.animation, {
|
||||
toValue: 1,
|
||||
duration: 100
|
||||
}).start(ev)
|
||||
}
|
||||
|
||||
componentWillLeave(ev){
|
||||
this.modules[0].default.timing(this.state.animation, {
|
||||
toValue: 0,
|
||||
duration: 100
|
||||
}).start(ev)
|
||||
}
|
||||
|
||||
render(){
|
||||
let interpolation = this.state.animation.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [1.07, 1]
|
||||
})
|
||||
let style = {
|
||||
opacity: this.state.animation,
|
||||
transform: [{
|
||||
scale: interpolation
|
||||
}]
|
||||
}
|
||||
let Div = this.modules[0].default.div
|
||||
return <Div className={[__SECRET_EMOTION__.css({
|
||||
backgroundColor:"var(--background-primary)",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
display: "flex",
|
||||
overflow: "hidden",
|
||||
WebkitBoxOrient: "vertical",
|
||||
WebkitBoxDirection: "normal",
|
||||
msFlexDirection: "column",
|
||||
flexDirection: "column",
|
||||
zIndex: 101
|
||||
}),process.platform==="win32"?__SECRET_EMOTION__.css({
|
||||
top: "-22px",
|
||||
paddingTop: "22px"
|
||||
}):null,...(this.props.className?this.props.className.split(" "):[])].filter(e=>e).join(" ") || null} style={style}>
|
||||
{this.props.children(() => {
|
||||
this.remove()
|
||||
})}
|
||||
</Div>
|
||||
}
|
||||
}
|
||||
Layer.defaultProps = {
|
||||
id: null,
|
||||
children: null
|
||||
import Utils from "./utils";
|
||||
import v2 from "./v2";
|
||||
import { uuidv4 } from "./distant";
|
||||
import { timingSafeEqual } from "crypto";
|
||||
|
||||
export default new class Layers {
|
||||
constructor(){
|
||||
this.layers = []
|
||||
window.Lightcord.BetterDiscord.Layers = this
|
||||
}
|
||||
|
||||
get isInjected(){
|
||||
return !!document.querySelector("div.Layers.injected")
|
||||
}
|
||||
|
||||
inject(){
|
||||
if(this.isInjected)return
|
||||
let [
|
||||
classNameLayers
|
||||
] = [
|
||||
Utils.removeDa(v2.WebpackModules.find(e => e.layers && e.layer).layers)
|
||||
]
|
||||
const layers = document.querySelector("."+classNameLayers);
|
||||
if (!layers) return false;
|
||||
const ReactInstance = Lightcord.Api.Utils.FindReact(layers, 1)
|
||||
if(!ReactInstance)return layers
|
||||
this.ReactInstance = ReactInstance
|
||||
let render = ReactInstance.render
|
||||
ReactInstance.render = () => {
|
||||
let returnValue = render.call(ReactInstance)
|
||||
let classList = returnValue.props.children[1].props.className.split(" ")
|
||||
classList.push("Layers")
|
||||
classList.push("injected")
|
||||
returnValue.props.children[1].props.className = classList.join(" ")
|
||||
|
||||
this.layers.forEach(layer => {
|
||||
returnValue.props.children[1].props.children.push(layer[1])
|
||||
})
|
||||
|
||||
return returnValue
|
||||
}
|
||||
ReactInstance.forceUpdate()
|
||||
}
|
||||
|
||||
createLayer(children, props = {}){
|
||||
let id = uuidv4()
|
||||
let layer = null
|
||||
this.layers.push([id, <Layer {...props} getLayer={lay => {
|
||||
layer = lay
|
||||
}} key={id} id={id} remove={() => {
|
||||
let index = this.layers.findIndex(e => e[0] === id)
|
||||
if(index === -1)return
|
||||
this.layers.splice(index, 1)
|
||||
layer = null
|
||||
if(this.ReactInstance)this.ReactInstance.forceUpdate()
|
||||
this.inject()
|
||||
}}>{children}</Layer>])
|
||||
if(this.ReactInstance)this.ReactInstance.forceUpdate()
|
||||
this.inject()
|
||||
return {
|
||||
remove: () => {
|
||||
if(!layer){
|
||||
let index = this.layers.findIndex(e => e[0] === id)
|
||||
if(index === -1)return
|
||||
this.layers.splice(index, 1)
|
||||
layer = null
|
||||
if(this.ReactInstance)this.ReactInstance.forceUpdate()
|
||||
this.inject()
|
||||
}else{
|
||||
layer.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let LayerModules
|
||||
export class Layer extends React.Component {
|
||||
constructor(){
|
||||
super(...arguments)
|
||||
this.state = {
|
||||
animation: new this.modules[0].default.Value(0)
|
||||
}
|
||||
this.props.getLayer(this)
|
||||
this.keydown = this.keydown.bind(this)
|
||||
}
|
||||
|
||||
keydown(e){
|
||||
if (e.which === 27) {
|
||||
this.remove()
|
||||
}
|
||||
}
|
||||
|
||||
get modules(){
|
||||
return LayerModules || (LayerModules = [
|
||||
v2.WebpackModules.find(e => e.default && e.default.Value && e.default.div)
|
||||
])
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
window.removeEventListener("keydown", this.keydown)
|
||||
}
|
||||
|
||||
componentDidMount(ev){
|
||||
window.addEventListener("keydown", this.keydown)
|
||||
}
|
||||
|
||||
remove(){
|
||||
this.props.remove()
|
||||
}
|
||||
|
||||
componentWillEnter(ev){
|
||||
this.modules[0].default.timing(this.state.animation, {
|
||||
toValue: 1,
|
||||
duration: 100
|
||||
}).start(ev)
|
||||
}
|
||||
|
||||
componentWillLeave(ev){
|
||||
this.modules[0].default.timing(this.state.animation, {
|
||||
toValue: 0,
|
||||
duration: 100
|
||||
}).start(ev)
|
||||
}
|
||||
|
||||
render(){
|
||||
let interpolation = this.state.animation.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [1.07, 1]
|
||||
})
|
||||
let style = {
|
||||
opacity: this.state.animation,
|
||||
transform: [{
|
||||
scale: interpolation
|
||||
}]
|
||||
}
|
||||
let Div = this.modules[0].default.div
|
||||
return <Div className={[__SECRET_EMOTION__.css({
|
||||
backgroundColor:"var(--background-primary)",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
display: "flex",
|
||||
overflow: "hidden",
|
||||
WebkitBoxOrient: "vertical",
|
||||
WebkitBoxDirection: "normal",
|
||||
msFlexDirection: "column",
|
||||
flexDirection: "column",
|
||||
zIndex: 101
|
||||
}),process.platform==="win32"?__SECRET_EMOTION__.css({
|
||||
top: "-22px",
|
||||
paddingTop: "22px"
|
||||
}):null,...(this.props.className?this.props.className.split(" "):[])].filter(e=>e).join(" ") || null} style={style}>
|
||||
{this.props.children(() => {
|
||||
this.remove()
|
||||
})}
|
||||
</Div>
|
||||
}
|
||||
}
|
||||
Layer.defaultProps = {
|
||||
id: null,
|
||||
children: null
|
||||
}
|
|
@ -1,254 +1,254 @@
|
|||
import {pluginCookie, themeCookie, bdplugins, bdthemes, settingsCookie, settings} from "../0globals";
|
||||
import mainCore from "./core";
|
||||
import Utils from "./utils";
|
||||
import BDV2 from "./v2";
|
||||
import DataStore from "./dataStore";
|
||||
import pluginModule from "./pluginModule";
|
||||
import themeModule from "./themeModule";
|
||||
import settingsPanel from "./settingsPanel";
|
||||
import DOM from "./domtools";
|
||||
|
||||
const BdApi = {
|
||||
get React() { return BDV2.React; },
|
||||
get ReactDOM() { return BDV2.ReactDom; },
|
||||
get ReactComponent() {return BDV2.ReactComponent;},
|
||||
get WindowConfigFile() {return Utils.WindowConfigFile;},
|
||||
get settings() {return settings;},
|
||||
get emotes() {return null}, // deprecated, deleted all emotes from betterdiscord.
|
||||
get screenWidth() { return Math.max(document.documentElement.clientWidth, window.innerWidth || 0); },
|
||||
get screenHeight() { return Math.max(document.documentElement.clientHeight, window.innerHeight || 0); }
|
||||
};
|
||||
|
||||
BdApi.getAllWindowPreferences = function() {
|
||||
return Utils.getAllWindowPreferences();
|
||||
};
|
||||
|
||||
BdApi.getWindowPreference = function(key) {
|
||||
return Utils.getWindowPreference(key);
|
||||
};
|
||||
|
||||
BdApi.setWindowPreference = function(key, value) {
|
||||
return Utils.setWindowPreference(key, value);
|
||||
};
|
||||
|
||||
//Inject CSS to document head
|
||||
//id = id of element
|
||||
//css = custom css
|
||||
BdApi.injectCSS = function (id, css) {
|
||||
DOM.addStyle(DOM.escapeID(id), css);
|
||||
};
|
||||
|
||||
//Clear css/remove any element
|
||||
//id = id of element
|
||||
BdApi.clearCSS = function (id) {
|
||||
DOM.removeStyle(DOM.escapeID(id));
|
||||
};
|
||||
|
||||
//Inject CSS to document head
|
||||
//id = id of element
|
||||
//css = custom css
|
||||
BdApi.linkJS = function (id, url) {
|
||||
DOM.addScript(DOM.escapeID(id), url);
|
||||
};
|
||||
|
||||
//Clear css/remove any element
|
||||
//id = id of element
|
||||
BdApi.unlinkJS = function (id) {
|
||||
DOM.removeScript(DOM.escapeID(id));
|
||||
};
|
||||
|
||||
//Get another plugin
|
||||
//name = name of plugin
|
||||
BdApi.getPlugin = function (name) {
|
||||
if (bdplugins.hasOwnProperty(name)) {
|
||||
return bdplugins[name].plugin;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
//Get BetterDiscord Core
|
||||
BdApi.getCore = function () {
|
||||
Utils.warn("Deprecation Notice", `BdApi.getCore() will be removed in future versions.`);
|
||||
return mainCore;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows a generic but very customizable modal.
|
||||
* @param {string} title - title of the modal
|
||||
* @param {string} content - a string of text to display in the modal
|
||||
*/
|
||||
BdApi.alert = function (title, content) {
|
||||
return Utils.showConfirmationModal(title, content, {cancelText: null});
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows a generic but very customizable confirmation modal with optional confirm and cancel callbacks.
|
||||
* @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 {object} [options] - options to modify the modal
|
||||
* @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.cancelText=Cancel] - text for the cancel 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 {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
|
||||
*/
|
||||
BdApi.showConfirmationModal = function (title, content, options = {}) {
|
||||
return Utils.showConfirmationModal(title, content, options);
|
||||
};
|
||||
|
||||
//Show toast alert
|
||||
BdApi.showToast = function(content, options = {}) {
|
||||
Utils.showToast(content, options);
|
||||
};
|
||||
|
||||
// Finds module
|
||||
BdApi.findModule = function(filter) {
|
||||
return BDV2.WebpackModules.find(filter);
|
||||
};
|
||||
|
||||
// Finds module
|
||||
BdApi.findAllModules = function(filter) {
|
||||
return BDV2.WebpackModules.findAll(filter);
|
||||
};
|
||||
|
||||
// Finds module
|
||||
BdApi.findModuleByProps = function(...props) {
|
||||
return BDV2.WebpackModules.findByUniqueProperties(props);
|
||||
};
|
||||
|
||||
BdApi.findModuleByPrototypes = function(...protos) {
|
||||
return BDV2.WebpackModules.findByPrototypes(protos);
|
||||
};
|
||||
|
||||
BdApi.findModuleByDisplayName = function(name) {
|
||||
return BDV2.WebpackModules.findByDisplayName(name);
|
||||
};
|
||||
|
||||
// Gets react instance
|
||||
BdApi.getInternalInstance = function(node) {
|
||||
if (!(node instanceof window.jQuery) && !(node instanceof Element)) return undefined;
|
||||
if (node instanceof jQuery) node = node[0];
|
||||
return BDV2.getInternalInstance(node);
|
||||
};
|
||||
|
||||
// Gets data
|
||||
BdApi.loadData = function(pluginName, key) {
|
||||
return DataStore.getPluginData(pluginName, key);
|
||||
};
|
||||
|
||||
BdApi.getData = BdApi.loadData;
|
||||
|
||||
// Sets data
|
||||
BdApi.saveData = function(pluginName, key, data) {
|
||||
return DataStore.setPluginData(pluginName, key, data);
|
||||
};
|
||||
|
||||
BdApi.setData = BdApi.saveData;
|
||||
|
||||
// Deletes data
|
||||
BdApi.deleteData = function(pluginName, key) {
|
||||
return DataStore.deletePluginData(pluginName, key);
|
||||
};
|
||||
|
||||
// Patches other functions
|
||||
BdApi.monkeyPatch = function(what, methodName, options) {
|
||||
return Utils.monkeyPatch(what, methodName, options);
|
||||
};
|
||||
|
||||
// Event when element is removed
|
||||
BdApi.onRemoved = function(node, callback) {
|
||||
return Utils.onRemoved(node, callback);
|
||||
};
|
||||
|
||||
// Wraps function in try..catch
|
||||
BdApi.suppressErrors = function(method, message) {
|
||||
return Utils.suppressErrors(method, message);
|
||||
};
|
||||
|
||||
// Tests for valid JSON
|
||||
BdApi.testJSON = function(data) {
|
||||
return Utils.testJSON(data);
|
||||
};
|
||||
|
||||
BdApi.isPluginEnabled = function(name) {
|
||||
return !!pluginCookie[name];
|
||||
};
|
||||
|
||||
BdApi.isThemeEnabled = function(name) {
|
||||
return !!themeCookie[name];
|
||||
};
|
||||
|
||||
BdApi.isSettingEnabled = function(id) {
|
||||
return !!settingsCookie[id];
|
||||
};
|
||||
|
||||
BdApi.enableSetting = function(id) {
|
||||
return settingsPanel.onChange(id, true);
|
||||
};
|
||||
|
||||
BdApi.disableSetting = function(id) {
|
||||
return settingsPanel.onChange(id, false);
|
||||
};
|
||||
|
||||
BdApi.toggleSetting = function(id) {
|
||||
return settingsPanel.onChange(id, !settingsCookie[id]);
|
||||
};
|
||||
|
||||
// Gets data
|
||||
BdApi.getBDData = function(key) {
|
||||
return DataStore.getBDData(key);
|
||||
};
|
||||
|
||||
// Sets data
|
||||
BdApi.setBDData = function(key, data) {
|
||||
return DataStore.setBDData(key, data);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const makeAddonAPI = (cookie, list, manager) => new class AddonAPI {
|
||||
|
||||
get folder() {return manager.folder;}
|
||||
|
||||
isEnabled(name) {
|
||||
return !!cookie[name];
|
||||
}
|
||||
|
||||
enable(name) {
|
||||
return manager.enable(name);
|
||||
}
|
||||
|
||||
disable(name) {
|
||||
return manager.disable(name);
|
||||
}
|
||||
|
||||
toggle(name) {
|
||||
if (cookie[name]) this.disable(name);
|
||||
else this.enable(name);
|
||||
}
|
||||
|
||||
reload(name) {
|
||||
return manager.reload(name);
|
||||
}
|
||||
|
||||
get(name) {
|
||||
if (list.hasOwnProperty(name)) {
|
||||
if (list[name].plugin) return list[name].plugin;
|
||||
return list[name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return Object.keys(list).map(k => this.get(k)).filter(a => a);
|
||||
}
|
||||
};
|
||||
|
||||
BdApi.Plugins = makeAddonAPI(pluginCookie, bdplugins, pluginModule);
|
||||
BdApi.Themes = makeAddonAPI(themeCookie, bdthemes, themeModule);
|
||||
|
||||
export default BdApi;
|
||||
|
||||
import {pluginCookie, themeCookie, bdplugins, bdthemes, settingsCookie, settings} from "../0globals";
|
||||
import mainCore from "./core";
|
||||
import Utils from "./utils";
|
||||
import BDV2 from "./v2";
|
||||
import DataStore from "./dataStore";
|
||||
import pluginModule from "./pluginModule";
|
||||
import themeModule from "./themeModule";
|
||||
import settingsPanel from "./settingsPanel";
|
||||
import DOM from "./domtools";
|
||||
|
||||
const BdApi = {
|
||||
get React() { return BDV2.React; },
|
||||
get ReactDOM() { return BDV2.ReactDom; },
|
||||
get ReactComponent() {return BDV2.ReactComponent;},
|
||||
get WindowConfigFile() {return Utils.WindowConfigFile;},
|
||||
get settings() {return settings;},
|
||||
get emotes() {return null}, // deprecated, deleted all emotes from betterdiscord.
|
||||
get screenWidth() { return Math.max(document.documentElement.clientWidth, window.innerWidth || 0); },
|
||||
get screenHeight() { return Math.max(document.documentElement.clientHeight, window.innerHeight || 0); }
|
||||
};
|
||||
|
||||
BdApi.getAllWindowPreferences = function() {
|
||||
return Utils.getAllWindowPreferences();
|
||||
};
|
||||
|
||||
BdApi.getWindowPreference = function(key) {
|
||||
return Utils.getWindowPreference(key);
|
||||
};
|
||||
|
||||
BdApi.setWindowPreference = function(key, value) {
|
||||
return Utils.setWindowPreference(key, value);
|
||||
};
|
||||
|
||||
//Inject CSS to document head
|
||||
//id = id of element
|
||||
//css = custom css
|
||||
BdApi.injectCSS = function (id, css) {
|
||||
DOM.addStyle(DOM.escapeID(id), css);
|
||||
};
|
||||
|
||||
//Clear css/remove any element
|
||||
//id = id of element
|
||||
BdApi.clearCSS = function (id) {
|
||||
DOM.removeStyle(DOM.escapeID(id));
|
||||
};
|
||||
|
||||
//Inject CSS to document head
|
||||
//id = id of element
|
||||
//css = custom css
|
||||
BdApi.linkJS = function (id, url) {
|
||||
DOM.addScript(DOM.escapeID(id), url);
|
||||
};
|
||||
|
||||
//Clear css/remove any element
|
||||
//id = id of element
|
||||
BdApi.unlinkJS = function (id) {
|
||||
DOM.removeScript(DOM.escapeID(id));
|
||||
};
|
||||
|
||||
//Get another plugin
|
||||
//name = name of plugin
|
||||
BdApi.getPlugin = function (name) {
|
||||
if (bdplugins.hasOwnProperty(name)) {
|
||||
return bdplugins[name].plugin;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
//Get BetterDiscord Core
|
||||
BdApi.getCore = function () {
|
||||
Utils.warn("Deprecation Notice", `BdApi.getCore() will be removed in future versions.`);
|
||||
return mainCore;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows a generic but very customizable modal.
|
||||
* @param {string} title - title of the modal
|
||||
* @param {string} content - a string of text to display in the modal
|
||||
*/
|
||||
BdApi.alert = function (title, content) {
|
||||
return Utils.showConfirmationModal(title, content, {cancelText: null});
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows a generic but very customizable confirmation modal with optional confirm and cancel callbacks.
|
||||
* @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 {object} [options] - options to modify the modal
|
||||
* @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.cancelText=Cancel] - text for the cancel 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 {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
|
||||
*/
|
||||
BdApi.showConfirmationModal = function (title, content, options = {}) {
|
||||
return Utils.showConfirmationModal(title, content, options);
|
||||
};
|
||||
|
||||
//Show toast alert
|
||||
BdApi.showToast = function(content, options = {}) {
|
||||
Utils.showToast(content, options);
|
||||
};
|
||||
|
||||
// Finds module
|
||||
BdApi.findModule = function(filter) {
|
||||
return BDV2.WebpackModules.find(filter);
|
||||
};
|
||||
|
||||
// Finds module
|
||||
BdApi.findAllModules = function(filter) {
|
||||
return BDV2.WebpackModules.findAll(filter);
|
||||
};
|
||||
|
||||
// Finds module
|
||||
BdApi.findModuleByProps = function(...props) {
|
||||
return BDV2.WebpackModules.findByUniqueProperties(props);
|
||||
};
|
||||
|
||||
BdApi.findModuleByPrototypes = function(...protos) {
|
||||
return BDV2.WebpackModules.findByPrototypes(protos);
|
||||
};
|
||||
|
||||
BdApi.findModuleByDisplayName = function(name) {
|
||||
return BDV2.WebpackModules.findByDisplayName(name);
|
||||
};
|
||||
|
||||
// Gets react instance
|
||||
BdApi.getInternalInstance = function(node) {
|
||||
if (!(node instanceof window.jQuery) && !(node instanceof Element)) return undefined;
|
||||
if (node instanceof jQuery) node = node[0];
|
||||
return BDV2.getInternalInstance(node);
|
||||
};
|
||||
|
||||
// Gets data
|
||||
BdApi.loadData = function(pluginName, key) {
|
||||
return DataStore.getPluginData(pluginName, key);
|
||||
};
|
||||
|
||||
BdApi.getData = BdApi.loadData;
|
||||
|
||||
// Sets data
|
||||
BdApi.saveData = function(pluginName, key, data) {
|
||||
return DataStore.setPluginData(pluginName, key, data);
|
||||
};
|
||||
|
||||
BdApi.setData = BdApi.saveData;
|
||||
|
||||
// Deletes data
|
||||
BdApi.deleteData = function(pluginName, key) {
|
||||
return DataStore.deletePluginData(pluginName, key);
|
||||
};
|
||||
|
||||
// Patches other functions
|
||||
BdApi.monkeyPatch = function(what, methodName, options) {
|
||||
return Utils.monkeyPatch(what, methodName, options);
|
||||
};
|
||||
|
||||
// Event when element is removed
|
||||
BdApi.onRemoved = function(node, callback) {
|
||||
return Utils.onRemoved(node, callback);
|
||||
};
|
||||
|
||||
// Wraps function in try..catch
|
||||
BdApi.suppressErrors = function(method, message) {
|
||||
return Utils.suppressErrors(method, message);
|
||||
};
|
||||
|
||||
// Tests for valid JSON
|
||||
BdApi.testJSON = function(data) {
|
||||
return Utils.testJSON(data);
|
||||
};
|
||||
|
||||
BdApi.isPluginEnabled = function(name) {
|
||||
return !!pluginCookie[name];
|
||||
};
|
||||
|
||||
BdApi.isThemeEnabled = function(name) {
|
||||
return !!themeCookie[name];
|
||||
};
|
||||
|
||||
BdApi.isSettingEnabled = function(id) {
|
||||
return !!settingsCookie[id];
|
||||
};
|
||||
|
||||
BdApi.enableSetting = function(id) {
|
||||
return settingsPanel.onChange(id, true);
|
||||
};
|
||||
|
||||
BdApi.disableSetting = function(id) {
|
||||
return settingsPanel.onChange(id, false);
|
||||
};
|
||||
|
||||
BdApi.toggleSetting = function(id) {
|
||||
return settingsPanel.onChange(id, !settingsCookie[id]);
|
||||
};
|
||||
|
||||
// Gets data
|
||||
BdApi.getBDData = function(key) {
|
||||
return DataStore.getBDData(key);
|
||||
};
|
||||
|
||||
// Sets data
|
||||
BdApi.setBDData = function(key, data) {
|
||||
return DataStore.setBDData(key, data);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const makeAddonAPI = (cookie, list, manager) => new class AddonAPI {
|
||||
|
||||
get folder() {return manager.folder;}
|
||||
|
||||
isEnabled(name) {
|
||||
return !!cookie[name];
|
||||
}
|
||||
|
||||
enable(name) {
|
||||
return manager.enable(name);
|
||||
}
|
||||
|
||||
disable(name) {
|
||||
return manager.disable(name);
|
||||
}
|
||||
|
||||
toggle(name) {
|
||||
if (cookie[name]) this.disable(name);
|
||||
else this.enable(name);
|
||||
}
|
||||
|
||||
reload(name) {
|
||||
return manager.reload(name);
|
||||
}
|
||||
|
||||
get(name) {
|
||||
if (list.hasOwnProperty(name)) {
|
||||
if (list[name].plugin) return list[name].plugin;
|
||||
return list[name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return Object.keys(list).map(k => this.get(k)).filter(a => a);
|
||||
}
|
||||
};
|
||||
|
||||
BdApi.Plugins = makeAddonAPI(pluginCookie, bdplugins, pluginModule);
|
||||
BdApi.Themes = makeAddonAPI(themeCookie, bdthemes, themeModule);
|
||||
|
||||
export default BdApi;
|
||||
|
||||
window.Lightcord.BetterDiscord.BdApi = BdApi
|
|
@ -1,10 +1,10 @@
|
|||
/* BDEvents */
|
||||
const EventEmitter = require("events");
|
||||
export default new class BDEvents extends EventEmitter {
|
||||
constructor(){
|
||||
super()
|
||||
window.Lightcord.BetterDiscord.BDEvents = this
|
||||
}
|
||||
dispatch(eventName, ...args) {this.emit(eventName, ...args);}
|
||||
off(eventName, eventAction) {this.removeListener(eventName, eventAction);}
|
||||
/* BDEvents */
|
||||
const EventEmitter = require("events");
|
||||
export default new class BDEvents extends EventEmitter {
|
||||
constructor(){
|
||||
super()
|
||||
window.Lightcord.BetterDiscord.BDEvents = this
|
||||
}
|
||||
dispatch(eventName, ...args) {this.emit(eventName, ...args);}
|
||||
off(eventName, eventAction) {this.removeListener(eventName, eventAction);}
|
||||
};
|
|
@ -1,57 +1,57 @@
|
|||
/**
|
||||
* Credits to @hellbound1337 on github for the css
|
||||
*/
|
||||
|
||||
import DOMTools from "./domtools"
|
||||
import Utils from "./utils"
|
||||
|
||||
let selectors
|
||||
const removeDa = Utils.removeDa
|
||||
function getSelectors(){
|
||||
let standardSidebarView = BDModules.get(e => e.standardSidebarView)[0]
|
||||
if(!standardSidebarView)return null
|
||||
let defaultClassName = removeDa(standardSidebarView.standardSidebarView)
|
||||
let selects = []
|
||||
let userSettingsAccount = BDModules.get(e => e.userSettingsAccount)[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} .${removeDa(BDModules.get(e => e.paymentSourceRow)[0].paymentSourceRow)} .${removeDa(BDModules.get(e => e.subText && e.descriptionWrapper)[0].subText)}`)
|
||||
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(`.${removeDa(userSettingsAccount.userSettingsSecurity)} .${removeDa(children.children)} > div:nth-child(2)`)
|
||||
}
|
||||
console.log(selects)
|
||||
return selects
|
||||
}
|
||||
|
||||
export default new class BlurPrivate {
|
||||
constructor(){
|
||||
this.enabled = false
|
||||
}
|
||||
|
||||
enable(){
|
||||
if(this.enabled)return
|
||||
this.enabled = true
|
||||
selectors = selectors || getSelectors()
|
||||
|
||||
if(!selectors)console.error(new Error("Couldn't find selectors to blur personnal informations."))
|
||||
DOMTools.addStyle("blurPrivate", `
|
||||
${selectors.join(", ")} {
|
||||
transition: all 150ms cubic-bezier(.55,.085,.68,.53);
|
||||
filter: blur(4px);
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
${selectors.map(e => e+":hover").join(", ")} {
|
||||
transition: all 150ms cubic-bezier(.55,.09,.68,.53);
|
||||
filter: none;
|
||||
opacity: 1;
|
||||
}`)
|
||||
}
|
||||
|
||||
disable(){
|
||||
if(!this.enabled)return
|
||||
this.enabled = false
|
||||
DOMTools.removeStyle("blurPrivate")
|
||||
}
|
||||
/**
|
||||
* Credits to @hellbound1337 on github for the css
|
||||
*/
|
||||
|
||||
import DOMTools from "./domtools"
|
||||
import Utils from "./utils"
|
||||
|
||||
let selectors
|
||||
const removeDa = Utils.removeDa
|
||||
function getSelectors(){
|
||||
let standardSidebarView = BDModules.get(e => e.standardSidebarView)[0]
|
||||
if(!standardSidebarView)return null
|
||||
let defaultClassName = removeDa(standardSidebarView.standardSidebarView)
|
||||
let selects = []
|
||||
let userSettingsAccount = BDModules.get(e => e.userSettingsAccount)[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} .${removeDa(BDModules.get(e => e.paymentSourceRow)[0].paymentSourceRow)} .${removeDa(BDModules.get(e => e.subText && e.descriptionWrapper)[0].subText)}`)
|
||||
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(`.${removeDa(userSettingsAccount.userSettingsSecurity)} .${removeDa(children.children)} > div:nth-child(2)`)
|
||||
}
|
||||
console.log(selects)
|
||||
return selects
|
||||
}
|
||||
|
||||
export default new class BlurPrivate {
|
||||
constructor(){
|
||||
this.enabled = false
|
||||
}
|
||||
|
||||
enable(){
|
||||
if(this.enabled)return
|
||||
this.enabled = true
|
||||
selectors = selectors || getSelectors()
|
||||
|
||||
if(!selectors)console.error(new Error("Couldn't find selectors to blur personnal informations."))
|
||||
DOMTools.addStyle("blurPrivate", `
|
||||
${selectors.join(", ")} {
|
||||
transition: all 150ms cubic-bezier(.55,.085,.68,.53);
|
||||
filter: blur(4px);
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
${selectors.map(e => e+":hover").join(", ")} {
|
||||
transition: all 150ms cubic-bezier(.55,.09,.68,.53);
|
||||
filter: none;
|
||||
opacity: 1;
|
||||
}`)
|
||||
}
|
||||
|
||||
disable(){
|
||||
if(!this.enabled)return
|
||||
this.enabled = false
|
||||
DOMTools.removeStyle("blurPrivate")
|
||||
}
|
||||
}
|
|
@ -1,138 +1,138 @@
|
|||
import WebpackModules from "./webpackModules";
|
||||
|
||||
const normalizedPrefix = "da";
|
||||
const randClass = new RegExp(`^(?!${normalizedPrefix}-)((?:[A-Za-z]|[0-9]|-)+)-(?:[A-Za-z]|[0-9]|-|_){6}$`);
|
||||
|
||||
export default new class ClassNormalizer {
|
||||
constructor(){
|
||||
window.Lightcord.BetterDiscord.ClassNormalizer = this
|
||||
}
|
||||
stop() {
|
||||
if (!this.hasPatched) return;
|
||||
this.unpatchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this)));
|
||||
this.revertElement(document.querySelector("#app-mount"));
|
||||
this.hasPatched = false;
|
||||
}
|
||||
|
||||
start() {
|
||||
if (this.hasPatched) return;
|
||||
this.patchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this)));
|
||||
this.normalizeElement(document.querySelector("#app-mount"));
|
||||
this.hasPatched = true;
|
||||
this.patchDOMMethods();
|
||||
}
|
||||
|
||||
patchClassModules(modules) {
|
||||
for (const module of modules) {
|
||||
this.patchClassModule(normalizedPrefix, module);
|
||||
}
|
||||
}
|
||||
|
||||
unpatchClassModules(modules) {
|
||||
for (const module of modules) {
|
||||
this.unpatchClassModule(normalizedPrefix, module);
|
||||
}
|
||||
}
|
||||
|
||||
shouldIgnore(value) {
|
||||
if (!isNaN(value)) 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("#") && (value.length == 7 || value.length == 4)) return true;
|
||||
if (value.includes("calc(") || value.includes("rgba")) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
moduleFilter(module) {
|
||||
if (typeof module !== "object" || Array.isArray(module)) return false;
|
||||
if (module.__esModule) return false;
|
||||
if (!Object.keys(module).length) return false;
|
||||
for (const baseClassName in module) {
|
||||
const value = module[baseClassName];
|
||||
if (typeof value !== "string") return false;
|
||||
if (this.shouldIgnore(value)) continue;
|
||||
if (value.split("-").length === 1) return false;
|
||||
if (!randClass.test(value.split(" ")[0])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
patchClassModule(componentName, classNames) {
|
||||
for (const baseClassName in classNames) {
|
||||
const value = classNames[baseClassName];
|
||||
if (this.shouldIgnore(value)) continue;
|
||||
const classList = value.split(" ");
|
||||
for (const normalClass of classList) {
|
||||
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
|
||||
const camelCase = match[1].split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join("");
|
||||
classNames[baseClassName] += ` ${componentName}-${camelCase}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unpatchClassModule(componentName, classNames) {
|
||||
for (const baseClassName in classNames) {
|
||||
const value = classNames[baseClassName];
|
||||
if (this.shouldIgnore(value)) continue;
|
||||
let newString = "";
|
||||
const classList = value.split(" ");
|
||||
for (const normalClass of classList) {
|
||||
if (normalClass.startsWith(`${componentName}-`)) continue;
|
||||
newString += ` ${normalClass}`;
|
||||
}
|
||||
classNames[baseClassName] = newString.trim();
|
||||
}
|
||||
}
|
||||
|
||||
normalizeElement(element) {
|
||||
if (!(element instanceof Element)) return;
|
||||
const classes = element.classList;
|
||||
for (let c = 0, clen = classes.length; c < clen; c++) {
|
||||
if (!randClass.test(classes[c])) continue;
|
||||
const match = classes[c].match(randClass)[1];
|
||||
const newClass = match.split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join("");
|
||||
element.classList.add(`${normalizedPrefix}-${newClass}`);
|
||||
}
|
||||
for (const child of element.children) this.normalizeElement(child);
|
||||
}
|
||||
|
||||
revertElement(element) {
|
||||
if (!(element instanceof Element)) return;
|
||||
if (element.children && element.children.length) this.revertElement(element.children[0]);
|
||||
if (element.nextElementSibling) this.revertElement(element.nextElementSibling);
|
||||
const classes = element.classList;
|
||||
const toRemove = [];
|
||||
for (let c = 0; c < classes.length; c++) {
|
||||
if (classes[c].startsWith(`${normalizedPrefix}-`)) toRemove.push(classes[c]);
|
||||
}
|
||||
element.classList.remove(...toRemove);
|
||||
}
|
||||
|
||||
patchDOMMethods() {
|
||||
const contains = DOMTokenList.prototype.contains;
|
||||
DOMTokenList.prototype.contains = function(token) {
|
||||
// const tokens = token.split(" ");
|
||||
return Reflect.apply(contains, this, [token.split(" ")[0]]);
|
||||
// return tokens.every(t => contains.call(this, t));
|
||||
};
|
||||
|
||||
const add = DOMTokenList.prototype.add;
|
||||
DOMTokenList.prototype.add = function(...tokens) {
|
||||
for (let t = 0; t < tokens.length; t++) {
|
||||
tokens[t] = tokens[t].split(" ")[0];
|
||||
}
|
||||
return Reflect.apply(add, this, tokens);
|
||||
};
|
||||
|
||||
const remove = DOMTokenList.prototype.remove;
|
||||
DOMTokenList.prototype.remove = function(...tokens) {
|
||||
for (let t = 0; t < tokens.length; t++) {
|
||||
tokens[t] = tokens[t].split(" ")[0];
|
||||
}
|
||||
return Reflect.apply(remove, this, tokens);
|
||||
};
|
||||
}
|
||||
|
||||
import WebpackModules from "./webpackModules";
|
||||
|
||||
const normalizedPrefix = "da";
|
||||
const randClass = new RegExp(`^(?!${normalizedPrefix}-)((?:[A-Za-z]|[0-9]|-)+)-(?:[A-Za-z]|[0-9]|-|_){6}$`);
|
||||
|
||||
export default new class ClassNormalizer {
|
||||
constructor(){
|
||||
window.Lightcord.BetterDiscord.ClassNormalizer = this
|
||||
}
|
||||
stop() {
|
||||
if (!this.hasPatched) return;
|
||||
this.unpatchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this)));
|
||||
this.revertElement(document.querySelector("#app-mount"));
|
||||
this.hasPatched = false;
|
||||
}
|
||||
|
||||
start() {
|
||||
if (this.hasPatched) return;
|
||||
this.patchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this)));
|
||||
this.normalizeElement(document.querySelector("#app-mount"));
|
||||
this.hasPatched = true;
|
||||
this.patchDOMMethods();
|
||||
}
|
||||
|
||||
patchClassModules(modules) {
|
||||
for (const module of modules) {
|
||||
this.patchClassModule(normalizedPrefix, module);
|
||||
}
|
||||
}
|
||||
|
||||
unpatchClassModules(modules) {
|
||||
for (const module of modules) {
|
||||
this.unpatchClassModule(normalizedPrefix, module);
|
||||
}
|
||||
}
|
||||
|
||||
shouldIgnore(value) {
|
||||
if (!isNaN(value)) 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("#") && (value.length == 7 || value.length == 4)) return true;
|
||||
if (value.includes("calc(") || value.includes("rgba")) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
moduleFilter(module) {
|
||||
if (typeof module !== "object" || Array.isArray(module)) return false;
|
||||
if (module.__esModule) return false;
|
||||
if (!Object.keys(module).length) return false;
|
||||
for (const baseClassName in module) {
|
||||
const value = module[baseClassName];
|
||||
if (typeof value !== "string") return false;
|
||||
if (this.shouldIgnore(value)) continue;
|
||||
if (value.split("-").length === 1) return false;
|
||||
if (!randClass.test(value.split(" ")[0])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
patchClassModule(componentName, classNames) {
|
||||
for (const baseClassName in classNames) {
|
||||
const value = classNames[baseClassName];
|
||||
if (this.shouldIgnore(value)) continue;
|
||||
const classList = value.split(" ");
|
||||
for (const normalClass of classList) {
|
||||
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
|
||||
const camelCase = match[1].split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join("");
|
||||
classNames[baseClassName] += ` ${componentName}-${camelCase}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unpatchClassModule(componentName, classNames) {
|
||||
for (const baseClassName in classNames) {
|
||||
const value = classNames[baseClassName];
|
||||
if (this.shouldIgnore(value)) continue;
|
||||
let newString = "";
|
||||
const classList = value.split(" ");
|
||||
for (const normalClass of classList) {
|
||||
if (normalClass.startsWith(`${componentName}-`)) continue;
|
||||
newString += ` ${normalClass}`;
|
||||
}
|
||||
classNames[baseClassName] = newString.trim();
|
||||
}
|
||||
}
|
||||
|
||||
normalizeElement(element) {
|
||||
if (!(element instanceof Element)) return;
|
||||
const classes = element.classList;
|
||||
for (let c = 0, clen = classes.length; c < clen; c++) {
|
||||
if (!randClass.test(classes[c])) continue;
|
||||
const match = classes[c].match(randClass)[1];
|
||||
const newClass = match.split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join("");
|
||||
element.classList.add(`${normalizedPrefix}-${newClass}`);
|
||||
}
|
||||
for (const child of element.children) this.normalizeElement(child);
|
||||
}
|
||||
|
||||
revertElement(element) {
|
||||
if (!(element instanceof Element)) return;
|
||||
if (element.children && element.children.length) this.revertElement(element.children[0]);
|
||||
if (element.nextElementSibling) this.revertElement(element.nextElementSibling);
|
||||
const classes = element.classList;
|
||||
const toRemove = [];
|
||||
for (let c = 0; c < classes.length; c++) {
|
||||
if (classes[c].startsWith(`${normalizedPrefix}-`)) toRemove.push(classes[c]);
|
||||
}
|
||||
element.classList.remove(...toRemove);
|
||||
}
|
||||
|
||||
patchDOMMethods() {
|
||||
const contains = DOMTokenList.prototype.contains;
|
||||
DOMTokenList.prototype.contains = function(token) {
|
||||
// const tokens = token.split(" ");
|
||||
return Reflect.apply(contains, this, [token.split(" ")[0]]);
|
||||
// return tokens.every(t => contains.call(this, t));
|
||||
};
|
||||
|
||||
const add = DOMTokenList.prototype.add;
|
||||
DOMTokenList.prototype.add = function(...tokens) {
|
||||
for (let t = 0; t < tokens.length; t++) {
|
||||
tokens[t] = tokens[t].split(" ")[0];
|
||||
}
|
||||
return Reflect.apply(add, this, tokens);
|
||||
};
|
||||
|
||||
const remove = DOMTokenList.prototype.remove;
|
||||
DOMTokenList.prototype.remove = function(...tokens) {
|
||||
for (let t = 0; t < tokens.length; t++) {
|
||||
tokens[t] = tokens[t].split(" ")[0];
|
||||
}
|
||||
return Reflect.apply(remove, this, tokens);
|
||||
};
|
||||
}
|
||||
|
||||
};
|
|
@ -1,342 +1,342 @@
|
|||
import {bdConfig, bdplugins, bdthemes, settingsCookie} from "../0globals";
|
||||
import pluginModule from "./pluginModule";
|
||||
import themeModule from "./themeModule";
|
||||
import Utils from "./utils";
|
||||
import dataStore from "./dataStore";
|
||||
import { encryptSettingsCache, decryptSettingsCache, processFile } from "./pluginCertifier";
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const Module = require("module").Module;
|
||||
Module.globalPaths.push(path.resolve(require("electron").remote.app.getAppPath(), "node_modules"));
|
||||
class MetaError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = "MetaError";
|
||||
}
|
||||
}
|
||||
const originalJSRequire = Module._extensions[".js"];
|
||||
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 escapedAtRegex = /^\\@/;
|
||||
|
||||
export let addonCache = {}
|
||||
|
||||
let hasPatched = false
|
||||
export default new class ContentManager {
|
||||
|
||||
constructor() {
|
||||
this.timeCache = {};
|
||||
this.watchers = {};
|
||||
}
|
||||
|
||||
patchExtensions(){
|
||||
if(hasPatched)return
|
||||
hasPatched = true
|
||||
Module._extensions[".js"] = this.getContentRequire("plugin");
|
||||
Module._extensions[".css"] = this.getContentRequire("theme");
|
||||
}
|
||||
|
||||
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/")));}
|
||||
|
||||
loadAddonCertifierCache(){
|
||||
if(typeof dataStore.getSettingGroup("PluginCertifierHashes") !== "string"){
|
||||
dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache("{}"))
|
||||
}else{
|
||||
try{
|
||||
addonCache = JSON.parse(decryptSettingsCache(dataStore.getSettingGroup("PluginCertifierHashes")))
|
||||
}catch(e){
|
||||
dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache("{}"))
|
||||
addonCache = {}
|
||||
}
|
||||
}
|
||||
Object.keys(addonCache)
|
||||
.forEach(key => {
|
||||
let value = addonCache[key]
|
||||
if(!value || typeof value !== "object" || Array.isArray(value))return delete addonCache[key]
|
||||
|
||||
let props = [{
|
||||
key: "timestamp",
|
||||
type: "number"
|
||||
}, {
|
||||
key: "result",
|
||||
type: "object"
|
||||
}, {
|
||||
key: "hash",
|
||||
type: "string"
|
||||
}]
|
||||
for(let prop of props){
|
||||
if(!(prop.key in value) || typeof value[prop.key] !== prop.type){
|
||||
delete addonCache[key]
|
||||
return
|
||||
}
|
||||
}
|
||||
if(value.hash !== key){
|
||||
delete addonCache[key]
|
||||
return
|
||||
}
|
||||
if(value.result.suspect){ // refetch from remote to be sure you're up to date.
|
||||
delete addonCache[key]
|
||||
return
|
||||
}
|
||||
})
|
||||
this.saveAddonCache()
|
||||
}
|
||||
|
||||
saveAddonCache(){
|
||||
dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache(JSON.stringify(addonCache)))
|
||||
}
|
||||
|
||||
watchContent(contentType) {
|
||||
if (this.watchers[contentType]) return;
|
||||
const isPlugin = contentType === "plugin";
|
||||
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
const fileEnding = isPlugin ? ".plugin.js" : ".theme.css";
|
||||
this.watchers[contentType] = fs.watch(baseFolder, {persistent: false}, async (eventType, filename) => {
|
||||
if (!eventType || !filename || !filename.endsWith(fileEnding)) return;
|
||||
await new Promise(r => setTimeout(r, 50));
|
||||
try {fs.statSync(path.resolve(baseFolder, filename));}
|
||||
catch (err) {
|
||||
if (err.code !== "ENOENT") return;
|
||||
delete this.timeCache[filename];
|
||||
if (isPlugin) return pluginModule.unloadPlugin(filename);
|
||||
return themeModule.unloadTheme(filename);
|
||||
}
|
||||
if (!fs.statSync(path.resolve(baseFolder, filename)).isFile()) return;
|
||||
const stats = fs.statSync(path.resolve(baseFolder, filename));
|
||||
if (!stats || !stats.mtime || !stats.mtime.getTime()) return;
|
||||
if (typeof(stats.mtime.getTime()) !== "number") return;
|
||||
if (this.timeCache[filename] == stats.mtime.getTime()) return;
|
||||
this.timeCache[filename] = stats.mtime.getTime();
|
||||
if (eventType == "rename") {
|
||||
if (isPlugin) await pluginModule.loadPlugin(filename);
|
||||
else await themeModule.loadTheme(filename);
|
||||
}
|
||||
if (eventType == "change") {
|
||||
if (isPlugin) await pluginModule.reloadPlugin(filename);
|
||||
else await themeModule.reloadTheme(filename);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
unwatchContent(contentType) {
|
||||
if (!this.watchers[contentType]) return;
|
||||
this.watchers[contentType].close();
|
||||
delete this.watchers[contentType];
|
||||
}
|
||||
|
||||
extractMeta(content) {
|
||||
const firstLine = content.split("\n")[0];
|
||||
const hasOldMeta = firstLine.includes("//META");
|
||||
if (hasOldMeta) return this.parseOldMeta(content);
|
||||
const hasNewMeta = firstLine.includes("/**");
|
||||
if (hasNewMeta) return this.parseNewMeta(content);
|
||||
throw new MetaError("META was not found.");
|
||||
}
|
||||
|
||||
parseOldMeta(content) {
|
||||
const meta = content.split("\n")[0];
|
||||
const rawMeta = meta.substring(meta.lastIndexOf("//META") + 6, meta.lastIndexOf("*//"));
|
||||
if (meta.indexOf("META") < 0) throw new MetaError("META was not found.");
|
||||
const parsed = Utils.testJSON(rawMeta);
|
||||
if (!parsed) throw new MetaError("META could not be parsed.");
|
||||
if (!parsed.name) throw new MetaError("META missing name data.");
|
||||
parsed.format = "json";
|
||||
return parsed;
|
||||
}
|
||||
|
||||
parseNewMeta(content) {
|
||||
const block = content.split("/**", 2)[1].split("*/", 1)[0];
|
||||
const out = {};
|
||||
let field = "";
|
||||
let accum = "";
|
||||
for (const line of block.split(splitRegex)) {
|
||||
if (line.length === 0) continue;
|
||||
if (line.charAt(0) === "@" && line.charAt(1) !== " ") {
|
||||
out[field] = accum;
|
||||
const l = line.indexOf(" ");
|
||||
field = line.substr(1, l - 1);
|
||||
accum = line.substr(l + 1);
|
||||
}
|
||||
else {
|
||||
accum += " " + line.replace("\\n", "\n").replace(escapedAtRegex, "@");
|
||||
}
|
||||
}
|
||||
out[field] = accum.trim();
|
||||
delete out[""];
|
||||
out.format = "jsdoc";
|
||||
return out;
|
||||
}
|
||||
|
||||
getContentRequire(type) {
|
||||
const isPlugin = type === "plugin";
|
||||
const self = this;
|
||||
const originalRequire = isPlugin ? originalJSRequire : originalCSSRequire;
|
||||
return function(module, filename) {
|
||||
const baseFolder = isPlugin ? self.pluginsFolder : self.themesFolder;
|
||||
const possiblePath = path.resolve(baseFolder, path.basename(filename));
|
||||
if (!fs.existsSync(possiblePath) || filename !== fs.realpathSync(possiblePath)) return Reflect.apply(originalRequire, this, arguments);
|
||||
let content = fs.readFileSync(filename, "utf8");
|
||||
content = Utils.stripBOM(content);
|
||||
|
||||
const stats = fs.statSync(filename);
|
||||
const meta = self.extractMeta(content);
|
||||
meta.filename = path.basename(filename);
|
||||
meta.added = stats.atimeMs;
|
||||
meta.modified = stats.mtimeMs;
|
||||
meta.size = stats.size;
|
||||
if (!isPlugin) {
|
||||
meta.css = content;
|
||||
if (meta.format == "json") meta.css = meta.css.split("\n").slice(1).join("\n");
|
||||
content = `module.exports = ${JSON.stringify(meta)};`;
|
||||
}
|
||||
if (isPlugin) {
|
||||
module._compile(content, module.filename);
|
||||
const didExport = !Utils.isEmpty(module.exports);
|
||||
if (didExport) {
|
||||
meta.type = module.exports;
|
||||
module.exports = meta;
|
||||
content = "";
|
||||
}
|
||||
else {
|
||||
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};`;
|
||||
}
|
||||
}
|
||||
module._compile(content, filename);
|
||||
};
|
||||
}
|
||||
|
||||
makePlaceholderPlugin(data) {
|
||||
return {plugin: {
|
||||
start: () => {},
|
||||
getName: () => {return data.name || data.filename;},
|
||||
getAuthor: () => {return "???";},
|
||||
getDescription: () => {return data.message ? data.message : "This plugin was unable to be loaded. Check the author's page for updates.";},
|
||||
getVersion: () => {return "???";}
|
||||
},
|
||||
name: data.name || data.filename,
|
||||
filename: data.filename,
|
||||
source: data.source ? data.source : "",
|
||||
website: data.website ? data.website : ""
|
||||
};
|
||||
}
|
||||
|
||||
async loadContent(filename, type) {
|
||||
if (typeof(filename) === "undefined" || typeof(type) === "undefined") return;
|
||||
const isPlugin = type === "plugin";
|
||||
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
|
||||
if(settingsCookie["fork-ps-6"]){
|
||||
let result = await new Promise(resolve => {
|
||||
processFile(path.resolve(baseFolder, filename), (result) => {
|
||||
console.log(result)
|
||||
resolve(result)
|
||||
}, (hash) => {
|
||||
resolve({
|
||||
suspect: false,
|
||||
hash: hash,
|
||||
filename: filename,
|
||||
name: filename
|
||||
})
|
||||
}, true)
|
||||
})
|
||||
if(result){
|
||||
addonCache[result.hash] = {
|
||||
timestamp: Date.now(),
|
||||
hash: result.hash,
|
||||
result: result
|
||||
}
|
||||
this.saveAddonCache()
|
||||
if(result.suspect){
|
||||
return {
|
||||
name: filename,
|
||||
file: filename,
|
||||
message: "This plugin might be dangerous ("+result.harm+").",
|
||||
error: new Error("This plugin might be dangerous ("+result.harm+").")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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}};}
|
||||
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")}
|
||||
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 (isPlugin) {
|
||||
if (!content.type) return;
|
||||
try {
|
||||
content.plugin = new content.type();
|
||||
delete bdplugins[content.plugin.getName()];
|
||||
bdplugins[content.plugin.getName()] = content;
|
||||
}
|
||||
catch (error) {return {name: filename, file: filename, message: "Could not be constructed.", error: {message: error.message, stack: error.stack}};}
|
||||
}
|
||||
else {
|
||||
delete bdthemes[content.name];
|
||||
bdthemes[content.name] = content;
|
||||
}
|
||||
}
|
||||
|
||||
unloadContent(filename, type) {
|
||||
if (typeof(filename) === "undefined" || typeof(type) === "undefined") return;
|
||||
const isPlugin = type === "plugin";
|
||||
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
try {
|
||||
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}};}
|
||||
}
|
||||
|
||||
isLoaded(filename, type) {
|
||||
const isPlugin = type === "plugin";
|
||||
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
try {window.require.cache[window.require.resolve(path.resolve(baseFolder, filename))];}
|
||||
catch (err) {return false;}
|
||||
return true;
|
||||
}
|
||||
|
||||
async reloadContent(filename, type) {
|
||||
const cantUnload = this.unloadContent(filename, type);
|
||||
if (cantUnload) return cantUnload;
|
||||
return await this.loadContent(filename, type);
|
||||
}
|
||||
|
||||
loadNewContent(type) {
|
||||
const isPlugin = type === "plugin";
|
||||
const fileEnding = isPlugin ? ".plugin.js" : ".theme.css";
|
||||
const basedir = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
const files = fs.readdirSync(basedir);
|
||||
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 added = files.filter(f => !contentList.find(t => t.filename == f) && f.endsWith(fileEnding) && fs.statSync(path.resolve(basedir, f)).isFile());
|
||||
return {added, removed};
|
||||
}
|
||||
|
||||
async loadAllContent(type) {
|
||||
this.patchExtensions()
|
||||
const isPlugin = type === "plugin";
|
||||
const fileEnding = isPlugin ? ".plugin.js" : ".theme.css";
|
||||
const basedir = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
const errors = [];
|
||||
const files = fs.readdirSync(basedir);
|
||||
|
||||
for (const filename of files) {
|
||||
if (!fs.statSync(path.resolve(basedir, filename)).isFile() || !filename.endsWith(fileEnding)) continue;
|
||||
const error = await this.loadContent(filename, type);
|
||||
if (error) errors.push(error);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
loadPlugins() {return this.loadAllContent("plugin");}
|
||||
loadThemes() {return this.loadAllContent("theme");}
|
||||
};
|
||||
|
||||
/**
|
||||
* Don't expose contentManager - could be dangerous for now
|
||||
import {bdConfig, bdplugins, bdthemes, settingsCookie} from "../0globals";
|
||||
import pluginModule from "./pluginModule";
|
||||
import themeModule from "./themeModule";
|
||||
import Utils from "./utils";
|
||||
import dataStore from "./dataStore";
|
||||
import { encryptSettingsCache, decryptSettingsCache, processFile } from "./pluginCertifier";
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const Module = require("module").Module;
|
||||
Module.globalPaths.push(path.resolve(require("electron").remote.app.getAppPath(), "node_modules"));
|
||||
class MetaError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = "MetaError";
|
||||
}
|
||||
}
|
||||
const originalJSRequire = Module._extensions[".js"];
|
||||
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 escapedAtRegex = /^\\@/;
|
||||
|
||||
export let addonCache = {}
|
||||
|
||||
let hasPatched = false
|
||||
export default new class ContentManager {
|
||||
|
||||
constructor() {
|
||||
this.timeCache = {};
|
||||
this.watchers = {};
|
||||
}
|
||||
|
||||
patchExtensions(){
|
||||
if(hasPatched)return
|
||||
hasPatched = true
|
||||
Module._extensions[".js"] = this.getContentRequire("plugin");
|
||||
Module._extensions[".css"] = this.getContentRequire("theme");
|
||||
}
|
||||
|
||||
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/")));}
|
||||
|
||||
loadAddonCertifierCache(){
|
||||
if(typeof dataStore.getSettingGroup("PluginCertifierHashes") !== "string"){
|
||||
dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache("{}"))
|
||||
}else{
|
||||
try{
|
||||
addonCache = JSON.parse(decryptSettingsCache(dataStore.getSettingGroup("PluginCertifierHashes")))
|
||||
}catch(e){
|
||||
dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache("{}"))
|
||||
addonCache = {}
|
||||
}
|
||||
}
|
||||
Object.keys(addonCache)
|
||||
.forEach(key => {
|
||||
let value = addonCache[key]
|
||||
if(!value || typeof value !== "object" || Array.isArray(value))return delete addonCache[key]
|
||||
|
||||
let props = [{
|
||||
key: "timestamp",
|
||||
type: "number"
|
||||
}, {
|
||||
key: "result",
|
||||
type: "object"
|
||||
}, {
|
||||
key: "hash",
|
||||
type: "string"
|
||||
}]
|
||||
for(let prop of props){
|
||||
if(!(prop.key in value) || typeof value[prop.key] !== prop.type){
|
||||
delete addonCache[key]
|
||||
return
|
||||
}
|
||||
}
|
||||
if(value.hash !== key){
|
||||
delete addonCache[key]
|
||||
return
|
||||
}
|
||||
if(value.result.suspect){ // refetch from remote to be sure you're up to date.
|
||||
delete addonCache[key]
|
||||
return
|
||||
}
|
||||
})
|
||||
this.saveAddonCache()
|
||||
}
|
||||
|
||||
saveAddonCache(){
|
||||
dataStore.setSettingGroup("PluginCertifierHashes", encryptSettingsCache(JSON.stringify(addonCache)))
|
||||
}
|
||||
|
||||
watchContent(contentType) {
|
||||
if (this.watchers[contentType]) return;
|
||||
const isPlugin = contentType === "plugin";
|
||||
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
const fileEnding = isPlugin ? ".plugin.js" : ".theme.css";
|
||||
this.watchers[contentType] = fs.watch(baseFolder, {persistent: false}, async (eventType, filename) => {
|
||||
if (!eventType || !filename || !filename.endsWith(fileEnding)) return;
|
||||
await new Promise(r => setTimeout(r, 50));
|
||||
try {fs.statSync(path.resolve(baseFolder, filename));}
|
||||
catch (err) {
|
||||
if (err.code !== "ENOENT") return;
|
||||
delete this.timeCache[filename];
|
||||
if (isPlugin) return pluginModule.unloadPlugin(filename);
|
||||
return themeModule.unloadTheme(filename);
|
||||
}
|
||||
if (!fs.statSync(path.resolve(baseFolder, filename)).isFile()) return;
|
||||
const stats = fs.statSync(path.resolve(baseFolder, filename));
|
||||
if (!stats || !stats.mtime || !stats.mtime.getTime()) return;
|
||||
if (typeof(stats.mtime.getTime()) !== "number") return;
|
||||
if (this.timeCache[filename] == stats.mtime.getTime()) return;
|
||||
this.timeCache[filename] = stats.mtime.getTime();
|
||||
if (eventType == "rename") {
|
||||
if (isPlugin) await pluginModule.loadPlugin(filename);
|
||||
else await themeModule.loadTheme(filename);
|
||||
}
|
||||
if (eventType == "change") {
|
||||
if (isPlugin) await pluginModule.reloadPlugin(filename);
|
||||
else await themeModule.reloadTheme(filename);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
unwatchContent(contentType) {
|
||||
if (!this.watchers[contentType]) return;
|
||||
this.watchers[contentType].close();
|
||||
delete this.watchers[contentType];
|
||||
}
|
||||
|
||||
extractMeta(content) {
|
||||
const firstLine = content.split("\n")[0];
|
||||
const hasOldMeta = firstLine.includes("//META");
|
||||
if (hasOldMeta) return this.parseOldMeta(content);
|
||||
const hasNewMeta = firstLine.includes("/**");
|
||||
if (hasNewMeta) return this.parseNewMeta(content);
|
||||
throw new MetaError("META was not found.");
|
||||
}
|
||||
|
||||
parseOldMeta(content) {
|
||||
const meta = content.split("\n")[0];
|
||||
const rawMeta = meta.substring(meta.lastIndexOf("//META") + 6, meta.lastIndexOf("*//"));
|
||||
if (meta.indexOf("META") < 0) throw new MetaError("META was not found.");
|
||||
const parsed = Utils.testJSON(rawMeta);
|
||||
if (!parsed) throw new MetaError("META could not be parsed.");
|
||||
if (!parsed.name) throw new MetaError("META missing name data.");
|
||||
parsed.format = "json";
|
||||
return parsed;
|
||||
}
|
||||
|
||||
parseNewMeta(content) {
|
||||
const block = content.split("/**", 2)[1].split("*/", 1)[0];
|
||||
const out = {};
|
||||
let field = "";
|
||||
let accum = "";
|
||||
for (const line of block.split(splitRegex)) {
|
||||
if (line.length === 0) continue;
|
||||
if (line.charAt(0) === "@" && line.charAt(1) !== " ") {
|
||||
out[field] = accum;
|
||||
const l = line.indexOf(" ");
|
||||
field = line.substr(1, l - 1);
|
||||
accum = line.substr(l + 1);
|
||||
}
|
||||
else {
|
||||
accum += " " + line.replace("\\n", "\n").replace(escapedAtRegex, "@");
|
||||
}
|
||||
}
|
||||
out[field] = accum.trim();
|
||||
delete out[""];
|
||||
out.format = "jsdoc";
|
||||
return out;
|
||||
}
|
||||
|
||||
getContentRequire(type) {
|
||||
const isPlugin = type === "plugin";
|
||||
const self = this;
|
||||
const originalRequire = isPlugin ? originalJSRequire : originalCSSRequire;
|
||||
return function(module, filename) {
|
||||
const baseFolder = isPlugin ? self.pluginsFolder : self.themesFolder;
|
||||
const possiblePath = path.resolve(baseFolder, path.basename(filename));
|
||||
if (!fs.existsSync(possiblePath) || filename !== fs.realpathSync(possiblePath)) return Reflect.apply(originalRequire, this, arguments);
|
||||
let content = fs.readFileSync(filename, "utf8");
|
||||
content = Utils.stripBOM(content);
|
||||
|
||||
const stats = fs.statSync(filename);
|
||||
const meta = self.extractMeta(content);
|
||||
meta.filename = path.basename(filename);
|
||||
meta.added = stats.atimeMs;
|
||||
meta.modified = stats.mtimeMs;
|
||||
meta.size = stats.size;
|
||||
if (!isPlugin) {
|
||||
meta.css = content;
|
||||
if (meta.format == "json") meta.css = meta.css.split("\n").slice(1).join("\n");
|
||||
content = `module.exports = ${JSON.stringify(meta)};`;
|
||||
}
|
||||
if (isPlugin) {
|
||||
module._compile(content, module.filename);
|
||||
const didExport = !Utils.isEmpty(module.exports);
|
||||
if (didExport) {
|
||||
meta.type = module.exports;
|
||||
module.exports = meta;
|
||||
content = "";
|
||||
}
|
||||
else {
|
||||
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};`;
|
||||
}
|
||||
}
|
||||
module._compile(content, filename);
|
||||
};
|
||||
}
|
||||
|
||||
makePlaceholderPlugin(data) {
|
||||
return {plugin: {
|
||||
start: () => {},
|
||||
getName: () => {return data.name || data.filename;},
|
||||
getAuthor: () => {return "???";},
|
||||
getDescription: () => {return data.message ? data.message : "This plugin was unable to be loaded. Check the author's page for updates.";},
|
||||
getVersion: () => {return "???";}
|
||||
},
|
||||
name: data.name || data.filename,
|
||||
filename: data.filename,
|
||||
source: data.source ? data.source : "",
|
||||
website: data.website ? data.website : ""
|
||||
};
|
||||
}
|
||||
|
||||
async loadContent(filename, type) {
|
||||
if (typeof(filename) === "undefined" || typeof(type) === "undefined") return;
|
||||
const isPlugin = type === "plugin";
|
||||
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
|
||||
if(settingsCookie["fork-ps-6"]){
|
||||
let result = await new Promise(resolve => {
|
||||
processFile(path.resolve(baseFolder, filename), (result) => {
|
||||
console.log(result)
|
||||
resolve(result)
|
||||
}, (hash) => {
|
||||
resolve({
|
||||
suspect: false,
|
||||
hash: hash,
|
||||
filename: filename,
|
||||
name: filename
|
||||
})
|
||||
}, true)
|
||||
})
|
||||
if(result){
|
||||
addonCache[result.hash] = {
|
||||
timestamp: Date.now(),
|
||||
hash: result.hash,
|
||||
result: result
|
||||
}
|
||||
this.saveAddonCache()
|
||||
if(result.suspect){
|
||||
return {
|
||||
name: filename,
|
||||
file: filename,
|
||||
message: "This plugin might be dangerous ("+result.harm+").",
|
||||
error: new Error("This plugin might be dangerous ("+result.harm+").")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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}};}
|
||||
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")}
|
||||
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 (isPlugin) {
|
||||
if (!content.type) return;
|
||||
try {
|
||||
content.plugin = new content.type();
|
||||
delete bdplugins[content.plugin.getName()];
|
||||
bdplugins[content.plugin.getName()] = content;
|
||||
}
|
||||
catch (error) {return {name: filename, file: filename, message: "Could not be constructed.", error: {message: error.message, stack: error.stack}};}
|
||||
}
|
||||
else {
|
||||
delete bdthemes[content.name];
|
||||
bdthemes[content.name] = content;
|
||||
}
|
||||
}
|
||||
|
||||
unloadContent(filename, type) {
|
||||
if (typeof(filename) === "undefined" || typeof(type) === "undefined") return;
|
||||
const isPlugin = type === "plugin";
|
||||
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
try {
|
||||
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}};}
|
||||
}
|
||||
|
||||
isLoaded(filename, type) {
|
||||
const isPlugin = type === "plugin";
|
||||
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
try {window.require.cache[window.require.resolve(path.resolve(baseFolder, filename))];}
|
||||
catch (err) {return false;}
|
||||
return true;
|
||||
}
|
||||
|
||||
async reloadContent(filename, type) {
|
||||
const cantUnload = this.unloadContent(filename, type);
|
||||
if (cantUnload) return cantUnload;
|
||||
return await this.loadContent(filename, type);
|
||||
}
|
||||
|
||||
loadNewContent(type) {
|
||||
const isPlugin = type === "plugin";
|
||||
const fileEnding = isPlugin ? ".plugin.js" : ".theme.css";
|
||||
const basedir = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
const files = fs.readdirSync(basedir);
|
||||
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 added = files.filter(f => !contentList.find(t => t.filename == f) && f.endsWith(fileEnding) && fs.statSync(path.resolve(basedir, f)).isFile());
|
||||
return {added, removed};
|
||||
}
|
||||
|
||||
async loadAllContent(type) {
|
||||
this.patchExtensions()
|
||||
const isPlugin = type === "plugin";
|
||||
const fileEnding = isPlugin ? ".plugin.js" : ".theme.css";
|
||||
const basedir = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
const errors = [];
|
||||
const files = fs.readdirSync(basedir);
|
||||
|
||||
for (const filename of files) {
|
||||
if (!fs.statSync(path.resolve(basedir, filename)).isFile() || !filename.endsWith(fileEnding)) continue;
|
||||
const error = await this.loadContent(filename, type);
|
||||
if (error) errors.push(error);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
loadPlugins() {return this.loadAllContent("plugin");}
|
||||
loadThemes() {return this.loadAllContent("theme");}
|
||||
};
|
||||
|
||||
/**
|
||||
* Don't expose contentManager - could be dangerous for now
|
||||
*/
|
File diff suppressed because it is too large
Load Diff
|
@ -1,96 +1,96 @@
|
|||
const __non_webpack_require__ = window.require
|
||||
|
||||
import Utils from "./utils";
|
||||
import ContentManager from "./contentManager";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const releaseChannel = DiscordNative.globals ? DiscordNative.globals.releaseChannel : DiscordNative.app ? DiscordNative.app.getReleaseChannel() : "stable";
|
||||
|
||||
let dataPath = "";
|
||||
if (process.platform === "win32") dataPath = process.env.APPDATA;
|
||||
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");
|
||||
dataPath = path.join(dataPath, "BetterDiscord");
|
||||
|
||||
export default new class DataStore {
|
||||
constructor() {
|
||||
this.data = {settings: {stable: {}, canary: {}, ptb: {}}};
|
||||
this.pluginData = {};
|
||||
window.Lightcord.BetterDiscord.DataStore = this
|
||||
}
|
||||
|
||||
initialize() {
|
||||
try {
|
||||
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"))
|
||||
if (data.hasOwnProperty("settings")) this.data = data;
|
||||
if (!fs.existsSync(this.settingsFile)) return;
|
||||
let settings = __non_webpack_require__(this.settingsFile);
|
||||
fs.unlinkSync(this.settingsFile);
|
||||
if (settings.hasOwnProperty("settings")) settings = Object.assign({stable: {}, canary: {}, ptb: {}}, {[releaseChannel]: settings});
|
||||
else settings = Object.assign({stable: {}, canary: {}, ptb: {}}, settings);
|
||||
this.setBDData("settings", settings);
|
||||
}
|
||||
catch (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.");
|
||||
}
|
||||
}
|
||||
|
||||
get injectionPath() {
|
||||
return this._injectionPath = null;
|
||||
if (this._injectionPath) return this._injectionPath;
|
||||
const electron = require("electron").remote.app;
|
||||
const base = electron.getAppPath();
|
||||
const roamingBase = electron.getPath("userData");
|
||||
const roamingLocation = path.resolve(roamingBase, electron.getVersion(), "modules", "discord_desktop_core", "injector");
|
||||
const location = path.resolve(base, "..", "app");
|
||||
const realLocation = fs.existsSync(location) ? location : fs.existsSync(roamingLocation) ? roamingLocation : null;
|
||||
if (!realLocation) return this._injectionPath = null;
|
||||
return this._injectionPath = realLocation;
|
||||
}
|
||||
|
||||
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 settingsFile() {return this._settingsFile || (this._settingsFile = path.resolve(dataPath, "bdsettings.json"));}
|
||||
getPluginFile(pluginName) {return path.resolve(ContentManager.pluginsFolder, pluginName + ".config.json");}
|
||||
|
||||
getSettingGroup(key) {
|
||||
return this.data.settings[releaseChannel][key] || null;
|
||||
}
|
||||
|
||||
setSettingGroup(key, data) {
|
||||
this.data.settings[releaseChannel][key] = data;
|
||||
fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8");
|
||||
}
|
||||
|
||||
getBDData(key) {
|
||||
return this.data[key] || "";
|
||||
}
|
||||
|
||||
setBDData(key, value) {
|
||||
this.data[key] = value;
|
||||
fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8");
|
||||
}
|
||||
|
||||
getPluginData(pluginName, key) {
|
||||
if (this.pluginData[pluginName] !== undefined) return this.pluginData[pluginName][key];
|
||||
if (!fs.existsSync(this.getPluginFile(pluginName))) return undefined;
|
||||
this.pluginData[pluginName] = JSON.parse(fs.readFileSync(this.getPluginFile(pluginName)));
|
||||
return this.pluginData[pluginName][key];
|
||||
}
|
||||
|
||||
setPluginData(pluginName, key, value) {
|
||||
if (value === undefined) return;
|
||||
if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {};
|
||||
this.pluginData[pluginName][key] = value;
|
||||
fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4), "utf-8");
|
||||
}
|
||||
|
||||
deletePluginData(pluginName, key) {
|
||||
if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {};
|
||||
delete this.pluginData[pluginName][key];
|
||||
fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4), "utf-8");
|
||||
}
|
||||
const __non_webpack_require__ = window.require
|
||||
|
||||
import Utils from "./utils";
|
||||
import ContentManager from "./contentManager";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const releaseChannel = DiscordNative.globals ? DiscordNative.globals.releaseChannel : DiscordNative.app ? DiscordNative.app.getReleaseChannel() : "stable";
|
||||
|
||||
let dataPath = "";
|
||||
if (process.platform === "win32") dataPath = process.env.APPDATA;
|
||||
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");
|
||||
dataPath = path.join(dataPath, "BetterDiscord");
|
||||
|
||||
export default new class DataStore {
|
||||
constructor() {
|
||||
this.data = {settings: {stable: {}, canary: {}, ptb: {}}};
|
||||
this.pluginData = {};
|
||||
window.Lightcord.BetterDiscord.DataStore = this
|
||||
}
|
||||
|
||||
initialize() {
|
||||
try {
|
||||
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"))
|
||||
if (data.hasOwnProperty("settings")) this.data = data;
|
||||
if (!fs.existsSync(this.settingsFile)) return;
|
||||
let settings = __non_webpack_require__(this.settingsFile);
|
||||
fs.unlinkSync(this.settingsFile);
|
||||
if (settings.hasOwnProperty("settings")) settings = Object.assign({stable: {}, canary: {}, ptb: {}}, {[releaseChannel]: settings});
|
||||
else settings = Object.assign({stable: {}, canary: {}, ptb: {}}, settings);
|
||||
this.setBDData("settings", settings);
|
||||
}
|
||||
catch (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.");
|
||||
}
|
||||
}
|
||||
|
||||
get injectionPath() {
|
||||
return this._injectionPath = null;
|
||||
if (this._injectionPath) return this._injectionPath;
|
||||
const electron = require("electron").remote.app;
|
||||
const base = electron.getAppPath();
|
||||
const roamingBase = electron.getPath("userData");
|
||||
const roamingLocation = path.resolve(roamingBase, electron.getVersion(), "modules", "discord_desktop_core", "injector");
|
||||
const location = path.resolve(base, "..", "app");
|
||||
const realLocation = fs.existsSync(location) ? location : fs.existsSync(roamingLocation) ? roamingLocation : null;
|
||||
if (!realLocation) return this._injectionPath = null;
|
||||
return this._injectionPath = realLocation;
|
||||
}
|
||||
|
||||
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 settingsFile() {return this._settingsFile || (this._settingsFile = path.resolve(dataPath, "bdsettings.json"));}
|
||||
getPluginFile(pluginName) {return path.resolve(ContentManager.pluginsFolder, pluginName + ".config.json");}
|
||||
|
||||
getSettingGroup(key) {
|
||||
return this.data.settings[releaseChannel][key] || null;
|
||||
}
|
||||
|
||||
setSettingGroup(key, data) {
|
||||
this.data.settings[releaseChannel][key] = data;
|
||||
fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8");
|
||||
}
|
||||
|
||||
getBDData(key) {
|
||||
return this.data[key] || "";
|
||||
}
|
||||
|
||||
setBDData(key, value) {
|
||||
this.data[key] = value;
|
||||
fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8");
|
||||
}
|
||||
|
||||
getPluginData(pluginName, key) {
|
||||
if (this.pluginData[pluginName] !== undefined) return this.pluginData[pluginName][key];
|
||||
if (!fs.existsSync(this.getPluginFile(pluginName))) return undefined;
|
||||
this.pluginData[pluginName] = JSON.parse(fs.readFileSync(this.getPluginFile(pluginName)));
|
||||
return this.pluginData[pluginName][key];
|
||||
}
|
||||
|
||||
setPluginData(pluginName, key, value) {
|
||||
if (value === undefined) return;
|
||||
if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {};
|
||||
this.pluginData[pluginName][key] = value;
|
||||
fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4), "utf-8");
|
||||
}
|
||||
|
||||
deletePluginData(pluginName, key) {
|
||||
if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {};
|
||||
delete this.pluginData[pluginName][key];
|
||||
fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4), "utf-8");
|
||||
}
|
||||
};
|
|
@ -1,161 +1,161 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "./v2";
|
||||
import DOM from "./domtools";
|
||||
import Utils from "./utils";
|
||||
|
||||
export default new class DevMode {
|
||||
constructor() {
|
||||
this.debugListener = this.debugListener.bind(this);
|
||||
this.copySelectorListener = this.copySelectorListener.bind(this);
|
||||
}
|
||||
|
||||
start() {
|
||||
this.startDebugListener();
|
||||
if (settingsCookie["fork-dm-1"]) this.startCopySelector();
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.stopDebugListener();
|
||||
this.stopCopySelector();
|
||||
}
|
||||
|
||||
startDebugListener() {
|
||||
this.stopDebugListener();
|
||||
document.addEventListener("keydown", this.debugListener);
|
||||
}
|
||||
|
||||
stopDebugListener() {
|
||||
document.removeEventListener("keydown", this.debugListener);
|
||||
}
|
||||
|
||||
startCopySelector() {
|
||||
this.stopCopySelector();
|
||||
document.addEventListener("contextmenu", this.copySelectorListener);
|
||||
}
|
||||
|
||||
stopCopySelector() {
|
||||
document.removeEventListener("contextmenu", this.copySelectorListener);
|
||||
}
|
||||
|
||||
debugListener(e) {
|
||||
if (e.which === 119 || e.which == 118) {//F8
|
||||
console.log("%c[%cDevMode%c] %cBreak/Resume", "color: red;", "color: #303030; font-weight:700;", "color:red;", "");
|
||||
debugger; // eslint-disable-line no-debugger
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
}
|
||||
|
||||
copySelectorListener(e) {
|
||||
try{
|
||||
e.stopPropagation();
|
||||
const selector = this.getSelector(e.target);
|
||||
|
||||
let [
|
||||
classLayer,
|
||||
classItems
|
||||
] = [
|
||||
BDModules.get((e) => e.layer && typeof e.layer === "string" && e.disabledPointerEvents)[0],
|
||||
BDModules.get((e) => e.menu)[0]
|
||||
]
|
||||
|
||||
function attach() {
|
||||
if(!classItems || !classLayer.layer)return console.log(classItems, classLayer.layer)
|
||||
|
||||
let cm = DOM.query("."+Utils.removeDa(classItems.menu));
|
||||
if (!cm) {
|
||||
const container = DOM.query("#app-mount > ."+Utils.removeDa(classLayer.layerContainer));
|
||||
const cmWrap = DOM.createElement(`<div class="${classLayer.layer}">`);
|
||||
cm = DOM.createElement(`<div class="${classItems.menu} ${classItems.styleFlexible} ${classItems.accommodateScrollbar} bd-context-menu" style=""></div>`);
|
||||
cmWrap.append(cm);
|
||||
container.append(cmWrap);
|
||||
cmWrap.style.top = e.clientY + "px";
|
||||
cmWrap.style.left = e.clientX + "px";
|
||||
cmWrap.setAttribute("role", "menu")
|
||||
cmWrap.setAttribute("tabindex", "-1")
|
||||
cmWrap.id = "bd-copy-selector-context"
|
||||
cmWrap.setAttribute("aria-label", "Copy Selector Actions")
|
||||
|
||||
const scrollerClasses = BDModules.get((e) => e.scrollerWrap)[0]
|
||||
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>`)
|
||||
scrollerWrap.append(scroller)
|
||||
cm.append(scrollerWrap)
|
||||
|
||||
const removeCM = function(e) {
|
||||
if (e.keyCode && e.keyCode !== 27) return;
|
||||
cmWrap.remove();
|
||||
document.removeEventListener("click", removeCM);
|
||||
document.removeEventListener("contextmenu", removeCM);
|
||||
document.removeEventListener("keyup", removeCM);
|
||||
};
|
||||
document.addEventListener("click", removeCM);
|
||||
document.addEventListener("contextmenu", removeCM);
|
||||
document.addEventListener("keyup", removeCM);
|
||||
}
|
||||
const cmWrap = cm.parentElement
|
||||
|
||||
const scroller = cm.childNodes[0].childNodes[0]
|
||||
const cmg = DOM.createElement(`<div role="group"></div>`);
|
||||
/**
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
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.addEventListener("click", () => {
|
||||
BDV2.NativeModule.copy(selector);
|
||||
cmWrap.style.display = "none"
|
||||
});
|
||||
cmi.addEventListener("mouseover", (e) => {
|
||||
let elements = DOM.queryAll("div[role=menuitem]."+Utils.removeDa(classItems.focused))
|
||||
elements && elements.forEach(elem => elem.classList.remove(classItems.focused))
|
||||
cmi.classList.add(classItems.focused)
|
||||
})
|
||||
cmi.addEventListener("mouseout", (e) => {
|
||||
cmi.classList.remove(classItems.focused)
|
||||
})
|
||||
cmg.append(cmi);
|
||||
if(scroller.childNodes.length){ // apend a separator
|
||||
const separator = DOM.createElement(`<div role="separator" class="${classItems.separator}"></div>`)
|
||||
scroller.append(separator)
|
||||
}
|
||||
scroller.append(cmg);
|
||||
if(cmWrap.clientHeight < cmWrap.scrollHeight){
|
||||
console.log("overflowing "+cmWrap.style.top)
|
||||
cmWrap.style.top = (cmWrap.style.top - cmg.clientHeight) + "px";
|
||||
console.log("overflowing"+cmWrap.style.top)
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(attach, 1);
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
getSelector(element) {
|
||||
if (element.id) return `#${element.id}`;
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} el
|
||||
*/
|
||||
function fullPath(el){
|
||||
var names = [];
|
||||
while (el.parentNode){
|
||||
if (el.id){
|
||||
names.unshift('#'+el.id);
|
||||
break;
|
||||
}else{
|
||||
if (el==el.ownerDocument.documentElement) names.unshift(el.tagName.toLowerCase()+Array.from(el.classList.entries()).map(e => "."+e).join(""));
|
||||
else{
|
||||
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+")");
|
||||
}
|
||||
el=el.parentNode;
|
||||
}
|
||||
}
|
||||
return names.join(" > ");
|
||||
}
|
||||
return fullPath(element)
|
||||
}
|
||||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "./v2";
|
||||
import DOM from "./domtools";
|
||||
import Utils from "./utils";
|
||||
|
||||
export default new class DevMode {
|
||||
constructor() {
|
||||
this.debugListener = this.debugListener.bind(this);
|
||||
this.copySelectorListener = this.copySelectorListener.bind(this);
|
||||
}
|
||||
|
||||
start() {
|
||||
this.startDebugListener();
|
||||
if (settingsCookie["fork-dm-1"]) this.startCopySelector();
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.stopDebugListener();
|
||||
this.stopCopySelector();
|
||||
}
|
||||
|
||||
startDebugListener() {
|
||||
this.stopDebugListener();
|
||||
document.addEventListener("keydown", this.debugListener);
|
||||
}
|
||||
|
||||
stopDebugListener() {
|
||||
document.removeEventListener("keydown", this.debugListener);
|
||||
}
|
||||
|
||||
startCopySelector() {
|
||||
this.stopCopySelector();
|
||||
document.addEventListener("contextmenu", this.copySelectorListener);
|
||||
}
|
||||
|
||||
stopCopySelector() {
|
||||
document.removeEventListener("contextmenu", this.copySelectorListener);
|
||||
}
|
||||
|
||||
debugListener(e) {
|
||||
if (e.which === 119 || e.which == 118) {//F8
|
||||
console.log("%c[%cDevMode%c] %cBreak/Resume", "color: red;", "color: #303030; font-weight:700;", "color:red;", "");
|
||||
debugger; // eslint-disable-line no-debugger
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
}
|
||||
|
||||
copySelectorListener(e) {
|
||||
try{
|
||||
e.stopPropagation();
|
||||
const selector = this.getSelector(e.target);
|
||||
|
||||
let [
|
||||
classLayer,
|
||||
classItems
|
||||
] = [
|
||||
BDModules.get((e) => e.layer && typeof e.layer === "string" && e.disabledPointerEvents)[0],
|
||||
BDModules.get((e) => e.menu)[0]
|
||||
]
|
||||
|
||||
function attach() {
|
||||
if(!classItems || !classLayer.layer)return console.log(classItems, classLayer.layer)
|
||||
|
||||
let cm = DOM.query("."+Utils.removeDa(classItems.menu));
|
||||
if (!cm) {
|
||||
const container = DOM.query("#app-mount > ."+Utils.removeDa(classLayer.layerContainer));
|
||||
const cmWrap = DOM.createElement(`<div class="${classLayer.layer}">`);
|
||||
cm = DOM.createElement(`<div class="${classItems.menu} ${classItems.styleFlexible} ${classItems.accommodateScrollbar} bd-context-menu" style=""></div>`);
|
||||
cmWrap.append(cm);
|
||||
container.append(cmWrap);
|
||||
cmWrap.style.top = e.clientY + "px";
|
||||
cmWrap.style.left = e.clientX + "px";
|
||||
cmWrap.setAttribute("role", "menu")
|
||||
cmWrap.setAttribute("tabindex", "-1")
|
||||
cmWrap.id = "bd-copy-selector-context"
|
||||
cmWrap.setAttribute("aria-label", "Copy Selector Actions")
|
||||
|
||||
const scrollerClasses = BDModules.get((e) => e.scrollerWrap)[0]
|
||||
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>`)
|
||||
scrollerWrap.append(scroller)
|
||||
cm.append(scrollerWrap)
|
||||
|
||||
const removeCM = function(e) {
|
||||
if (e.keyCode && e.keyCode !== 27) return;
|
||||
cmWrap.remove();
|
||||
document.removeEventListener("click", removeCM);
|
||||
document.removeEventListener("contextmenu", removeCM);
|
||||
document.removeEventListener("keyup", removeCM);
|
||||
};
|
||||
document.addEventListener("click", removeCM);
|
||||
document.addEventListener("contextmenu", removeCM);
|
||||
document.addEventListener("keyup", removeCM);
|
||||
}
|
||||
const cmWrap = cm.parentElement
|
||||
|
||||
const scroller = cm.childNodes[0].childNodes[0]
|
||||
const cmg = DOM.createElement(`<div role="group"></div>`);
|
||||
/**
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
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.addEventListener("click", () => {
|
||||
BDV2.NativeModule.copy(selector);
|
||||
cmWrap.style.display = "none"
|
||||
});
|
||||
cmi.addEventListener("mouseover", (e) => {
|
||||
let elements = DOM.queryAll("div[role=menuitem]."+Utils.removeDa(classItems.focused))
|
||||
elements && elements.forEach(elem => elem.classList.remove(classItems.focused))
|
||||
cmi.classList.add(classItems.focused)
|
||||
})
|
||||
cmi.addEventListener("mouseout", (e) => {
|
||||
cmi.classList.remove(classItems.focused)
|
||||
})
|
||||
cmg.append(cmi);
|
||||
if(scroller.childNodes.length){ // apend a separator
|
||||
const separator = DOM.createElement(`<div role="separator" class="${classItems.separator}"></div>`)
|
||||
scroller.append(separator)
|
||||
}
|
||||
scroller.append(cmg);
|
||||
if(cmWrap.clientHeight < cmWrap.scrollHeight){
|
||||
console.log("overflowing "+cmWrap.style.top)
|
||||
cmWrap.style.top = (cmWrap.style.top - cmg.clientHeight) + "px";
|
||||
console.log("overflowing"+cmWrap.style.top)
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(attach, 1);
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
getSelector(element) {
|
||||
if (element.id) return `#${element.id}`;
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} el
|
||||
*/
|
||||
function fullPath(el){
|
||||
var names = [];
|
||||
while (el.parentNode){
|
||||
if (el.id){
|
||||
names.unshift('#'+el.id);
|
||||
break;
|
||||
}else{
|
||||
if (el==el.ownerDocument.documentElement) names.unshift(el.tagName.toLowerCase()+Array.from(el.classList.entries()).map(e => "."+e).join(""));
|
||||
else{
|
||||
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+")");
|
||||
}
|
||||
el=el.parentNode;
|
||||
}
|
||||
}
|
||||
return names.join(" > ");
|
||||
}
|
||||
return fullPath(element)
|
||||
}
|
||||
};
|
|
@ -1,151 +1,151 @@
|
|||
import BugHunterBadge from "../svg/bug_hunter"
|
||||
import nodeFetch from "node-fetch"
|
||||
import { settingsCookie } from "../0globals";
|
||||
import Circus from "../svg/circus";
|
||||
|
||||
export function uuidv4() { // Generate UUID (No crypto rng)
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
const awaitingBadgesPromises = {}
|
||||
let badgesToFetch = []
|
||||
|
||||
export default new class DistantServer {
|
||||
constructor(){
|
||||
this._cache = {
|
||||
badges: {
|
||||
|
||||
}
|
||||
}
|
||||
window.Lightcord.BetterDiscord.DistantServer = this
|
||||
}
|
||||
|
||||
get cache(){
|
||||
return this._cache
|
||||
}
|
||||
|
||||
set cache(data){
|
||||
if(typeof data !== "object" || typeof this._cache !== "object")return this._cache = data
|
||||
return this._cache = Object.assign(this._cache, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom badges from the user ID.
|
||||
* @param {string} user The user ID
|
||||
* @returns {Promise<Constants["badges"]>}
|
||||
*/
|
||||
async getBadges(user){
|
||||
if(this.cache.badges[user])return this.cache.badges[user]
|
||||
if(awaitingBadgesPromises[user])return awaitingBadgesPromises[user]
|
||||
|
||||
let resolve
|
||||
const newPromise = new Promise((res) => (resolve = res))
|
||||
awaitingBadgesPromises[user] = newPromise
|
||||
|
||||
const badges = []
|
||||
for(let badge of Constants.badges){
|
||||
if(badge.defaultUsers.includes(user))badges.push(badge)
|
||||
}
|
||||
const fetchedBadges = await new Promise((resolve) => {
|
||||
if(!settingsCookie["lightcord-5"])return resolve([])
|
||||
badgesToFetch.push([user, resolve])
|
||||
setTimeout(() => {
|
||||
let users = badgesToFetch
|
||||
if(users.length === 0)return
|
||||
badgesToFetch = []
|
||||
handleRequest(Routes.badges, "POST", JSON.stringify(users.map(e => e[0])))
|
||||
.then(async res => {
|
||||
if(res.status !== 200){// Couldn't fetch badges: server error
|
||||
users.forEach(data => {
|
||||
data[1]([])// resolve no badge fetched
|
||||
})
|
||||
}
|
||||
const responseBody = await res.json()
|
||||
|
||||
for(let user of responseBody){
|
||||
let promise = users.find(promise => promise[0] === user.user_id)
|
||||
promise[1](user.badges)
|
||||
}
|
||||
}).catch((err) => {// Couldn't fetch badges: error
|
||||
if(!(err instanceof LightcordError))console.error(err)
|
||||
users.forEach(data => {
|
||||
data[1]([])// resolve no badge fetched
|
||||
})
|
||||
})
|
||||
}, 0)
|
||||
})
|
||||
for(let badge of fetchedBadges){
|
||||
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.
|
||||
badges.push(Constants.badges.find(e => e.id === badge))
|
||||
}
|
||||
this.cache = {
|
||||
badges: Object.assign(this.cache.badges, {[user]: badges})
|
||||
}
|
||||
setTimeout(() => {
|
||||
delete this.cache.badges[user]
|
||||
}, 600000);
|
||||
|
||||
resolve(badges)
|
||||
delete awaitingBadgesPromises[user]
|
||||
return badges
|
||||
}
|
||||
}
|
||||
|
||||
const handleRequest = function(route, method, data){
|
||||
if(!settingsCookie["lightcord-5"]){
|
||||
return Promise.reject(new LightcordError("The current settings blocked the request."))
|
||||
}
|
||||
return nodeFetch(`${Constants.SERVER_URL}/api/v1${route}`, {
|
||||
method,
|
||||
headers: {
|
||||
"CLIENT": "Lightcord",
|
||||
"Authorization": window.Lightcord.Api.Authorization || "None::Anonymous"
|
||||
},
|
||||
...(data ? {
|
||||
body: data
|
||||
} : {})
|
||||
})
|
||||
}
|
||||
|
||||
class LightcordError extends Error {
|
||||
constructor(){
|
||||
super(...arguments)
|
||||
this.name = "LightcordError"
|
||||
}
|
||||
}
|
||||
|
||||
export const Constants = {
|
||||
SERVER_URL: "https://lightcord.deroku.xyz",
|
||||
badges: [ // TODO: Add more badges + server side svg
|
||||
{
|
||||
name: "Lightcord Bug Hunter",
|
||||
id: "f04698f5-816b-41e3-bd01-92291193d7a5",
|
||||
defaultUsers: [
|
||||
"696481194443014174",
|
||||
"696003456611385396"
|
||||
],
|
||||
scopes: [],
|
||||
component: BugHunterBadge,
|
||||
href: "https://github.com/lightcord/lightcord/wiki/badges#bug_hunter"
|
||||
}, {
|
||||
name: "Buffoon",
|
||||
id: "06904d31-65b4-41ec-a50c-8658bbd1af96",
|
||||
defaultUsers: [
|
||||
"389016895543705602",
|
||||
"664600134528663565",
|
||||
"625350657829896224"
|
||||
],
|
||||
scopes: [],
|
||||
component: Circus,
|
||||
href: "https://youtu.be/EJtb6z-dlT8?t=145"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const Routes = {
|
||||
badges: `/users/badges`
|
||||
import BugHunterBadge from "../svg/bug_hunter"
|
||||
import nodeFetch from "node-fetch"
|
||||
import { settingsCookie } from "../0globals";
|
||||
import Circus from "../svg/circus";
|
||||
|
||||
export function uuidv4() { // Generate UUID (No crypto rng)
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
const awaitingBadgesPromises = {}
|
||||
let badgesToFetch = []
|
||||
|
||||
export default new class DistantServer {
|
||||
constructor(){
|
||||
this._cache = {
|
||||
badges: {
|
||||
|
||||
}
|
||||
}
|
||||
window.Lightcord.BetterDiscord.DistantServer = this
|
||||
}
|
||||
|
||||
get cache(){
|
||||
return this._cache
|
||||
}
|
||||
|
||||
set cache(data){
|
||||
if(typeof data !== "object" || typeof this._cache !== "object")return this._cache = data
|
||||
return this._cache = Object.assign(this._cache, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom badges from the user ID.
|
||||
* @param {string} user The user ID
|
||||
* @returns {Promise<Constants["badges"]>}
|
||||
*/
|
||||
async getBadges(user){
|
||||
if(this.cache.badges[user])return this.cache.badges[user]
|
||||
if(awaitingBadgesPromises[user])return awaitingBadgesPromises[user]
|
||||
|
||||
let resolve
|
||||
const newPromise = new Promise((res) => (resolve = res))
|
||||
awaitingBadgesPromises[user] = newPromise
|
||||
|
||||
const badges = []
|
||||
for(let badge of Constants.badges){
|
||||
if(badge.defaultUsers.includes(user))badges.push(badge)
|
||||
}
|
||||
const fetchedBadges = await new Promise((resolve) => {
|
||||
if(!settingsCookie["lightcord-5"])return resolve([])
|
||||
badgesToFetch.push([user, resolve])
|
||||
setTimeout(() => {
|
||||
let users = badgesToFetch
|
||||
if(users.length === 0)return
|
||||
badgesToFetch = []
|
||||
handleRequest(Routes.badges, "POST", JSON.stringify(users.map(e => e[0])))
|
||||
.then(async res => {
|
||||
if(res.status !== 200){// Couldn't fetch badges: server error
|
||||
users.forEach(data => {
|
||||
data[1]([])// resolve no badge fetched
|
||||
})
|
||||
}
|
||||
const responseBody = await res.json()
|
||||
|
||||
for(let user of responseBody){
|
||||
let promise = users.find(promise => promise[0] === user.user_id)
|
||||
promise[1](user.badges)
|
||||
}
|
||||
}).catch((err) => {// Couldn't fetch badges: error
|
||||
if(!(err instanceof LightcordError))console.error(err)
|
||||
users.forEach(data => {
|
||||
data[1]([])// resolve no badge fetched
|
||||
})
|
||||
})
|
||||
}, 0)
|
||||
})
|
||||
for(let badge of fetchedBadges){
|
||||
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.
|
||||
badges.push(Constants.badges.find(e => e.id === badge))
|
||||
}
|
||||
this.cache = {
|
||||
badges: Object.assign(this.cache.badges, {[user]: badges})
|
||||
}
|
||||
setTimeout(() => {
|
||||
delete this.cache.badges[user]
|
||||
}, 600000);
|
||||
|
||||
resolve(badges)
|
||||
delete awaitingBadgesPromises[user]
|
||||
return badges
|
||||
}
|
||||
}
|
||||
|
||||
const handleRequest = function(route, method, data){
|
||||
if(!settingsCookie["lightcord-5"]){
|
||||
return Promise.reject(new LightcordError("The current settings blocked the request."))
|
||||
}
|
||||
return nodeFetch(`${Constants.SERVER_URL}/api/v1${route}`, {
|
||||
method,
|
||||
headers: {
|
||||
"CLIENT": "Lightcord",
|
||||
"Authorization": window.Lightcord.Api.Authorization || "None::Anonymous"
|
||||
},
|
||||
...(data ? {
|
||||
body: data
|
||||
} : {})
|
||||
})
|
||||
}
|
||||
|
||||
class LightcordError extends Error {
|
||||
constructor(){
|
||||
super(...arguments)
|
||||
this.name = "LightcordError"
|
||||
}
|
||||
}
|
||||
|
||||
export const Constants = {
|
||||
SERVER_URL: "https://lightcord.deroku.xyz",
|
||||
badges: [ // TODO: Add more badges + server side svg
|
||||
{
|
||||
name: "Lightcord Bug Hunter",
|
||||
id: "f04698f5-816b-41e3-bd01-92291193d7a5",
|
||||
defaultUsers: [
|
||||
"696481194443014174",
|
||||
"696003456611385396"
|
||||
],
|
||||
scopes: [],
|
||||
component: BugHunterBadge,
|
||||
href: "https://github.com/lightcord/lightcord/wiki/badges#bug_hunter"
|
||||
}, {
|
||||
name: "Buffoon",
|
||||
id: "06904d31-65b4-41ec-a50c-8658bbd1af96",
|
||||
defaultUsers: [
|
||||
"389016895543705602",
|
||||
"664600134528663565",
|
||||
"625350657829896224"
|
||||
],
|
||||
scopes: [],
|
||||
component: Circus,
|
||||
href: "https://youtu.be/EJtb6z-dlT8?t=145"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const Routes = {
|
||||
badges: `/users/badges`
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
import BDV2 from "./v2"
|
||||
const {useState} = BDV2.react
|
||||
|
||||
|
||||
export function useForceUpdate(){
|
||||
return useState()[1];
|
||||
import BDV2 from "./v2"
|
||||
const {useState} = BDV2.react
|
||||
|
||||
|
||||
export function useForceUpdate(){
|
||||
return useState()[1];
|
||||
}
|
|
@ -1,451 +1,451 @@
|
|||
import nodeFetch from "node-fetch"
|
||||
import * as electron from "electron"
|
||||
import * as crypto from "crypto"
|
||||
import BDV2 from "./v2"
|
||||
import tooltipWrap from "../ui/tooltipWrap"
|
||||
import Utils from "./utils"
|
||||
import { createReadStream, writeFileSync } from "fs"
|
||||
import { basename, join } from "path"
|
||||
import contentManager from "./contentManager"
|
||||
import { addonCache } from "./contentManager"
|
||||
|
||||
const cache = {}
|
||||
const cache2 = {}
|
||||
|
||||
export default new class PluginCertifier {
|
||||
constructor(){
|
||||
window.Lightcord.BetterDiscord.PluginCertifier = this
|
||||
}
|
||||
|
||||
patch(attachment, id){
|
||||
process.nextTick(() => {
|
||||
processAttachment(attachment, id)
|
||||
})
|
||||
}
|
||||
|
||||
start(){
|
||||
|
||||
}
|
||||
|
||||
isTrusted(hash){
|
||||
return cache[hash] && !cache[hash].suspect
|
||||
}
|
||||
}
|
||||
|
||||
const tests = [
|
||||
[/token/gi, 0],
|
||||
[/email/gi, 0],
|
||||
[/mfa/gi, 0],
|
||||
[/2fa/gi, 0],
|
||||
[/phone/gi, 0],
|
||||
[/child_process/gi, 0],
|
||||
[/localStorage/gi, 0],
|
||||
[/getGlobal/gi, 0],
|
||||
[/BrowserWindow/gi, 0],
|
||||
[/\.exe/gi, 0],
|
||||
[/eval/gi, 0],
|
||||
[/WebAssembly/gi, 0],
|
||||
[/XMLHttpRequest(\.|\[["'`])prototype/gi, 0],
|
||||
[/window\.fetch( +)?=/gi, 0],
|
||||
/** Obfuscation / hidden / workarounds */
|
||||
[/(["'`]\+)["'`]\w["'`]/gi, 1],
|
||||
[/["'`]\w["'`](\+["'`])/gi, 1],
|
||||
[/\${["'`]\w+["'`]}/gi, 1],
|
||||
/** hexadecimal */
|
||||
[/_0x\w{4}\('0x[\dabcdef]+'\)/g, 1],
|
||||
[/_0x\w{4}\('0x[\dabcdef]+'( +)?,( +)?'[^']{4}'\)/g, 1], // _0x8db7('0x0', 'x1]f')
|
||||
/** mangled */
|
||||
[/\w+\('0x[\dabcdef]+'\)/g, 1], // b('0x0')
|
||||
[/\w+\('0x[\dabcdef]+'( +)?,( +)?'[^']{4}'\)/g, 1], // b('0x0', 'x1]f')
|
||||
/** string array at start */
|
||||
[/^var [\w\d_$]+=\["/gi, 1]
|
||||
]
|
||||
|
||||
const threats = [
|
||||
"Account Stealer/Virus",
|
||||
"Obfuscation/Hidden code"
|
||||
]
|
||||
|
||||
export function checkViruses(hash, data, resultCallback, removeCallback, filename){
|
||||
data = data.toString("utf8")
|
||||
let isHarmful = false
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
const no_comments = data.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "").trim()// removing all comments from plugins (remove meta and other.)
|
||||
|
||||
for(const [test, type] of tests){
|
||||
const scrpt = type === 1 ? no_comments : data
|
||||
if(test.exec(scrpt)){
|
||||
isHarmful = threats[type]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if(!isHarmful)return removeCallback(hash)
|
||||
cache[hash] = {
|
||||
suspect: true,
|
||||
name: hashToUrl[hash].split("/").pop(),
|
||||
type: hashToUrl[hash].endsWith(".js") ? "Plugin" : "Theme",
|
||||
harm: isHarmful,
|
||||
hash: hash,
|
||||
filename
|
||||
}
|
||||
|
||||
console.log(`Found potentially dangerous ${cache[hash].type.toLowerCase()}: ${cache[hash].name}`)
|
||||
|
||||
resultCallback(cache[hash])
|
||||
}
|
||||
|
||||
const hashToUrl = {}
|
||||
|
||||
export function checkHash(hash, data, filename, resultCallback, removeCallback){
|
||||
console.log(`File: ${filename} hash: ${hash}`)
|
||||
if(!cache[hash]){
|
||||
nodeFetch("https://cdn.jsdelivr.net/gh/Lightcord/filehashes@master/hashes/"+hash, { // Using node-fetch to bypass cors
|
||||
headers: {
|
||||
"User-Agent": electron.remote.getCurrentWebContents().userAgent // have to set user-agent
|
||||
}
|
||||
}).then(async res => {
|
||||
if(res.status !== 200){
|
||||
if(filename.endsWith(".theme.css"))return removeCallback(hash)
|
||||
try{
|
||||
checkViruses(hash, data, resultCallback, removeCallback, filename)
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
removeCallback()
|
||||
}
|
||||
return
|
||||
}
|
||||
const result = await res.json()
|
||||
result.hash = hash
|
||||
result.filename = filename
|
||||
|
||||
cache[hash] = result
|
||||
|
||||
resultCallback(result)
|
||||
}).catch((err) => {
|
||||
console.error(`Could not read from github. ${err}`)
|
||||
if(filename.endsWith(".theme.css"))return removeCallback(hash)
|
||||
checkViruses(hash, data, resultCallback, removeCallback, filename)
|
||||
})
|
||||
}else{
|
||||
const result = cache[hash]
|
||||
|
||||
resultCallback(result)
|
||||
}
|
||||
}
|
||||
|
||||
export function processFile(__path, resultCallback, removeCallback = (hash) => {}, isFromLoader = false){
|
||||
const hash = crypto.createHash("sha256")
|
||||
let data = Buffer.alloc(0)
|
||||
|
||||
createReadStream(__path).on("data", chunk => {
|
||||
data = Buffer.concat([data, chunk])
|
||||
hash.update(chunk)
|
||||
}).on("end", () => {
|
||||
const hashResult = hash.digest("hex")
|
||||
|
||||
hashToUrl[hashResult] = __path
|
||||
|
||||
if(isFromLoader && addonCache[hashResult]){
|
||||
let value = addonCache[hashResult]
|
||||
if(value.timestamp < (Date.now() - 6.048e+8)){
|
||||
delete addonCache[hashResult]
|
||||
contentManager.saveAddonCache()
|
||||
}else{
|
||||
resultCallback(value.result)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
checkHash(hashResult, data, basename(__path), resultCallback, removeCallback)
|
||||
})
|
||||
}
|
||||
|
||||
export function processAttachment(attachment, id){
|
||||
if(!document.getElementById(id))return
|
||||
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()
|
||||
|
||||
nodeFetch(attachment.url, {
|
||||
headers: {
|
||||
"User-Agent": electron.remote.getCurrentWebContents().userAgent
|
||||
}
|
||||
}).then(res => {
|
||||
if(res.status !== 200)throw new Error("File doesn't exist.")
|
||||
const hash = crypto.createHash("sha256")
|
||||
let data = Buffer.alloc(0)
|
||||
res.body.on("data", chunk => {
|
||||
data = Buffer.concat([data, chunk])
|
||||
hash.update(chunk)
|
||||
})
|
||||
res.body.on("end", () => {
|
||||
const hashResult = hash.digest("hex")
|
||||
|
||||
cache2[attachment.url] = hashResult
|
||||
hashToUrl[hashResult] = attachment.url
|
||||
|
||||
checkHash(hashResult, data, attachment.filename, (result) => {
|
||||
renderToElements(id, result, attachment.filename)
|
||||
}, () => {
|
||||
let elem = document.getElementById(id)
|
||||
if(elem)elem.remove()
|
||||
})
|
||||
})
|
||||
}).catch(()=>{})
|
||||
}
|
||||
|
||||
let flowerStarModule = BDModules.get(e => e.flowerStarContainer)[0]
|
||||
let childModule = BDModules.get(e => e.childContainer)[0]
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLDivElement[]} elements
|
||||
* @param {{type: "Theme"|"Plugin", name: string, official?: boolean}|{suspect:true, type: "Theme"|"Plugin", name: string, harm: string}} result
|
||||
*/
|
||||
function renderToElements(id, result, filename){
|
||||
const div = document.getElementById(id)
|
||||
if(!div || div.childNodes.length > 0)return // already certified/div does not exist anymore.
|
||||
|
||||
if(!flowerStarModule)flowerStarModule = BDModules.get(e => e.flowerStarContainer)[0]
|
||||
if(!childModule)childModule = BDModules.get(e => e.childContainer)[0]
|
||||
|
||||
if(result.suspect){
|
||||
try{
|
||||
div.parentNode.style.borderColor = "rgb(240, 71, 71)"
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} node
|
||||
*/
|
||||
let nextNode = (node) => {
|
||||
for(let child of node.children){
|
||||
if(child.tagName === "A"){
|
||||
child.addEventListener("click", (e) => {
|
||||
e.preventDefault()
|
||||
e.stopImmediatePropagation()
|
||||
|
||||
Utils.showConfirmationModal(
|
||||
"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.",
|
||||
{
|
||||
confirmText: "Download Anyway",
|
||||
cancelText: "Don't !",
|
||||
danger: true,
|
||||
onCancel: () => {},
|
||||
onConfirm: () => {
|
||||
electron.remote.shell.openExternal(child.href)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}else if(["div"].includes(child.tagName.toLowerCase())){
|
||||
nextNode(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
nextNode(div.parentNode)
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
}
|
||||
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("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", {
|
||||
x:"0",
|
||||
y:"0",
|
||||
width:"32",
|
||||
height:"32",
|
||||
mask:"url(#svg-mask-avatar-status-round-32)",
|
||||
fill:"#f04747",
|
||||
mask:"url(#svg-mask-status-dnd)",
|
||||
className:BDModules.get(e => e.pointerEvents)[0].pointerEvents
|
||||
})
|
||||
)
|
||||
)
|
||||
), div)
|
||||
}else if(!result.official){
|
||||
div.parentNode.style.borderColor = "#4087ed"
|
||||
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("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("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("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(tooltipWrap, {text: "Install this "+result.type.toLowerCase()+" on Lightcord."},
|
||||
BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}, onClick(){
|
||||
Utils.showConfirmationModal(
|
||||
"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.",
|
||||
{
|
||||
confirmText: "Download and Install",
|
||||
cancelText: "I've changed my mind",
|
||||
danger: false,
|
||||
onCancel: () => {},
|
||||
onConfirm: () => {
|
||||
let link = getKeyedArray(cache2).find(e => e[1] === result.hash)[0]
|
||||
console.log(link)
|
||||
nodeFetch(link)
|
||||
.then(async res => {
|
||||
if(res.status !== 200)throw new Error("Status was not 200")
|
||||
let content = await res.buffer()
|
||||
let installPath = join(result.type === "Plugin" ? contentManager._pluginsFolder : contentManager._themesFolder, result.filename)
|
||||
console.log(installPath)
|
||||
writeFileSync(installPath, content)
|
||||
Utils.showToast(result.type+" succesfully installed.")
|
||||
}).catch(err => {
|
||||
err = err instanceof Error ? err : new Error(err)
|
||||
Utils.showToast(err.message, {
|
||||
type: "error"
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}},
|
||||
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)",
|
||||
cursor: "pointer"
|
||||
}},
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path d="M0 0h24v24H0z"></path>
|
||||
<path class="fill" fill="currentColor" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path>
|
||||
</g>
|
||||
)
|
||||
)
|
||||
)
|
||||
])
|
||||
BDV2.reactDom.render(span, div)
|
||||
}else{
|
||||
div.parentNode.style.borderColor = "#4087ed"
|
||||
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("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("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("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(tooltipWrap, {text: "Install this "+result.type.toLowerCase()+" on Lightcord."},
|
||||
BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}, onClick(){
|
||||
Utils.showConfirmationModal(
|
||||
"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.",
|
||||
{
|
||||
confirmText: "Download and Install",
|
||||
cancelText: "I've changed my mind",
|
||||
danger: false,
|
||||
onCancel: () => {},
|
||||
onConfirm: () => {
|
||||
let link = getKeyedArray(cache2).find(e => e[1] === result.hash)[0]
|
||||
|
||||
nodeFetch(link)
|
||||
.then(async res => {
|
||||
if(res.status !== 200)throw new Error("Status was not 200")
|
||||
let content = await res.buffer()
|
||||
let installPath = join(result.type === "Plugin" ? contentManager._pluginsFolder : contentManager._themesFolder, result.filename)
|
||||
|
||||
writeFileSync(installPath, content)
|
||||
Utils.showToast(result.type+" succesfully installed.")
|
||||
}).catch(err => {
|
||||
err = err instanceof Error ? err : new Error(err)
|
||||
Utils.showToast(err.message, {
|
||||
type: "error"
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}},
|
||||
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)",
|
||||
cursor: "pointer"
|
||||
}},
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path d="M0 0h24v24H0z"></path>
|
||||
<path class="fill" fill="currentColor" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path>
|
||||
</g>
|
||||
)
|
||||
)
|
||||
)
|
||||
])
|
||||
BDV2.reactDom.render(span, div)
|
||||
}
|
||||
}
|
||||
|
||||
function getKeyedArray(obj){
|
||||
let arr = []
|
||||
Object.keys(obj).forEach(k => {
|
||||
arr.push([k, obj[k]])
|
||||
})
|
||||
return arr
|
||||
}
|
||||
|
||||
let key = null
|
||||
let save = null
|
||||
|
||||
window.Lightcord.Api.ensureExported(m=>m.ObjectStorage)
|
||||
.then(localStorageModule => {
|
||||
let localStorage = localStorageModule.impl
|
||||
save = function(){
|
||||
localStorage.set("PluginCertifierKeyEncryption__", btoa(JSON.stringify(key)))
|
||||
}
|
||||
setInterval(() => {
|
||||
save()
|
||||
}, 100000);
|
||||
try{
|
||||
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){
|
||||
generateKey()
|
||||
save()
|
||||
return
|
||||
}
|
||||
key = val
|
||||
}catch(e){
|
||||
generateKey()
|
||||
save()
|
||||
}
|
||||
})
|
||||
|
||||
function generateKey(){
|
||||
key = [crypto.randomBytes(16).toString("base64"), crypto.randomBytes(32).toString("base64")]
|
||||
}
|
||||
|
||||
function safeJSONParse(json){
|
||||
try{
|
||||
return JSON.parse(json)
|
||||
}catch(e){
|
||||
return e instanceof Error ? new Error(e) : e
|
||||
}
|
||||
}
|
||||
|
||||
export function decryptSettingsCache(data){
|
||||
try{
|
||||
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"));
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
return decrypted.toString("utf8")
|
||||
}catch(e){
|
||||
return "{}"
|
||||
}
|
||||
}
|
||||
export function encryptSettingsCache(data){
|
||||
let args = [Buffer.from(key[1], "base64"), Buffer.from(key[0], "base64")]
|
||||
|
||||
let cipher = crypto.createCipheriv('aes-256-cbc', ...args);
|
||||
let encrypted = cipher.update(Buffer.from(data, "utf8"));
|
||||
encrypted = Buffer.concat([encrypted, cipher.final()]);
|
||||
return encrypted.toString("base64")
|
||||
import nodeFetch from "node-fetch"
|
||||
import * as electron from "electron"
|
||||
import * as crypto from "crypto"
|
||||
import BDV2 from "./v2"
|
||||
import tooltipWrap from "../ui/tooltipWrap"
|
||||
import Utils from "./utils"
|
||||
import { createReadStream, writeFileSync } from "fs"
|
||||
import { basename, join } from "path"
|
||||
import contentManager from "./contentManager"
|
||||
import { addonCache } from "./contentManager"
|
||||
|
||||
const cache = {}
|
||||
const cache2 = {}
|
||||
|
||||
export default new class PluginCertifier {
|
||||
constructor(){
|
||||
window.Lightcord.BetterDiscord.PluginCertifier = this
|
||||
}
|
||||
|
||||
patch(attachment, id){
|
||||
process.nextTick(() => {
|
||||
processAttachment(attachment, id)
|
||||
})
|
||||
}
|
||||
|
||||
start(){
|
||||
|
||||
}
|
||||
|
||||
isTrusted(hash){
|
||||
return cache[hash] && !cache[hash].suspect
|
||||
}
|
||||
}
|
||||
|
||||
const tests = [
|
||||
[/token/gi, 0],
|
||||
[/email/gi, 0],
|
||||
[/mfa/gi, 0],
|
||||
[/2fa/gi, 0],
|
||||
[/phone/gi, 0],
|
||||
[/child_process/gi, 0],
|
||||
[/localStorage/gi, 0],
|
||||
[/getGlobal/gi, 0],
|
||||
[/BrowserWindow/gi, 0],
|
||||
[/\.exe/gi, 0],
|
||||
[/eval/gi, 0],
|
||||
[/WebAssembly/gi, 0],
|
||||
[/XMLHttpRequest(\.|\[["'`])prototype/gi, 0],
|
||||
[/window\.fetch( +)?=/gi, 0],
|
||||
/** Obfuscation / hidden / workarounds */
|
||||
[/(["'`]\+)["'`]\w["'`]/gi, 1],
|
||||
[/["'`]\w["'`](\+["'`])/gi, 1],
|
||||
[/\${["'`]\w+["'`]}/gi, 1],
|
||||
/** hexadecimal */
|
||||
[/_0x\w{4}\('0x[\dabcdef]+'\)/g, 1],
|
||||
[/_0x\w{4}\('0x[\dabcdef]+'( +)?,( +)?'[^']{4}'\)/g, 1], // _0x8db7('0x0', 'x1]f')
|
||||
/** mangled */
|
||||
[/\w+\('0x[\dabcdef]+'\)/g, 1], // b('0x0')
|
||||
[/\w+\('0x[\dabcdef]+'( +)?,( +)?'[^']{4}'\)/g, 1], // b('0x0', 'x1]f')
|
||||
/** string array at start */
|
||||
[/^var [\w\d_$]+=\["/gi, 1]
|
||||
]
|
||||
|
||||
const threats = [
|
||||
"Account Stealer/Virus",
|
||||
"Obfuscation/Hidden code"
|
||||
]
|
||||
|
||||
export function checkViruses(hash, data, resultCallback, removeCallback, filename){
|
||||
data = data.toString("utf8")
|
||||
let isHarmful = false
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
const no_comments = data.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "").trim()// removing all comments from plugins (remove meta and other.)
|
||||
|
||||
for(const [test, type] of tests){
|
||||
const scrpt = type === 1 ? no_comments : data
|
||||
if(test.exec(scrpt)){
|
||||
isHarmful = threats[type]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if(!isHarmful)return removeCallback(hash)
|
||||
cache[hash] = {
|
||||
suspect: true,
|
||||
name: hashToUrl[hash].split("/").pop(),
|
||||
type: hashToUrl[hash].endsWith(".js") ? "Plugin" : "Theme",
|
||||
harm: isHarmful,
|
||||
hash: hash,
|
||||
filename
|
||||
}
|
||||
|
||||
console.log(`Found potentially dangerous ${cache[hash].type.toLowerCase()}: ${cache[hash].name}`)
|
||||
|
||||
resultCallback(cache[hash])
|
||||
}
|
||||
|
||||
const hashToUrl = {}
|
||||
|
||||
export function checkHash(hash, data, filename, resultCallback, removeCallback){
|
||||
console.log(`File: ${filename} hash: ${hash}`)
|
||||
if(!cache[hash]){
|
||||
nodeFetch("https://cdn.jsdelivr.net/gh/Lightcord/filehashes@master/hashes/"+hash, { // Using node-fetch to bypass cors
|
||||
headers: {
|
||||
"User-Agent": electron.remote.getCurrentWebContents().userAgent // have to set user-agent
|
||||
}
|
||||
}).then(async res => {
|
||||
if(res.status !== 200){
|
||||
if(filename.endsWith(".theme.css"))return removeCallback(hash)
|
||||
try{
|
||||
checkViruses(hash, data, resultCallback, removeCallback, filename)
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
removeCallback()
|
||||
}
|
||||
return
|
||||
}
|
||||
const result = await res.json()
|
||||
result.hash = hash
|
||||
result.filename = filename
|
||||
|
||||
cache[hash] = result
|
||||
|
||||
resultCallback(result)
|
||||
}).catch((err) => {
|
||||
console.error(`Could not read from github. ${err}`)
|
||||
if(filename.endsWith(".theme.css"))return removeCallback(hash)
|
||||
checkViruses(hash, data, resultCallback, removeCallback, filename)
|
||||
})
|
||||
}else{
|
||||
const result = cache[hash]
|
||||
|
||||
resultCallback(result)
|
||||
}
|
||||
}
|
||||
|
||||
export function processFile(__path, resultCallback, removeCallback = (hash) => {}, isFromLoader = false){
|
||||
const hash = crypto.createHash("sha256")
|
||||
let data = Buffer.alloc(0)
|
||||
|
||||
createReadStream(__path).on("data", chunk => {
|
||||
data = Buffer.concat([data, chunk])
|
||||
hash.update(chunk)
|
||||
}).on("end", () => {
|
||||
const hashResult = hash.digest("hex")
|
||||
|
||||
hashToUrl[hashResult] = __path
|
||||
|
||||
if(isFromLoader && addonCache[hashResult]){
|
||||
let value = addonCache[hashResult]
|
||||
if(value.timestamp < (Date.now() - 6.048e+8)){
|
||||
delete addonCache[hashResult]
|
||||
contentManager.saveAddonCache()
|
||||
}else{
|
||||
resultCallback(value.result)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
checkHash(hashResult, data, basename(__path), resultCallback, removeCallback)
|
||||
})
|
||||
}
|
||||
|
||||
export function processAttachment(attachment, id){
|
||||
if(!document.getElementById(id))return
|
||||
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()
|
||||
|
||||
nodeFetch(attachment.url, {
|
||||
headers: {
|
||||
"User-Agent": electron.remote.getCurrentWebContents().userAgent
|
||||
}
|
||||
}).then(res => {
|
||||
if(res.status !== 200)throw new Error("File doesn't exist.")
|
||||
const hash = crypto.createHash("sha256")
|
||||
let data = Buffer.alloc(0)
|
||||
res.body.on("data", chunk => {
|
||||
data = Buffer.concat([data, chunk])
|
||||
hash.update(chunk)
|
||||
})
|
||||
res.body.on("end", () => {
|
||||
const hashResult = hash.digest("hex")
|
||||
|
||||
cache2[attachment.url] = hashResult
|
||||
hashToUrl[hashResult] = attachment.url
|
||||
|
||||
checkHash(hashResult, data, attachment.filename, (result) => {
|
||||
renderToElements(id, result, attachment.filename)
|
||||
}, () => {
|
||||
let elem = document.getElementById(id)
|
||||
if(elem)elem.remove()
|
||||
})
|
||||
})
|
||||
}).catch(()=>{})
|
||||
}
|
||||
|
||||
let flowerStarModule = BDModules.get(e => e.flowerStarContainer)[0]
|
||||
let childModule = BDModules.get(e => e.childContainer)[0]
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLDivElement[]} elements
|
||||
* @param {{type: "Theme"|"Plugin", name: string, official?: boolean}|{suspect:true, type: "Theme"|"Plugin", name: string, harm: string}} result
|
||||
*/
|
||||
function renderToElements(id, result, filename){
|
||||
const div = document.getElementById(id)
|
||||
if(!div || div.childNodes.length > 0)return // already certified/div does not exist anymore.
|
||||
|
||||
if(!flowerStarModule)flowerStarModule = BDModules.get(e => e.flowerStarContainer)[0]
|
||||
if(!childModule)childModule = BDModules.get(e => e.childContainer)[0]
|
||||
|
||||
if(result.suspect){
|
||||
try{
|
||||
div.parentNode.style.borderColor = "rgb(240, 71, 71)"
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} node
|
||||
*/
|
||||
let nextNode = (node) => {
|
||||
for(let child of node.children){
|
||||
if(child.tagName === "A"){
|
||||
child.addEventListener("click", (e) => {
|
||||
e.preventDefault()
|
||||
e.stopImmediatePropagation()
|
||||
|
||||
Utils.showConfirmationModal(
|
||||
"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.",
|
||||
{
|
||||
confirmText: "Download Anyway",
|
||||
cancelText: "Don't !",
|
||||
danger: true,
|
||||
onCancel: () => {},
|
||||
onConfirm: () => {
|
||||
electron.remote.shell.openExternal(child.href)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}else if(["div"].includes(child.tagName.toLowerCase())){
|
||||
nextNode(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
nextNode(div.parentNode)
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
}
|
||||
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("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", {
|
||||
x:"0",
|
||||
y:"0",
|
||||
width:"32",
|
||||
height:"32",
|
||||
mask:"url(#svg-mask-avatar-status-round-32)",
|
||||
fill:"#f04747",
|
||||
mask:"url(#svg-mask-status-dnd)",
|
||||
className:BDModules.get(e => e.pointerEvents)[0].pointerEvents
|
||||
})
|
||||
)
|
||||
)
|
||||
), div)
|
||||
}else if(!result.official){
|
||||
div.parentNode.style.borderColor = "#4087ed"
|
||||
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("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("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("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(tooltipWrap, {text: "Install this "+result.type.toLowerCase()+" on Lightcord."},
|
||||
BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}, onClick(){
|
||||
Utils.showConfirmationModal(
|
||||
"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.",
|
||||
{
|
||||
confirmText: "Download and Install",
|
||||
cancelText: "I've changed my mind",
|
||||
danger: false,
|
||||
onCancel: () => {},
|
||||
onConfirm: () => {
|
||||
let link = getKeyedArray(cache2).find(e => e[1] === result.hash)[0]
|
||||
console.log(link)
|
||||
nodeFetch(link)
|
||||
.then(async res => {
|
||||
if(res.status !== 200)throw new Error("Status was not 200")
|
||||
let content = await res.buffer()
|
||||
let installPath = join(result.type === "Plugin" ? contentManager._pluginsFolder : contentManager._themesFolder, result.filename)
|
||||
console.log(installPath)
|
||||
writeFileSync(installPath, content)
|
||||
Utils.showToast(result.type+" succesfully installed.")
|
||||
}).catch(err => {
|
||||
err = err instanceof Error ? err : new Error(err)
|
||||
Utils.showToast(err.message, {
|
||||
type: "error"
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}},
|
||||
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)",
|
||||
cursor: "pointer"
|
||||
}},
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path d="M0 0h24v24H0z"></path>
|
||||
<path class="fill" fill="currentColor" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path>
|
||||
</g>
|
||||
)
|
||||
)
|
||||
)
|
||||
])
|
||||
BDV2.reactDom.render(span, div)
|
||||
}else{
|
||||
div.parentNode.style.borderColor = "#4087ed"
|
||||
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("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("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("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(tooltipWrap, {text: "Install this "+result.type.toLowerCase()+" on Lightcord."},
|
||||
BDV2.react.createElement("div", {className: flowerStarModule.flowerStarContainer, style: {width: "20px", height: "20px"}, onClick(){
|
||||
Utils.showConfirmationModal(
|
||||
"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.",
|
||||
{
|
||||
confirmText: "Download and Install",
|
||||
cancelText: "I've changed my mind",
|
||||
danger: false,
|
||||
onCancel: () => {},
|
||||
onConfirm: () => {
|
||||
let link = getKeyedArray(cache2).find(e => e[1] === result.hash)[0]
|
||||
|
||||
nodeFetch(link)
|
||||
.then(async res => {
|
||||
if(res.status !== 200)throw new Error("Status was not 200")
|
||||
let content = await res.buffer()
|
||||
let installPath = join(result.type === "Plugin" ? contentManager._pluginsFolder : contentManager._themesFolder, result.filename)
|
||||
|
||||
writeFileSync(installPath, content)
|
||||
Utils.showToast(result.type+" succesfully installed.")
|
||||
}).catch(err => {
|
||||
err = err instanceof Error ? err : new Error(err)
|
||||
Utils.showToast(err.message, {
|
||||
type: "error"
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}},
|
||||
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)",
|
||||
cursor: "pointer"
|
||||
}},
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path d="M0 0h24v24H0z"></path>
|
||||
<path class="fill" fill="currentColor" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path>
|
||||
</g>
|
||||
)
|
||||
)
|
||||
)
|
||||
])
|
||||
BDV2.reactDom.render(span, div)
|
||||
}
|
||||
}
|
||||
|
||||
function getKeyedArray(obj){
|
||||
let arr = []
|
||||
Object.keys(obj).forEach(k => {
|
||||
arr.push([k, obj[k]])
|
||||
})
|
||||
return arr
|
||||
}
|
||||
|
||||
let key = null
|
||||
let save = null
|
||||
|
||||
window.Lightcord.Api.ensureExported(m=>m.ObjectStorage)
|
||||
.then(localStorageModule => {
|
||||
let localStorage = localStorageModule.impl
|
||||
save = function(){
|
||||
localStorage.set("PluginCertifierKeyEncryption__", btoa(JSON.stringify(key)))
|
||||
}
|
||||
setInterval(() => {
|
||||
save()
|
||||
}, 100000);
|
||||
try{
|
||||
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){
|
||||
generateKey()
|
||||
save()
|
||||
return
|
||||
}
|
||||
key = val
|
||||
}catch(e){
|
||||
generateKey()
|
||||
save()
|
||||
}
|
||||
})
|
||||
|
||||
function generateKey(){
|
||||
key = [crypto.randomBytes(16).toString("base64"), crypto.randomBytes(32).toString("base64")]
|
||||
}
|
||||
|
||||
function safeJSONParse(json){
|
||||
try{
|
||||
return JSON.parse(json)
|
||||
}catch(e){
|
||||
return e instanceof Error ? new Error(e) : e
|
||||
}
|
||||
}
|
||||
|
||||
export function decryptSettingsCache(data){
|
||||
try{
|
||||
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"));
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
return decrypted.toString("utf8")
|
||||
}catch(e){
|
||||
return "{}"
|
||||
}
|
||||
}
|
||||
export function encryptSettingsCache(data){
|
||||
let args = [Buffer.from(key[1], "base64"), Buffer.from(key[0], "base64")]
|
||||
|
||||
let cipher = crypto.createCipheriv('aes-256-cbc', ...args);
|
||||
let encrypted = cipher.update(Buffer.from(data, "utf8"));
|
||||
encrypted = Buffer.concat([encrypted, cipher.final()]);
|
||||
return encrypted.toString("base64")
|
||||
}
|
|
@ -1,130 +1,130 @@
|
|||
import { themeCookie } from "../0globals"
|
||||
import bdEvents from "./bdEvents"
|
||||
import DOM from "./domtools"
|
||||
|
||||
export default new class popoutWindow {
|
||||
constructor(){
|
||||
/**
|
||||
* @type {Map<string, Window>}
|
||||
*/
|
||||
this.windows = new Map()
|
||||
this.enabled = false
|
||||
this.init()
|
||||
}
|
||||
|
||||
async init(){
|
||||
let popoutModule = await window.Lightcord.Api.ensureExported(e => e.default && e.default.getWindow)
|
||||
window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_OPEN", (ev) => {
|
||||
setImmediate(() => {
|
||||
/**
|
||||
* @type {Window}
|
||||
*/
|
||||
const window = popoutModule.default.getWindow(ev.key)
|
||||
this.windows.set(ev.key, window)
|
||||
|
||||
let classList = window.document.body.classList
|
||||
classList.add("window-popout")
|
||||
classList.add("lightcord")
|
||||
classList.add("lightcord")
|
||||
|
||||
this.update(ev.key)
|
||||
})
|
||||
})
|
||||
window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_CLOSE", (ev) => {
|
||||
setImmediate(() => {
|
||||
this.windows.delete(ev.key)
|
||||
})
|
||||
})
|
||||
bdEvents.on("theme-enabled", () => {
|
||||
this.update()
|
||||
})
|
||||
bdEvents.on("theme-disabled", () => {
|
||||
this.update()
|
||||
})
|
||||
bdEvents.on("theme-reloaded", () => {
|
||||
this.update()
|
||||
})
|
||||
bdEvents.on("theme-unloaded", () => {
|
||||
this.update()
|
||||
})
|
||||
bdEvents.on("theme-loaded", () => {
|
||||
this.update()
|
||||
})
|
||||
}
|
||||
|
||||
enable(){
|
||||
this.enabled = true
|
||||
this.update()
|
||||
}
|
||||
|
||||
disable(){
|
||||
this.enabled = false
|
||||
this.update()
|
||||
}
|
||||
|
||||
update(key){
|
||||
if(!this.windows.size)return
|
||||
if(!this.enabled){
|
||||
return this.removeThemes(key)
|
||||
}else{
|
||||
return this.applyThemes(key)
|
||||
}
|
||||
}
|
||||
|
||||
removeThemes(key){
|
||||
if(this.enabled)return
|
||||
if(key){
|
||||
let window = this.windows.get(key)
|
||||
if(!window)return
|
||||
let document = window.document
|
||||
|
||||
for(let style of document.querySelectorAll("style[data-lightcord-theme=true]")){
|
||||
style.remove()
|
||||
}
|
||||
}else{
|
||||
for(let key of this.windows.keys()){
|
||||
this.removeThemes(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applyThemes(key){
|
||||
if(!this.enabled)return
|
||||
if(key){
|
||||
let window = this.windows.get(key)
|
||||
if(!window)return
|
||||
let document = window.document
|
||||
|
||||
for(let style of document.querySelectorAll("style[data-lightcord-theme=true]")){
|
||||
style.setAttribute("will-remove", "true")
|
||||
}
|
||||
|
||||
Object.keys(bdthemes)
|
||||
.forEach(themeName => {
|
||||
if(!themeCookie[themeName])return // theme disabled
|
||||
const theme = bdthemes[themeName]
|
||||
if(!theme)return //:shrug:
|
||||
|
||||
let existing = document.querySelector("style[data-lightcord-theme=true]#"+DOM.escapeID(theme.id))
|
||||
if(existing){
|
||||
existing.innerHTML = unescape(theme.css)
|
||||
existing.removeAttribute("will-remove")
|
||||
}else{
|
||||
const style = document.createElement("style")
|
||||
style.id = DOM.escapeID(theme.id)
|
||||
style.innerHTML = unescape(theme.css)
|
||||
style.setAttribute("data-lightcord-theme", "true")
|
||||
document.head.append(style)
|
||||
}
|
||||
})
|
||||
|
||||
for(let style of document.querySelectorAll("style[will-remove=true]")){
|
||||
style.remove()
|
||||
}
|
||||
}else{
|
||||
for(let key of this.windows.keys()){
|
||||
this.applyThemes(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
import { themeCookie } from "../0globals"
|
||||
import bdEvents from "./bdEvents"
|
||||
import DOM from "./domtools"
|
||||
|
||||
export default new class popoutWindow {
|
||||
constructor(){
|
||||
/**
|
||||
* @type {Map<string, Window>}
|
||||
*/
|
||||
this.windows = new Map()
|
||||
this.enabled = false
|
||||
this.init()
|
||||
}
|
||||
|
||||
async init(){
|
||||
let popoutModule = await window.Lightcord.Api.ensureExported(e => e.default && e.default.getWindow)
|
||||
window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_OPEN", (ev) => {
|
||||
setImmediate(() => {
|
||||
/**
|
||||
* @type {Window}
|
||||
*/
|
||||
const window = popoutModule.default.getWindow(ev.key)
|
||||
this.windows.set(ev.key, window)
|
||||
|
||||
let classList = window.document.body.classList
|
||||
classList.add("window-popout")
|
||||
classList.add("lightcord")
|
||||
classList.add("lightcord")
|
||||
|
||||
this.update(ev.key)
|
||||
})
|
||||
})
|
||||
window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_CLOSE", (ev) => {
|
||||
setImmediate(() => {
|
||||
this.windows.delete(ev.key)
|
||||
})
|
||||
})
|
||||
bdEvents.on("theme-enabled", () => {
|
||||
this.update()
|
||||
})
|
||||
bdEvents.on("theme-disabled", () => {
|
||||
this.update()
|
||||
})
|
||||
bdEvents.on("theme-reloaded", () => {
|
||||
this.update()
|
||||
})
|
||||
bdEvents.on("theme-unloaded", () => {
|
||||
this.update()
|
||||
})
|
||||
bdEvents.on("theme-loaded", () => {
|
||||
this.update()
|
||||
})
|
||||
}
|
||||
|
||||
enable(){
|
||||
this.enabled = true
|
||||
this.update()
|
||||
}
|
||||
|
||||
disable(){
|
||||
this.enabled = false
|
||||
this.update()
|
||||
}
|
||||
|
||||
update(key){
|
||||
if(!this.windows.size)return
|
||||
if(!this.enabled){
|
||||
return this.removeThemes(key)
|
||||
}else{
|
||||
return this.applyThemes(key)
|
||||
}
|
||||
}
|
||||
|
||||
removeThemes(key){
|
||||
if(this.enabled)return
|
||||
if(key){
|
||||
let window = this.windows.get(key)
|
||||
if(!window)return
|
||||
let document = window.document
|
||||
|
||||
for(let style of document.querySelectorAll("style[data-lightcord-theme=true]")){
|
||||
style.remove()
|
||||
}
|
||||
}else{
|
||||
for(let key of this.windows.keys()){
|
||||
this.removeThemes(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applyThemes(key){
|
||||
if(!this.enabled)return
|
||||
if(key){
|
||||
let window = this.windows.get(key)
|
||||
if(!window)return
|
||||
let document = window.document
|
||||
|
||||
for(let style of document.querySelectorAll("style[data-lightcord-theme=true]")){
|
||||
style.setAttribute("will-remove", "true")
|
||||
}
|
||||
|
||||
Object.keys(bdthemes)
|
||||
.forEach(themeName => {
|
||||
if(!themeCookie[themeName])return // theme disabled
|
||||
const theme = bdthemes[themeName]
|
||||
if(!theme)return //:shrug:
|
||||
|
||||
let existing = document.querySelector("style[data-lightcord-theme=true]#"+DOM.escapeID(theme.id))
|
||||
if(existing){
|
||||
existing.innerHTML = unescape(theme.css)
|
||||
existing.removeAttribute("will-remove")
|
||||
}else{
|
||||
const style = document.createElement("style")
|
||||
style.id = DOM.escapeID(theme.id)
|
||||
style.innerHTML = unescape(theme.css)
|
||||
style.setAttribute("data-lightcord-theme", "true")
|
||||
document.head.append(style)
|
||||
}
|
||||
})
|
||||
|
||||
for(let style of document.querySelectorAll("style[will-remove=true]")){
|
||||
style.remove()
|
||||
}
|
||||
}else{
|
||||
for(let key of this.windows.keys()){
|
||||
this.applyThemes(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,58 +1,58 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "./v2";
|
||||
import webpackModules from "./webpackModules";
|
||||
import Utils from "./utils";
|
||||
import DOM from "./domtools";
|
||||
|
||||
import V2C_PublicServers from "../ui/publicservers/publicServers";
|
||||
import Layers from "./Layers";
|
||||
|
||||
export default new class V2_PublicServers {
|
||||
|
||||
constructor() {
|
||||
this._appendButton = this._appendButton.bind(this);
|
||||
window.Lightcord.BetterDiscord.V2_PublicServers = this
|
||||
}
|
||||
|
||||
render() {
|
||||
Layers.createLayer((close) => {
|
||||
return BDV2.react.createElement(V2C_PublicServers, {rootId: "pubslayerroot", close})
|
||||
})
|
||||
}
|
||||
|
||||
get button() {
|
||||
const btn = DOM.createElement(`<div id="bd-pub-li" class="${BDV2.guildClasses.listItem}">`);
|
||||
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>`);
|
||||
label.addEventListener("click", () => {this.render();});
|
||||
btn.append(label);
|
||||
return btn;
|
||||
}
|
||||
|
||||
_appendButton() {
|
||||
let [
|
||||
classNametutorialContainer
|
||||
] = [
|
||||
Utils.removeDa(BDModules.get(e => e.downloadProgress && e.tutorialContainer)[0].tutorialContainer)
|
||||
]
|
||||
if (DOM.query("#bd-pub-li")) return;
|
||||
const guilds = DOM.query(`div.${classNametutorialContainer} > div`);
|
||||
DOM.after(guilds, this.button);
|
||||
}
|
||||
|
||||
addButton() {
|
||||
if (this.guildPatch) return;
|
||||
const GuildList = webpackModules.find(m => m.default && m.default.displayName == "NavigableGuilds");
|
||||
const GuildListOld = webpackModules.findByDisplayName("Guilds");
|
||||
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._appendButton();
|
||||
}
|
||||
|
||||
removeButton() {
|
||||
this.guildPatch();
|
||||
delete this.guildPatch;
|
||||
const button = DOM.query("#bd-pub-li");
|
||||
if (button) button.remove();
|
||||
}
|
||||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "./v2";
|
||||
import webpackModules from "./webpackModules";
|
||||
import Utils from "./utils";
|
||||
import DOM from "./domtools";
|
||||
|
||||
import V2C_PublicServers from "../ui/publicservers/publicServers";
|
||||
import Layers from "./Layers";
|
||||
|
||||
export default new class V2_PublicServers {
|
||||
|
||||
constructor() {
|
||||
this._appendButton = this._appendButton.bind(this);
|
||||
window.Lightcord.BetterDiscord.V2_PublicServers = this
|
||||
}
|
||||
|
||||
render() {
|
||||
Layers.createLayer((close) => {
|
||||
return BDV2.react.createElement(V2C_PublicServers, {rootId: "pubslayerroot", close})
|
||||
})
|
||||
}
|
||||
|
||||
get button() {
|
||||
const btn = DOM.createElement(`<div id="bd-pub-li" class="${BDV2.guildClasses.listItem}">`);
|
||||
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>`);
|
||||
label.addEventListener("click", () => {this.render();});
|
||||
btn.append(label);
|
||||
return btn;
|
||||
}
|
||||
|
||||
_appendButton() {
|
||||
let [
|
||||
classNametutorialContainer
|
||||
] = [
|
||||
Utils.removeDa(BDModules.get(e => e.downloadProgress && e.tutorialContainer)[0].tutorialContainer)
|
||||
]
|
||||
if (DOM.query("#bd-pub-li")) return;
|
||||
const guilds = DOM.query(`div.${classNametutorialContainer} > div`);
|
||||
DOM.after(guilds, this.button);
|
||||
}
|
||||
|
||||
addButton() {
|
||||
if (this.guildPatch) return;
|
||||
const GuildList = webpackModules.find(m => m.default && m.default.displayName == "NavigableGuilds");
|
||||
const GuildListOld = webpackModules.findByDisplayName("Guilds");
|
||||
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._appendButton();
|
||||
}
|
||||
|
||||
removeButton() {
|
||||
this.guildPatch();
|
||||
delete this.guildPatch;
|
||||
const button = DOM.query("#bd-pub-li");
|
||||
if (button) button.remove();
|
||||
}
|
||||
};
|
|
@ -1,469 +1,469 @@
|
|||
import {settings, settingsCookie, settingsRPC, lightcordSettings} from "../0globals";
|
||||
import DataStore from "./dataStore";
|
||||
import V2_SettingsPanel_Sidebar from "./settingsPanelSidebar";
|
||||
import Utils from "./utils";
|
||||
import BDV2 from "./v2";
|
||||
import ContentManager from "./contentManager";
|
||||
import coloredText from "./coloredText";
|
||||
import tfHour from "./24hour";
|
||||
import DOM from "./domtools";
|
||||
|
||||
import publicServersModule from "./publicServers";
|
||||
import voiceMode from "./voiceMode";
|
||||
import ClassNormalizer from "./classNormalizer";
|
||||
import dMode from "./devMode";
|
||||
|
||||
import SectionedSettingsPanel from "../ui/sectionedSettingsPanel";
|
||||
import CssEditor from "../ui/cssEditor";
|
||||
import CardList from "../ui/addonlist";
|
||||
import V2C_PresenceSettings from "../ui/presenceSettings";
|
||||
import CustomRichPresence from "./CustomRichPresence";
|
||||
import V2C_AccountInfos from "../ui/AccountInfos";
|
||||
import { remote } from "electron";
|
||||
import AntiAdDM from "./AntiAdDM";
|
||||
import blurPrivate from "./blurPrivate";
|
||||
import disableTyping from "./disableTyping";
|
||||
import ApiPreview from "../ui/ApiPreview";
|
||||
import Switch from "../ui/switch";
|
||||
import MarginTop from "../ui/margintop";
|
||||
import webpackModules from "./webpackModules";
|
||||
import tooltipWrap from "../ui/tooltipWrap";
|
||||
import History from "../ui/icons/history";
|
||||
import core from "./core";
|
||||
import popoutWindow from "./popoutWindow";
|
||||
|
||||
class BDSidebarHeader extends React.PureComponent {
|
||||
render(){
|
||||
let sidebarComponents = webpackModules.find(e => e.Separator && e.Header && e.Item)
|
||||
|
||||
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(History, {className: "bd-icon", size: "16px"})
|
||||
)
|
||||
);
|
||||
let rendered = new sidebarComponents.Header({
|
||||
children: React.createElement("span", null, "Bandaged BD", changelogButton),
|
||||
className: "ui-tab-bar-header"
|
||||
})
|
||||
return rendered
|
||||
}
|
||||
}
|
||||
|
||||
let isClearingCache = false
|
||||
|
||||
export default new class V2_SettingsPanel {
|
||||
|
||||
constructor() {
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.updateSettings = this.updateSettings.bind(this);
|
||||
this.sidebar = new V2_SettingsPanel_Sidebar();
|
||||
|
||||
this.registerComponents()
|
||||
}
|
||||
|
||||
registerComponents(){
|
||||
/** Lightcord */
|
||||
this.sidebar.register("lightcord", makeComponent(this.lightcordComponent.bind(this)))
|
||||
this.sidebar.register("status", makeComponent(this.PresenceComponent.bind(this)))
|
||||
this.sidebar.register("accountinfo", makeComponent(this.AccountInfosComponent.bind(this)))
|
||||
this.sidebar.register("lcapipreview", makeComponent(this.ApiPreviewComponent.bind(this)))
|
||||
|
||||
/* Bandaged BD */
|
||||
this.sidebar.register("BDChangelogTitle", makeComponent(() => {
|
||||
return new BDSidebarHeader().render()
|
||||
}))
|
||||
this.sidebar.register("core", makeComponent(this.coreComponent.bind(this)))
|
||||
this.sidebar.register("customcss", makeComponent(this.customCssComponent.bind(this)))
|
||||
this.sidebar.register("plugins", makeComponent(this.renderAddonPane("plugins")))
|
||||
this.sidebar.register("themes", makeComponent(this.renderAddonPane("themes")))
|
||||
}
|
||||
|
||||
get coreSettings() {
|
||||
const settings = this.getSettings("core");
|
||||
const categories = [...new Set(settings.map(s => s.category))];
|
||||
const sections = categories.map(c => {return {title: c, settings: settings.filter(s => s.category == c)};});
|
||||
return sections;
|
||||
}
|
||||
|
||||
get lightcordSettings() {
|
||||
const settings = this.getSettings("lightcord");
|
||||
const categories = [...new Set(settings.map(s => s.category))];
|
||||
const sections = categories.map(c => {return {title: c, settings: settings.filter(s => s.category == c)};});
|
||||
return sections;
|
||||
}
|
||||
|
||||
get PresenceSettings() {
|
||||
return this.getSettings("status")
|
||||
}
|
||||
|
||||
getSettings(category) {
|
||||
return Object.keys(settings).reduce((arr, key) => {
|
||||
const setting = settings[key];
|
||||
if (setting.cat === category && setting.implemented && !setting.hidden) {
|
||||
setting.text = key;
|
||||
arr.push(setting);
|
||||
}
|
||||
return arr;
|
||||
}, []);
|
||||
}
|
||||
|
||||
|
||||
onChange(id, checked, sidebar) {
|
||||
this.updateSettings(id, checked, sidebar);
|
||||
}
|
||||
|
||||
updateSettings(id, enabled, sidebar) {
|
||||
if(!["lightcord-8", "no_window_bound", "enable_glasstron", "lightcord-10"].includes(id))settingsCookie[id] = enabled;
|
||||
|
||||
if (id == "bda-gs-2") {
|
||||
if (enabled) DOM.addClass(document.body, "bd-minimal");
|
||||
else DOM.removeClass(document.body, "bd-minimal");
|
||||
}
|
||||
|
||||
if (id == "bda-gs-3") {
|
||||
if (enabled) DOM.addClass(document.body, "bd-minimal-chan");
|
||||
else DOM.removeClass(document.body, "bd-minimal-chan");
|
||||
}
|
||||
|
||||
if (id == "bda-gs-1") {
|
||||
if (enabled) publicServersModule.addButton();
|
||||
else publicServersModule.removeButton();
|
||||
}
|
||||
|
||||
if (id == "bda-gs-4") {
|
||||
if (enabled) voiceMode.start();
|
||||
else voiceMode.stop();
|
||||
}
|
||||
|
||||
if (id == "bda-gs-5") {
|
||||
if (enabled) DOM.addClass(DOM.query("#app-mount"), "bda-dark");
|
||||
else DOM.removeClass(DOM.query("#app-mount"), "bda-dark");
|
||||
}
|
||||
|
||||
if (enabled && id == "bda-gs-6") tfHour.inject24Hour();
|
||||
|
||||
if (id == "bda-gs-7") {
|
||||
if (enabled) coloredText.injectColoredText();
|
||||
else coloredText.removeColoredText();
|
||||
}
|
||||
|
||||
if (id == "fork-ps-4") {
|
||||
if (enabled) ClassNormalizer.start();
|
||||
else ClassNormalizer.stop();
|
||||
}
|
||||
|
||||
if (id == "fork-ps-5") {
|
||||
if (enabled) {
|
||||
ContentManager.watchContent("plugin");
|
||||
ContentManager.watchContent("theme");
|
||||
}
|
||||
else {
|
||||
ContentManager.unwatchContent("plugin");
|
||||
ContentManager.unwatchContent("theme");
|
||||
}
|
||||
}
|
||||
|
||||
if (id == "fork-wp-1") {
|
||||
Utils.setWindowPreference("transparent", enabled);
|
||||
if (enabled) Utils.setWindowPreference("backgroundColor", null);
|
||||
else Utils.setWindowPreference("backgroundColor", "#2f3136");
|
||||
}
|
||||
|
||||
|
||||
if (id == "bda-gs-8") {
|
||||
if (enabled) dMode.startDebugListener();
|
||||
else dMode.stopDebugListener();
|
||||
}
|
||||
|
||||
if (id == "fork-dm-1") {
|
||||
if (enabled) dMode.startCopySelector();
|
||||
else dMode.stopCopySelector();
|
||||
}
|
||||
|
||||
if (id === "lightcord-1") {
|
||||
if (enabled) window.Lightcord.Settings.devMode = true
|
||||
else window.Lightcord.Settings.devMode = false
|
||||
sidebar.forceUpdate()
|
||||
}
|
||||
if (id === "lightcord-2") {
|
||||
if (enabled) window.Lightcord.Settings.callRingingBeat = true
|
||||
else window.Lightcord.Settings.callRingingBeat = false
|
||||
}
|
||||
if (id === "lightcord-presence-1") {
|
||||
if (enabled) {
|
||||
CustomRichPresence.enable()
|
||||
const settingsStore = BDModules.get(e => e.default && typeof e.default === "object" && "showCurrentGame" in e.default)[0]
|
||||
if(settingsStore && !settingsStore.default.showCurrentGame){
|
||||
BDModules.get(e => e.default && e.default.updateRemoteSettings)[0].default.updateRemoteSettings({
|
||||
showCurrentGame: true
|
||||
})
|
||||
}
|
||||
}
|
||||
else CustomRichPresence.disable()
|
||||
}
|
||||
if (id === "lightcord-3") {
|
||||
if (enabled) remote.getCurrentWindow().setAlwaysOnTop(true)
|
||||
else remote.getCurrentWindow().setAlwaysOnTop(false)
|
||||
}
|
||||
if (id === "lightcord-4") {
|
||||
if(enabled){
|
||||
AntiAdDM.enable()
|
||||
}else{
|
||||
AntiAdDM.disable()
|
||||
}
|
||||
}
|
||||
if (id === "lightcord-6") {
|
||||
if(enabled){
|
||||
blurPrivate.enable()
|
||||
}else{
|
||||
blurPrivate.disable()
|
||||
}
|
||||
}
|
||||
if (id === "lightcord-7") {
|
||||
if(enabled){
|
||||
disableTyping.enable()
|
||||
}else{
|
||||
disableTyping.disable()
|
||||
}
|
||||
}
|
||||
if (id === "lightcord-8"){
|
||||
let appSettings = remote.getGlobal("appSettings")
|
||||
appSettings.set("isTabs", enabled)
|
||||
appSettings.save()
|
||||
remote.app.relaunch()
|
||||
remote.app.exit()
|
||||
}
|
||||
if (id === "lightcord-9") {
|
||||
popoutWindow[enabled ? "enable" : "disable"]()
|
||||
}
|
||||
if (id === "lightcord-10"){
|
||||
core.methods.NotificationsUseShim(enabled)
|
||||
return
|
||||
}
|
||||
if (id === "no_window_bound"){
|
||||
let appSettings = remote.getGlobal("appSettings")
|
||||
appSettings.set("NO_WINDOWS_BOUND", enabled)
|
||||
|
||||
appSettings.delete("IS_MAXIMIZED")
|
||||
appSettings.delete("IS_MINIMIZED")
|
||||
appSettings.delete("WINDOW_BOUNDS")
|
||||
|
||||
appSettings.save()
|
||||
remote.app.relaunch()
|
||||
remote.app.exit()
|
||||
}
|
||||
if (id === "enable_glasstron"){
|
||||
let appSettings = remote.getGlobal("appSettings")
|
||||
appSettings.set("GLASSTRON", enabled)
|
||||
appSettings.save()
|
||||
remote.app.relaunch()
|
||||
remote.app.exit()
|
||||
}
|
||||
|
||||
this.saveSettings();
|
||||
}
|
||||
|
||||
async initializeSettings() {
|
||||
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-1"]) publicServersModule.addButton();
|
||||
if (settingsCookie["bda-gs-4"]) voiceMode.start();
|
||||
if (settingsCookie["bda-gs-5"]) DOM.addClass(DOM.query("#app-mount"), "bda-dark");
|
||||
if (settingsCookie["bda-gs-6"]) tfHour.inject24Hour();
|
||||
if (settingsCookie["bda-gs-7"]) coloredText.injectColoredText();
|
||||
if (settingsCookie["fork-ps-4"]) ClassNormalizer.start();
|
||||
if (settingsCookie["lightcord-1"]) window.Lightcord.Settings.devMode = true
|
||||
if (settingsCookie["lightcord-2"]) window.Lightcord.Settings.callRingingBeat = true
|
||||
if (settingsCookie["lightcord-presence-1"]) CustomRichPresence.enable()
|
||||
if (settingsCookie["lightcord-3"]) remote.getCurrentWindow().setAlwaysOnTop(true)
|
||||
if (settingsCookie["lightcord-4"]) AntiAdDM.enable()
|
||||
if (settingsCookie["lightcord-6"]) blurPrivate.enable()
|
||||
if (settingsCookie["lightcord-7"]) disableTyping.enable()
|
||||
if (settingsCookie["lightcord-9"]) popoutWindow.enable()
|
||||
|
||||
if (settingsCookie["fork-ps-5"]) {
|
||||
ContentManager.watchContent("plugin");
|
||||
ContentManager.watchContent("theme");
|
||||
}
|
||||
|
||||
if (settingsCookie["bda-gs-8"]) dMode.startDebugListener();
|
||||
if (settingsCookie["fork-dm-1"]) dMode.startCopySelector();
|
||||
|
||||
this.saveSettings();
|
||||
}
|
||||
|
||||
saveSettings() {
|
||||
DataStore.setSettingGroup("settings", settingsCookie);
|
||||
DataStore.setSettingGroup("rpc", settingsRPC);
|
||||
DataStore.setSettingGroup("lightcord-settings", lightcordSettings);
|
||||
}
|
||||
|
||||
loadSettings() {
|
||||
Object.assign(settingsCookie, DataStore.getSettingGroup("settings"));
|
||||
Object.assign(settingsRPC, DataStore.getSettingGroup("rpc"));
|
||||
console.log(lightcordSettings, DataStore.getSettingGroup("lightcord-settings"))
|
||||
Object.assign(lightcordSettings, DataStore.getSettingGroup("lightcord-settings"));
|
||||
}
|
||||
|
||||
renderSidebar(sidebar) {
|
||||
return this.sidebar.render(sidebar);
|
||||
}
|
||||
|
||||
coreComponent() {
|
||||
return BDV2.react.createElement(SectionedSettingsPanel, {key: "cspanel", onChange: this.onChange, sections: this.coreSettings})
|
||||
}
|
||||
|
||||
lightcordComponent(sidebar, forceUpdate) {
|
||||
let appSettings = remote.getGlobal("appSettings")
|
||||
return [
|
||||
this.lightcordSettings.map((section, i) => {
|
||||
return [
|
||||
(i === 0 ? null : BDV2.react.createElement(MarginTop)),
|
||||
BDV2.react.createElement("h2", {className: "ui-form-title h2 margin-reset margin-bottom-20"}, section.title),
|
||||
section.settings.map(setting => {
|
||||
let isChecked = settingsCookie[setting.id]
|
||||
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 === "enable_glasstron")isChecked = appSettings.get("GLASSTRON", 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) => {
|
||||
this.onChange(id, checked, sidebar);
|
||||
}})
|
||||
if(setting.id == "lightcord-8" && isChecked){
|
||||
return [
|
||||
returnValue,
|
||||
React.createElement(Lightcord.Api.Components.inputs.Button, {
|
||||
color: "green",
|
||||
look: "outlined",
|
||||
size: "small",
|
||||
hoverColor: "brand",
|
||||
onClick: () => {
|
||||
DiscordNative.ipc.send("NEW_TAB")
|
||||
},
|
||||
wrapper: false,
|
||||
disabled: false
|
||||
}, "Open a new Tab")
|
||||
]
|
||||
}
|
||||
return returnValue
|
||||
})
|
||||
]
|
||||
}),
|
||||
BDV2.react.createElement(window.Lightcord.Api.Components.inputs.Button, {
|
||||
color: "yellow",
|
||||
look: "ghost",
|
||||
size: "medium",
|
||||
hoverColor: "red",
|
||||
onClick(){
|
||||
console.log("Should relaunch")
|
||||
remote.app.relaunch({
|
||||
args: remote.process.argv.slice(1).concat(["--disable-betterdiscord"])
|
||||
})
|
||||
remote.app.quit()
|
||||
},
|
||||
wrapper: true
|
||||
}, "Relaunch without BetterDiscord"),
|
||||
React.createElement(Lightcord.Api.Components.inputs.Button, {
|
||||
color: "yellow",
|
||||
look: "ghost",
|
||||
size: "medium",
|
||||
hoverColor: "red",
|
||||
onClick: () => {
|
||||
if(isClearingCache)return
|
||||
isClearingCache = true
|
||||
Utils.showToast("Clearing cache...", {
|
||||
type: "info"
|
||||
})
|
||||
forceUpdate()
|
||||
remote.getCurrentWebContents().session.clearCache()
|
||||
.then(() => {
|
||||
Utils.showToast("Cache is cleared !", {
|
||||
type: "success"
|
||||
})
|
||||
isClearingCache = false
|
||||
forceUpdate()
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
Utils.showToast("An error occured. Check console for more informations.", {
|
||||
type: "error"
|
||||
})
|
||||
isClearingCache = false
|
||||
forceUpdate()
|
||||
})
|
||||
},
|
||||
wrapper: true,
|
||||
disabled: isClearingCache
|
||||
}, "Clear cache")
|
||||
]
|
||||
}
|
||||
|
||||
PresenceComponent() {
|
||||
return BDV2.react.createElement(V2C_PresenceSettings, {
|
||||
key: "lppannel",
|
||||
onChange: this.onChange,
|
||||
settings: this.PresenceSettings
|
||||
})
|
||||
}
|
||||
|
||||
AccountInfosComponent() {
|
||||
return BDV2.react.createElement(V2C_AccountInfos, {
|
||||
key: "lapannel"
|
||||
})
|
||||
}
|
||||
|
||||
ApiPreviewComponent() {
|
||||
return BDV2.react.createElement(ApiPreview, {
|
||||
key: "lapipannel"
|
||||
})
|
||||
}
|
||||
|
||||
customCssComponent() {
|
||||
return BDV2.react.createElement(CssEditor, {key: "csseditor"})
|
||||
}
|
||||
|
||||
renderAddonPane(type) {
|
||||
// I know this shouldn't be here, but when it isn't,
|
||||
// React refuses to change the button when going
|
||||
// between plugins and themes page... something
|
||||
// to debug later.
|
||||
class ContentList extends BDV2.react.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.prefix = this.props.type.replace("s", "");
|
||||
}
|
||||
|
||||
onChange() {
|
||||
this.props.onChange(this.props.type);
|
||||
}
|
||||
|
||||
render() {return this.props.children;}
|
||||
}
|
||||
const originalRender = ContentList.prototype.render;
|
||||
Object.defineProperty(ContentList.prototype, "render", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");},
|
||||
get: () => originalRender
|
||||
});
|
||||
return function(){
|
||||
return BDV2.react.createElement(ContentList, {type}, BDV2.react.createElement(CardList, {type}))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* No need to export settingsPanel on window
|
||||
*/
|
||||
|
||||
function makeComponent(children){
|
||||
class SettingComponent extends React.Component {
|
||||
render(){
|
||||
return children(sidebar, () => this.forceUpdate())
|
||||
}
|
||||
}
|
||||
let sidebar
|
||||
return (s) => {
|
||||
sidebar = s
|
||||
return SettingComponent
|
||||
}
|
||||
import {settings, settingsCookie, settingsRPC, lightcordSettings} from "../0globals";
|
||||
import DataStore from "./dataStore";
|
||||
import V2_SettingsPanel_Sidebar from "./settingsPanelSidebar";
|
||||
import Utils from "./utils";
|
||||
import BDV2 from "./v2";
|
||||
import ContentManager from "./contentManager";
|
||||
import coloredText from "./coloredText";
|
||||
import tfHour from "./24hour";
|
||||
import DOM from "./domtools";
|
||||
|
||||
import publicServersModule from "./publicServers";
|
||||
import voiceMode from "./voiceMode";
|
||||
import ClassNormalizer from "./classNormalizer";
|
||||
import dMode from "./devMode";
|
||||
|
||||
import SectionedSettingsPanel from "../ui/sectionedSettingsPanel";
|
||||
import CssEditor from "../ui/cssEditor";
|
||||
import CardList from "../ui/addonlist";
|
||||
import V2C_PresenceSettings from "../ui/presenceSettings";
|
||||
import CustomRichPresence from "./CustomRichPresence";
|
||||
import V2C_AccountInfos from "../ui/AccountInfos";
|
||||
import { remote } from "electron";
|
||||
import AntiAdDM from "./AntiAdDM";
|
||||
import blurPrivate from "./blurPrivate";
|
||||
import disableTyping from "./disableTyping";
|
||||
import ApiPreview from "../ui/ApiPreview";
|
||||
import Switch from "../ui/switch";
|
||||
import MarginTop from "../ui/margintop";
|
||||
import webpackModules from "./webpackModules";
|
||||
import tooltipWrap from "../ui/tooltipWrap";
|
||||
import History from "../ui/icons/history";
|
||||
import core from "./core";
|
||||
import popoutWindow from "./popoutWindow";
|
||||
|
||||
class BDSidebarHeader extends React.PureComponent {
|
||||
render(){
|
||||
let sidebarComponents = webpackModules.find(e => e.Separator && e.Header && e.Item)
|
||||
|
||||
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(History, {className: "bd-icon", size: "16px"})
|
||||
)
|
||||
);
|
||||
let rendered = new sidebarComponents.Header({
|
||||
children: React.createElement("span", null, "Bandaged BD", changelogButton),
|
||||
className: "ui-tab-bar-header"
|
||||
})
|
||||
return rendered
|
||||
}
|
||||
}
|
||||
|
||||
let isClearingCache = false
|
||||
|
||||
export default new class V2_SettingsPanel {
|
||||
|
||||
constructor() {
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.updateSettings = this.updateSettings.bind(this);
|
||||
this.sidebar = new V2_SettingsPanel_Sidebar();
|
||||
|
||||
this.registerComponents()
|
||||
}
|
||||
|
||||
registerComponents(){
|
||||
/** Lightcord */
|
||||
this.sidebar.register("lightcord", makeComponent(this.lightcordComponent.bind(this)))
|
||||
this.sidebar.register("status", makeComponent(this.PresenceComponent.bind(this)))
|
||||
this.sidebar.register("accountinfo", makeComponent(this.AccountInfosComponent.bind(this)))
|
||||
this.sidebar.register("lcapipreview", makeComponent(this.ApiPreviewComponent.bind(this)))
|
||||
|
||||
/* Bandaged BD */
|
||||
this.sidebar.register("BDChangelogTitle", makeComponent(() => {
|
||||
return new BDSidebarHeader().render()
|
||||
}))
|
||||
this.sidebar.register("core", makeComponent(this.coreComponent.bind(this)))
|
||||
this.sidebar.register("customcss", makeComponent(this.customCssComponent.bind(this)))
|
||||
this.sidebar.register("plugins", makeComponent(this.renderAddonPane("plugins")))
|
||||
this.sidebar.register("themes", makeComponent(this.renderAddonPane("themes")))
|
||||
}
|
||||
|
||||
get coreSettings() {
|
||||
const settings = this.getSettings("core");
|
||||
const categories = [...new Set(settings.map(s => s.category))];
|
||||
const sections = categories.map(c => {return {title: c, settings: settings.filter(s => s.category == c)};});
|
||||
return sections;
|
||||
}
|
||||
|
||||
get lightcordSettings() {
|
||||
const settings = this.getSettings("lightcord");
|
||||
const categories = [...new Set(settings.map(s => s.category))];
|
||||
const sections = categories.map(c => {return {title: c, settings: settings.filter(s => s.category == c)};});
|
||||
return sections;
|
||||
}
|
||||
|
||||
get PresenceSettings() {
|
||||
return this.getSettings("status")
|
||||
}
|
||||
|
||||
getSettings(category) {
|
||||
return Object.keys(settings).reduce((arr, key) => {
|
||||
const setting = settings[key];
|
||||
if (setting.cat === category && setting.implemented && !setting.hidden) {
|
||||
setting.text = key;
|
||||
arr.push(setting);
|
||||
}
|
||||
return arr;
|
||||
}, []);
|
||||
}
|
||||
|
||||
|
||||
onChange(id, checked, sidebar) {
|
||||
this.updateSettings(id, checked, sidebar);
|
||||
}
|
||||
|
||||
updateSettings(id, enabled, sidebar) {
|
||||
if(!["lightcord-8", "no_window_bound", "enable_glasstron", "lightcord-10"].includes(id))settingsCookie[id] = enabled;
|
||||
|
||||
if (id == "bda-gs-2") {
|
||||
if (enabled) DOM.addClass(document.body, "bd-minimal");
|
||||
else DOM.removeClass(document.body, "bd-minimal");
|
||||
}
|
||||
|
||||
if (id == "bda-gs-3") {
|
||||
if (enabled) DOM.addClass(document.body, "bd-minimal-chan");
|
||||
else DOM.removeClass(document.body, "bd-minimal-chan");
|
||||
}
|
||||
|
||||
if (id == "bda-gs-1") {
|
||||
if (enabled) publicServersModule.addButton();
|
||||
else publicServersModule.removeButton();
|
||||
}
|
||||
|
||||
if (id == "bda-gs-4") {
|
||||
if (enabled) voiceMode.start();
|
||||
else voiceMode.stop();
|
||||
}
|
||||
|
||||
if (id == "bda-gs-5") {
|
||||
if (enabled) DOM.addClass(DOM.query("#app-mount"), "bda-dark");
|
||||
else DOM.removeClass(DOM.query("#app-mount"), "bda-dark");
|
||||
}
|
||||
|
||||
if (enabled && id == "bda-gs-6") tfHour.inject24Hour();
|
||||
|
||||
if (id == "bda-gs-7") {
|
||||
if (enabled) coloredText.injectColoredText();
|
||||
else coloredText.removeColoredText();
|
||||
}
|
||||
|
||||
if (id == "fork-ps-4") {
|
||||
if (enabled) ClassNormalizer.start();
|
||||
else ClassNormalizer.stop();
|
||||
}
|
||||
|
||||
if (id == "fork-ps-5") {
|
||||
if (enabled) {
|
||||
ContentManager.watchContent("plugin");
|
||||
ContentManager.watchContent("theme");
|
||||
}
|
||||
else {
|
||||
ContentManager.unwatchContent("plugin");
|
||||
ContentManager.unwatchContent("theme");
|
||||
}
|
||||
}
|
||||
|
||||
if (id == "fork-wp-1") {
|
||||
Utils.setWindowPreference("transparent", enabled);
|
||||
if (enabled) Utils.setWindowPreference("backgroundColor", null);
|
||||
else Utils.setWindowPreference("backgroundColor", "#2f3136");
|
||||
}
|
||||
|
||||
|
||||
if (id == "bda-gs-8") {
|
||||
if (enabled) dMode.startDebugListener();
|
||||
else dMode.stopDebugListener();
|
||||
}
|
||||
|
||||
if (id == "fork-dm-1") {
|
||||
if (enabled) dMode.startCopySelector();
|
||||
else dMode.stopCopySelector();
|
||||
}
|
||||
|
||||
if (id === "lightcord-1") {
|
||||
if (enabled) window.Lightcord.Settings.devMode = true
|
||||
else window.Lightcord.Settings.devMode = false
|
||||
sidebar.forceUpdate()
|
||||
}
|
||||
if (id === "lightcord-2") {
|
||||
if (enabled) window.Lightcord.Settings.callRingingBeat = true
|
||||
else window.Lightcord.Settings.callRingingBeat = false
|
||||
}
|
||||
if (id === "lightcord-presence-1") {
|
||||
if (enabled) {
|
||||
CustomRichPresence.enable()
|
||||
const settingsStore = BDModules.get(e => e.default && typeof e.default === "object" && "showCurrentGame" in e.default)[0]
|
||||
if(settingsStore && !settingsStore.default.showCurrentGame){
|
||||
BDModules.get(e => e.default && e.default.updateRemoteSettings)[0].default.updateRemoteSettings({
|
||||
showCurrentGame: true
|
||||
})
|
||||
}
|
||||
}
|
||||
else CustomRichPresence.disable()
|
||||
}
|
||||
if (id === "lightcord-3") {
|
||||
if (enabled) remote.getCurrentWindow().setAlwaysOnTop(true)
|
||||
else remote.getCurrentWindow().setAlwaysOnTop(false)
|
||||
}
|
||||
if (id === "lightcord-4") {
|
||||
if(enabled){
|
||||
AntiAdDM.enable()
|
||||
}else{
|
||||
AntiAdDM.disable()
|
||||
}
|
||||
}
|
||||
if (id === "lightcord-6") {
|
||||
if(enabled){
|
||||
blurPrivate.enable()
|
||||
}else{
|
||||
blurPrivate.disable()
|
||||
}
|
||||
}
|
||||
if (id === "lightcord-7") {
|
||||
if(enabled){
|
||||
disableTyping.enable()
|
||||
}else{
|
||||
disableTyping.disable()
|
||||
}
|
||||
}
|
||||
if (id === "lightcord-8"){
|
||||
let appSettings = remote.getGlobal("appSettings")
|
||||
appSettings.set("isTabs", enabled)
|
||||
appSettings.save()
|
||||
remote.app.relaunch()
|
||||
remote.app.exit()
|
||||
}
|
||||
if (id === "lightcord-9") {
|
||||
popoutWindow[enabled ? "enable" : "disable"]()
|
||||
}
|
||||
if (id === "lightcord-10"){
|
||||
core.methods.NotificationsUseShim(enabled)
|
||||
return
|
||||
}
|
||||
if (id === "no_window_bound"){
|
||||
let appSettings = remote.getGlobal("appSettings")
|
||||
appSettings.set("NO_WINDOWS_BOUND", enabled)
|
||||
|
||||
appSettings.delete("IS_MAXIMIZED")
|
||||
appSettings.delete("IS_MINIMIZED")
|
||||
appSettings.delete("WINDOW_BOUNDS")
|
||||
|
||||
appSettings.save()
|
||||
remote.app.relaunch()
|
||||
remote.app.exit()
|
||||
}
|
||||
if (id === "enable_glasstron"){
|
||||
let appSettings = remote.getGlobal("appSettings")
|
||||
appSettings.set("GLASSTRON", enabled)
|
||||
appSettings.save()
|
||||
remote.app.relaunch()
|
||||
remote.app.exit()
|
||||
}
|
||||
|
||||
this.saveSettings();
|
||||
}
|
||||
|
||||
async initializeSettings() {
|
||||
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-1"]) publicServersModule.addButton();
|
||||
if (settingsCookie["bda-gs-4"]) voiceMode.start();
|
||||
if (settingsCookie["bda-gs-5"]) DOM.addClass(DOM.query("#app-mount"), "bda-dark");
|
||||
if (settingsCookie["bda-gs-6"]) tfHour.inject24Hour();
|
||||
if (settingsCookie["bda-gs-7"]) coloredText.injectColoredText();
|
||||
if (settingsCookie["fork-ps-4"]) ClassNormalizer.start();
|
||||
if (settingsCookie["lightcord-1"]) window.Lightcord.Settings.devMode = true
|
||||
if (settingsCookie["lightcord-2"]) window.Lightcord.Settings.callRingingBeat = true
|
||||
if (settingsCookie["lightcord-presence-1"]) CustomRichPresence.enable()
|
||||
if (settingsCookie["lightcord-3"]) remote.getCurrentWindow().setAlwaysOnTop(true)
|
||||
if (settingsCookie["lightcord-4"]) AntiAdDM.enable()
|
||||
if (settingsCookie["lightcord-6"]) blurPrivate.enable()
|
||||
if (settingsCookie["lightcord-7"]) disableTyping.enable()
|
||||
if (settingsCookie["lightcord-9"]) popoutWindow.enable()
|
||||
|
||||
if (settingsCookie["fork-ps-5"]) {
|
||||
ContentManager.watchContent("plugin");
|
||||
ContentManager.watchContent("theme");
|
||||
}
|
||||
|
||||
if (settingsCookie["bda-gs-8"]) dMode.startDebugListener();
|
||||
if (settingsCookie["fork-dm-1"]) dMode.startCopySelector();
|
||||
|
||||
this.saveSettings();
|
||||
}
|
||||
|
||||
saveSettings() {
|
||||
DataStore.setSettingGroup("settings", settingsCookie);
|
||||
DataStore.setSettingGroup("rpc", settingsRPC);
|
||||
DataStore.setSettingGroup("lightcord-settings", lightcordSettings);
|
||||
}
|
||||
|
||||
loadSettings() {
|
||||
Object.assign(settingsCookie, DataStore.getSettingGroup("settings"));
|
||||
Object.assign(settingsRPC, DataStore.getSettingGroup("rpc"));
|
||||
console.log(lightcordSettings, DataStore.getSettingGroup("lightcord-settings"))
|
||||
Object.assign(lightcordSettings, DataStore.getSettingGroup("lightcord-settings"));
|
||||
}
|
||||
|
||||
renderSidebar(sidebar) {
|
||||
return this.sidebar.render(sidebar);
|
||||
}
|
||||
|
||||
coreComponent() {
|
||||
return BDV2.react.createElement(SectionedSettingsPanel, {key: "cspanel", onChange: this.onChange, sections: this.coreSettings})
|
||||
}
|
||||
|
||||
lightcordComponent(sidebar, forceUpdate) {
|
||||
let appSettings = remote.getGlobal("appSettings")
|
||||
return [
|
||||
this.lightcordSettings.map((section, i) => {
|
||||
return [
|
||||
(i === 0 ? null : BDV2.react.createElement(MarginTop)),
|
||||
BDV2.react.createElement("h2", {className: "ui-form-title h2 margin-reset margin-bottom-20"}, section.title),
|
||||
section.settings.map(setting => {
|
||||
let isChecked = settingsCookie[setting.id]
|
||||
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 === "enable_glasstron")isChecked = appSettings.get("GLASSTRON", 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) => {
|
||||
this.onChange(id, checked, sidebar);
|
||||
}})
|
||||
if(setting.id == "lightcord-8" && isChecked){
|
||||
return [
|
||||
returnValue,
|
||||
React.createElement(Lightcord.Api.Components.inputs.Button, {
|
||||
color: "green",
|
||||
look: "outlined",
|
||||
size: "small",
|
||||
hoverColor: "brand",
|
||||
onClick: () => {
|
||||
DiscordNative.ipc.send("NEW_TAB")
|
||||
},
|
||||
wrapper: false,
|
||||
disabled: false
|
||||
}, "Open a new Tab")
|
||||
]
|
||||
}
|
||||
return returnValue
|
||||
})
|
||||
]
|
||||
}),
|
||||
BDV2.react.createElement(window.Lightcord.Api.Components.inputs.Button, {
|
||||
color: "yellow",
|
||||
look: "ghost",
|
||||
size: "medium",
|
||||
hoverColor: "red",
|
||||
onClick(){
|
||||
console.log("Should relaunch")
|
||||
remote.app.relaunch({
|
||||
args: remote.process.argv.slice(1).concat(["--disable-betterdiscord"])
|
||||
})
|
||||
remote.app.quit()
|
||||
},
|
||||
wrapper: true
|
||||
}, "Relaunch without BetterDiscord"),
|
||||
React.createElement(Lightcord.Api.Components.inputs.Button, {
|
||||
color: "yellow",
|
||||
look: "ghost",
|
||||
size: "medium",
|
||||
hoverColor: "red",
|
||||
onClick: () => {
|
||||
if(isClearingCache)return
|
||||
isClearingCache = true
|
||||
Utils.showToast("Clearing cache...", {
|
||||
type: "info"
|
||||
})
|
||||
forceUpdate()
|
||||
remote.getCurrentWebContents().session.clearCache()
|
||||
.then(() => {
|
||||
Utils.showToast("Cache is cleared !", {
|
||||
type: "success"
|
||||
})
|
||||
isClearingCache = false
|
||||
forceUpdate()
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
Utils.showToast("An error occured. Check console for more informations.", {
|
||||
type: "error"
|
||||
})
|
||||
isClearingCache = false
|
||||
forceUpdate()
|
||||
})
|
||||
},
|
||||
wrapper: true,
|
||||
disabled: isClearingCache
|
||||
}, "Clear cache")
|
||||
]
|
||||
}
|
||||
|
||||
PresenceComponent() {
|
||||
return BDV2.react.createElement(V2C_PresenceSettings, {
|
||||
key: "lppannel",
|
||||
onChange: this.onChange,
|
||||
settings: this.PresenceSettings
|
||||
})
|
||||
}
|
||||
|
||||
AccountInfosComponent() {
|
||||
return BDV2.react.createElement(V2C_AccountInfos, {
|
||||
key: "lapannel"
|
||||
})
|
||||
}
|
||||
|
||||
ApiPreviewComponent() {
|
||||
return BDV2.react.createElement(ApiPreview, {
|
||||
key: "lapipannel"
|
||||
})
|
||||
}
|
||||
|
||||
customCssComponent() {
|
||||
return BDV2.react.createElement(CssEditor, {key: "csseditor"})
|
||||
}
|
||||
|
||||
renderAddonPane(type) {
|
||||
// I know this shouldn't be here, but when it isn't,
|
||||
// React refuses to change the button when going
|
||||
// between plugins and themes page... something
|
||||
// to debug later.
|
||||
class ContentList extends BDV2.react.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.prefix = this.props.type.replace("s", "");
|
||||
}
|
||||
|
||||
onChange() {
|
||||
this.props.onChange(this.props.type);
|
||||
}
|
||||
|
||||
render() {return this.props.children;}
|
||||
}
|
||||
const originalRender = ContentList.prototype.render;
|
||||
Object.defineProperty(ContentList.prototype, "render", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");},
|
||||
get: () => originalRender
|
||||
});
|
||||
return function(){
|
||||
return BDV2.react.createElement(ContentList, {type}, BDV2.react.createElement(CardList, {type}))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* No need to export settingsPanel on window
|
||||
*/
|
||||
|
||||
function makeComponent(children){
|
||||
class SettingComponent extends React.Component {
|
||||
render(){
|
||||
return children(sidebar, () => this.forceUpdate())
|
||||
}
|
||||
}
|
||||
let sidebar
|
||||
return (s) => {
|
||||
sidebar = s
|
||||
return SettingComponent
|
||||
}
|
||||
}
|
|
@ -1,110 +1,110 @@
|
|||
export default class V2_SettingsPanel_Sidebar {
|
||||
|
||||
constructor() {
|
||||
this.panels = {}
|
||||
}
|
||||
|
||||
register(panel, component){
|
||||
this.panels[panel] = component
|
||||
}
|
||||
|
||||
getComponent(panel, sidebar){
|
||||
return this.panels[panel](sidebar)
|
||||
}
|
||||
|
||||
get items() {
|
||||
return [{
|
||||
text: "BetterDiscord Settings",
|
||||
id: "core"
|
||||
}, {
|
||||
text: "Plugins",
|
||||
id: "plugins"
|
||||
}, {
|
||||
text: "Themes",
|
||||
id: "themes"
|
||||
}, {
|
||||
text: "Custom CSS",
|
||||
id: "customcss"
|
||||
}];
|
||||
}
|
||||
|
||||
get LCitems(){
|
||||
let items = [
|
||||
{
|
||||
text: "Lightcord Settings",
|
||||
id: "lightcord"
|
||||
}, {
|
||||
text: "RichPresence",
|
||||
id: "status"
|
||||
}, {
|
||||
text: "Account Info",
|
||||
id: "accountinfo"
|
||||
}
|
||||
]
|
||||
return items
|
||||
}
|
||||
|
||||
get LCDevItems(){
|
||||
let items = []
|
||||
if(!window.Lightcord.Settings.devMode)return items
|
||||
items.push(...[
|
||||
{
|
||||
section: "DIVIDER"
|
||||
},
|
||||
{
|
||||
section: "HEADER",
|
||||
label: "Lightcord Api"
|
||||
},
|
||||
{
|
||||
text: "Components Preview",
|
||||
id: "lcapipreview"
|
||||
}
|
||||
])
|
||||
return items
|
||||
}
|
||||
|
||||
render(sidebar) {
|
||||
return [
|
||||
{
|
||||
section: "HEADER",
|
||||
label: "Lightcord"
|
||||
},
|
||||
...this.LCitems.map(e => {
|
||||
return {
|
||||
section: e.id,
|
||||
label: e.text,
|
||||
element: this.getComponent(e.id, sidebar)
|
||||
}
|
||||
}),
|
||||
...this.LCDevItems.map(e => {
|
||||
if(e.section)return e
|
||||
return {
|
||||
section: e.id,
|
||||
label: e.text,
|
||||
element: this.getComponent(e.id, sidebar)
|
||||
}
|
||||
}),
|
||||
{
|
||||
section: "DIVIDER"
|
||||
},
|
||||
{
|
||||
section: "CUSTOM",
|
||||
element: this.getComponent("BDChangelogTitle")
|
||||
},
|
||||
...this.items.map(e => {
|
||||
return {
|
||||
section: e.id,
|
||||
label: e.text,
|
||||
element: this.getComponent(e.id, sidebar)
|
||||
}
|
||||
}),
|
||||
{
|
||||
section: "DIVIDER"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No need to export settingsPanelSidebar on window
|
||||
export default class V2_SettingsPanel_Sidebar {
|
||||
|
||||
constructor() {
|
||||
this.panels = {}
|
||||
}
|
||||
|
||||
register(panel, component){
|
||||
this.panels[panel] = component
|
||||
}
|
||||
|
||||
getComponent(panel, sidebar){
|
||||
return this.panels[panel](sidebar)
|
||||
}
|
||||
|
||||
get items() {
|
||||
return [{
|
||||
text: "BetterDiscord Settings",
|
||||
id: "core"
|
||||
}, {
|
||||
text: "Plugins",
|
||||
id: "plugins"
|
||||
}, {
|
||||
text: "Themes",
|
||||
id: "themes"
|
||||
}, {
|
||||
text: "Custom CSS",
|
||||
id: "customcss"
|
||||
}];
|
||||
}
|
||||
|
||||
get LCitems(){
|
||||
let items = [
|
||||
{
|
||||
text: "Lightcord Settings",
|
||||
id: "lightcord"
|
||||
}, {
|
||||
text: "RichPresence",
|
||||
id: "status"
|
||||
}, {
|
||||
text: "Account Info",
|
||||
id: "accountinfo"
|
||||
}
|
||||
]
|
||||
return items
|
||||
}
|
||||
|
||||
get LCDevItems(){
|
||||
let items = []
|
||||
if(!window.Lightcord.Settings.devMode)return items
|
||||
items.push(...[
|
||||
{
|
||||
section: "DIVIDER"
|
||||
},
|
||||
{
|
||||
section: "HEADER",
|
||||
label: "Lightcord Api"
|
||||
},
|
||||
{
|
||||
text: "Components Preview",
|
||||
id: "lcapipreview"
|
||||
}
|
||||
])
|
||||
return items
|
||||
}
|
||||
|
||||
render(sidebar) {
|
||||
return [
|
||||
{
|
||||
section: "HEADER",
|
||||
label: "Lightcord"
|
||||
},
|
||||
...this.LCitems.map(e => {
|
||||
return {
|
||||
section: e.id,
|
||||
label: e.text,
|
||||
element: this.getComponent(e.id, sidebar)
|
||||
}
|
||||
}),
|
||||
...this.LCDevItems.map(e => {
|
||||
if(e.section)return e
|
||||
return {
|
||||
section: e.id,
|
||||
label: e.text,
|
||||
element: this.getComponent(e.id, sidebar)
|
||||
}
|
||||
}),
|
||||
{
|
||||
section: "DIVIDER"
|
||||
},
|
||||
{
|
||||
section: "CUSTOM",
|
||||
element: this.getComponent("BDChangelogTitle")
|
||||
},
|
||||
...this.items.map(e => {
|
||||
return {
|
||||
section: e.id,
|
||||
label: e.text,
|
||||
element: this.getComponent(e.id, sidebar)
|
||||
}
|
||||
}),
|
||||
{
|
||||
section: "DIVIDER"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No need to export settingsPanelSidebar on window
|
||||
*/
|
|
@ -1,152 +1,152 @@
|
|||
import {bdthemeErrors, themeCookie, settingsCookie, bdthemes} from "../0globals";
|
||||
import ContentManager from "./contentManager";
|
||||
import DataStore from "./dataStore";
|
||||
import BDEvents from "./bdEvents";
|
||||
import Utils from "./utils";
|
||||
import DOM from "./domtools";
|
||||
import bdEvents from "./bdEvents";
|
||||
|
||||
class ThemeModule {
|
||||
constructor(){
|
||||
window.Lightcord.BetterDiscord.ThemeModule = this
|
||||
}
|
||||
get folder() {return ContentManager.themesFolder;}
|
||||
}
|
||||
|
||||
ThemeModule.prototype.loadThemes = async function () {
|
||||
this.loadThemeData();
|
||||
bdthemeErrors.splice(0, 0, ...(await ContentManager.loadThemes()));
|
||||
const themes = Object.keys(bdthemes);
|
||||
|
||||
for (let i = 0; i < themes.length; i++) {
|
||||
const theme = bdthemes[themes[i]];
|
||||
if (!themeCookie[theme.name]) themeCookie[theme.name] = false;
|
||||
if (themeCookie[theme.name]) DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css));
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 10))
|
||||
}
|
||||
for (const theme in themeCookie) {
|
||||
if (!bdthemes[theme]) delete themeCookie[theme];
|
||||
}
|
||||
this.saveThemeData();
|
||||
// if (settingsCookie["fork-ps-5"]) ContentManager.watchContent("theme");
|
||||
};
|
||||
|
||||
ThemeModule.prototype.enableTheme = function(name, reload = false) {
|
||||
themeCookie[name] = true;
|
||||
this.saveThemeData();
|
||||
const theme = bdthemes[name];
|
||||
DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css));
|
||||
bdEvents.dispatch("theme-enabled")
|
||||
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been applied.`);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.enable = function (name, reload = false) {
|
||||
return this.enableTheme(name, reload);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.disableTheme = function(name, reload = false) {
|
||||
themeCookie[name] = false;
|
||||
this.saveThemeData();
|
||||
const theme = bdthemes[name];
|
||||
DOM.removeStyle(DOM.escapeID(theme.id));
|
||||
bdEvents.dispatch("theme-disabled")
|
||||
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been disabled.`);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.disable = function (name, reload = false) {
|
||||
return this.disableTheme(name, reload);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.toggleTheme = function(theme) {
|
||||
if (themeCookie[theme]) this.disableTheme(theme);
|
||||
else this.enableTheme(theme);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.toggle = function (name) {
|
||||
return this.toggleTheme(name);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.loadTheme = async function(filename) {
|
||||
const error = await ContentManager.loadContent(filename, "theme");
|
||||
if (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"});
|
||||
return Utils.err("ContentManager", `${filename} could not be loaded.`, error);
|
||||
}
|
||||
const theme = Object.values(bdthemes).find(p => p.filename == filename);
|
||||
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"});
|
||||
BDEvents.dispatch("theme-loaded", theme.name);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.unloadTheme = function(filenameOrName) {
|
||||
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
|
||||
if (!bdtheme) return;
|
||||
const theme = bdtheme.name;
|
||||
if (themeCookie[theme]) this.disableTheme(theme, true);
|
||||
const error = ContentManager.unloadContent(bdthemes[theme].filename, "theme");
|
||||
delete bdthemes[theme];
|
||||
if (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"});
|
||||
return Utils.err("ContentManager", `${theme} could not be unloaded. It may have not been loaded yet.`, error);
|
||||
}
|
||||
Utils.log("ContentManager", `${theme} was unloaded.`);
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} was unloaded.`, {type: "success"});
|
||||
BDEvents.dispatch("theme-unloaded", theme);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.delete = function(filenameOrName) {
|
||||
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
|
||||
if (!bdtheme) return;
|
||||
this.unloadTheme(bdtheme.filename);
|
||||
const fullPath = require("path").resolve(ContentManager.themesFolder, bdtheme.filename);
|
||||
require("fs").unlinkSync(fullPath);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.reloadTheme = async function(filenameOrName) {
|
||||
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
|
||||
if (!bdtheme) return this.loadTheme(filenameOrName);
|
||||
const theme = bdtheme.name;
|
||||
const error = await ContentManager.reloadContent(bdthemes[theme].filename, "theme");
|
||||
if (themeCookie[theme]) this.disableTheme(theme, true), this.enableTheme(theme, true);
|
||||
if (error) {
|
||||
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [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);
|
||||
}
|
||||
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"});
|
||||
BDEvents.dispatch("theme-reloaded", theme);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.reload = function(name) {
|
||||
return this.reloadTheme(name);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.edit = function(filenameOrName) {
|
||||
const bdplugin = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
|
||||
if (!bdplugin) return;
|
||||
const fullPath = require("path").resolve(ContentManager.themesFolder, bdplugin.filename);
|
||||
require("electron").shell.openItem(`${fullPath}`);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.updateThemeList = function() {
|
||||
const results = ContentManager.loadNewContent("theme");
|
||||
for (const filename of results.added) this.loadTheme(filename);
|
||||
for (const name of results.removed) this.unloadTheme(name);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.loadThemeData = function() {
|
||||
const saved = DataStore.getSettingGroup("themes");
|
||||
if (saved) {
|
||||
Object.assign(themeCookie, saved);
|
||||
}
|
||||
};
|
||||
|
||||
ThemeModule.prototype.saveThemeData = function () {
|
||||
DataStore.setSettingGroup("themes", themeCookie);
|
||||
};
|
||||
|
||||
import {bdthemeErrors, themeCookie, settingsCookie, bdthemes} from "../0globals";
|
||||
import ContentManager from "./contentManager";
|
||||
import DataStore from "./dataStore";
|
||||
import BDEvents from "./bdEvents";
|
||||
import Utils from "./utils";
|
||||
import DOM from "./domtools";
|
||||
import bdEvents from "./bdEvents";
|
||||
|
||||
class ThemeModule {
|
||||
constructor(){
|
||||
window.Lightcord.BetterDiscord.ThemeModule = this
|
||||
}
|
||||
get folder() {return ContentManager.themesFolder;}
|
||||
}
|
||||
|
||||
ThemeModule.prototype.loadThemes = async function () {
|
||||
this.loadThemeData();
|
||||
bdthemeErrors.splice(0, 0, ...(await ContentManager.loadThemes()));
|
||||
const themes = Object.keys(bdthemes);
|
||||
|
||||
for (let i = 0; i < themes.length; i++) {
|
||||
const theme = bdthemes[themes[i]];
|
||||
if (!themeCookie[theme.name]) themeCookie[theme.name] = false;
|
||||
if (themeCookie[theme.name]) DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css));
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 10))
|
||||
}
|
||||
for (const theme in themeCookie) {
|
||||
if (!bdthemes[theme]) delete themeCookie[theme];
|
||||
}
|
||||
this.saveThemeData();
|
||||
// if (settingsCookie["fork-ps-5"]) ContentManager.watchContent("theme");
|
||||
};
|
||||
|
||||
ThemeModule.prototype.enableTheme = function(name, reload = false) {
|
||||
themeCookie[name] = true;
|
||||
this.saveThemeData();
|
||||
const theme = bdthemes[name];
|
||||
DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css));
|
||||
bdEvents.dispatch("theme-enabled")
|
||||
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been applied.`);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.enable = function (name, reload = false) {
|
||||
return this.enableTheme(name, reload);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.disableTheme = function(name, reload = false) {
|
||||
themeCookie[name] = false;
|
||||
this.saveThemeData();
|
||||
const theme = bdthemes[name];
|
||||
DOM.removeStyle(DOM.escapeID(theme.id));
|
||||
bdEvents.dispatch("theme-disabled")
|
||||
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been disabled.`);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.disable = function (name, reload = false) {
|
||||
return this.disableTheme(name, reload);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.toggleTheme = function(theme) {
|
||||
if (themeCookie[theme]) this.disableTheme(theme);
|
||||
else this.enableTheme(theme);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.toggle = function (name) {
|
||||
return this.toggleTheme(name);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.loadTheme = async function(filename) {
|
||||
const error = await ContentManager.loadContent(filename, "theme");
|
||||
if (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"});
|
||||
return Utils.err("ContentManager", `${filename} could not be loaded.`, error);
|
||||
}
|
||||
const theme = Object.values(bdthemes).find(p => p.filename == filename);
|
||||
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"});
|
||||
BDEvents.dispatch("theme-loaded", theme.name);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.unloadTheme = function(filenameOrName) {
|
||||
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
|
||||
if (!bdtheme) return;
|
||||
const theme = bdtheme.name;
|
||||
if (themeCookie[theme]) this.disableTheme(theme, true);
|
||||
const error = ContentManager.unloadContent(bdthemes[theme].filename, "theme");
|
||||
delete bdthemes[theme];
|
||||
if (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"});
|
||||
return Utils.err("ContentManager", `${theme} could not be unloaded. It may have not been loaded yet.`, error);
|
||||
}
|
||||
Utils.log("ContentManager", `${theme} was unloaded.`);
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} was unloaded.`, {type: "success"});
|
||||
BDEvents.dispatch("theme-unloaded", theme);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.delete = function(filenameOrName) {
|
||||
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
|
||||
if (!bdtheme) return;
|
||||
this.unloadTheme(bdtheme.filename);
|
||||
const fullPath = require("path").resolve(ContentManager.themesFolder, bdtheme.filename);
|
||||
require("fs").unlinkSync(fullPath);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.reloadTheme = async function(filenameOrName) {
|
||||
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
|
||||
if (!bdtheme) return this.loadTheme(filenameOrName);
|
||||
const theme = bdtheme.name;
|
||||
const error = await ContentManager.reloadContent(bdthemes[theme].filename, "theme");
|
||||
if (themeCookie[theme]) this.disableTheme(theme, true), this.enableTheme(theme, true);
|
||||
if (error) {
|
||||
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [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);
|
||||
}
|
||||
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"});
|
||||
BDEvents.dispatch("theme-reloaded", theme);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.reload = function(name) {
|
||||
return this.reloadTheme(name);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.edit = function(filenameOrName) {
|
||||
const bdplugin = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
|
||||
if (!bdplugin) return;
|
||||
const fullPath = require("path").resolve(ContentManager.themesFolder, bdplugin.filename);
|
||||
require("electron").shell.openItem(`${fullPath}`);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.updateThemeList = function() {
|
||||
const results = ContentManager.loadNewContent("theme");
|
||||
for (const filename of results.added) this.loadTheme(filename);
|
||||
for (const name of results.removed) this.unloadTheme(name);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.loadThemeData = function() {
|
||||
const saved = DataStore.getSettingGroup("themes");
|
||||
if (saved) {
|
||||
Object.assign(themeCookie, saved);
|
||||
}
|
||||
};
|
||||
|
||||
ThemeModule.prototype.saveThemeData = function () {
|
||||
DataStore.setSettingGroup("themes", themeCookie);
|
||||
};
|
||||
|
||||
export default new ThemeModule();
|
|
@ -1,167 +1,167 @@
|
|||
import {settings} from "../0globals";
|
||||
import themeModule from "./themeModule";
|
||||
|
||||
export default new class V2 {
|
||||
|
||||
constructor() {
|
||||
this.editorDetached = false;
|
||||
this.WebpackModules = (() => {
|
||||
const req = webpackJsonp.push([[], {__extra_id__: (module, exports, req) => module.exports = req}, [["__extra_id__"]]]);
|
||||
delete req.m.__extra_id__;
|
||||
delete req.c.__extra_id__;
|
||||
|
||||
const shouldProtect = theModule => {
|
||||
if (theModule.remove && theModule.set && theModule.clear && theModule.get && !theModule.sort) return true;
|
||||
if (theModule.getToken || theModule.getEmail || theModule.showToken) return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
const protect = (theModule, isDefault) => {
|
||||
let mod = !isDefault ? theModule.default : theModule
|
||||
if(!mod)return theModule
|
||||
if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null;
|
||||
if (!mod.getToken && !mod.getEmail && !mod.showToken)return theModule
|
||||
|
||||
const proxy = new Proxy(mod, {
|
||||
getOwnPropertyDescriptor: function(obj, prop) {
|
||||
if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined;
|
||||
return Object.getOwnPropertyDescriptor(obj, prop);
|
||||
},
|
||||
get: function(obj, func) {
|
||||
if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa";
|
||||
if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com";
|
||||
if (func == "showToken" && obj.showToken) return () => true;
|
||||
if (func == "__proto__" && obj.__proto__) return proxy;
|
||||
|
||||
return obj[func];
|
||||
}
|
||||
});
|
||||
|
||||
if(!isDefault){
|
||||
return Object.assign({}, theModule, {default: proxy})
|
||||
}
|
||||
|
||||
return proxy;
|
||||
};
|
||||
|
||||
const find = (filter) => {
|
||||
for (const i in req.c) {
|
||||
if (req.c.hasOwnProperty(i)) {
|
||||
const m = req.c[i].exports;
|
||||
if (m && m.__esModule && m.default && filter(m.default)) return protect(m.default, true);
|
||||
if (m && filter(m)) return protect(m, false);
|
||||
}
|
||||
}
|
||||
// console.warn("Cannot find loaded module in cache");
|
||||
return null;
|
||||
};
|
||||
|
||||
const findAll = (filter) => {
|
||||
const modules = [];
|
||||
for (const i in req.c) {
|
||||
if (req.c.hasOwnProperty(i)) {
|
||||
const m = req.c[i].exports;
|
||||
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));
|
||||
}
|
||||
}
|
||||
return modules;
|
||||
};
|
||||
|
||||
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 findByDisplayName = (displayName) => find(module => module.displayName === displayName);
|
||||
|
||||
return {find, findAll, findByUniqueProperties, findByPrototypes, findByDisplayName};
|
||||
})();
|
||||
|
||||
this.internal = {
|
||||
react: this.WebpackModules.findByUniqueProperties(["Component", "PureComponent", "Children", "createElement", "cloneElement"]),
|
||||
reactDom: this.WebpackModules.findByUniqueProperties(["findDOMNode"])
|
||||
};
|
||||
this.getInternalInstance = e => e[Object.keys(e).find(k => k.startsWith("__reactInternalInstance"))];
|
||||
window.Lightcord.BetterDiscord.V2 = this
|
||||
}
|
||||
|
||||
initialize() {
|
||||
|
||||
}
|
||||
|
||||
joinBD1() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("0Tmfo5ZbORCRqbAd");}
|
||||
leaveBD1() {this.GuildActions.leaveGuild("86004744966914048");}
|
||||
|
||||
joinBD2() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("2HScm8j");}
|
||||
leaveBD2() {this.GuildActions.leaveGuild("280806472928198656");}
|
||||
|
||||
joinLC() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("7eFff2A");}
|
||||
leaveLC() {this.GuildActions.leaveGuild("705908350218666117");}
|
||||
|
||||
/**
|
||||
* @type {typeof React}
|
||||
*/
|
||||
get react() {return this.internal.react;}
|
||||
/**
|
||||
* @type {typeof React}
|
||||
*/
|
||||
get React() {return this.internal.react;}
|
||||
/**
|
||||
* @type {typeof import("react-dom")}
|
||||
*/
|
||||
get reactDom() {return this.internal.reactDom;}
|
||||
/**
|
||||
* @type {typeof import("react-dom")}
|
||||
*/
|
||||
get ReactDom() {return this.internal.reactDom;}
|
||||
/**
|
||||
* @type {typeof React.Component}
|
||||
*/
|
||||
get reactComponent() {return this.internal.react.Component;}
|
||||
/**
|
||||
* @type {typeof React.Component}
|
||||
*/
|
||||
get ReactComponent() {return this.internal.react.Component;}
|
||||
|
||||
get anchorClasses() {return this.WebpackModules.findByUniqueProperties(["anchorUnderlineOnHover"]) || {anchor: "anchor-3Z-8Bb", anchorUnderlineOnHover: "anchorUnderlineOnHover-2ESHQB"};}
|
||||
get slateEditorClasses() {return this.WebpackModules.findByUniqueProperties(["slateTextArea"]);}
|
||||
get messageClasses() {return this.WebpackModules.findByUniqueProperties(["message", "containerCozy"]);}
|
||||
get guildClasses() {
|
||||
const guildsWrapper = BDModules.get(e => e.wrapper && e.unreadMentionsBar)[0];
|
||||
const guilds = BDModules.get(e => e.guildsError && e.selected)[0]
|
||||
const pill = BDModules.get(e => e.blobContainer)[0]
|
||||
return Object.assign({}, guildsWrapper, guilds, pill);
|
||||
}
|
||||
|
||||
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 TimeFormatter() {return this.WebpackModules.findByUniqueProperties(["dateFormat"]);}
|
||||
get TooltipWrapper() {return this.WebpackModules.findByDisplayName("Tooltip");}
|
||||
get NativeModule() {return this.WebpackModules.findByUniqueProperties(["setBadge"]);}
|
||||
get InviteActions() {return this.WebpackModules.findByUniqueProperties(["acceptInvite"]);}
|
||||
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 KeyGenerator() {return this.WebpackModules.find(m => m.toString && /"binary"/.test(m.toString()));}
|
||||
get LayerStack() {return this.WebpackModules.findByUniqueProperties(["popLayer"]);}
|
||||
get UserStore() {return this.WebpackModules.findByUniqueProperties(["getCurrentUser"]);}
|
||||
get ChannelStore() {return this.WebpackModules.findByUniqueProperties(["getChannel"]);}
|
||||
get ChannelActions() {return this.WebpackModules.findByUniqueProperties(["openPrivateChannel"]);}
|
||||
get PrivateChannelActions() {return this.WebpackModules.findByUniqueProperties(["selectPrivateChannel"]);}
|
||||
|
||||
openDM(userId) {
|
||||
const selfId = this.UserStore.getCurrentUser().id;
|
||||
if (selfId == userId) return;
|
||||
const privateChannelId = this.ChannelStore.getDMFromUserId(userId);
|
||||
if (privateChannelId) return this.PrivateChannelActions.selectPrivateChannel(privateChannelId);
|
||||
this.ChannelActions.openPrivateChannel(selfId, userId);
|
||||
}
|
||||
|
||||
parseSettings(cat) {
|
||||
return Object.keys(settings).reduce((arr, key) => {
|
||||
const setting = settings[key];
|
||||
if (setting.cat === cat && setting.implemented && !setting.hidden) {
|
||||
setting.text = key;
|
||||
arr.push(setting);
|
||||
} return arr;
|
||||
}, []);
|
||||
}
|
||||
|
||||
import {settings} from "../0globals";
|
||||
import themeModule from "./themeModule";
|
||||
|
||||
export default new class V2 {
|
||||
|
||||
constructor() {
|
||||
this.editorDetached = false;
|
||||
this.WebpackModules = (() => {
|
||||
const req = webpackJsonp.push([[], {__extra_id__: (module, exports, req) => module.exports = req}, [["__extra_id__"]]]);
|
||||
delete req.m.__extra_id__;
|
||||
delete req.c.__extra_id__;
|
||||
|
||||
const shouldProtect = theModule => {
|
||||
if (theModule.remove && theModule.set && theModule.clear && theModule.get && !theModule.sort) return true;
|
||||
if (theModule.getToken || theModule.getEmail || theModule.showToken) return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
const protect = (theModule, isDefault) => {
|
||||
let mod = !isDefault ? theModule.default : theModule
|
||||
if(!mod)return theModule
|
||||
if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null;
|
||||
if (!mod.getToken && !mod.getEmail && !mod.showToken)return theModule
|
||||
|
||||
const proxy = new Proxy(mod, {
|
||||
getOwnPropertyDescriptor: function(obj, prop) {
|
||||
if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined;
|
||||
return Object.getOwnPropertyDescriptor(obj, prop);
|
||||
},
|
||||
get: function(obj, func) {
|
||||
if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa";
|
||||
if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com";
|
||||
if (func == "showToken" && obj.showToken) return () => true;
|
||||
if (func == "__proto__" && obj.__proto__) return proxy;
|
||||
|
||||
return obj[func];
|
||||
}
|
||||
});
|
||||
|
||||
if(!isDefault){
|
||||
return Object.assign({}, theModule, {default: proxy})
|
||||
}
|
||||
|
||||
return proxy;
|
||||
};
|
||||
|
||||
const find = (filter) => {
|
||||
for (const i in req.c) {
|
||||
if (req.c.hasOwnProperty(i)) {
|
||||
const m = req.c[i].exports;
|
||||
if (m && m.__esModule && m.default && filter(m.default)) return protect(m.default, true);
|
||||
if (m && filter(m)) return protect(m, false);
|
||||
}
|
||||
}
|
||||
// console.warn("Cannot find loaded module in cache");
|
||||
return null;
|
||||
};
|
||||
|
||||
const findAll = (filter) => {
|
||||
const modules = [];
|
||||
for (const i in req.c) {
|
||||
if (req.c.hasOwnProperty(i)) {
|
||||
const m = req.c[i].exports;
|
||||
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));
|
||||
}
|
||||
}
|
||||
return modules;
|
||||
};
|
||||
|
||||
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 findByDisplayName = (displayName) => find(module => module.displayName === displayName);
|
||||
|
||||
return {find, findAll, findByUniqueProperties, findByPrototypes, findByDisplayName};
|
||||
})();
|
||||
|
||||
this.internal = {
|
||||
react: this.WebpackModules.findByUniqueProperties(["Component", "PureComponent", "Children", "createElement", "cloneElement"]),
|
||||
reactDom: this.WebpackModules.findByUniqueProperties(["findDOMNode"])
|
||||
};
|
||||
this.getInternalInstance = e => e[Object.keys(e).find(k => k.startsWith("__reactInternalInstance"))];
|
||||
window.Lightcord.BetterDiscord.V2 = this
|
||||
}
|
||||
|
||||
initialize() {
|
||||
|
||||
}
|
||||
|
||||
joinBD1() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("0Tmfo5ZbORCRqbAd");}
|
||||
leaveBD1() {this.GuildActions.leaveGuild("86004744966914048");}
|
||||
|
||||
joinBD2() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("2HScm8j");}
|
||||
leaveBD2() {this.GuildActions.leaveGuild("280806472928198656");}
|
||||
|
||||
joinLC() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("7eFff2A");}
|
||||
leaveLC() {this.GuildActions.leaveGuild("705908350218666117");}
|
||||
|
||||
/**
|
||||
* @type {typeof React}
|
||||
*/
|
||||
get react() {return this.internal.react;}
|
||||
/**
|
||||
* @type {typeof React}
|
||||
*/
|
||||
get React() {return this.internal.react;}
|
||||
/**
|
||||
* @type {typeof import("react-dom")}
|
||||
*/
|
||||
get reactDom() {return this.internal.reactDom;}
|
||||
/**
|
||||
* @type {typeof import("react-dom")}
|
||||
*/
|
||||
get ReactDom() {return this.internal.reactDom;}
|
||||
/**
|
||||
* @type {typeof React.Component}
|
||||
*/
|
||||
get reactComponent() {return this.internal.react.Component;}
|
||||
/**
|
||||
* @type {typeof React.Component}
|
||||
*/
|
||||
get ReactComponent() {return this.internal.react.Component;}
|
||||
|
||||
get anchorClasses() {return this.WebpackModules.findByUniqueProperties(["anchorUnderlineOnHover"]) || {anchor: "anchor-3Z-8Bb", anchorUnderlineOnHover: "anchorUnderlineOnHover-2ESHQB"};}
|
||||
get slateEditorClasses() {return this.WebpackModules.findByUniqueProperties(["slateTextArea"]);}
|
||||
get messageClasses() {return this.WebpackModules.findByUniqueProperties(["message", "containerCozy"]);}
|
||||
get guildClasses() {
|
||||
const guildsWrapper = BDModules.get(e => e.wrapper && e.unreadMentionsBar)[0];
|
||||
const guilds = BDModules.get(e => e.guildsError && e.selected)[0]
|
||||
const pill = BDModules.get(e => e.blobContainer)[0]
|
||||
return Object.assign({}, guildsWrapper, guilds, pill);
|
||||
}
|
||||
|
||||
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 TimeFormatter() {return this.WebpackModules.findByUniqueProperties(["dateFormat"]);}
|
||||
get TooltipWrapper() {return this.WebpackModules.findByDisplayName("Tooltip");}
|
||||
get NativeModule() {return this.WebpackModules.findByUniqueProperties(["setBadge"]);}
|
||||
get InviteActions() {return this.WebpackModules.findByUniqueProperties(["acceptInvite"]);}
|
||||
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 KeyGenerator() {return this.WebpackModules.find(m => m.toString && /"binary"/.test(m.toString()));}
|
||||
get LayerStack() {return this.WebpackModules.findByUniqueProperties(["popLayer"]);}
|
||||
get UserStore() {return this.WebpackModules.findByUniqueProperties(["getCurrentUser"]);}
|
||||
get ChannelStore() {return this.WebpackModules.findByUniqueProperties(["getChannel"]);}
|
||||
get ChannelActions() {return this.WebpackModules.findByUniqueProperties(["openPrivateChannel"]);}
|
||||
get PrivateChannelActions() {return this.WebpackModules.findByUniqueProperties(["selectPrivateChannel"]);}
|
||||
|
||||
openDM(userId) {
|
||||
const selfId = this.UserStore.getCurrentUser().id;
|
||||
if (selfId == userId) return;
|
||||
const privateChannelId = this.ChannelStore.getDMFromUserId(userId);
|
||||
if (privateChannelId) return this.PrivateChannelActions.selectPrivateChannel(privateChannelId);
|
||||
this.ChannelActions.openPrivateChannel(selfId, userId);
|
||||
}
|
||||
|
||||
parseSettings(cat) {
|
||||
return Object.keys(settings).reduce((arr, key) => {
|
||||
const setting = settings[key];
|
||||
if (setting.cat === cat && setting.implemented && !setting.hidden) {
|
||||
setting.text = key;
|
||||
arr.push(setting);
|
||||
} return arr;
|
||||
}, []);
|
||||
}
|
||||
|
||||
};
|
|
@ -1,132 +1,132 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
import V2C_SettingsTitle from "./settingsTitle";
|
||||
|
||||
/**
|
||||
* @type {typeof import("react")}
|
||||
*/
|
||||
const React = BDV2.React;
|
||||
|
||||
let marginModule2 = BDModules.get(e => e.defaultMarginh5)[0]
|
||||
let colorModule = BDModules.get(e => e.colorStandard)[0]
|
||||
let sizeModule = BDModules.get(e => e.size32)[0]
|
||||
let scrollbarModule1 = BDModules.get(e => e.scrollbarGhostHairline)[0]
|
||||
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 sessionModule = BDModules.get(e => e.default && e.default.getSessions)[0].default
|
||||
const userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0].default
|
||||
|
||||
export default class V2C_AccountInfos extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
if(!marginModule2)marginModule2 = BDModules.get(e => e.defaultMarginh5)[0]
|
||||
if(!colorModule)colorModule = BDModules.get(e => e.colorStandard)[0]
|
||||
if(!sizeModule)sizeModule = BDModules.get(e => e.size32)[0]
|
||||
return [
|
||||
<V2C_SettingsTitle text="Account Infos"/>,
|
||||
<div>
|
||||
<h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}>
|
||||
Profile
|
||||
</h5>
|
||||
<CodeContent content={this.getProfileValue()} language="diff" />
|
||||
<h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}>
|
||||
Statistics
|
||||
</h5>
|
||||
<CodeContent content={this.getStatistics()} language="diff" />
|
||||
<h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}>
|
||||
Connected Sessions
|
||||
</h5>
|
||||
<CodeContent content={this.getSessionValue()} language="diff" />
|
||||
</div>,
|
||||
<div className={BDModules.get(e => e.marginBottom20)[0].marginBottom20}></div>
|
||||
]
|
||||
}
|
||||
|
||||
getSessionValue(){
|
||||
const sessionsRaw = sessionModule.getSessions()
|
||||
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"
|
||||
|
||||
return sessions.map(e => {
|
||||
return `+ id: ${e.sessionId}
|
||||
+ os: ${e.clientInfo.os[0].toUpperCase()+e.clientInfo.os.slice(1)}
|
||||
+ client: ${e.clientInfo.client}
|
||||
+ status: ${e.status}
|
||||
+ Activities: ${e.activities.length}`
|
||||
}).join("\n"+"-".repeat(38)+"\n")
|
||||
}
|
||||
|
||||
getProfileValue(){
|
||||
const user = userModule.getCurrentUser()
|
||||
/**
|
||||
* @type {Date}
|
||||
*/
|
||||
const createdAt = user.createdAt
|
||||
|
||||
let avatarURL = user.avatarURL
|
||||
if(user.avatar && user.avatar.startsWith("a_")){
|
||||
avatarURL = user.getAvatarURL("gif")
|
||||
}
|
||||
|
||||
if(avatarURL.startsWith("/")){
|
||||
avatarURL = "https://discord.com"+avatarURL
|
||||
}
|
||||
if(avatarURL.endsWith("?size=128")){
|
||||
avatarURL = avatarURL.replace("?size=128", "?size=4096")
|
||||
}
|
||||
|
||||
return `+ Username: ${user.username}
|
||||
+ Discriminator: ${user.discriminator}
|
||||
+ Tag: ${user.tag}
|
||||
+ ID: ${user.id}
|
||||
+ Avatar: ${user.avatar}
|
||||
+ 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
|
||||
+ Flags: ${user.flags}
|
||||
+ Has Nitro: ${user.hasPremiumSubscription ? "Yes" : "No"}
|
||||
- Email: ${user.email}
|
||||
- 2FA: ${user.mfaEnabled ? "Yes" : "No"}
|
||||
- Has Been On Mobile: ${user.mobile ? "Yes" : "No"}
|
||||
- Phone: ${user.phone || "None"}
|
||||
- Verified: ${user.verified}
|
||||
- Can See NSFW Channels: ${user.nsfwAllowed}`;
|
||||
}
|
||||
|
||||
getStatistics(){
|
||||
|
||||
const guilds = Object.values(GuildModule.getGuilds())
|
||||
const relations = Object.keys(relationShipModule.getRelationships())
|
||||
const friends = relations.filter(e => relationShipModule.isFriend(e))
|
||||
const blocked = relations.filter(e => relationShipModule.isBlocked(e))
|
||||
|
||||
return `+ Server Count: ${guilds.length} servers
|
||||
+ Relations: ${relations.length} relations
|
||||
+ Friends Count: ${friends.length} friends
|
||||
- Blocked Users Count: ${blocked.length} blocked users`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let hightlightJS = BDModules.get(e => e.highlight)[0]
|
||||
let messageModule1 = BDModules.get(e => e.markup)[0]
|
||||
let messageModule2 = BDModules.get(e => e.messageContent)[0]
|
||||
|
||||
class CodeContent extends React.Component {
|
||||
render(){
|
||||
if(!messageModule1)messageModule1 = BDModules.get(e => e.markup)[0]
|
||||
if(!messageModule2)messageModule2 = BDModules.get(e => e.messageContent)[0]
|
||||
if(!scrollbarModule1)scrollbarModule1 = BDModules.get(e => e.scrollbarGhostHairline)[0]
|
||||
if(!hightlightJS)hightlightJS = BDModules.get(e => e.listLanguages)[0]
|
||||
return (<div class={`${messageModule1.markup} ${messageModule2.messageContent}`}>
|
||||
<pre>
|
||||
<code class={`${scrollbarModule1.scrollbarGhostHairline} hljs`} dangerouslySetInnerHTML={{__html: hightlightJS.highlight(this.props.language, this.props.content).value}}>
|
||||
|
||||
</code>
|
||||
</pre>
|
||||
<div className={BDModules.get(e => e.marginBottom8)[0].marginBottom8}></div>
|
||||
</div>)
|
||||
}
|
||||
import BDV2 from "../modules/v2";
|
||||
import V2C_SettingsTitle from "./settingsTitle";
|
||||
|
||||
/**
|
||||
* @type {typeof import("react")}
|
||||
*/
|
||||
const React = BDV2.React;
|
||||
|
||||
let marginModule2 = BDModules.get(e => e.defaultMarginh5)[0]
|
||||
let colorModule = BDModules.get(e => e.colorStandard)[0]
|
||||
let sizeModule = BDModules.get(e => e.size32)[0]
|
||||
let scrollbarModule1 = BDModules.get(e => e.scrollbarGhostHairline)[0]
|
||||
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 sessionModule = BDModules.get(e => e.default && e.default.getSessions)[0].default
|
||||
const userModule = BDModules.get(e => e.default && e.default.getCurrentUser)[0].default
|
||||
|
||||
export default class V2C_AccountInfos extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
if(!marginModule2)marginModule2 = BDModules.get(e => e.defaultMarginh5)[0]
|
||||
if(!colorModule)colorModule = BDModules.get(e => e.colorStandard)[0]
|
||||
if(!sizeModule)sizeModule = BDModules.get(e => e.size32)[0]
|
||||
return [
|
||||
<V2C_SettingsTitle text="Account Infos"/>,
|
||||
<div>
|
||||
<h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}>
|
||||
Profile
|
||||
</h5>
|
||||
<CodeContent content={this.getProfileValue()} language="diff" />
|
||||
<h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}>
|
||||
Statistics
|
||||
</h5>
|
||||
<CodeContent content={this.getStatistics()} language="diff" />
|
||||
<h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}>
|
||||
Connected Sessions
|
||||
</h5>
|
||||
<CodeContent content={this.getSessionValue()} language="diff" />
|
||||
</div>,
|
||||
<div className={BDModules.get(e => e.marginBottom20)[0].marginBottom20}></div>
|
||||
]
|
||||
}
|
||||
|
||||
getSessionValue(){
|
||||
const sessionsRaw = sessionModule.getSessions()
|
||||
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"
|
||||
|
||||
return sessions.map(e => {
|
||||
return `+ id: ${e.sessionId}
|
||||
+ os: ${e.clientInfo.os[0].toUpperCase()+e.clientInfo.os.slice(1)}
|
||||
+ client: ${e.clientInfo.client}
|
||||
+ status: ${e.status}
|
||||
+ Activities: ${e.activities.length}`
|
||||
}).join("\n"+"-".repeat(38)+"\n")
|
||||
}
|
||||
|
||||
getProfileValue(){
|
||||
const user = userModule.getCurrentUser()
|
||||
/**
|
||||
* @type {Date}
|
||||
*/
|
||||
const createdAt = user.createdAt
|
||||
|
||||
let avatarURL = user.avatarURL
|
||||
if(user.avatar && user.avatar.startsWith("a_")){
|
||||
avatarURL = user.getAvatarURL("gif")
|
||||
}
|
||||
|
||||
if(avatarURL.startsWith("/")){
|
||||
avatarURL = "https://discord.com"+avatarURL
|
||||
}
|
||||
if(avatarURL.endsWith("?size=128")){
|
||||
avatarURL = avatarURL.replace("?size=128", "?size=4096")
|
||||
}
|
||||
|
||||
return `+ Username: ${user.username}
|
||||
+ Discriminator: ${user.discriminator}
|
||||
+ Tag: ${user.tag}
|
||||
+ ID: ${user.id}
|
||||
+ Avatar: ${user.avatar}
|
||||
+ 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
|
||||
+ Flags: ${user.flags}
|
||||
+ Has Nitro: ${user.hasPremiumSubscription ? "Yes" : "No"}
|
||||
- Email: ${user.email}
|
||||
- 2FA: ${user.mfaEnabled ? "Yes" : "No"}
|
||||
- Has Been On Mobile: ${user.mobile ? "Yes" : "No"}
|
||||
- Phone: ${user.phone || "None"}
|
||||
- Verified: ${user.verified}
|
||||
- Can See NSFW Channels: ${user.nsfwAllowed}`;
|
||||
}
|
||||
|
||||
getStatistics(){
|
||||
|
||||
const guilds = Object.values(GuildModule.getGuilds())
|
||||
const relations = Object.keys(relationShipModule.getRelationships())
|
||||
const friends = relations.filter(e => relationShipModule.isFriend(e))
|
||||
const blocked = relations.filter(e => relationShipModule.isBlocked(e))
|
||||
|
||||
return `+ Server Count: ${guilds.length} servers
|
||||
+ Relations: ${relations.length} relations
|
||||
+ Friends Count: ${friends.length} friends
|
||||
- Blocked Users Count: ${blocked.length} blocked users`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let hightlightJS = BDModules.get(e => e.highlight)[0]
|
||||
let messageModule1 = BDModules.get(e => e.markup)[0]
|
||||
let messageModule2 = BDModules.get(e => e.messageContent)[0]
|
||||
|
||||
class CodeContent extends React.Component {
|
||||
render(){
|
||||
if(!messageModule1)messageModule1 = BDModules.get(e => e.markup)[0]
|
||||
if(!messageModule2)messageModule2 = BDModules.get(e => e.messageContent)[0]
|
||||
if(!scrollbarModule1)scrollbarModule1 = BDModules.get(e => e.scrollbarGhostHairline)[0]
|
||||
if(!hightlightJS)hightlightJS = BDModules.get(e => e.listLanguages)[0]
|
||||
return (<div class={`${messageModule1.markup} ${messageModule2.messageContent}`}>
|
||||
<pre>
|
||||
<code class={`${scrollbarModule1.scrollbarGhostHairline} hljs`} dangerouslySetInnerHTML={{__html: hightlightJS.highlight(this.props.language, this.props.content).value}}>
|
||||
|
||||
</code>
|
||||
</pre>
|
||||
<div className={BDModules.get(e => e.marginBottom8)[0].marginBottom8}></div>
|
||||
</div>)
|
||||
}
|
||||
}
|
|
@ -1,292 +1,292 @@
|
|||
// Good luck to read my code, Even me can't read it properly.
|
||||
|
||||
import { stat } from "fs"
|
||||
import { uuidv4 } from "../modules/distant"
|
||||
import webpackModules from "../modules/webpackModules"
|
||||
import { remote } from "electron"
|
||||
import MarginTop from "./margintop"
|
||||
|
||||
let formModule
|
||||
export default class ApiPreview extends React.PureComponent {
|
||||
constructor(){
|
||||
super(...arguments)
|
||||
this.state = {
|
||||
states: []
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
if(!formModule)formModule = webpackModules.find(e => e.FormSection)
|
||||
/**
|
||||
* @type {Function[]}
|
||||
*/
|
||||
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])
|
||||
}).flat())]
|
||||
return [
|
||||
<formModule.FormSection tag="h2" title="Lightcord's Api Availlable components">
|
||||
<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.
|
||||
<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="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>
|
||||
<MarginTop></MarginTop>
|
||||
<Lightcord.Api.Components.inputs.Button color="brand" look="outlined" size="medium" hoverColor="green" onClick={() => {
|
||||
remote.shell.openExternal("https://lightcord.deroku.xyz/LightcordApi/docs")
|
||||
}} wrapper={false}>
|
||||
Documentation
|
||||
</Lightcord.Api.Components.inputs.Button>
|
||||
</formModule.FormSection>,
|
||||
allComponents.map(comp => {
|
||||
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}`
|
||||
return <ComponentPreview key={compPath} comp={comp} />
|
||||
})
|
||||
]
|
||||
}
|
||||
|
||||
get renders(){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class ComponentPreview extends React.Component {
|
||||
constructor(props){
|
||||
super(props)
|
||||
this.state = {
|
||||
tab: "preview",
|
||||
elem: props.comp,
|
||||
options: {}
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
const comp = this.props.comp
|
||||
let AllPreviews = []
|
||||
if(comp.AllPreviews)AllPreviews = comp.AllPreviews
|
||||
let state = this.state
|
||||
let getProps = () => {
|
||||
let final = {}
|
||||
AllPreviews.forEach(category => {
|
||||
final[Object.keys(category[0])[0]] = category[0][Object.keys(category[0])[0]]
|
||||
})
|
||||
Object.keys(state.options).forEach(key => {
|
||||
final[key] = AllPreviews.find(e => e.find(e => e[key]))[state.options[key]][key]
|
||||
})
|
||||
return final
|
||||
}
|
||||
let renderPreview = () => {
|
||||
return <div style={{margin: "20px"}}>
|
||||
<div style={{
|
||||
backgroundColor: "var(--background-secondary)",
|
||||
padding: "30px 30px",
|
||||
borderRadius: "8px"
|
||||
}} className="lc-tab-box-shadow">
|
||||
{React.createElement(comp, getProps())}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
let renderCode = () => {
|
||||
return <div style={{margin: "20px"}}>
|
||||
<div style={{
|
||||
backgroundColor: "var(--background-secondary)",
|
||||
padding: "30px 30px",
|
||||
borderRadius: "8px"
|
||||
}} className="lc-tab-box-shadow">
|
||||
<window.Lightcord.Api.Components.general.SettingSubTitle>
|
||||
JSX
|
||||
</window.Lightcord.Api.Components.general.SettingSubTitle>
|
||||
<window.Lightcord.Api.Components.general.ErrorCatcher>
|
||||
{React.createElement(() => {
|
||||
return <window.Lightcord.Api.Components.general.CodeBlock language="jsx" content={generateCode("jsx")}/>
|
||||
})}
|
||||
</window.Lightcord.Api.Components.general.ErrorCatcher>
|
||||
<window.Lightcord.Api.Components.general.SettingSubTitle>
|
||||
React
|
||||
</window.Lightcord.Api.Components.general.SettingSubTitle>
|
||||
<window.Lightcord.Api.Components.general.ErrorCatcher>
|
||||
{React.createElement(() => {
|
||||
return <window.Lightcord.Api.Components.general.CodeBlock language="js" content={generateCode("react")}/>
|
||||
})}
|
||||
</window.Lightcord.Api.Components.general.ErrorCatcher>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
let getStrForProp = (value, compPath, lang) => {
|
||||
if(typeof value === "string"){
|
||||
return value
|
||||
}else if(typeof value === "boolean"){
|
||||
return String(value)
|
||||
}else if(typeof value === "function"){
|
||||
return value.toString()
|
||||
}else if(typeof value === "object"){
|
||||
if(value && value.$$typeof && (value.$$typeof === Symbol.for("react.element") || value.$$typeof === 0xeac7)){
|
||||
if(compPath === "Lightcord.Api.Components.general.Tabs"){
|
||||
if(lang === "react"){
|
||||
return `React.createElement("div", {style: {
|
||||
marginTop: "20px", marginBottom: "20px"
|
||||
}},
|
||||
React.createElement("div", {style: {
|
||||
backgroundColor: "var(--background-secondary)",
|
||||
padding: "30px 30px",
|
||||
borderRadius: "8px"
|
||||
}, className: "lc-tab-box-shadow" },
|
||||
React.createElement(Lightcord.Api.Components.general.Title, null, "Preview tabs")
|
||||
)
|
||||
)`
|
||||
}else if(lang === "jsx"){
|
||||
return `<div style={{
|
||||
marginTop: "20px", marginBottom: "20px"
|
||||
}}>
|
||||
<div style={{
|
||||
backgroundColor: "var(--background-secondary)",
|
||||
padding: "30px 30px",
|
||||
borderRadius: "8px"
|
||||
}} className="lc-tab-box-shadow">
|
||||
<Lightcord.Api.Components.general.Title>Preview tabs</Lightcord.Api.Components.general.Title>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
}
|
||||
return "Your components here."
|
||||
}
|
||||
return JSON.stringify(value, null, " ")
|
||||
}else if(typeof value === "number"){
|
||||
return String(value)
|
||||
}
|
||||
return String(value)
|
||||
}
|
||||
let generateCode = function(lang){ // code formatting is hard
|
||||
const compName = comp.displayName || comp.name
|
||||
let categories = Object.keys(window.Lightcord.Api.Components)
|
||||
const compCategory = categories.find(e => window.Lightcord.Api.Components[e][compName])
|
||||
const compPath = `Lightcord.Api.Components.${compCategory}.${compName}`
|
||||
const props = getProps()
|
||||
|
||||
if(lang === "jsx"){
|
||||
let propStrings = []
|
||||
let childrenProp = null
|
||||
Object.keys(props).forEach(key => {
|
||||
if(key == "children"){
|
||||
childrenProp = getStrForProp(props[key], compPath, lang)
|
||||
}else{
|
||||
let str = key+"="
|
||||
if(typeof props[key] === "string"){
|
||||
str += JSON.stringify(props[key])
|
||||
}else{
|
||||
str += `{${getStrForProp(props[key], compPath, lang)}}`
|
||||
}
|
||||
propStrings.push(str)
|
||||
}
|
||||
})
|
||||
let openTag
|
||||
if(childrenProp){
|
||||
openTag = `<${compPath} ${propStrings.join(" ")}>`
|
||||
let closeTag = `</${compPath}>`
|
||||
return `${openTag}\n ${childrenProp}\n${closeTag}`
|
||||
}else{
|
||||
openTag = `<${compPath} ${propStrings.join(" ")}/>`
|
||||
return openTag
|
||||
}
|
||||
}else if(lang === "react"){
|
||||
let children = props.children || null
|
||||
delete props.children
|
||||
if(children && children.$$typeof && (children.$$typeof === Symbol.for("react.element") || children.$$typeof === 0xeac7)){
|
||||
children = getStrForProp(children, compPath, lang)
|
||||
}
|
||||
let propStrings = []
|
||||
Object.keys(props).forEach(key => {
|
||||
let visibleKey = /[^\w\d_]/g.test(key) ? JSON.stringify(key) : key
|
||||
let str = visibleKey+": "
|
||||
if(typeof props[key] === "string"){
|
||||
str += JSON.stringify(props[key])
|
||||
}else{
|
||||
str += getStrForProp(props[key], compPath, lang).split("\n").map((str, i) => {
|
||||
if(i === 0)return str
|
||||
return " " + str
|
||||
}).join("\n")
|
||||
}
|
||||
propStrings.push(str)
|
||||
})
|
||||
let propObject = "{"
|
||||
if(propStrings.length){
|
||||
propStrings.forEach((str, i) => {
|
||||
let isLast = i === propStrings.length - 1
|
||||
let isFirst = i === 0
|
||||
if(!isFirst){
|
||||
propObject += ","
|
||||
}
|
||||
propObject += "\n "
|
||||
propObject += str
|
||||
if(isLast){
|
||||
propObject +="\n}"
|
||||
}
|
||||
})
|
||||
}else{
|
||||
propObject += "}"
|
||||
}
|
||||
let childrenData = typeof children === "string" && children.startsWith("React.createElement") ? children : JSON.stringify(children)
|
||||
return `React.createElement(${compPath}, ${propObject}, ${childrenData})`
|
||||
}
|
||||
}
|
||||
let help = comp.help || {}
|
||||
let info = help.info ? <window.Lightcord.Api.Components.general.AlertBox type="info">
|
||||
{help.info}
|
||||
</window.Lightcord.Api.Components.general.AlertBox> : null
|
||||
let warn = help.warn ? <window.Lightcord.Api.Components.general.AlertBox type="warn">
|
||||
{help.warn}
|
||||
</window.Lightcord.Api.Components.general.AlertBox> : null
|
||||
let danger = help.danger ? <window.Lightcord.Api.Components.general.AlertBox type="danger">
|
||||
{help.danger}
|
||||
</window.Lightcord.Api.Components.general.AlertBox> : null
|
||||
let error = help.error ? <window.Lightcord.Api.Components.general.AlertBox type="error">
|
||||
{help.error}
|
||||
</window.Lightcord.Api.Components.general.AlertBox> : null
|
||||
let success = help.success ? <window.Lightcord.Api.Components.general.AlertBox type="success">
|
||||
{help.success}
|
||||
</window.Lightcord.Api.Components.general.AlertBox> : null
|
||||
return (<div>
|
||||
<window.Lightcord.Api.Components.general.SettingsTitle>
|
||||
{comp.displayName || comp.name}
|
||||
</window.Lightcord.Api.Components.general.SettingsTitle>
|
||||
{info}
|
||||
{success}
|
||||
{warn}
|
||||
{error}
|
||||
{danger}
|
||||
{AllPreviews.map(category => {
|
||||
if(category[0].onClick)return null
|
||||
if(category[0].text)return null
|
||||
if(category[0].children)return null
|
||||
if(category.length === 1)return null
|
||||
|
||||
let key = Object.keys(category[0])[0]
|
||||
return [
|
||||
<window.Lightcord.Api.Components.general.SettingSubTitle>
|
||||
{key}
|
||||
</window.Lightcord.Api.Components.general.SettingSubTitle>,
|
||||
<window.Lightcord.Api.Components.inputs.Dropdown options={category.map((e, index) => {
|
||||
return {
|
||||
value: "opt-"+index,
|
||||
label: JSON.stringify(e[Object.keys(e)[0]])
|
||||
}
|
||||
})} value={"opt-"+(state.options[key] || "0")} onChange={(value) => {
|
||||
this.setState({
|
||||
options: Object.assign({}, state.options, {
|
||||
[key]: (value.value || "0").replace("opt-", "")
|
||||
})
|
||||
})
|
||||
}} searchable={true}/>,
|
||||
<div style={{marginBottom: "8px"}}></div>
|
||||
]
|
||||
})}
|
||||
<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) => {
|
||||
this.setState({
|
||||
tab
|
||||
})
|
||||
}}/>
|
||||
</div>)
|
||||
}
|
||||
// Good luck to read my code, Even me can't read it properly.
|
||||
|
||||
import { stat } from "fs"
|
||||
import { uuidv4 } from "../modules/distant"
|
||||
import webpackModules from "../modules/webpackModules"
|
||||
import { remote } from "electron"
|
||||
import MarginTop from "./margintop"
|
||||
|
||||
let formModule
|
||||
export default class ApiPreview extends React.PureComponent {
|
||||
constructor(){
|
||||
super(...arguments)
|
||||
this.state = {
|
||||
states: []
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
if(!formModule)formModule = webpackModules.find(e => e.FormSection)
|
||||
/**
|
||||
* @type {Function[]}
|
||||
*/
|
||||
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])
|
||||
}).flat())]
|
||||
return [
|
||||
<formModule.FormSection tag="h2" title="Lightcord's Api Availlable components">
|
||||
<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.
|
||||
<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="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>
|
||||
<MarginTop></MarginTop>
|
||||
<Lightcord.Api.Components.inputs.Button color="brand" look="outlined" size="medium" hoverColor="green" onClick={() => {
|
||||
remote.shell.openExternal("https://lightcord.deroku.xyz/LightcordApi/docs")
|
||||
}} wrapper={false}>
|
||||
Documentation
|
||||
</Lightcord.Api.Components.inputs.Button>
|
||||
</formModule.FormSection>,
|
||||
allComponents.map(comp => {
|
||||
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}`
|
||||
return <ComponentPreview key={compPath} comp={comp} />
|
||||
})
|
||||
]
|
||||
}
|
||||
|
||||
get renders(){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class ComponentPreview extends React.Component {
|
||||
constructor(props){
|
||||
super(props)
|
||||
this.state = {
|
||||
tab: "preview",
|
||||
elem: props.comp,
|
||||
options: {}
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
const comp = this.props.comp
|
||||
let AllPreviews = []
|
||||
if(comp.AllPreviews)AllPreviews = comp.AllPreviews
|
||||
let state = this.state
|
||||
let getProps = () => {
|
||||
let final = {}
|
||||
AllPreviews.forEach(category => {
|
||||
final[Object.keys(category[0])[0]] = category[0][Object.keys(category[0])[0]]
|
||||
})
|
||||
Object.keys(state.options).forEach(key => {
|
||||
final[key] = AllPreviews.find(e => e.find(e => e[key]))[state.options[key]][key]
|
||||
})
|
||||
return final
|
||||
}
|
||||
let renderPreview = () => {
|
||||
return <div style={{margin: "20px"}}>
|
||||
<div style={{
|
||||
backgroundColor: "var(--background-secondary)",
|
||||
padding: "30px 30px",
|
||||
borderRadius: "8px"
|
||||
}} className="lc-tab-box-shadow">
|
||||
{React.createElement(comp, getProps())}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
let renderCode = () => {
|
||||
return <div style={{margin: "20px"}}>
|
||||
<div style={{
|
||||
backgroundColor: "var(--background-secondary)",
|
||||
padding: "30px 30px",
|
||||
borderRadius: "8px"
|
||||
}} className="lc-tab-box-shadow">
|
||||
<window.Lightcord.Api.Components.general.SettingSubTitle>
|
||||
JSX
|
||||
</window.Lightcord.Api.Components.general.SettingSubTitle>
|
||||
<window.Lightcord.Api.Components.general.ErrorCatcher>
|
||||
{React.createElement(() => {
|
||||
return <window.Lightcord.Api.Components.general.CodeBlock language="jsx" content={generateCode("jsx")}/>
|
||||
})}
|
||||
</window.Lightcord.Api.Components.general.ErrorCatcher>
|
||||
<window.Lightcord.Api.Components.general.SettingSubTitle>
|
||||
React
|
||||
</window.Lightcord.Api.Components.general.SettingSubTitle>
|
||||
<window.Lightcord.Api.Components.general.ErrorCatcher>
|
||||
{React.createElement(() => {
|
||||
return <window.Lightcord.Api.Components.general.CodeBlock language="js" content={generateCode("react")}/>
|
||||
})}
|
||||
</window.Lightcord.Api.Components.general.ErrorCatcher>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
let getStrForProp = (value, compPath, lang) => {
|
||||
if(typeof value === "string"){
|
||||
return value
|
||||
}else if(typeof value === "boolean"){
|
||||
return String(value)
|
||||
}else if(typeof value === "function"){
|
||||
return value.toString()
|
||||
}else if(typeof value === "object"){
|
||||
if(value && value.$$typeof && (value.$$typeof === Symbol.for("react.element") || value.$$typeof === 0xeac7)){
|
||||
if(compPath === "Lightcord.Api.Components.general.Tabs"){
|
||||
if(lang === "react"){
|
||||
return `React.createElement("div", {style: {
|
||||
marginTop: "20px", marginBottom: "20px"
|
||||
}},
|
||||
React.createElement("div", {style: {
|
||||
backgroundColor: "var(--background-secondary)",
|
||||
padding: "30px 30px",
|
||||
borderRadius: "8px"
|
||||
}, className: "lc-tab-box-shadow" },
|
||||
React.createElement(Lightcord.Api.Components.general.Title, null, "Preview tabs")
|
||||
)
|
||||
)`
|
||||
}else if(lang === "jsx"){
|
||||
return `<div style={{
|
||||
marginTop: "20px", marginBottom: "20px"
|
||||
}}>
|
||||
<div style={{
|
||||
backgroundColor: "var(--background-secondary)",
|
||||
padding: "30px 30px",
|
||||
borderRadius: "8px"
|
||||
}} className="lc-tab-box-shadow">
|
||||
<Lightcord.Api.Components.general.Title>Preview tabs</Lightcord.Api.Components.general.Title>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
}
|
||||
return "Your components here."
|
||||
}
|
||||
return JSON.stringify(value, null, " ")
|
||||
}else if(typeof value === "number"){
|
||||
return String(value)
|
||||
}
|
||||
return String(value)
|
||||
}
|
||||
let generateCode = function(lang){ // code formatting is hard
|
||||
const compName = comp.displayName || comp.name
|
||||
let categories = Object.keys(window.Lightcord.Api.Components)
|
||||
const compCategory = categories.find(e => window.Lightcord.Api.Components[e][compName])
|
||||
const compPath = `Lightcord.Api.Components.${compCategory}.${compName}`
|
||||
const props = getProps()
|
||||
|
||||
if(lang === "jsx"){
|
||||
let propStrings = []
|
||||
let childrenProp = null
|
||||
Object.keys(props).forEach(key => {
|
||||
if(key == "children"){
|
||||
childrenProp = getStrForProp(props[key], compPath, lang)
|
||||
}else{
|
||||
let str = key+"="
|
||||
if(typeof props[key] === "string"){
|
||||
str += JSON.stringify(props[key])
|
||||
}else{
|
||||
str += `{${getStrForProp(props[key], compPath, lang)}}`
|
||||
}
|
||||
propStrings.push(str)
|
||||
}
|
||||
})
|
||||
let openTag
|
||||
if(childrenProp){
|
||||
openTag = `<${compPath} ${propStrings.join(" ")}>`
|
||||
let closeTag = `</${compPath}>`
|
||||
return `${openTag}\n ${childrenProp}\n${closeTag}`
|
||||
}else{
|
||||
openTag = `<${compPath} ${propStrings.join(" ")}/>`
|
||||
return openTag
|
||||
}
|
||||
}else if(lang === "react"){
|
||||
let children = props.children || null
|
||||
delete props.children
|
||||
if(children && children.$$typeof && (children.$$typeof === Symbol.for("react.element") || children.$$typeof === 0xeac7)){
|
||||
children = getStrForProp(children, compPath, lang)
|
||||
}
|
||||
let propStrings = []
|
||||
Object.keys(props).forEach(key => {
|
||||
let visibleKey = /[^\w\d_]/g.test(key) ? JSON.stringify(key) : key
|
||||
let str = visibleKey+": "
|
||||
if(typeof props[key] === "string"){
|
||||
str += JSON.stringify(props[key])
|
||||
}else{
|
||||
str += getStrForProp(props[key], compPath, lang).split("\n").map((str, i) => {
|
||||
if(i === 0)return str
|
||||
return " " + str
|
||||
}).join("\n")
|
||||
}
|
||||
propStrings.push(str)
|
||||
})
|
||||
let propObject = "{"
|
||||
if(propStrings.length){
|
||||
propStrings.forEach((str, i) => {
|
||||
let isLast = i === propStrings.length - 1
|
||||
let isFirst = i === 0
|
||||
if(!isFirst){
|
||||
propObject += ","
|
||||
}
|
||||
propObject += "\n "
|
||||
propObject += str
|
||||
if(isLast){
|
||||
propObject +="\n}"
|
||||
}
|
||||
})
|
||||
}else{
|
||||
propObject += "}"
|
||||
}
|
||||
let childrenData = typeof children === "string" && children.startsWith("React.createElement") ? children : JSON.stringify(children)
|
||||
return `React.createElement(${compPath}, ${propObject}, ${childrenData})`
|
||||
}
|
||||
}
|
||||
let help = comp.help || {}
|
||||
let info = help.info ? <window.Lightcord.Api.Components.general.AlertBox type="info">
|
||||
{help.info}
|
||||
</window.Lightcord.Api.Components.general.AlertBox> : null
|
||||
let warn = help.warn ? <window.Lightcord.Api.Components.general.AlertBox type="warn">
|
||||
{help.warn}
|
||||
</window.Lightcord.Api.Components.general.AlertBox> : null
|
||||
let danger = help.danger ? <window.Lightcord.Api.Components.general.AlertBox type="danger">
|
||||
{help.danger}
|
||||
</window.Lightcord.Api.Components.general.AlertBox> : null
|
||||
let error = help.error ? <window.Lightcord.Api.Components.general.AlertBox type="error">
|
||||
{help.error}
|
||||
</window.Lightcord.Api.Components.general.AlertBox> : null
|
||||
let success = help.success ? <window.Lightcord.Api.Components.general.AlertBox type="success">
|
||||
{help.success}
|
||||
</window.Lightcord.Api.Components.general.AlertBox> : null
|
||||
return (<div>
|
||||
<window.Lightcord.Api.Components.general.SettingsTitle>
|
||||
{comp.displayName || comp.name}
|
||||
</window.Lightcord.Api.Components.general.SettingsTitle>
|
||||
{info}
|
||||
{success}
|
||||
{warn}
|
||||
{error}
|
||||
{danger}
|
||||
{AllPreviews.map(category => {
|
||||
if(category[0].onClick)return null
|
||||
if(category[0].text)return null
|
||||
if(category[0].children)return null
|
||||
if(category.length === 1)return null
|
||||
|
||||
let key = Object.keys(category[0])[0]
|
||||
return [
|
||||
<window.Lightcord.Api.Components.general.SettingSubTitle>
|
||||
{key}
|
||||
</window.Lightcord.Api.Components.general.SettingSubTitle>,
|
||||
<window.Lightcord.Api.Components.inputs.Dropdown options={category.map((e, index) => {
|
||||
return {
|
||||
value: "opt-"+index,
|
||||
label: JSON.stringify(e[Object.keys(e)[0]])
|
||||
}
|
||||
})} value={"opt-"+(state.options[key] || "0")} onChange={(value) => {
|
||||
this.setState({
|
||||
options: Object.assign({}, state.options, {
|
||||
[key]: (value.value || "0").replace("opt-", "")
|
||||
})
|
||||
})
|
||||
}} searchable={true}/>,
|
||||
<div style={{marginBottom: "8px"}}></div>
|
||||
]
|
||||
})}
|
||||
<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) => {
|
||||
this.setState({
|
||||
tab
|
||||
})
|
||||
}}/>
|
||||
</div>)
|
||||
}
|
||||
}
|
|
@ -1,242 +1,242 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "../modules/v2";
|
||||
import Utils from "../modules/utils";
|
||||
import DOM from "../modules/domtools";
|
||||
|
||||
import XSvg from "./xSvg";
|
||||
import ReloadIcon from "./reloadIcon";
|
||||
import EditIcon from "./icons/edit";
|
||||
import DeleteIcon from "./icons/delete";
|
||||
import Switch from "./components/switch";
|
||||
import TooltipWrap from "./tooltipWrap";
|
||||
import { processFile } from "../modules/pluginCertifier";
|
||||
import contentManager from "../modules/contentManager";
|
||||
import { resolve } from "path";
|
||||
|
||||
const React = BDV2.React;
|
||||
const anchorClasses = BDV2.anchorClasses;
|
||||
|
||||
export default class V2C_PluginCard extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.showSettings = this.showSettings.bind(this);
|
||||
this.setInitialState();
|
||||
this.hasSettings = this.props.addon.plugin && typeof(this.props.addon.plugin.getSettingsPanel) === "function";
|
||||
this.settingsPanel = "";
|
||||
|
||||
this.edit = this.edit.bind(this);
|
||||
this.delete = this.delete.bind(this);
|
||||
this.reload = this.reload.bind(this);
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
this.state = {
|
||||
checked: this.props.enabled,
|
||||
settings: false,
|
||||
reloads: 0,
|
||||
trusted: false
|
||||
};
|
||||
}
|
||||
|
||||
showSettings() {
|
||||
if (!this.hasSettings) return;
|
||||
this.setState({settings: true});
|
||||
}
|
||||
|
||||
closeSettings() {
|
||||
this.panelRef.current.innerHTML = "";
|
||||
this.setState({settingsOpen: false});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (!this.state.settings) return;
|
||||
if (typeof this.settingsPanel === "object") {
|
||||
this.refs.settingspanel.appendChild(this.settingsPanel);
|
||||
}
|
||||
|
||||
if (!settingsCookie["fork-ps-3"]) return;
|
||||
setImmediate(() => {
|
||||
const isHidden = (container, element) => {
|
||||
if(!container){
|
||||
console.error(new Error(`Container is undefined.`))
|
||||
return false
|
||||
}
|
||||
const cTop = container.scrollTop;
|
||||
const cBottom = cTop + container.clientHeight;
|
||||
const eTop = element.offsetTop;
|
||||
const eBottom = eTop + element.clientHeight;
|
||||
return (eTop < cTop || eBottom > cBottom);
|
||||
};
|
||||
|
||||
const thisNode = this.refs.cardNode;
|
||||
const container = thisNode.closest("div[class*=\"contentRegionScroller-\"]")
|
||||
if (!isHidden(container, thisNode)) return;
|
||||
const thisNodeOffset = DOM.offset(thisNode);
|
||||
const containerOffset = DOM.offset(container);
|
||||
const original = container.scrollTop;
|
||||
const endPoint = thisNodeOffset.top - containerOffset.top + container.scrollTop - 30;
|
||||
DOM.animate({
|
||||
duration: 300,
|
||||
update: function(progress) {
|
||||
if (endPoint > original) container.scrollTop = original + (progress * (endPoint - original));
|
||||
else container.scrollTop = original - (progress * (original - endPoint));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
getString(value) {
|
||||
if (!value) return "???";
|
||||
return typeof value == "string" ? value : value.toString();
|
||||
}
|
||||
|
||||
get settingsComponent() {
|
||||
try { this.settingsPanel = this.props.addon.plugin.getSettingsPanel(); }
|
||||
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"},
|
||||
BDV2.react.createElement("div", {style: {"float": "right", "cursor": "pointer"}, onClick: () => {
|
||||
this.refs.settingspanel.innerHTML = "";
|
||||
this.setState({settings: false});
|
||||
}},
|
||||
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", dangerouslySetInnerHTML: {__html: this.settingsPanel}})
|
||||
);
|
||||
}
|
||||
|
||||
buildTitle(name, version, author) {
|
||||
const title = "{{name}} v{{version}} by {{author}}".split(/({{[A-Za-z]+}})/);
|
||||
const nameIndex = title.findIndex(s => s == "{{name}}");
|
||||
if (nameIndex) title[nameIndex] = React.createElement("span", {className: "name bda-name"}, name);
|
||||
const versionIndex = title.findIndex(s => s == "{{version}}");
|
||||
if (nameIndex) title[versionIndex] = React.createElement("span", {className: "version bda-version"}, version);
|
||||
const authorIndex = title.findIndex(s => s == "{{author}}");
|
||||
if (nameIndex) {
|
||||
const props = {className: "author bda-author"};
|
||||
if (author.link || author.id) {
|
||||
props.className += ` ${anchorClasses.anchor} ${anchorClasses.anchorUnderlineOnHover}`;
|
||||
props.target = "_blank";
|
||||
|
||||
if (author.link) props.href = author.link;
|
||||
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);
|
||||
}
|
||||
return title.flat();
|
||||
}
|
||||
|
||||
makeLink(title, url) {
|
||||
const props = {className: "bda-link bda-link-website", target: "_blank"};
|
||||
if (typeof(url) == "string") props.href = url;
|
||||
if (typeof(url) == "function") props.onClick = (event) => {event.preventDefault(); event.stopPropagation(); url();};
|
||||
return BDV2.react.createElement("a", props, title);
|
||||
}
|
||||
|
||||
makeButton(title, children, action) {
|
||||
return <TooltipWrap color="black" side="top" text={title}>
|
||||
<div className="bd-addon-button" onClick={action}>{children}</div>
|
||||
</TooltipWrap>;
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
this.unmounted = true
|
||||
}
|
||||
|
||||
get links() {
|
||||
const links = [];
|
||||
const addon = this.props.addon;
|
||||
if (addon.website) links.push(this.makeLink("Website", addon.website));
|
||||
if (addon.source) links.push(this.makeLink("Source", addon.source));
|
||||
if (addon.invite) {
|
||||
links.push(this.makeLink("Support Server", () => {
|
||||
const tester = /\.gg\/(.*)$/;
|
||||
let code = addon.invite;
|
||||
if (tester.test(code)) code = code.match(tester)[1];
|
||||
BDV2.LayerStack.popLayer();
|
||||
BDV2.InviteActions.acceptInviteAndTransitionToInviteChannel(code);
|
||||
}));
|
||||
}
|
||||
if (addon.donate) links.push(this.makeLink("Donate", addon.donate));
|
||||
if (addon.patreon) links.push(this.makeLink("Patreon", addon.patreon));
|
||||
return links;
|
||||
}
|
||||
|
||||
get footer() {
|
||||
const links = this.links;
|
||||
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"},
|
||||
...(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")
|
||||
);
|
||||
}
|
||||
|
||||
onChange() {
|
||||
this.props.toggle && this.props.toggle(this.name);
|
||||
this.setState({checked: !this.state.checked});
|
||||
}
|
||||
|
||||
edit() {this.props.edit(this.name);}
|
||||
delete() {this.props.remove(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 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 version() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getVersion() : this.props.addon.version);}
|
||||
|
||||
render() {
|
||||
if (this.state.settings) return this.settingsComponent;
|
||||
const {authorId, authorLink} = this.props.addon;
|
||||
|
||||
const style = {}
|
||||
if(settingsCookie["fork-ps-6"]){
|
||||
if(!this.isScanning){
|
||||
this.isScanning = true
|
||||
processFile(resolve(this.props.addon.filename.endsWith(".plugin.js") ? contentManager.pluginsFolder : contentManager.themesFolder, this.props.addon.filename), (result) => {
|
||||
if(this.unmounted)return
|
||||
|
||||
this.setState({
|
||||
isTrusted: result.suspect ? "suspect" : true
|
||||
})
|
||||
}, () => {})
|
||||
}else{
|
||||
if(this.state.isTrusted === true){
|
||||
style.borderColor = "#4087ed"
|
||||
}
|
||||
if(this.state.isTrusted === "suspect"){
|
||||
style.borderColor = "rgb(240, 71, 71)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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-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"},
|
||||
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.reload && this.makeButton("Reload", <ReloadIcon className="bd-icon" />, this.reload),
|
||||
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 bd-addon-description bda-description scroller"}, this.description)
|
||||
),
|
||||
this.footer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const originalRender = V2C_PluginCard.prototype.render;
|
||||
Object.defineProperty(V2C_PluginCard.prototype, "render", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");},
|
||||
get: () => originalRender
|
||||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "../modules/v2";
|
||||
import Utils from "../modules/utils";
|
||||
import DOM from "../modules/domtools";
|
||||
|
||||
import XSvg from "./xSvg";
|
||||
import ReloadIcon from "./reloadIcon";
|
||||
import EditIcon from "./icons/edit";
|
||||
import DeleteIcon from "./icons/delete";
|
||||
import Switch from "./components/switch";
|
||||
import TooltipWrap from "./tooltipWrap";
|
||||
import { processFile } from "../modules/pluginCertifier";
|
||||
import contentManager from "../modules/contentManager";
|
||||
import { resolve } from "path";
|
||||
|
||||
const React = BDV2.React;
|
||||
const anchorClasses = BDV2.anchorClasses;
|
||||
|
||||
export default class V2C_PluginCard extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.showSettings = this.showSettings.bind(this);
|
||||
this.setInitialState();
|
||||
this.hasSettings = this.props.addon.plugin && typeof(this.props.addon.plugin.getSettingsPanel) === "function";
|
||||
this.settingsPanel = "";
|
||||
|
||||
this.edit = this.edit.bind(this);
|
||||
this.delete = this.delete.bind(this);
|
||||
this.reload = this.reload.bind(this);
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
this.state = {
|
||||
checked: this.props.enabled,
|
||||
settings: false,
|
||||
reloads: 0,
|
||||
trusted: false
|
||||
};
|
||||
}
|
||||
|
||||
showSettings() {
|
||||
if (!this.hasSettings) return;
|
||||
this.setState({settings: true});
|
||||
}
|
||||
|
||||
closeSettings() {
|
||||
this.panelRef.current.innerHTML = "";
|
||||
this.setState({settingsOpen: false});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (!this.state.settings) return;
|
||||
if (typeof this.settingsPanel === "object") {
|
||||
this.refs.settingspanel.appendChild(this.settingsPanel);
|
||||
}
|
||||
|
||||
if (!settingsCookie["fork-ps-3"]) return;
|
||||
setImmediate(() => {
|
||||
const isHidden = (container, element) => {
|
||||
if(!container){
|
||||
console.error(new Error(`Container is undefined.`))
|
||||
return false
|
||||
}
|
||||
const cTop = container.scrollTop;
|
||||
const cBottom = cTop + container.clientHeight;
|
||||
const eTop = element.offsetTop;
|
||||
const eBottom = eTop + element.clientHeight;
|
||||
return (eTop < cTop || eBottom > cBottom);
|
||||
};
|
||||
|
||||
const thisNode = this.refs.cardNode;
|
||||
const container = thisNode.closest("div[class*=\"contentRegionScroller-\"]")
|
||||
if (!isHidden(container, thisNode)) return;
|
||||
const thisNodeOffset = DOM.offset(thisNode);
|
||||
const containerOffset = DOM.offset(container);
|
||||
const original = container.scrollTop;
|
||||
const endPoint = thisNodeOffset.top - containerOffset.top + container.scrollTop - 30;
|
||||
DOM.animate({
|
||||
duration: 300,
|
||||
update: function(progress) {
|
||||
if (endPoint > original) container.scrollTop = original + (progress * (endPoint - original));
|
||||
else container.scrollTop = original - (progress * (original - endPoint));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
getString(value) {
|
||||
if (!value) return "???";
|
||||
return typeof value == "string" ? value : value.toString();
|
||||
}
|
||||
|
||||
get settingsComponent() {
|
||||
try { this.settingsPanel = this.props.addon.plugin.getSettingsPanel(); }
|
||||
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"},
|
||||
BDV2.react.createElement("div", {style: {"float": "right", "cursor": "pointer"}, onClick: () => {
|
||||
this.refs.settingspanel.innerHTML = "";
|
||||
this.setState({settings: false});
|
||||
}},
|
||||
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", dangerouslySetInnerHTML: {__html: this.settingsPanel}})
|
||||
);
|
||||
}
|
||||
|
||||
buildTitle(name, version, author) {
|
||||
const title = "{{name}} v{{version}} by {{author}}".split(/({{[A-Za-z]+}})/);
|
||||
const nameIndex = title.findIndex(s => s == "{{name}}");
|
||||
if (nameIndex) title[nameIndex] = React.createElement("span", {className: "name bda-name"}, name);
|
||||
const versionIndex = title.findIndex(s => s == "{{version}}");
|
||||
if (nameIndex) title[versionIndex] = React.createElement("span", {className: "version bda-version"}, version);
|
||||
const authorIndex = title.findIndex(s => s == "{{author}}");
|
||||
if (nameIndex) {
|
||||
const props = {className: "author bda-author"};
|
||||
if (author.link || author.id) {
|
||||
props.className += ` ${anchorClasses.anchor} ${anchorClasses.anchorUnderlineOnHover}`;
|
||||
props.target = "_blank";
|
||||
|
||||
if (author.link) props.href = author.link;
|
||||
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);
|
||||
}
|
||||
return title.flat();
|
||||
}
|
||||
|
||||
makeLink(title, url) {
|
||||
const props = {className: "bda-link bda-link-website", target: "_blank"};
|
||||
if (typeof(url) == "string") props.href = url;
|
||||
if (typeof(url) == "function") props.onClick = (event) => {event.preventDefault(); event.stopPropagation(); url();};
|
||||
return BDV2.react.createElement("a", props, title);
|
||||
}
|
||||
|
||||
makeButton(title, children, action) {
|
||||
return <TooltipWrap color="black" side="top" text={title}>
|
||||
<div className="bd-addon-button" onClick={action}>{children}</div>
|
||||
</TooltipWrap>;
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
this.unmounted = true
|
||||
}
|
||||
|
||||
get links() {
|
||||
const links = [];
|
||||
const addon = this.props.addon;
|
||||
if (addon.website) links.push(this.makeLink("Website", addon.website));
|
||||
if (addon.source) links.push(this.makeLink("Source", addon.source));
|
||||
if (addon.invite) {
|
||||
links.push(this.makeLink("Support Server", () => {
|
||||
const tester = /\.gg\/(.*)$/;
|
||||
let code = addon.invite;
|
||||
if (tester.test(code)) code = code.match(tester)[1];
|
||||
BDV2.LayerStack.popLayer();
|
||||
BDV2.InviteActions.acceptInviteAndTransitionToInviteChannel(code);
|
||||
}));
|
||||
}
|
||||
if (addon.donate) links.push(this.makeLink("Donate", addon.donate));
|
||||
if (addon.patreon) links.push(this.makeLink("Patreon", addon.patreon));
|
||||
return links;
|
||||
}
|
||||
|
||||
get footer() {
|
||||
const links = this.links;
|
||||
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"},
|
||||
...(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")
|
||||
);
|
||||
}
|
||||
|
||||
onChange() {
|
||||
this.props.toggle && this.props.toggle(this.name);
|
||||
this.setState({checked: !this.state.checked});
|
||||
}
|
||||
|
||||
edit() {this.props.edit(this.name);}
|
||||
delete() {this.props.remove(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 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 version() {return this.getString(this.props.addon.plugin ? this.props.addon.plugin.getVersion() : this.props.addon.version);}
|
||||
|
||||
render() {
|
||||
if (this.state.settings) return this.settingsComponent;
|
||||
const {authorId, authorLink} = this.props.addon;
|
||||
|
||||
const style = {}
|
||||
if(settingsCookie["fork-ps-6"]){
|
||||
if(!this.isScanning){
|
||||
this.isScanning = true
|
||||
processFile(resolve(this.props.addon.filename.endsWith(".plugin.js") ? contentManager.pluginsFolder : contentManager.themesFolder, this.props.addon.filename), (result) => {
|
||||
if(this.unmounted)return
|
||||
|
||||
this.setState({
|
||||
isTrusted: result.suspect ? "suspect" : true
|
||||
})
|
||||
}, () => {})
|
||||
}else{
|
||||
if(this.state.isTrusted === true){
|
||||
style.borderColor = "#4087ed"
|
||||
}
|
||||
if(this.state.isTrusted === "suspect"){
|
||||
style.borderColor = "rgb(240, 71, 71)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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-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"},
|
||||
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.reload && this.makeButton("Reload", <ReloadIcon className="bd-icon" />, this.reload),
|
||||
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 bd-addon-description bda-description scroller"}, this.description)
|
||||
),
|
||||
this.footer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const originalRender = V2C_PluginCard.prototype.render;
|
||||
Object.defineProperty(V2C_PluginCard.prototype, "render", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");},
|
||||
get: () => originalRender
|
||||
});
|
|
@ -1,262 +1,262 @@
|
|||
import ErrorBoundary from "./errorBoundary";
|
||||
import ContentColumn from "./contentColumn";
|
||||
import ReloadIcon from "./reloadIcon";
|
||||
import AddonCard from "./addoncard";
|
||||
import Dropdown from "./components/dropdown";
|
||||
import Search from "./components/search";
|
||||
|
||||
import {settingsCookie, pluginCookie, themeCookie, bdplugins, bdthemes} from "../0globals";
|
||||
import ContentManager from "../modules/contentManager";
|
||||
import BDV2 from "../modules/v2";
|
||||
import pluginModule from "../modules/pluginModule";
|
||||
import themeModule from "../modules/themeModule";
|
||||
import WebpackModules from "../modules/webpackModules";
|
||||
import BdApi from "../modules/bdApi";
|
||||
import Utils from "../modules/utils";
|
||||
import TooltipWrap from "./tooltipWrap";
|
||||
import bdEvents from "../modules/bdEvents";
|
||||
import EmulatedTooltip from "./tooltip";
|
||||
|
||||
const Tooltip = WebpackModules.findByDisplayName("Tooltip");
|
||||
|
||||
const React = BDV2.react;
|
||||
|
||||
export default class CardList extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {sort: "name", ascending: true, query: ""};
|
||||
this.isPlugins = this.props.type == "plugins";
|
||||
this.cookie = this.isPlugins ? pluginCookie : themeCookie;
|
||||
this.manager = this.isPlugins ? pluginModule : themeModule;
|
||||
|
||||
this.sort = this.sort.bind(this);
|
||||
this.reverse = this.reverse.bind(this);
|
||||
this.search = this.search.bind(this);
|
||||
|
||||
this.onAddonChanges = function(){
|
||||
this.forceUpdate()
|
||||
}
|
||||
this.onAddonChanges = this.onAddonChanges.bind(this)
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
const type = (this.isPlugins ? "plugin" : "theme") + "-"
|
||||
bdEvents.on(`${type}loaded`, this.onAddonChanges)
|
||||
bdEvents.on(`${type}unloaded`, this.onAddonChanges)
|
||||
bdEvents.on(`${type}reloaded`, this.onAddonChanges)
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
const type = (this.isPlugins ? "plugin" : "theme") + "-"
|
||||
bdEvents.off(`${type}loaded`, this.onAddonChanges)
|
||||
bdEvents.off(`${type}unloaded`, this.onAddonChanges)
|
||||
bdEvents.off(`${type}reloaded`, this.onAddonChanges)
|
||||
}
|
||||
|
||||
openFolder() {
|
||||
const shell = require("electron").shell;
|
||||
const open = shell.openPath || shell.openItem;
|
||||
open(this.isPlugins ? ContentManager.pluginsFolder : ContentManager.themesFolder);
|
||||
}
|
||||
|
||||
edit(name) {
|
||||
console.log(name);
|
||||
this.manager.edit(name);
|
||||
}
|
||||
|
||||
async delete(name) {
|
||||
const shouldDelete = await this.confirmDelete(name);
|
||||
if (!shouldDelete) return;
|
||||
this.manager.delete(name);
|
||||
}
|
||||
|
||||
confirmDelete(name) {
|
||||
return new Promise(resolve => {
|
||||
BdApi.showConfirmationModal("Are You Sure?", `Are you sure you want to delete ${name}?`, {
|
||||
danger: true,
|
||||
confirmText: "Delete",
|
||||
onConfirm: () => {resolve(true);},
|
||||
onCancel: () => {resolve(false);}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get sortOptions() {
|
||||
return [
|
||||
{label: "Name", value: "name"},
|
||||
{label: "Author", value: "author"},
|
||||
{label: "Version", value: "version"},
|
||||
{label: "Recently Added", value: "added"},
|
||||
{label: "Last Modified", value: "modified"},
|
||||
{label: "File Size", value: "size"},
|
||||
];
|
||||
}
|
||||
|
||||
get directions() {
|
||||
return [
|
||||
{label: "Ascending", value: true},
|
||||
{label: "Descending", value: false}
|
||||
];
|
||||
}
|
||||
|
||||
reverse(value) {
|
||||
this.setState({ascending: value});
|
||||
}
|
||||
|
||||
sort(value) {
|
||||
this.setState({sort: value});
|
||||
}
|
||||
|
||||
search(event) {
|
||||
this.setState({query: event.target.value.toLocaleLowerCase()});
|
||||
}
|
||||
|
||||
getProps(addon) {
|
||||
return {
|
||||
key: this.getName(addon),
|
||||
enabled: this.cookie[this.getName(addon)],
|
||||
toggle: this.manager.toggle.bind(this.manager),
|
||||
edit: settingsCookie["fork-ps-7"] ? this.edit.bind(this) : null,
|
||||
remove: this.delete.bind(this),
|
||||
addon: addon,
|
||||
hash: addon.hash
|
||||
};
|
||||
}
|
||||
|
||||
getString(value) {
|
||||
if (!value) return "???";
|
||||
return typeof value == "string" ? value : value.toString();
|
||||
}
|
||||
|
||||
get list(){
|
||||
return this.props.type === "plugins" ? Object.values(bdplugins) : Object.values(bdthemes);
|
||||
}
|
||||
|
||||
getAddons() {
|
||||
const sortedAddons = this.list.sort((a, b) => {
|
||||
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 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 (first > second) return 1;
|
||||
if (second > first) return -1;
|
||||
return 0;
|
||||
});
|
||||
if (!this.state.ascending) sortedAddons.reverse();
|
||||
const rendered = [];
|
||||
for (let a = 0; a < sortedAddons.length; a++) {
|
||||
const addon = sortedAddons[a];
|
||||
if (this.state.query) {
|
||||
let matches = null;
|
||||
const name = this.getName(addon);
|
||||
const author = this.getAuthor(addon);
|
||||
const description = this.getDescription(addon);
|
||||
const version = this.getVersion(addon);
|
||||
if (name) matches = name.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 (version) matches = matches || version.toLocaleLowerCase().includes(this.state.query);
|
||||
if (!matches) continue;
|
||||
}
|
||||
const props = this.getProps(addon);
|
||||
rendered.push(<ErrorBoundary><AddonCard {...props} reload={!settingsCookie["fork-ps-5"] && this.manager.reload.bind(this.manager)} /></ErrorBoundary>);
|
||||
}
|
||||
return rendered;
|
||||
}
|
||||
|
||||
getName(addon) {return this.getString(addon.plugin ? addon.plugin.getName() : addon.name);}
|
||||
getAuthor(addon) {return this.getString(addon.plugin ? addon.plugin.getAuthor() : addon.author);}
|
||||
getDescription(addon) {return this.getString(addon.plugin ? addon.plugin.getDescription() : addon.description);}
|
||||
getVersion(addon) {return this.getString(addon.plugin ? addon.plugin.getVersion() : addon.version);}
|
||||
|
||||
renderCheckUpdates(){
|
||||
if(!window.ZeresPluginLibrary)return null
|
||||
if(!window.PluginUpdates)return null
|
||||
if(typeof window.PluginUpdates.checkAll !== "function")return null
|
||||
if(!this.isPlugins)return null
|
||||
|
||||
let tooltipIsShowing = false
|
||||
let tooltip
|
||||
return <TooltipWrap text="Checks for updates of plugins that support this feature. Right-click for a list.">
|
||||
<span style={{marginLeft: "10px"}} onMouseLeave={() => {
|
||||
tooltipIsShowing = false
|
||||
tooltip.hide()
|
||||
}}>
|
||||
<Lightcord.Api.Components.inputs.Button ref="checkUpdateButton" color="brand" look="filled" size="min" hoverColor="default" onClick={() => {
|
||||
try{
|
||||
Utils.showToast("Plugin update check in progress.", {type: "info"})
|
||||
window.PluginUpdates.checkAll()
|
||||
.then(() => {
|
||||
Utils.showToast("Plugin update check complete.", {type: "success"})
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
Utils.showToast("An error occured while checking update.", {type: "error"})
|
||||
})
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
Utils.showToast("An error occured while checking update.", {type: "error"})
|
||||
}
|
||||
}} wrapper={false} disabled={false} onRightClick={() => {
|
||||
if(!this.refs.checkUpdateButton)return
|
||||
if (!window.PluginUpdates || !window.PluginUpdates.plugins) return;
|
||||
if(tooltipIsShowing)return
|
||||
tooltip = new EmulatedTooltip(this.refs.checkUpdateButton.refs.original.refs.button, Object.values(window.PluginUpdates.plugins).map(p => p.name).join(", "), {
|
||||
side: "bottom",
|
||||
attachEvents: false
|
||||
})
|
||||
tooltip.show()
|
||||
tooltipIsShowing = true
|
||||
}}>
|
||||
Check for Updates
|
||||
</Lightcord.Api.Components.inputs.Button>
|
||||
</span>
|
||||
</TooltipWrap>
|
||||
}
|
||||
|
||||
render() {
|
||||
const refreshIcon = <Tooltip color="black" position="top" text="Reload List">
|
||||
{(props) =>
|
||||
<ReloadIcon {...props} className="bd-icon bd-reload bd-reload-header" size="18px" onClick={async () => {
|
||||
if (this.isPlugins) pluginModule.updatePluginList();
|
||||
else themeModule.updateThemeList();
|
||||
this.forceUpdate();
|
||||
}} />
|
||||
}</Tooltip>;
|
||||
const addonCards = this.getAddons();
|
||||
|
||||
return <ContentColumn title={`${this.props.type.toUpperCase()}—${addonCards.length}`}>
|
||||
<Lightcord.Api.Components.inputs.Button key="folder-button" color="brand"
|
||||
look="filled" size="min" hoverColor="default" onClick={this.openFolder.bind(this)} wrapper={false}
|
||||
style={{
|
||||
marginLeft: "10px"
|
||||
}}>
|
||||
Open {this.isPlugins ? "Plugin" : "Theme"} Folder
|
||||
</Lightcord.Api.Components.inputs.Button>
|
||||
{this.renderCheckUpdates()}
|
||||
{!settingsCookie["fork-ps-5"] && refreshIcon}
|
||||
<div className="bd-controls bd-addon-controls">
|
||||
<Search onChange={this.search} placeholder={`Search ${this.props.type}...`} />
|
||||
<div className="bd-addon-dropdowns">
|
||||
<div className="bd-select-wrapper">
|
||||
<label className="bd-label">Sort by:</label>
|
||||
<Dropdown options={this.sortOptions} onChange={this.sort} style="transparent" />
|
||||
</div>
|
||||
<div className="bd-select-wrapper">
|
||||
<label className="bd-label">Order:</label>
|
||||
<Dropdown options={this.directions} onChange={this.reverse} style="transparent" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className="bda-slist bd-addon-list">{addonCards}</div>
|
||||
</ContentColumn>
|
||||
}
|
||||
}
|
||||
|
||||
const originalRender = CardList.prototype.render;
|
||||
Object.defineProperty(CardList.prototype, "render", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");},
|
||||
get: () => originalRender
|
||||
import ErrorBoundary from "./errorBoundary";
|
||||
import ContentColumn from "./contentColumn";
|
||||
import ReloadIcon from "./reloadIcon";
|
||||
import AddonCard from "./addoncard";
|
||||
import Dropdown from "./components/dropdown";
|
||||
import Search from "./components/search";
|
||||
|
||||
import {settingsCookie, pluginCookie, themeCookie, bdplugins, bdthemes} from "../0globals";
|
||||
import ContentManager from "../modules/contentManager";
|
||||
import BDV2 from "../modules/v2";
|
||||
import pluginModule from "../modules/pluginModule";
|
||||
import themeModule from "../modules/themeModule";
|
||||
import WebpackModules from "../modules/webpackModules";
|
||||
import BdApi from "../modules/bdApi";
|
||||
import Utils from "../modules/utils";
|
||||
import TooltipWrap from "./tooltipWrap";
|
||||
import bdEvents from "../modules/bdEvents";
|
||||
import EmulatedTooltip from "./tooltip";
|
||||
|
||||
const Tooltip = WebpackModules.findByDisplayName("Tooltip");
|
||||
|
||||
const React = BDV2.react;
|
||||
|
||||
export default class CardList extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {sort: "name", ascending: true, query: ""};
|
||||
this.isPlugins = this.props.type == "plugins";
|
||||
this.cookie = this.isPlugins ? pluginCookie : themeCookie;
|
||||
this.manager = this.isPlugins ? pluginModule : themeModule;
|
||||
|
||||
this.sort = this.sort.bind(this);
|
||||
this.reverse = this.reverse.bind(this);
|
||||
this.search = this.search.bind(this);
|
||||
|
||||
this.onAddonChanges = function(){
|
||||
this.forceUpdate()
|
||||
}
|
||||
this.onAddonChanges = this.onAddonChanges.bind(this)
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
const type = (this.isPlugins ? "plugin" : "theme") + "-"
|
||||
bdEvents.on(`${type}loaded`, this.onAddonChanges)
|
||||
bdEvents.on(`${type}unloaded`, this.onAddonChanges)
|
||||
bdEvents.on(`${type}reloaded`, this.onAddonChanges)
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
const type = (this.isPlugins ? "plugin" : "theme") + "-"
|
||||
bdEvents.off(`${type}loaded`, this.onAddonChanges)
|
||||
bdEvents.off(`${type}unloaded`, this.onAddonChanges)
|
||||
bdEvents.off(`${type}reloaded`, this.onAddonChanges)
|
||||
}
|
||||
|
||||
openFolder() {
|
||||
const shell = require("electron").shell;
|
||||
const open = shell.openPath || shell.openItem;
|
||||
open(this.isPlugins ? ContentManager.pluginsFolder : ContentManager.themesFolder);
|
||||
}
|
||||
|
||||
edit(name) {
|
||||
console.log(name);
|
||||
this.manager.edit(name);
|
||||
}
|
||||
|
||||
async delete(name) {
|
||||
const shouldDelete = await this.confirmDelete(name);
|
||||
if (!shouldDelete) return;
|
||||
this.manager.delete(name);
|
||||
}
|
||||
|
||||
confirmDelete(name) {
|
||||
return new Promise(resolve => {
|
||||
BdApi.showConfirmationModal("Are You Sure?", `Are you sure you want to delete ${name}?`, {
|
||||
danger: true,
|
||||
confirmText: "Delete",
|
||||
onConfirm: () => {resolve(true);},
|
||||
onCancel: () => {resolve(false);}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get sortOptions() {
|
||||
return [
|
||||
{label: "Name", value: "name"},
|
||||
{label: "Author", value: "author"},
|
||||
{label: "Version", value: "version"},
|
||||
{label: "Recently Added", value: "added"},
|
||||
{label: "Last Modified", value: "modified"},
|
||||
{label: "File Size", value: "size"},
|
||||
];
|
||||
}
|
||||
|
||||
get directions() {
|
||||
return [
|
||||
{label: "Ascending", value: true},
|
||||
{label: "Descending", value: false}
|
||||
];
|
||||
}
|
||||
|
||||
reverse(value) {
|
||||
this.setState({ascending: value});
|
||||
}
|
||||
|
||||
sort(value) {
|
||||
this.setState({sort: value});
|
||||
}
|
||||
|
||||
search(event) {
|
||||
this.setState({query: event.target.value.toLocaleLowerCase()});
|
||||
}
|
||||
|
||||
getProps(addon) {
|
||||
return {
|
||||
key: this.getName(addon),
|
||||
enabled: this.cookie[this.getName(addon)],
|
||||
toggle: this.manager.toggle.bind(this.manager),
|
||||
edit: settingsCookie["fork-ps-7"] ? this.edit.bind(this) : null,
|
||||
remove: this.delete.bind(this),
|
||||
addon: addon,
|
||||
hash: addon.hash
|
||||
};
|
||||
}
|
||||
|
||||
getString(value) {
|
||||
if (!value) return "???";
|
||||
return typeof value == "string" ? value : value.toString();
|
||||
}
|
||||
|
||||
get list(){
|
||||
return this.props.type === "plugins" ? Object.values(bdplugins) : Object.values(bdthemes);
|
||||
}
|
||||
|
||||
getAddons() {
|
||||
const sortedAddons = this.list.sort((a, b) => {
|
||||
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 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 (first > second) return 1;
|
||||
if (second > first) return -1;
|
||||
return 0;
|
||||
});
|
||||
if (!this.state.ascending) sortedAddons.reverse();
|
||||
const rendered = [];
|
||||
for (let a = 0; a < sortedAddons.length; a++) {
|
||||
const addon = sortedAddons[a];
|
||||
if (this.state.query) {
|
||||
let matches = null;
|
||||
const name = this.getName(addon);
|
||||
const author = this.getAuthor(addon);
|
||||
const description = this.getDescription(addon);
|
||||
const version = this.getVersion(addon);
|
||||
if (name) matches = name.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 (version) matches = matches || version.toLocaleLowerCase().includes(this.state.query);
|
||||
if (!matches) continue;
|
||||
}
|
||||
const props = this.getProps(addon);
|
||||
rendered.push(<ErrorBoundary><AddonCard {...props} reload={!settingsCookie["fork-ps-5"] && this.manager.reload.bind(this.manager)} /></ErrorBoundary>);
|
||||
}
|
||||
return rendered;
|
||||
}
|
||||
|
||||
getName(addon) {return this.getString(addon.plugin ? addon.plugin.getName() : addon.name);}
|
||||
getAuthor(addon) {return this.getString(addon.plugin ? addon.plugin.getAuthor() : addon.author);}
|
||||
getDescription(addon) {return this.getString(addon.plugin ? addon.plugin.getDescription() : addon.description);}
|
||||
getVersion(addon) {return this.getString(addon.plugin ? addon.plugin.getVersion() : addon.version);}
|
||||
|
||||
renderCheckUpdates(){
|
||||
if(!window.ZeresPluginLibrary)return null
|
||||
if(!window.PluginUpdates)return null
|
||||
if(typeof window.PluginUpdates.checkAll !== "function")return null
|
||||
if(!this.isPlugins)return null
|
||||
|
||||
let tooltipIsShowing = false
|
||||
let tooltip
|
||||
return <TooltipWrap text="Checks for updates of plugins that support this feature. Right-click for a list.">
|
||||
<span style={{marginLeft: "10px"}} onMouseLeave={() => {
|
||||
tooltipIsShowing = false
|
||||
tooltip.hide()
|
||||
}}>
|
||||
<Lightcord.Api.Components.inputs.Button ref="checkUpdateButton" color="brand" look="filled" size="min" hoverColor="default" onClick={() => {
|
||||
try{
|
||||
Utils.showToast("Plugin update check in progress.", {type: "info"})
|
||||
window.PluginUpdates.checkAll()
|
||||
.then(() => {
|
||||
Utils.showToast("Plugin update check complete.", {type: "success"})
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
Utils.showToast("An error occured while checking update.", {type: "error"})
|
||||
})
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
Utils.showToast("An error occured while checking update.", {type: "error"})
|
||||
}
|
||||
}} wrapper={false} disabled={false} onRightClick={() => {
|
||||
if(!this.refs.checkUpdateButton)return
|
||||
if (!window.PluginUpdates || !window.PluginUpdates.plugins) return;
|
||||
if(tooltipIsShowing)return
|
||||
tooltip = new EmulatedTooltip(this.refs.checkUpdateButton.refs.original.refs.button, Object.values(window.PluginUpdates.plugins).map(p => p.name).join(", "), {
|
||||
side: "bottom",
|
||||
attachEvents: false
|
||||
})
|
||||
tooltip.show()
|
||||
tooltipIsShowing = true
|
||||
}}>
|
||||
Check for Updates
|
||||
</Lightcord.Api.Components.inputs.Button>
|
||||
</span>
|
||||
</TooltipWrap>
|
||||
}
|
||||
|
||||
render() {
|
||||
const refreshIcon = <Tooltip color="black" position="top" text="Reload List">
|
||||
{(props) =>
|
||||
<ReloadIcon {...props} className="bd-icon bd-reload bd-reload-header" size="18px" onClick={async () => {
|
||||
if (this.isPlugins) pluginModule.updatePluginList();
|
||||
else themeModule.updateThemeList();
|
||||
this.forceUpdate();
|
||||
}} />
|
||||
}</Tooltip>;
|
||||
const addonCards = this.getAddons();
|
||||
|
||||
return <ContentColumn title={`${this.props.type.toUpperCase()}—${addonCards.length}`}>
|
||||
<Lightcord.Api.Components.inputs.Button key="folder-button" color="brand"
|
||||
look="filled" size="min" hoverColor="default" onClick={this.openFolder.bind(this)} wrapper={false}
|
||||
style={{
|
||||
marginLeft: "10px"
|
||||
}}>
|
||||
Open {this.isPlugins ? "Plugin" : "Theme"} Folder
|
||||
</Lightcord.Api.Components.inputs.Button>
|
||||
{this.renderCheckUpdates()}
|
||||
{!settingsCookie["fork-ps-5"] && refreshIcon}
|
||||
<div className="bd-controls bd-addon-controls">
|
||||
<Search onChange={this.search} placeholder={`Search ${this.props.type}...`} />
|
||||
<div className="bd-addon-dropdowns">
|
||||
<div className="bd-select-wrapper">
|
||||
<label className="bd-label">Sort by:</label>
|
||||
<Dropdown options={this.sortOptions} onChange={this.sort} style="transparent" />
|
||||
</div>
|
||||
<div className="bd-select-wrapper">
|
||||
<label className="bd-label">Order:</label>
|
||||
<Dropdown options={this.directions} onChange={this.reverse} style="transparent" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className="bda-slist bd-addon-list">{addonCards}</div>
|
||||
</ContentColumn>
|
||||
}
|
||||
}
|
||||
|
||||
const originalRender = CardList.prototype.render;
|
||||
Object.defineProperty(CardList.prototype, "render", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");},
|
||||
get: () => originalRender
|
||||
});
|
|
@ -1,27 +1,27 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
import SearchIcon from "../icons/search";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Search extends React.Component {
|
||||
constructor(props){
|
||||
super(props)
|
||||
this.state = {
|
||||
focused: false
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const className = ["bd-search-wrapper"]
|
||||
if(this.state.focused)className.push("focused")
|
||||
return <div className={className.join(" ")}>
|
||||
<input onChange={this.props.onChange} onKeyDown={this.props.onKeyDown} type="text"
|
||||
className="bd-search" placeholder={this.props.placeholder} maxLength="50"
|
||||
onFocus={() => {
|
||||
this.setState({focused: true})
|
||||
}} onBlur={() => {
|
||||
this.setState({focused: false})
|
||||
}}/>
|
||||
<SearchIcon />
|
||||
</div>;
|
||||
}
|
||||
import BDV2 from "../../modules/v2";
|
||||
import SearchIcon from "../icons/search";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Search extends React.Component {
|
||||
constructor(props){
|
||||
super(props)
|
||||
this.state = {
|
||||
focused: false
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const className = ["bd-search-wrapper"]
|
||||
if(this.state.focused)className.push("focused")
|
||||
return <div className={className.join(" ")}>
|
||||
<input onChange={this.props.onChange} onKeyDown={this.props.onKeyDown} type="text"
|
||||
className="bd-search" placeholder={this.props.placeholder} maxLength="50"
|
||||
onFocus={() => {
|
||||
this.setState({focused: true})
|
||||
}} onBlur={() => {
|
||||
this.setState({focused: false})
|
||||
}}/>
|
||||
<SearchIcon />
|
||||
</div>;
|
||||
}
|
||||
}
|
|
@ -1,22 +1,22 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Switch extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {checked: this.props.checked};
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.switch = <Lightcord.Api.Components.inputs.Switch onChange={this.onChange} value={this.state.checked} />
|
||||
}
|
||||
|
||||
onChange(value) {
|
||||
if (this.props.disabled) return;
|
||||
this.props.onChange(value);
|
||||
this.setState({checked: value});
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.switch
|
||||
}
|
||||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Switch extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {checked: this.props.checked};
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.switch = <Lightcord.Api.Components.inputs.Switch onChange={this.onChange} value={this.state.checked} />
|
||||
}
|
||||
|
||||
onChange(value) {
|
||||
if (this.props.disabled) return;
|
||||
this.props.onChange(value);
|
||||
this.setState({checked: value});
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.switch
|
||||
}
|
||||
}
|
|
@ -1,234 +1,234 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import Settings from "../modules/settingsPanel";
|
||||
import BDV2 from "../modules/v2";
|
||||
import DataStore from "../modules/dataStore";
|
||||
import DOM from "../modules/domtools";
|
||||
import Utils from "../modules/utils"
|
||||
|
||||
import SettingsTitle from "./settingsTitle";
|
||||
import Checkbox from "./checkbox";
|
||||
import V2C_CssEditorDetached from "./cssEditorDetached";
|
||||
|
||||
export default class V2C_CssEditor extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const self = this;
|
||||
self.props.lines = 0;
|
||||
self.setInitialState();
|
||||
self.attach = self.attach.bind(self);
|
||||
self.detachedEditor = BDV2.react.createElement(V2C_CssEditorDetached, {attach: self.attach});
|
||||
self.onClick = self.onClick.bind(self);
|
||||
self.updateCss = self.updateCss.bind(self);
|
||||
self.saveCss = self.saveCss.bind(self);
|
||||
self.detach = self.detach.bind(self);
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
this.state = {
|
||||
detached: this.props.detached || BDV2.editorDetached
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// this.updateLineCount();
|
||||
this.editor = ace.edit("bd-customcss-editor");
|
||||
this.editor.setTheme("ace/theme/discord");
|
||||
this.editor.session.setMode("ace/mode/css");
|
||||
this.editor.setShowPrintMargin(false);
|
||||
this.editor.setFontSize(14);
|
||||
this.editor.on("change", () => {
|
||||
if (!settingsCookie["bda-css-0"]) return;
|
||||
this.saveCss();
|
||||
this.updateCss();
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.editor.destroy();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const self = this;
|
||||
if (prevState.detached && !self.state.detached) {
|
||||
BDV2.reactDom.unmountComponentAtNode(self.detachedRoot);
|
||||
}
|
||||
}
|
||||
|
||||
codeMirror() {
|
||||
}
|
||||
|
||||
get options() {
|
||||
return {
|
||||
lineNumbers: true,
|
||||
mode: "css",
|
||||
indentUnit: 4,
|
||||
theme: "material",
|
||||
scrollbarStyle: "simple"
|
||||
};
|
||||
}
|
||||
|
||||
get css() {
|
||||
const _ccss = DataStore.getBDData("bdcustomcss");
|
||||
let ccss = "";
|
||||
if (_ccss && _ccss !== "") {
|
||||
ccss = Buffer.from(_ccss, "base64").toString("utf8");
|
||||
}
|
||||
return ccss;
|
||||
}
|
||||
|
||||
updateLineCount() {
|
||||
const lineCount = this.refs.editor.value.split("\n").length;
|
||||
if (lineCount == this.props.lines) return;
|
||||
this.refs.lines.textContent = Array.from(new Array(lineCount), (_, i) => i + 1).join(".\n") + ".";
|
||||
this.props.lines = lineCount;
|
||||
}
|
||||
|
||||
render() {
|
||||
const self = this;
|
||||
|
||||
const {detached} = self.state;
|
||||
return [
|
||||
detached && BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "editor-detached"},
|
||||
BDV2.react.createElement(SettingsTitle, {text: "Custom CSS Editor"}),
|
||||
BDV2.react.createElement(
|
||||
"h3",
|
||||
null,
|
||||
"Editor Detached"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{className: "btn btn-primary", onClick: () => {
|
||||
self.attach();
|
||||
}},
|
||||
"Attach"
|
||||
)
|
||||
),
|
||||
!detached && BDV2.react.createElement(
|
||||
"div",
|
||||
null,
|
||||
BDV2.react.createElement(SettingsTitle, {text: "Custom CSS Editor"}),
|
||||
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-attach-controls"},
|
||||
BDV2.react.createElement(
|
||||
"ul",
|
||||
{className: "checkbox-group"},
|
||||
BDV2.react.createElement(Checkbox, {id: "live-update", text: "Live Update", onChange: this.onChange, checked: settingsCookie["bda-css-0"]})
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "bd-customcss-detach-controls-button"},
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "3px 0 0 3px", borderRight: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("update");
|
||||
}},
|
||||
"Update"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "0", borderLeft: "1px solid #2d2d2d", borderRight: "1px solid #2d2d2d"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("save");
|
||||
}},
|
||||
"Save"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "0 3px 3px 0", borderLeft: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("detach");
|
||||
}},
|
||||
"Detach"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"span",
|
||||
{style: {fontSize: "10px", marginLeft: "5px"}},
|
||||
"Unsaved changes are lost on detach"
|
||||
),
|
||||
BDV2.react.createElement("div", {className: "help-text"},
|
||||
"Press ",
|
||||
BDV2.react.createElement("code", {className: "inline"}, "ctrl"),
|
||||
"+",
|
||||
BDV2.react.createElement("span", {className: "inline"}, ","),
|
||||
" with the editor focused to access the editor's settings."
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
onClick(arg) {
|
||||
const self = this;
|
||||
switch (arg) {
|
||||
case "update":
|
||||
self.updateCss();
|
||||
break;
|
||||
case "save":
|
||||
self.saveCss();
|
||||
break;
|
||||
case "detach":
|
||||
self.detach();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onChange(id, checked) {
|
||||
switch (id) {
|
||||
case "live-update":
|
||||
settingsCookie["bda-css-0"] = checked;
|
||||
Settings.saveSettings();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
updateCss() {
|
||||
DOM.removeStyle("customcss");
|
||||
DOM.addStyle("customcss", this.editor.session.getValue());
|
||||
}
|
||||
|
||||
saveCss() {
|
||||
DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64"));
|
||||
}
|
||||
|
||||
detach() {
|
||||
const self = this;
|
||||
self.setState({
|
||||
detached: true
|
||||
});
|
||||
const droot = self.detachedRoot;
|
||||
if (!droot) {
|
||||
console.log("FAILED TO INJECT ROOT: .app");
|
||||
return;
|
||||
}
|
||||
BDV2.reactDom.render(self.detachedEditor, droot);
|
||||
}
|
||||
|
||||
get detachedRoot() {
|
||||
const _root = DOM.query("#bd-customcss-detach-container");
|
||||
if (!_root) {
|
||||
if (!this.injectDetachedRoot()) return null;
|
||||
return this.detachedRoot;
|
||||
}
|
||||
return _root;
|
||||
}
|
||||
|
||||
injectDetachedRoot() {
|
||||
const app = DOM.query(".app, ."+Utils.removeDa(BDModules.get(e => e.app && e.layers)[0].app));
|
||||
if (!app) return false;
|
||||
DOM.insertAfter(DOM.createElement(`<div id="bd-customcss-detach-container">`), app);
|
||||
return true;
|
||||
}
|
||||
|
||||
attach() {
|
||||
const self = this;
|
||||
self.setState({
|
||||
detached: false
|
||||
});
|
||||
}
|
||||
import {settingsCookie} from "../0globals";
|
||||
import Settings from "../modules/settingsPanel";
|
||||
import BDV2 from "../modules/v2";
|
||||
import DataStore from "../modules/dataStore";
|
||||
import DOM from "../modules/domtools";
|
||||
import Utils from "../modules/utils"
|
||||
|
||||
import SettingsTitle from "./settingsTitle";
|
||||
import Checkbox from "./checkbox";
|
||||
import V2C_CssEditorDetached from "./cssEditorDetached";
|
||||
|
||||
export default class V2C_CssEditor extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const self = this;
|
||||
self.props.lines = 0;
|
||||
self.setInitialState();
|
||||
self.attach = self.attach.bind(self);
|
||||
self.detachedEditor = BDV2.react.createElement(V2C_CssEditorDetached, {attach: self.attach});
|
||||
self.onClick = self.onClick.bind(self);
|
||||
self.updateCss = self.updateCss.bind(self);
|
||||
self.saveCss = self.saveCss.bind(self);
|
||||
self.detach = self.detach.bind(self);
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
this.state = {
|
||||
detached: this.props.detached || BDV2.editorDetached
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// this.updateLineCount();
|
||||
this.editor = ace.edit("bd-customcss-editor");
|
||||
this.editor.setTheme("ace/theme/discord");
|
||||
this.editor.session.setMode("ace/mode/css");
|
||||
this.editor.setShowPrintMargin(false);
|
||||
this.editor.setFontSize(14);
|
||||
this.editor.on("change", () => {
|
||||
if (!settingsCookie["bda-css-0"]) return;
|
||||
this.saveCss();
|
||||
this.updateCss();
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.editor.destroy();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const self = this;
|
||||
if (prevState.detached && !self.state.detached) {
|
||||
BDV2.reactDom.unmountComponentAtNode(self.detachedRoot);
|
||||
}
|
||||
}
|
||||
|
||||
codeMirror() {
|
||||
}
|
||||
|
||||
get options() {
|
||||
return {
|
||||
lineNumbers: true,
|
||||
mode: "css",
|
||||
indentUnit: 4,
|
||||
theme: "material",
|
||||
scrollbarStyle: "simple"
|
||||
};
|
||||
}
|
||||
|
||||
get css() {
|
||||
const _ccss = DataStore.getBDData("bdcustomcss");
|
||||
let ccss = "";
|
||||
if (_ccss && _ccss !== "") {
|
||||
ccss = Buffer.from(_ccss, "base64").toString("utf8");
|
||||
}
|
||||
return ccss;
|
||||
}
|
||||
|
||||
updateLineCount() {
|
||||
const lineCount = this.refs.editor.value.split("\n").length;
|
||||
if (lineCount == this.props.lines) return;
|
||||
this.refs.lines.textContent = Array.from(new Array(lineCount), (_, i) => i + 1).join(".\n") + ".";
|
||||
this.props.lines = lineCount;
|
||||
}
|
||||
|
||||
render() {
|
||||
const self = this;
|
||||
|
||||
const {detached} = self.state;
|
||||
return [
|
||||
detached && BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "editor-detached"},
|
||||
BDV2.react.createElement(SettingsTitle, {text: "Custom CSS Editor"}),
|
||||
BDV2.react.createElement(
|
||||
"h3",
|
||||
null,
|
||||
"Editor Detached"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{className: "btn btn-primary", onClick: () => {
|
||||
self.attach();
|
||||
}},
|
||||
"Attach"
|
||||
)
|
||||
),
|
||||
!detached && BDV2.react.createElement(
|
||||
"div",
|
||||
null,
|
||||
BDV2.react.createElement(SettingsTitle, {text: "Custom CSS Editor"}),
|
||||
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-attach-controls"},
|
||||
BDV2.react.createElement(
|
||||
"ul",
|
||||
{className: "checkbox-group"},
|
||||
BDV2.react.createElement(Checkbox, {id: "live-update", text: "Live Update", onChange: this.onChange, checked: settingsCookie["bda-css-0"]})
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "bd-customcss-detach-controls-button"},
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "3px 0 0 3px", borderRight: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("update");
|
||||
}},
|
||||
"Update"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "0", borderLeft: "1px solid #2d2d2d", borderRight: "1px solid #2d2d2d"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("save");
|
||||
}},
|
||||
"Save"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "0 3px 3px 0", borderLeft: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("detach");
|
||||
}},
|
||||
"Detach"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"span",
|
||||
{style: {fontSize: "10px", marginLeft: "5px"}},
|
||||
"Unsaved changes are lost on detach"
|
||||
),
|
||||
BDV2.react.createElement("div", {className: "help-text"},
|
||||
"Press ",
|
||||
BDV2.react.createElement("code", {className: "inline"}, "ctrl"),
|
||||
"+",
|
||||
BDV2.react.createElement("span", {className: "inline"}, ","),
|
||||
" with the editor focused to access the editor's settings."
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
onClick(arg) {
|
||||
const self = this;
|
||||
switch (arg) {
|
||||
case "update":
|
||||
self.updateCss();
|
||||
break;
|
||||
case "save":
|
||||
self.saveCss();
|
||||
break;
|
||||
case "detach":
|
||||
self.detach();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onChange(id, checked) {
|
||||
switch (id) {
|
||||
case "live-update":
|
||||
settingsCookie["bda-css-0"] = checked;
|
||||
Settings.saveSettings();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
updateCss() {
|
||||
DOM.removeStyle("customcss");
|
||||
DOM.addStyle("customcss", this.editor.session.getValue());
|
||||
}
|
||||
|
||||
saveCss() {
|
||||
DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64"));
|
||||
}
|
||||
|
||||
detach() {
|
||||
const self = this;
|
||||
self.setState({
|
||||
detached: true
|
||||
});
|
||||
const droot = self.detachedRoot;
|
||||
if (!droot) {
|
||||
console.log("FAILED TO INJECT ROOT: .app");
|
||||
return;
|
||||
}
|
||||
BDV2.reactDom.render(self.detachedEditor, droot);
|
||||
}
|
||||
|
||||
get detachedRoot() {
|
||||
const _root = DOM.query("#bd-customcss-detach-container");
|
||||
if (!_root) {
|
||||
if (!this.injectDetachedRoot()) return null;
|
||||
return this.detachedRoot;
|
||||
}
|
||||
return _root;
|
||||
}
|
||||
|
||||
injectDetachedRoot() {
|
||||
const app = DOM.query(".app, ."+Utils.removeDa(BDModules.get(e => e.app && e.layers)[0].app));
|
||||
if (!app) return false;
|
||||
DOM.insertAfter(DOM.createElement(`<div id="bd-customcss-detach-container">`), app);
|
||||
return true;
|
||||
}
|
||||
|
||||
attach() {
|
||||
const self = this;
|
||||
self.setState({
|
||||
detached: false
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,174 +1,174 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import Settings from "../modules/settingsPanel";
|
||||
import BDV2 from "../modules/v2";
|
||||
import DataStore from "../modules/dataStore";
|
||||
import DOM from "../modules/domtools";
|
||||
import Utils from "../modules/utils"
|
||||
|
||||
import Checkbox from "./checkbox";
|
||||
|
||||
export default class V2C_CssEditorDetached extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const self = this;
|
||||
self.onClick = self.onClick.bind(self);
|
||||
self.updateCss = self.updateCss.bind(self);
|
||||
self.saveCss = self.saveCss.bind(self);
|
||||
self.onChange = self.onChange.bind(self);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
DOM.addClass(DOM.query("#app-mount"), "bd-detached-editor");
|
||||
BDV2.editorDetached = true;
|
||||
// this.updateLineCount();
|
||||
this.editor = ace.edit("bd-customcss-editor-detached");
|
||||
this.editor.setTheme("ace/theme/discord");
|
||||
this.editor.session.setMode("ace/mode/css");
|
||||
this.editor.setShowPrintMargin(false);
|
||||
this.editor.setFontSize(14);
|
||||
this.editor.on("change", () => {
|
||||
if (!settingsCookie["bda-css-0"]) return;
|
||||
this.saveCss();
|
||||
this.updateCss();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
DOM.removeClass(DOM.query("#app-mount"), "bd-detached-editor");
|
||||
BDV2.editorDetached = false;
|
||||
this.editor.destroy();
|
||||
}
|
||||
|
||||
updateLineCount() {
|
||||
const lineCount = this.refs.editor.value.split("\n").length;
|
||||
if (lineCount == this.props.lines) return;
|
||||
this.refs.lines.textContent = Array.from(new Array(lineCount), (_, i) => i + 1).join(".\n") + ".";
|
||||
this.props.lines = lineCount;
|
||||
}
|
||||
|
||||
get options() {
|
||||
return {
|
||||
lineNumbers: true,
|
||||
mode: "css",
|
||||
indentUnit: 4,
|
||||
theme: "material",
|
||||
scrollbarStyle: "simple"
|
||||
};
|
||||
}
|
||||
|
||||
get css() {
|
||||
const _ccss = DataStore.getBDData("bdcustomcss");
|
||||
let ccss = "";
|
||||
if (_ccss && _ccss !== "") {
|
||||
ccss = Buffer.from(_ccss, "base64").toString("utf8");
|
||||
}
|
||||
return ccss;
|
||||
}
|
||||
|
||||
get root() {
|
||||
const _root = DOM.query("#bd-customcss-detach-container");
|
||||
if (!_root) {
|
||||
if (!this.injectRoot()) return null;
|
||||
return this.detachedRoot;
|
||||
}
|
||||
return _root;
|
||||
}
|
||||
|
||||
injectRoot() {
|
||||
const app = DOM.query(".app, ."+Utils.removeDa(BDModules.get(e => e.app && e.layers)[0].app));
|
||||
if (!app) return false;
|
||||
DOM.insertAfter(DOM.createElement(`<div id="bd-customcss-detach-container">`), app);
|
||||
return true;
|
||||
}
|
||||
|
||||
render() {
|
||||
const self = this;
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "bd-detached-css-editor", id: "bd-customcss-detach-editor"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "bd-customcss-innerpane"},
|
||||
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-attach-controls"},
|
||||
BDV2.react.createElement(
|
||||
"ul",
|
||||
{className: "checkbox-group"},
|
||||
BDV2.react.createElement(Checkbox, {id: "live-update", text: "Live Update", onChange: self.onChange, checked: settingsCookie["bda-css-0"]})
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "bd-customcss-detach-controls-button"},
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "3px 0 0 3px", borderRight: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("update");
|
||||
}},
|
||||
"Update"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "0", borderLeft: "1px solid #2d2d2d", borderRight: "1px solid #2d2d2d"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("save");
|
||||
}},
|
||||
"Save"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "0 3px 3px 0", borderLeft: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("attach");
|
||||
}},
|
||||
"Attach"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"span",
|
||||
{style: {fontSize: "10px", marginLeft: "5px"}},
|
||||
"Unsaved changes are lost on attach"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
onChange(id, checked) {
|
||||
switch (id) {
|
||||
case "live-update":
|
||||
settingsCookie["bda-css-0"] = checked;
|
||||
Settings.saveSettings();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onClick(id) {
|
||||
const self = this;
|
||||
switch (id) {
|
||||
case "attach":
|
||||
if (DOM.query("#editor-detached")) self.props.attach();
|
||||
BDV2.reactDom.unmountComponentAtNode(self.root);
|
||||
self.root.remove();
|
||||
break;
|
||||
case "update":
|
||||
self.updateCss();
|
||||
break;
|
||||
case "save":
|
||||
self.saveCss();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
updateCss() {
|
||||
DOM.removeStyle("customcss");
|
||||
DOM.addStyle("customcss", this.editor.session.getValue());
|
||||
}
|
||||
|
||||
saveCss() {
|
||||
DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64"));
|
||||
}
|
||||
import {settingsCookie} from "../0globals";
|
||||
import Settings from "../modules/settingsPanel";
|
||||
import BDV2 from "../modules/v2";
|
||||
import DataStore from "../modules/dataStore";
|
||||
import DOM from "../modules/domtools";
|
||||
import Utils from "../modules/utils"
|
||||
|
||||
import Checkbox from "./checkbox";
|
||||
|
||||
export default class V2C_CssEditorDetached extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const self = this;
|
||||
self.onClick = self.onClick.bind(self);
|
||||
self.updateCss = self.updateCss.bind(self);
|
||||
self.saveCss = self.saveCss.bind(self);
|
||||
self.onChange = self.onChange.bind(self);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
DOM.addClass(DOM.query("#app-mount"), "bd-detached-editor");
|
||||
BDV2.editorDetached = true;
|
||||
// this.updateLineCount();
|
||||
this.editor = ace.edit("bd-customcss-editor-detached");
|
||||
this.editor.setTheme("ace/theme/discord");
|
||||
this.editor.session.setMode("ace/mode/css");
|
||||
this.editor.setShowPrintMargin(false);
|
||||
this.editor.setFontSize(14);
|
||||
this.editor.on("change", () => {
|
||||
if (!settingsCookie["bda-css-0"]) return;
|
||||
this.saveCss();
|
||||
this.updateCss();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
DOM.removeClass(DOM.query("#app-mount"), "bd-detached-editor");
|
||||
BDV2.editorDetached = false;
|
||||
this.editor.destroy();
|
||||
}
|
||||
|
||||
updateLineCount() {
|
||||
const lineCount = this.refs.editor.value.split("\n").length;
|
||||
if (lineCount == this.props.lines) return;
|
||||
this.refs.lines.textContent = Array.from(new Array(lineCount), (_, i) => i + 1).join(".\n") + ".";
|
||||
this.props.lines = lineCount;
|
||||
}
|
||||
|
||||
get options() {
|
||||
return {
|
||||
lineNumbers: true,
|
||||
mode: "css",
|
||||
indentUnit: 4,
|
||||
theme: "material",
|
||||
scrollbarStyle: "simple"
|
||||
};
|
||||
}
|
||||
|
||||
get css() {
|
||||
const _ccss = DataStore.getBDData("bdcustomcss");
|
||||
let ccss = "";
|
||||
if (_ccss && _ccss !== "") {
|
||||
ccss = Buffer.from(_ccss, "base64").toString("utf8");
|
||||
}
|
||||
return ccss;
|
||||
}
|
||||
|
||||
get root() {
|
||||
const _root = DOM.query("#bd-customcss-detach-container");
|
||||
if (!_root) {
|
||||
if (!this.injectRoot()) return null;
|
||||
return this.detachedRoot;
|
||||
}
|
||||
return _root;
|
||||
}
|
||||
|
||||
injectRoot() {
|
||||
const app = DOM.query(".app, ."+Utils.removeDa(BDModules.get(e => e.app && e.layers)[0].app));
|
||||
if (!app) return false;
|
||||
DOM.insertAfter(DOM.createElement(`<div id="bd-customcss-detach-container">`), app);
|
||||
return true;
|
||||
}
|
||||
|
||||
render() {
|
||||
const self = this;
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "bd-detached-css-editor", id: "bd-customcss-detach-editor"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "bd-customcss-innerpane"},
|
||||
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-attach-controls"},
|
||||
BDV2.react.createElement(
|
||||
"ul",
|
||||
{className: "checkbox-group"},
|
||||
BDV2.react.createElement(Checkbox, {id: "live-update", text: "Live Update", onChange: self.onChange, checked: settingsCookie["bda-css-0"]})
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "bd-customcss-detach-controls-button"},
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "3px 0 0 3px", borderRight: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("update");
|
||||
}},
|
||||
"Update"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "0", borderLeft: "1px solid #2d2d2d", borderRight: "1px solid #2d2d2d"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("save");
|
||||
}},
|
||||
"Save"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "0 3px 3px 0", borderLeft: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("attach");
|
||||
}},
|
||||
"Attach"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"span",
|
||||
{style: {fontSize: "10px", marginLeft: "5px"}},
|
||||
"Unsaved changes are lost on attach"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
onChange(id, checked) {
|
||||
switch (id) {
|
||||
case "live-update":
|
||||
settingsCookie["bda-css-0"] = checked;
|
||||
Settings.saveSettings();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onClick(id) {
|
||||
const self = this;
|
||||
switch (id) {
|
||||
case "attach":
|
||||
if (DOM.query("#editor-detached")) self.props.attach();
|
||||
BDV2.reactDom.unmountComponentAtNode(self.root);
|
||||
self.root.remove();
|
||||
break;
|
||||
case "update":
|
||||
self.updateCss();
|
||||
break;
|
||||
case "save":
|
||||
self.saveCss();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
updateCss() {
|
||||
DOM.removeStyle("customcss");
|
||||
DOM.addStyle("customcss", this.editor.session.getValue());
|
||||
}
|
||||
|
||||
saveCss() {
|
||||
DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64"));
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Delete extends React.Component {
|
||||
render() {
|
||||
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}>
|
||||
<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"/>
|
||||
</svg>;
|
||||
}
|
||||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Delete extends React.Component {
|
||||
render() {
|
||||
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}>
|
||||
<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"/>
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class DownArrow extends React.Component {
|
||||
render() {
|
||||
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}}>
|
||||
<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>;
|
||||
}
|
||||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class DownArrow extends React.Component {
|
||||
render() {
|
||||
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}}>
|
||||
<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>;
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Edit extends React.Component {
|
||||
render() {
|
||||
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}>
|
||||
<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" />
|
||||
</svg>;
|
||||
}
|
||||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Edit extends React.Component {
|
||||
render() {
|
||||
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}>
|
||||
<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" />
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -1,18 +1,18 @@
|
|||
{/* <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="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> */}
|
||||
|
||||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class History extends React.Component {
|
||||
render() {
|
||||
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}>
|
||||
<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"/>
|
||||
</svg>;
|
||||
}
|
||||
{/* <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="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> */}
|
||||
|
||||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class History extends React.Component {
|
||||
render() {
|
||||
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}>
|
||||
<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"/>
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Search extends React.Component {
|
||||
render() {
|
||||
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}}>
|
||||
<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"/>
|
||||
</svg>;
|
||||
}
|
||||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Search extends React.Component {
|
||||
render() {
|
||||
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}}>
|
||||
<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"/>
|
||||
</svg>;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,485 +1,485 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
|
||||
import SettingsTitle from "../settingsTitle";
|
||||
import TabBarSeparator from "../tabBarSeparator";
|
||||
|
||||
import ServerCard from "./serverCard";
|
||||
import { useForceUpdate } from "../../modules/hooks";
|
||||
|
||||
let SettingsView
|
||||
export default class V2C_PublicServers extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.setInitialState();
|
||||
this.close = this.close.bind(this);
|
||||
this.changeCategory = this.changeCategory.bind(this);
|
||||
this.search = this.search.bind(this);
|
||||
this.searchKeyDown = this.searchKeyDown.bind(this);
|
||||
this.checkConnection = this.checkConnection.bind(this);
|
||||
this.join = this.join.bind(this);
|
||||
this.connect = this.connect.bind(this);
|
||||
|
||||
this.GuildStore = BDV2.WebpackModules.findByUniqueProperties(["getGuilds"]);
|
||||
this.AvatarDefaults = BDV2.WebpackModules.findByUniqueProperties(["getUserAvatarURL", "DEFAULT_AVATARS"]);
|
||||
this.InviteActions = BDV2.WebpackModules.findByUniqueProperties(["acceptInvite"]);
|
||||
this.SortedGuildStore = BDV2.WebpackModules.findByUniqueProperties(["getSortedGuilds"]);
|
||||
|
||||
this.hooks = []
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.checkConnection();
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
this.state = {
|
||||
selectedCategory: -1,
|
||||
title: "Loading...",
|
||||
loading: true,
|
||||
servers: [],
|
||||
next: null,
|
||||
connection: {
|
||||
state: 0,
|
||||
user: null
|
||||
},
|
||||
section: this.categorySlugs[0],
|
||||
theme: "dark"
|
||||
};
|
||||
}
|
||||
|
||||
close() {
|
||||
this.props.close()
|
||||
}
|
||||
|
||||
search(query, clear) {
|
||||
const self = this;
|
||||
fetch(`${self.searchEndPoint}${query}${query ? "&schema=new" : "?schema=new"}`, {
|
||||
method: "get"
|
||||
}).then(async res => {
|
||||
if(res.status !== 200)throw await res.text()
|
||||
let data = await res.json()
|
||||
|
||||
let servers = data.results.reduce((arr, server) => {
|
||||
server.joined = false;
|
||||
arr.push(server);
|
||||
// arr.push(<ServerCard server={server} join={self.join}/>);
|
||||
return arr;
|
||||
}, []);
|
||||
|
||||
if (!clear) {
|
||||
servers = self.state.servers.concat(servers);
|
||||
}
|
||||
else {
|
||||
//servers.unshift(self.bdServer);
|
||||
}
|
||||
|
||||
let end = data.size + data.from;
|
||||
data.next = `?from=${end}`;
|
||||
if (self.state.term) data.next += `&term=${self.state.term}`;
|
||||
if (self.state.selectedCategory) data.next += `&category=${self.categoryButtons[self.state.selectedCategory]}`;
|
||||
if (end >= data.total) {
|
||||
end = data.total;
|
||||
data.next = null;
|
||||
}
|
||||
|
||||
let title = `Showing 1-${end} of ${data.total} results in ${self.categoryButtons[self.state.selectedCategory]}`;
|
||||
if (self.state.term) title += ` for ${self.state.term}`;
|
||||
|
||||
self.setState({
|
||||
loading: false,
|
||||
title: title,
|
||||
servers: servers,
|
||||
next: data.next
|
||||
});
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
return self.setState({
|
||||
loading: false,
|
||||
title: "Failed to load servers. Check console for details"
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
async join(serverCard) {
|
||||
if (serverCard.props.pinned) return this.InviteActions.acceptInvite(serverCard.props.invite_code);
|
||||
|
||||
await fetch(`${this.joinEndPoint}/${serverCard.props.server.identifier}`,{
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
mode: "cors",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
|
||||
serverCard.setState({joined: true});
|
||||
}
|
||||
|
||||
connect() {
|
||||
const self = this;
|
||||
const options = self.windowOptions;
|
||||
options.x = Math.round(window.screenX + window.innerWidth / 2 - options.width / 2);
|
||||
options.y = Math.round(window.screenY + window.innerHeight / 2 - options.height / 2);
|
||||
|
||||
const 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";
|
||||
win.webContents.on("did-navigate", (event, url) => {
|
||||
if (url != "https://auth.discordservers.com/info") return;
|
||||
win.close();
|
||||
self.checkConnection();
|
||||
});
|
||||
win.loadURL(url);
|
||||
win.setMenuBarVisibility(false)
|
||||
}
|
||||
|
||||
get windowOptions() {
|
||||
return {
|
||||
width: 500,
|
||||
height: 550,
|
||||
backgroundColor: "#282b30",
|
||||
show: true,
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
minimizable: false,
|
||||
alwaysOnTop: true,
|
||||
frame: true,
|
||||
center: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
get bdServer() {
|
||||
const server = {
|
||||
name: "BetterDiscord",
|
||||
online: "30000+",
|
||||
members: "70000+",
|
||||
categories: ["community", "programming", "support"],
|
||||
description: "Official BetterDiscord server for support etc",
|
||||
identifier: "86004744966914048",
|
||||
iconUrl: "https://cdn.discordapp.com/icons/86004744966914048/292e7f6bfff2b71dfd13e508a859aedd.webp",
|
||||
nativejoin: true,
|
||||
invite_code: "0Tmfo5ZbORCRqbAd",
|
||||
pinned: true
|
||||
};
|
||||
const server2 = {
|
||||
name: "Lightcord",
|
||||
online: "100+",
|
||||
members: "300+",
|
||||
categories: ["community", "programming", "support"],
|
||||
description: "Official Lightcord server for support etc",
|
||||
identifier: "705908350218666117",
|
||||
iconUrl: "https://github.com/lightcord.png",
|
||||
nativejoin: true,
|
||||
invite_code: "7eFff2A",
|
||||
pinned: true
|
||||
};
|
||||
const guildList = this.SortedGuildStore.getFlattenedGuildIds();
|
||||
const defaultList = this.AvatarDefaults.DEFAULT_AVATARS;
|
||||
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: server, pinned: true, join: this.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]})
|
||||
]
|
||||
}
|
||||
|
||||
get searchEndPoint() {
|
||||
return "https://search.discordservers.com";
|
||||
}
|
||||
|
||||
get joinEndPoint() {
|
||||
return "https://j.discordservers.com";
|
||||
}
|
||||
|
||||
get connectEndPoint() {
|
||||
return "https://join.discordservers.com/connect";
|
||||
}
|
||||
|
||||
async checkConnection() {
|
||||
const self = this;
|
||||
try {
|
||||
const response = await fetch(`https://auth.discordservers.com/info`,{
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
mode: "cors",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
const text = await response.text()
|
||||
if(!text){
|
||||
self.setState({
|
||||
title: "Not connected to discordservers.com!",
|
||||
loading: true,
|
||||
selectedCategory: -1,
|
||||
connection: {
|
||||
state: 1,
|
||||
user: null
|
||||
}
|
||||
});
|
||||
return
|
||||
}
|
||||
const data = JSON.parse(text)
|
||||
self.setState({
|
||||
selectedCategory: 0,
|
||||
connection: {
|
||||
state: 2,
|
||||
user: data
|
||||
}
|
||||
});
|
||||
self.search("", true);
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error)
|
||||
self.setState({
|
||||
title: "Not connected to discordservers.com!",
|
||||
loading: true,
|
||||
selectedCategory: -1,
|
||||
connection: {
|
||||
state: 1,
|
||||
user: null
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
this.hooks.forEach((e) => e())
|
||||
SettingsView = SettingsView || BDV2.WebpackModules.findByDisplayName("SettingsView")
|
||||
return BDV2.react.createElement("div", {id: "pubslayerroot"},
|
||||
BDV2.react.createElement("div", {id: "pubslayer"}, BDV2.react.createElement(SettingsView, {
|
||||
onSetSection: (section) => {
|
||||
this.changeCategory(this.categorySlugs.indexOf(section))
|
||||
},
|
||||
sections: this.sections,
|
||||
onClose: this.close,
|
||||
section: this.state.section
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
get sections(){
|
||||
let sections = []
|
||||
sections.push({
|
||||
section: "HEADER",
|
||||
label: "Public Servers"
|
||||
}, {
|
||||
section: "DIVIDER"
|
||||
}, {
|
||||
section: "CUSTOM",
|
||||
element: this.searchInput.bind(null, () => this, this.searchKeyDown)
|
||||
}, {
|
||||
section: "DIVIDER"
|
||||
}, {
|
||||
section: "HEADER",
|
||||
label: "Categories"
|
||||
}, ...this.categoryButtons.map((value, index) => {
|
||||
return {
|
||||
section: this.categorySlugs[index],
|
||||
label: value,
|
||||
element: this.content.bind(null, () => this)
|
||||
}
|
||||
}), {
|
||||
section: "DIVIDER"
|
||||
}, {
|
||||
section: "CUSTOM",
|
||||
element: this.footer
|
||||
}, {
|
||||
section: "CUSTOM",
|
||||
element: this.connection.bind(null, () => this)
|
||||
})
|
||||
return sections
|
||||
}
|
||||
|
||||
searchInput(getThis, searchKeyDown) {
|
||||
const [value, setValue] = BDV2.react.useState("")
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-form-item"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-text-input flex-vertical", style: {width: "172px", marginLeft: "10px"}},
|
||||
BDV2.react.createElement("input", {
|
||||
ref: (serchinput) => (getThis().refs.searchinput = serchinput),
|
||||
onKeyDown: searchKeyDown,
|
||||
onChange: (e) => {
|
||||
setValue(e.target.value)
|
||||
},
|
||||
type: "text",
|
||||
className: "input default",
|
||||
placeholder: "Search...",
|
||||
maxLength: "50",
|
||||
value: value
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
searchKeyDown(e) {
|
||||
const self = this;
|
||||
if (self.state.loading || e.which !== 13) return;
|
||||
self.setState({
|
||||
loading: true,
|
||||
title: "Loading...",
|
||||
term: e.target.value
|
||||
});
|
||||
let query = `?term=${e.target.value}`;
|
||||
if (self.state.selectedCategory !== 0) {
|
||||
query += `&category=${self.categoryButtons[self.state.selectedCategory]}`;
|
||||
}
|
||||
self.search(query, true);
|
||||
}
|
||||
|
||||
get categorySlugs(){
|
||||
return this.categoryButtons.map(e => e.toLowerCase().replace(/[^\w\d]+/g, "_"))
|
||||
}
|
||||
|
||||
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"];
|
||||
}
|
||||
|
||||
changeCategory(id) {
|
||||
const self = this;
|
||||
if (self.state.loading) return;
|
||||
self.setState({
|
||||
loading: true,
|
||||
selectedCategory: id,
|
||||
title: "Loading...",
|
||||
term: null,
|
||||
section: self.categorySlugs[id]
|
||||
});
|
||||
if (id === 0) {
|
||||
self.search("", true);
|
||||
return;
|
||||
}
|
||||
self.search(`?category=${self.categoryButtons[id]}`, true);
|
||||
}
|
||||
|
||||
content(getThis) {
|
||||
const self = getThis();
|
||||
self.useState()
|
||||
const guildList = self.SortedGuildStore.getFlattenedGuildIds();
|
||||
const defaultList = self.AvatarDefaults.DEFAULT_AVATARS;
|
||||
if (self.state.connection.state === 1) return BDV2.react.createElement(self.notConnected.bind(null, getThis));
|
||||
let columnModule = BDModules.get(e => e.contentColumnDefault)[0]
|
||||
return [BDV2.react.createElement(
|
||||
"div",
|
||||
{ref: (ref) => {
|
||||
(self.refs.content = ref)
|
||||
}, key: "pc", className: columnModule.contentColumn+" "+columnModule.contentColumn+" content-column default"},
|
||||
BDV2.react.createElement(SettingsTitle, {text: self.state.title}),
|
||||
self.bdServer,
|
||||
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)]});
|
||||
}),
|
||||
self.state.next && BDV2.react.createElement(
|
||||
"button",
|
||||
{type: "button", onClick: () => {
|
||||
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"}},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-button-contents"},
|
||||
self.state.loading ? "Loading" : "Load More"
|
||||
)
|
||||
),
|
||||
self.state.servers.length > 0 && BDV2.react.createElement(SettingsTitle, {text: self.state.title})
|
||||
)];
|
||||
}
|
||||
|
||||
notConnected(getThis) {
|
||||
const self = getThis();
|
||||
//return BDV2.react.createElement(SettingsTitle, { text: self.state.title });
|
||||
let columnModule = BDModules.get(e => e.contentColumnDefault)[0]
|
||||
return [BDV2.react.createElement(
|
||||
"div",
|
||||
{key: "ncc", ref: (ref) => (self.refs.content = ref), className: columnModule.contentColumn+" "+columnModule.contentColumn+" content-column default"},
|
||||
BDV2.react.createElement(
|
||||
"h2",
|
||||
{className: "ui-form-title h2 margin-reset margin-bottom-20"},
|
||||
"Not connected to discordservers.com!",
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{
|
||||
onClick: self.connect,
|
||||
type: "button",
|
||||
className: "ui-button filled brand small grow",
|
||||
style: {
|
||||
display: "inline-block",
|
||||
minHeight: "18px",
|
||||
marginLeft: "10px",
|
||||
lineHeight: "14px"
|
||||
}
|
||||
},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-button-contents"},
|
||||
"Connect"
|
||||
)
|
||||
)
|
||||
), self.bdServer
|
||||
)];
|
||||
}
|
||||
|
||||
footer() {
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-tab-bar-header"},
|
||||
BDV2.react.createElement(
|
||||
"a",
|
||||
{href: "https://discordservers.com", target: "_blank"},
|
||||
"Discordservers.com"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
useState(){
|
||||
const forceUpdate = useForceUpdate()
|
||||
BDV2.React.useEffect(() => {
|
||||
const listener = () => {
|
||||
forceUpdate()
|
||||
}
|
||||
this.hooks.push(listener)
|
||||
return () => {
|
||||
const index = this.hooks.findIndex(e => e===listener)
|
||||
if(index < 0)return
|
||||
this.hooks.splice(index, 1)
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
|
||||
connection(getThis) {
|
||||
const self = getThis();
|
||||
self.useState()
|
||||
const {connection} = self.state;
|
||||
if (connection.state !== 2) return BDV2.react.createElement("span", null);
|
||||
|
||||
return BDV2.react.createElement(
|
||||
"span",
|
||||
null,
|
||||
BDV2.react.createElement(TabBarSeparator, null),
|
||||
BDV2.react.createElement(
|
||||
"span",
|
||||
{style: {color: "#b9bbbe", fontSize: "10px", marginLeft: "10px"}},
|
||||
"Connected as: ",
|
||||
`${connection.user.username}#${connection.user.discriminator}`
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{style: {padding: "5px 10px 0 10px"}},
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {width: "100%", minHeight: "20px"}, type: "button", className: "ui-button filled brand small grow"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-button-contents", onClick: self.connect},
|
||||
"Reconnect"
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
import BDV2 from "../../modules/v2";
|
||||
|
||||
import SettingsTitle from "../settingsTitle";
|
||||
import TabBarSeparator from "../tabBarSeparator";
|
||||
|
||||
import ServerCard from "./serverCard";
|
||||
import { useForceUpdate } from "../../modules/hooks";
|
||||
|
||||
let SettingsView
|
||||
export default class V2C_PublicServers extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.setInitialState();
|
||||
this.close = this.close.bind(this);
|
||||
this.changeCategory = this.changeCategory.bind(this);
|
||||
this.search = this.search.bind(this);
|
||||
this.searchKeyDown = this.searchKeyDown.bind(this);
|
||||
this.checkConnection = this.checkConnection.bind(this);
|
||||
this.join = this.join.bind(this);
|
||||
this.connect = this.connect.bind(this);
|
||||
|
||||
this.GuildStore = BDV2.WebpackModules.findByUniqueProperties(["getGuilds"]);
|
||||
this.AvatarDefaults = BDV2.WebpackModules.findByUniqueProperties(["getUserAvatarURL", "DEFAULT_AVATARS"]);
|
||||
this.InviteActions = BDV2.WebpackModules.findByUniqueProperties(["acceptInvite"]);
|
||||
this.SortedGuildStore = BDV2.WebpackModules.findByUniqueProperties(["getSortedGuilds"]);
|
||||
|
||||
this.hooks = []
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.checkConnection();
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
this.state = {
|
||||
selectedCategory: -1,
|
||||
title: "Loading...",
|
||||
loading: true,
|
||||
servers: [],
|
||||
next: null,
|
||||
connection: {
|
||||
state: 0,
|
||||
user: null
|
||||
},
|
||||
section: this.categorySlugs[0],
|
||||
theme: "dark"
|
||||
};
|
||||
}
|
||||
|
||||
close() {
|
||||
this.props.close()
|
||||
}
|
||||
|
||||
search(query, clear) {
|
||||
const self = this;
|
||||
fetch(`${self.searchEndPoint}${query}${query ? "&schema=new" : "?schema=new"}`, {
|
||||
method: "get"
|
||||
}).then(async res => {
|
||||
if(res.status !== 200)throw await res.text()
|
||||
let data = await res.json()
|
||||
|
||||
let servers = data.results.reduce((arr, server) => {
|
||||
server.joined = false;
|
||||
arr.push(server);
|
||||
// arr.push(<ServerCard server={server} join={self.join}/>);
|
||||
return arr;
|
||||
}, []);
|
||||
|
||||
if (!clear) {
|
||||
servers = self.state.servers.concat(servers);
|
||||
}
|
||||
else {
|
||||
//servers.unshift(self.bdServer);
|
||||
}
|
||||
|
||||
let end = data.size + data.from;
|
||||
data.next = `?from=${end}`;
|
||||
if (self.state.term) data.next += `&term=${self.state.term}`;
|
||||
if (self.state.selectedCategory) data.next += `&category=${self.categoryButtons[self.state.selectedCategory]}`;
|
||||
if (end >= data.total) {
|
||||
end = data.total;
|
||||
data.next = null;
|
||||
}
|
||||
|
||||
let title = `Showing 1-${end} of ${data.total} results in ${self.categoryButtons[self.state.selectedCategory]}`;
|
||||
if (self.state.term) title += ` for ${self.state.term}`;
|
||||
|
||||
self.setState({
|
||||
loading: false,
|
||||
title: title,
|
||||
servers: servers,
|
||||
next: data.next
|
||||
});
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
return self.setState({
|
||||
loading: false,
|
||||
title: "Failed to load servers. Check console for details"
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
async join(serverCard) {
|
||||
if (serverCard.props.pinned) return this.InviteActions.acceptInvite(serverCard.props.invite_code);
|
||||
|
||||
await fetch(`${this.joinEndPoint}/${serverCard.props.server.identifier}`,{
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
mode: "cors",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
|
||||
serverCard.setState({joined: true});
|
||||
}
|
||||
|
||||
connect() {
|
||||
const self = this;
|
||||
const options = self.windowOptions;
|
||||
options.x = Math.round(window.screenX + window.innerWidth / 2 - options.width / 2);
|
||||
options.y = Math.round(window.screenY + window.innerHeight / 2 - options.height / 2);
|
||||
|
||||
const 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";
|
||||
win.webContents.on("did-navigate", (event, url) => {
|
||||
if (url != "https://auth.discordservers.com/info") return;
|
||||
win.close();
|
||||
self.checkConnection();
|
||||
});
|
||||
win.loadURL(url);
|
||||
win.setMenuBarVisibility(false)
|
||||
}
|
||||
|
||||
get windowOptions() {
|
||||
return {
|
||||
width: 500,
|
||||
height: 550,
|
||||
backgroundColor: "#282b30",
|
||||
show: true,
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
minimizable: false,
|
||||
alwaysOnTop: true,
|
||||
frame: true,
|
||||
center: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
get bdServer() {
|
||||
const server = {
|
||||
name: "BetterDiscord",
|
||||
online: "30000+",
|
||||
members: "70000+",
|
||||
categories: ["community", "programming", "support"],
|
||||
description: "Official BetterDiscord server for support etc",
|
||||
identifier: "86004744966914048",
|
||||
iconUrl: "https://cdn.discordapp.com/icons/86004744966914048/292e7f6bfff2b71dfd13e508a859aedd.webp",
|
||||
nativejoin: true,
|
||||
invite_code: "0Tmfo5ZbORCRqbAd",
|
||||
pinned: true
|
||||
};
|
||||
const server2 = {
|
||||
name: "Lightcord",
|
||||
online: "100+",
|
||||
members: "300+",
|
||||
categories: ["community", "programming", "support"],
|
||||
description: "Official Lightcord server for support etc",
|
||||
identifier: "705908350218666117",
|
||||
iconUrl: "https://github.com/lightcord.png",
|
||||
nativejoin: true,
|
||||
invite_code: "7eFff2A",
|
||||
pinned: true
|
||||
};
|
||||
const guildList = this.SortedGuildStore.getFlattenedGuildIds();
|
||||
const defaultList = this.AvatarDefaults.DEFAULT_AVATARS;
|
||||
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: server, pinned: true, join: this.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]})
|
||||
]
|
||||
}
|
||||
|
||||
get searchEndPoint() {
|
||||
return "https://search.discordservers.com";
|
||||
}
|
||||
|
||||
get joinEndPoint() {
|
||||
return "https://j.discordservers.com";
|
||||
}
|
||||
|
||||
get connectEndPoint() {
|
||||
return "https://join.discordservers.com/connect";
|
||||
}
|
||||
|
||||
async checkConnection() {
|
||||
const self = this;
|
||||
try {
|
||||
const response = await fetch(`https://auth.discordservers.com/info`,{
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
mode: "cors",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
const text = await response.text()
|
||||
if(!text){
|
||||
self.setState({
|
||||
title: "Not connected to discordservers.com!",
|
||||
loading: true,
|
||||
selectedCategory: -1,
|
||||
connection: {
|
||||
state: 1,
|
||||
user: null
|
||||
}
|
||||
});
|
||||
return
|
||||
}
|
||||
const data = JSON.parse(text)
|
||||
self.setState({
|
||||
selectedCategory: 0,
|
||||
connection: {
|
||||
state: 2,
|
||||
user: data
|
||||
}
|
||||
});
|
||||
self.search("", true);
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error)
|
||||
self.setState({
|
||||
title: "Not connected to discordservers.com!",
|
||||
loading: true,
|
||||
selectedCategory: -1,
|
||||
connection: {
|
||||
state: 1,
|
||||
user: null
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
this.hooks.forEach((e) => e())
|
||||
SettingsView = SettingsView || BDV2.WebpackModules.findByDisplayName("SettingsView")
|
||||
return BDV2.react.createElement("div", {id: "pubslayerroot"},
|
||||
BDV2.react.createElement("div", {id: "pubslayer"}, BDV2.react.createElement(SettingsView, {
|
||||
onSetSection: (section) => {
|
||||
this.changeCategory(this.categorySlugs.indexOf(section))
|
||||
},
|
||||
sections: this.sections,
|
||||
onClose: this.close,
|
||||
section: this.state.section
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
get sections(){
|
||||
let sections = []
|
||||
sections.push({
|
||||
section: "HEADER",
|
||||
label: "Public Servers"
|
||||
}, {
|
||||
section: "DIVIDER"
|
||||
}, {
|
||||
section: "CUSTOM",
|
||||
element: this.searchInput.bind(null, () => this, this.searchKeyDown)
|
||||
}, {
|
||||
section: "DIVIDER"
|
||||
}, {
|
||||
section: "HEADER",
|
||||
label: "Categories"
|
||||
}, ...this.categoryButtons.map((value, index) => {
|
||||
return {
|
||||
section: this.categorySlugs[index],
|
||||
label: value,
|
||||
element: this.content.bind(null, () => this)
|
||||
}
|
||||
}), {
|
||||
section: "DIVIDER"
|
||||
}, {
|
||||
section: "CUSTOM",
|
||||
element: this.footer
|
||||
}, {
|
||||
section: "CUSTOM",
|
||||
element: this.connection.bind(null, () => this)
|
||||
})
|
||||
return sections
|
||||
}
|
||||
|
||||
searchInput(getThis, searchKeyDown) {
|
||||
const [value, setValue] = BDV2.react.useState("")
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-form-item"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-text-input flex-vertical", style: {width: "172px", marginLeft: "10px"}},
|
||||
BDV2.react.createElement("input", {
|
||||
ref: (serchinput) => (getThis().refs.searchinput = serchinput),
|
||||
onKeyDown: searchKeyDown,
|
||||
onChange: (e) => {
|
||||
setValue(e.target.value)
|
||||
},
|
||||
type: "text",
|
||||
className: "input default",
|
||||
placeholder: "Search...",
|
||||
maxLength: "50",
|
||||
value: value
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
searchKeyDown(e) {
|
||||
const self = this;
|
||||
if (self.state.loading || e.which !== 13) return;
|
||||
self.setState({
|
||||
loading: true,
|
||||
title: "Loading...",
|
||||
term: e.target.value
|
||||
});
|
||||
let query = `?term=${e.target.value}`;
|
||||
if (self.state.selectedCategory !== 0) {
|
||||
query += `&category=${self.categoryButtons[self.state.selectedCategory]}`;
|
||||
}
|
||||
self.search(query, true);
|
||||
}
|
||||
|
||||
get categorySlugs(){
|
||||
return this.categoryButtons.map(e => e.toLowerCase().replace(/[^\w\d]+/g, "_"))
|
||||
}
|
||||
|
||||
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"];
|
||||
}
|
||||
|
||||
changeCategory(id) {
|
||||
const self = this;
|
||||
if (self.state.loading) return;
|
||||
self.setState({
|
||||
loading: true,
|
||||
selectedCategory: id,
|
||||
title: "Loading...",
|
||||
term: null,
|
||||
section: self.categorySlugs[id]
|
||||
});
|
||||
if (id === 0) {
|
||||
self.search("", true);
|
||||
return;
|
||||
}
|
||||
self.search(`?category=${self.categoryButtons[id]}`, true);
|
||||
}
|
||||
|
||||
content(getThis) {
|
||||
const self = getThis();
|
||||
self.useState()
|
||||
const guildList = self.SortedGuildStore.getFlattenedGuildIds();
|
||||
const defaultList = self.AvatarDefaults.DEFAULT_AVATARS;
|
||||
if (self.state.connection.state === 1) return BDV2.react.createElement(self.notConnected.bind(null, getThis));
|
||||
let columnModule = BDModules.get(e => e.contentColumnDefault)[0]
|
||||
return [BDV2.react.createElement(
|
||||
"div",
|
||||
{ref: (ref) => {
|
||||
(self.refs.content = ref)
|
||||
}, key: "pc", className: columnModule.contentColumn+" "+columnModule.contentColumn+" content-column default"},
|
||||
BDV2.react.createElement(SettingsTitle, {text: self.state.title}),
|
||||
self.bdServer,
|
||||
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)]});
|
||||
}),
|
||||
self.state.next && BDV2.react.createElement(
|
||||
"button",
|
||||
{type: "button", onClick: () => {
|
||||
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"}},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-button-contents"},
|
||||
self.state.loading ? "Loading" : "Load More"
|
||||
)
|
||||
),
|
||||
self.state.servers.length > 0 && BDV2.react.createElement(SettingsTitle, {text: self.state.title})
|
||||
)];
|
||||
}
|
||||
|
||||
notConnected(getThis) {
|
||||
const self = getThis();
|
||||
//return BDV2.react.createElement(SettingsTitle, { text: self.state.title });
|
||||
let columnModule = BDModules.get(e => e.contentColumnDefault)[0]
|
||||
return [BDV2.react.createElement(
|
||||
"div",
|
||||
{key: "ncc", ref: (ref) => (self.refs.content = ref), className: columnModule.contentColumn+" "+columnModule.contentColumn+" content-column default"},
|
||||
BDV2.react.createElement(
|
||||
"h2",
|
||||
{className: "ui-form-title h2 margin-reset margin-bottom-20"},
|
||||
"Not connected to discordservers.com!",
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{
|
||||
onClick: self.connect,
|
||||
type: "button",
|
||||
className: "ui-button filled brand small grow",
|
||||
style: {
|
||||
display: "inline-block",
|
||||
minHeight: "18px",
|
||||
marginLeft: "10px",
|
||||
lineHeight: "14px"
|
||||
}
|
||||
},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-button-contents"},
|
||||
"Connect"
|
||||
)
|
||||
)
|
||||
), self.bdServer
|
||||
)];
|
||||
}
|
||||
|
||||
footer() {
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-tab-bar-header"},
|
||||
BDV2.react.createElement(
|
||||
"a",
|
||||
{href: "https://discordservers.com", target: "_blank"},
|
||||
"Discordservers.com"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
useState(){
|
||||
const forceUpdate = useForceUpdate()
|
||||
BDV2.React.useEffect(() => {
|
||||
const listener = () => {
|
||||
forceUpdate()
|
||||
}
|
||||
this.hooks.push(listener)
|
||||
return () => {
|
||||
const index = this.hooks.findIndex(e => e===listener)
|
||||
if(index < 0)return
|
||||
this.hooks.splice(index, 1)
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
|
||||
connection(getThis) {
|
||||
const self = getThis();
|
||||
self.useState()
|
||||
const {connection} = self.state;
|
||||
if (connection.state !== 2) return BDV2.react.createElement("span", null);
|
||||
|
||||
return BDV2.react.createElement(
|
||||
"span",
|
||||
null,
|
||||
BDV2.react.createElement(TabBarSeparator, null),
|
||||
BDV2.react.createElement(
|
||||
"span",
|
||||
{style: {color: "#b9bbbe", fontSize: "10px", marginLeft: "10px"}},
|
||||
"Connected as: ",
|
||||
`${connection.user.username}#${connection.user.discriminator}`
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{style: {padding: "5px 10px 0 10px"}},
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {width: "100%", minHeight: "20px"}, type: "button", className: "ui-button filled brand small grow"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-button-contents", onClick: self.connect},
|
||||
"Reconnect"
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,47 +1,47 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_Scroller extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
//scrollerWrap-2lJEkd scrollerThemed-2oenus themeGhostHairline-DBD-2d scrollerFade-1Ijw5y
|
||||
const scrollerModule1 = BDModules.get(e => e.scrollerWrap)[0]
|
||||
const scrollerModule2 = BDModules.get(e => e.sidebarRegionScroller)[0]
|
||||
const scrollerTypes = BDModules.get(e => e.thin)[0]
|
||||
let wrapperClass = `${scrollerModule1.scrollerWrap} ${scrollerModule1.scrollerThemed} ${scrollerModule1.themeGhostHairline}${this.props.fade ? " "+scrollerModule1.scrollerFade : ""}`;
|
||||
let scrollerClass = scrollerModule1.scroller+" scroller"; /* fuck */
|
||||
if (this.props.sidebar) scrollerClass += ` ${scrollerModule2.sidebarRegionScroller} ${scrollerTypes.thin} ${scrollerTypes.scrollerBase} ${scrollerTypes.fade}`
|
||||
if (this.props.contentColumn) {
|
||||
scrollerClass += `${scrollerModule2.contentRegionScroller} ${scrollerTypes.auto} content-region-scroller scroller`
|
||||
wrapperClass = `${scrollerModule2.contentTransitionWrap}`;
|
||||
}
|
||||
const {children} = this.props;
|
||||
if (this.props.sidebar) {
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{key: "scroller", ref: "scroller", className: scrollerClass},
|
||||
children
|
||||
);
|
||||
}
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{key: "scrollerwrap", className: wrapperClass},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{key: "scroller", ref: "scroller", className: scrollerClass},
|
||||
children
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const originalRender = V2C_Scroller.prototype.render;
|
||||
Object.defineProperty(V2C_Scroller.prototype, "render", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");},
|
||||
get: () => originalRender
|
||||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_Scroller extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
//scrollerWrap-2lJEkd scrollerThemed-2oenus themeGhostHairline-DBD-2d scrollerFade-1Ijw5y
|
||||
const scrollerModule1 = BDModules.get(e => e.scrollerWrap)[0]
|
||||
const scrollerModule2 = BDModules.get(e => e.sidebarRegionScroller)[0]
|
||||
const scrollerTypes = BDModules.get(e => e.thin)[0]
|
||||
let wrapperClass = `${scrollerModule1.scrollerWrap} ${scrollerModule1.scrollerThemed} ${scrollerModule1.themeGhostHairline}${this.props.fade ? " "+scrollerModule1.scrollerFade : ""}`;
|
||||
let scrollerClass = scrollerModule1.scroller+" scroller"; /* fuck */
|
||||
if (this.props.sidebar) scrollerClass += ` ${scrollerModule2.sidebarRegionScroller} ${scrollerTypes.thin} ${scrollerTypes.scrollerBase} ${scrollerTypes.fade}`
|
||||
if (this.props.contentColumn) {
|
||||
scrollerClass += `${scrollerModule2.contentRegionScroller} ${scrollerTypes.auto} content-region-scroller scroller`
|
||||
wrapperClass = `${scrollerModule2.contentTransitionWrap}`;
|
||||
}
|
||||
const {children} = this.props;
|
||||
if (this.props.sidebar) {
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{key: "scroller", ref: "scroller", className: scrollerClass},
|
||||
children
|
||||
);
|
||||
}
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{key: "scrollerwrap", className: wrapperClass},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{key: "scroller", ref: "scroller", className: scrollerClass},
|
||||
children
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const originalRender = V2C_Scroller.prototype.render;
|
||||
Object.defineProperty(V2C_Scroller.prototype, "render", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");},
|
||||
get: () => originalRender
|
||||
});
|
|
@ -1,91 +1,91 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "../modules/v2";
|
||||
|
||||
import SettingsTitle from "./settingsTitle";
|
||||
import Switch from "./switch";
|
||||
import MarginTop from "./margintop";
|
||||
|
||||
let formModule
|
||||
let switchItem
|
||||
let betaClassNames
|
||||
export default class V2C_SettingsGroup extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
error: false
|
||||
}
|
||||
}
|
||||
|
||||
renderOldSettings() {
|
||||
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;
|
||||
return [BDV2.react.createElement(SettingsTitle, {text: title}),
|
||||
buttonComponent,
|
||||
settings.map(setting => {
|
||||
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);
|
||||
}});
|
||||
})];
|
||||
}
|
||||
|
||||
componentDidCatch(err, errInfo){
|
||||
console.log(err, errInfo)
|
||||
this.setState({
|
||||
error: true
|
||||
})
|
||||
}
|
||||
|
||||
render(){
|
||||
if(this.state.error){
|
||||
try{
|
||||
return this.renderOldSettings()
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
return null
|
||||
}
|
||||
}else{
|
||||
try{
|
||||
if(!formModule)formModule = BDV2.WebpackModules.find(e => e.FormSection)
|
||||
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))
|
||||
|
||||
let children = []
|
||||
if(this.props.description){
|
||||
children.push(<formModule.FormText type="description" selectable={false}>
|
||||
{this.props.description}
|
||||
</formModule.FormText>, <MarginTop></MarginTop>)
|
||||
}
|
||||
children.push(...this.props.settings.map(setting => {
|
||||
let info = [
|
||||
setting.text
|
||||
]
|
||||
if(setting.experimental){
|
||||
info.push(<sup className={betaClassNames.beta}>(EXPERIMENTAL)</sup>)
|
||||
}
|
||||
return <switchItem.default onChange={(ev) => {
|
||||
this.props.onChange(setting.id, ev.target.checked);
|
||||
this.forceUpdate()
|
||||
}} key={setting.id} value={settingsCookie[setting.id]} className={__SECRET_EMOTION__.css({
|
||||
marginBottom: "20px"
|
||||
})} disabled={false} hideBorder={false}
|
||||
size={switchItem.default.Sizes.DEFAULT} theme={switchItem.default.Themes.DEFAULT} note={setting.info}>
|
||||
{info}
|
||||
</switchItem.default>
|
||||
}))
|
||||
return <formModule.FormSection tag="h2" title={this.props.title}>
|
||||
{children}
|
||||
</formModule.FormSection>
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
setImmediate(()=>{
|
||||
this.setState({
|
||||
error: true
|
||||
})
|
||||
})
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "../modules/v2";
|
||||
|
||||
import SettingsTitle from "./settingsTitle";
|
||||
import Switch from "./switch";
|
||||
import MarginTop from "./margintop";
|
||||
|
||||
let formModule
|
||||
let switchItem
|
||||
let betaClassNames
|
||||
export default class V2C_SettingsGroup extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
error: false
|
||||
}
|
||||
}
|
||||
|
||||
renderOldSettings() {
|
||||
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;
|
||||
return [BDV2.react.createElement(SettingsTitle, {text: title}),
|
||||
buttonComponent,
|
||||
settings.map(setting => {
|
||||
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);
|
||||
}});
|
||||
})];
|
||||
}
|
||||
|
||||
componentDidCatch(err, errInfo){
|
||||
console.log(err, errInfo)
|
||||
this.setState({
|
||||
error: true
|
||||
})
|
||||
}
|
||||
|
||||
render(){
|
||||
if(this.state.error){
|
||||
try{
|
||||
return this.renderOldSettings()
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
return null
|
||||
}
|
||||
}else{
|
||||
try{
|
||||
if(!formModule)formModule = BDV2.WebpackModules.find(e => e.FormSection)
|
||||
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))
|
||||
|
||||
let children = []
|
||||
if(this.props.description){
|
||||
children.push(<formModule.FormText type="description" selectable={false}>
|
||||
{this.props.description}
|
||||
</formModule.FormText>, <MarginTop></MarginTop>)
|
||||
}
|
||||
children.push(...this.props.settings.map(setting => {
|
||||
let info = [
|
||||
setting.text
|
||||
]
|
||||
if(setting.experimental){
|
||||
info.push(<sup className={betaClassNames.beta}>(EXPERIMENTAL)</sup>)
|
||||
}
|
||||
return <switchItem.default onChange={(ev) => {
|
||||
this.props.onChange(setting.id, ev.target.checked);
|
||||
this.forceUpdate()
|
||||
}} key={setting.id} value={settingsCookie[setting.id]} className={__SECRET_EMOTION__.css({
|
||||
marginBottom: "20px"
|
||||
})} disabled={false} hideBorder={false}
|
||||
size={switchItem.default.Sizes.DEFAULT} theme={switchItem.default.Themes.DEFAULT} note={setting.info}>
|
||||
{info}
|
||||
</switchItem.default>
|
||||
}))
|
||||
return <formModule.FormSection tag="h2" title={this.props.title}>
|
||||
{children}
|
||||
</formModule.FormSection>
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
setImmediate(()=>{
|
||||
this.setState({
|
||||
error: true
|
||||
})
|
||||
})
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,44 +1,44 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
import Switch from "./components/switch";
|
||||
|
||||
let classnames = []
|
||||
function getClassName(name){
|
||||
let className = classnames.find(e => e.startsWith(name+"-"))
|
||||
if(className)return className
|
||||
className = BDModules.get(e => e[name])[0][name]
|
||||
classnames.push(className)
|
||||
return className
|
||||
}
|
||||
|
||||
export default class SwitchItem extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
}
|
||||
|
||||
onChange() {
|
||||
this.props.checked = !this.props.checked;
|
||||
this.props.onChange(this.props.id, this.props.checked);
|
||||
}
|
||||
|
||||
render() {
|
||||
let {text, info} = this.props.data;
|
||||
if(!classnames.find(e => e.startsWith("beta-"))){
|
||||
classnames.push(BDModules.get(e => e.beta && (!e.container && !e.userSettingsVoice))[0].beta)
|
||||
}
|
||||
if(this.props.data.experimental){
|
||||
info = [
|
||||
info,
|
||||
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"},
|
||||
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(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)
|
||||
);
|
||||
}
|
||||
import BDV2 from "../modules/v2";
|
||||
import Switch from "./components/switch";
|
||||
|
||||
let classnames = []
|
||||
function getClassName(name){
|
||||
let className = classnames.find(e => e.startsWith(name+"-"))
|
||||
if(className)return className
|
||||
className = BDModules.get(e => e[name])[0][name]
|
||||
classnames.push(className)
|
||||
return className
|
||||
}
|
||||
|
||||
export default class SwitchItem extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
}
|
||||
|
||||
onChange() {
|
||||
this.props.checked = !this.props.checked;
|
||||
this.props.onChange(this.props.id, this.props.checked);
|
||||
}
|
||||
|
||||
render() {
|
||||
let {text, info} = this.props.data;
|
||||
if(!classnames.find(e => e.startsWith("beta-"))){
|
||||
classnames.push(BDModules.get(e => e.beta && (!e.container && !e.userSettingsVoice))[0].beta)
|
||||
}
|
||||
if(this.props.data.experimental){
|
||||
info = [
|
||||
info,
|
||||
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"},
|
||||
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(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)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,191 +1,191 @@
|
|||
/**
|
||||
* 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
|
||||
* a MutationObserver.
|
||||
*
|
||||
* Note this is not using Discord's internals but normal DOM manipulation and emulates
|
||||
* Discord's own tooltips as closely as possible.
|
||||
*
|
||||
* @module EmulatedTooltip
|
||||
* @version 0.0.1
|
||||
*/
|
||||
|
||||
import Utils from "../modules/utils";
|
||||
import WebpackModules from "../modules/webpackModules";
|
||||
let TooltipClasses
|
||||
function getTooltipClasses(){
|
||||
if(TooltipClasses)return TooltipClasses
|
||||
return TooltipClasses = WebpackModules.findByProps("tooltip", "tooltipBlack");
|
||||
}
|
||||
let TooltipLayers
|
||||
function getTooltipLayers(){
|
||||
if(TooltipLayers)return TooltipLayers
|
||||
return TooltipLayers = WebpackModules.findByProps("layer", "layerContainer");
|
||||
}
|
||||
|
||||
const getClass = function(sideOrColor) {
|
||||
const upperCase = sideOrColor[0].toUpperCase() + sideOrColor.slice(1);
|
||||
const tooltipClass = getTooltipClasses()[`tooltip${upperCase}`];
|
||||
if (tooltipClass) return tooltipClass;
|
||||
return null;
|
||||
};
|
||||
|
||||
const classExists = function(sideOrColor) {
|
||||
return getClass(sideOrColor) ? true : false;
|
||||
};
|
||||
|
||||
const toPx = function(value) {
|
||||
return `${value}px`;
|
||||
};
|
||||
|
||||
export default class EmulatedTooltip {
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
* @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on
|
||||
* @param {string} tip - string to show in 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.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.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.
|
||||
*/
|
||||
constructor(node, text, options = {}) {
|
||||
const {style = "black", side = "top", preventFlip = false, disabled = false, attachEvents = true} = options;
|
||||
this.node = node instanceof jQuery ? node[0] : node;
|
||||
this.label = text;
|
||||
this.style = style.toLowerCase();
|
||||
this.side = side.toLowerCase();
|
||||
this.preventFlip = preventFlip;
|
||||
this.disabled = disabled;
|
||||
|
||||
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.`);
|
||||
|
||||
this.element = document.createElement("div");
|
||||
this.element.className = getTooltipLayers().layer + " " + getTooltipLayers().disabledPointerEvents;
|
||||
|
||||
this.tooltipElement = document.createElement("div");
|
||||
this.tooltipElement.className = `${getTooltipClasses().tooltip} ${getClass(this.style)}`;
|
||||
|
||||
this.labelElement = document.createElement("div");
|
||||
this.labelElement.className = getTooltipClasses().tooltipContent
|
||||
|
||||
const pointerElement = document.createElement("div");
|
||||
pointerElement.className = getTooltipClasses().tooltipPointer;
|
||||
|
||||
this.tooltipElement.append(pointerElement);
|
||||
this.tooltipElement.append(this.labelElement);
|
||||
this.element.append(this.tooltipElement);
|
||||
|
||||
if(attachEvents){
|
||||
this.node.addEventListener("mouseenter", () => {
|
||||
if (this.disabled) return;
|
||||
this.show();
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
const nodes = Array.from(mutation.removedNodes);
|
||||
const directMatch = nodes.indexOf(this.node) > -1;
|
||||
const parentMatch = nodes.some(parent => parent.contains(this.node));
|
||||
if (directMatch || parentMatch) {
|
||||
this.hide();
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, {subtree: true, childList: true});
|
||||
});
|
||||
|
||||
this.node.addEventListener("mouseleave", () => {
|
||||
this.hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Container where the tooltip will be appended. */
|
||||
get container() {
|
||||
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 */
|
||||
get canShowAbove() { return this.node.getBoundingClientRect().top - this.element.offsetHeight >= 0; }
|
||||
/** 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; }
|
||||
/** 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; }
|
||||
/** 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; }
|
||||
|
||||
/** Hides the tooltip. Automatically called on mouseleave. */
|
||||
hide() {
|
||||
this.element.remove();
|
||||
this.tooltipElement.className = this._className;
|
||||
}
|
||||
|
||||
/** Shows the tooltip. Automatically called on mouseenter. Will attempt to flip if position was wrong. */
|
||||
show() {
|
||||
this.tooltipElement.className = `${getTooltipClasses().tooltip} ${getClass(this.style)}`;
|
||||
this.labelElement.textContent = this.label;
|
||||
this.container.append(this.element);
|
||||
|
||||
if (this.side == "top") {
|
||||
if (this.canShowAbove || (!this.canShowAbove && this.preventFlip)) this.showAbove();
|
||||
else this.showBelow();
|
||||
}
|
||||
|
||||
if (this.side == "bottom") {
|
||||
if (this.canShowBelow || (!this.canShowBelow && this.preventFlip)) this.showBelow();
|
||||
else this.showAbove();
|
||||
}
|
||||
|
||||
if (this.side == "left") {
|
||||
if (this.canShowLeft || (!this.canShowLeft && this.preventFlip)) this.showLeft();
|
||||
else this.showRight();
|
||||
}
|
||||
|
||||
if (this.side == "right") {
|
||||
if (this.canShowRight || (!this.canShowRight && this.preventFlip)) this.showRight();
|
||||
else this.showLeft();
|
||||
}
|
||||
}
|
||||
|
||||
/** Force showing the tooltip above the node. */
|
||||
showAbove() {
|
||||
this.tooltipElement.classList.add(getClass("top"));
|
||||
this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top - this.element.offsetHeight - 10));
|
||||
this.centerHorizontally();
|
||||
}
|
||||
|
||||
/** Force showing the tooltip below the node. */
|
||||
showBelow() {
|
||||
this.tooltipElement.classList.add(getClass("bottom"));
|
||||
this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top + this.node.offsetHeight + 10));
|
||||
this.centerHorizontally();
|
||||
}
|
||||
|
||||
/** Force showing the tooltip to the left of the node. */
|
||||
showLeft() {
|
||||
this.tooltipElement.classList.add(getClass("left"));
|
||||
this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left - this.element.offsetWidth - 10));
|
||||
this.centerVertically();
|
||||
}
|
||||
|
||||
/** Force showing the tooltip to the right of the node. */
|
||||
showRight() {
|
||||
this.tooltipElement.classList.add(getClass("right"));
|
||||
this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left + this.node.offsetWidth + 10));
|
||||
this.centerVertically();
|
||||
}
|
||||
|
||||
centerHorizontally() {
|
||||
const nodecenter = this.node.getBoundingClientRect().left + (this.node.offsetWidth / 2);
|
||||
this.element.style.setProperty("left", toPx(nodecenter - (this.element.offsetWidth / 2)));
|
||||
}
|
||||
|
||||
centerVertically() {
|
||||
const nodecenter = this.node.getBoundingClientRect().top + (this.node.offsetHeight / 2);
|
||||
this.element.style.setProperty("top", toPx(nodecenter - (this.element.offsetHeight / 2)));
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
* a MutationObserver.
|
||||
*
|
||||
* Note this is not using Discord's internals but normal DOM manipulation and emulates
|
||||
* Discord's own tooltips as closely as possible.
|
||||
*
|
||||
* @module EmulatedTooltip
|
||||
* @version 0.0.1
|
||||
*/
|
||||
|
||||
import Utils from "../modules/utils";
|
||||
import WebpackModules from "../modules/webpackModules";
|
||||
let TooltipClasses
|
||||
function getTooltipClasses(){
|
||||
if(TooltipClasses)return TooltipClasses
|
||||
return TooltipClasses = WebpackModules.findByProps("tooltip", "tooltipBlack");
|
||||
}
|
||||
let TooltipLayers
|
||||
function getTooltipLayers(){
|
||||
if(TooltipLayers)return TooltipLayers
|
||||
return TooltipLayers = WebpackModules.findByProps("layer", "layerContainer");
|
||||
}
|
||||
|
||||
const getClass = function(sideOrColor) {
|
||||
const upperCase = sideOrColor[0].toUpperCase() + sideOrColor.slice(1);
|
||||
const tooltipClass = getTooltipClasses()[`tooltip${upperCase}`];
|
||||
if (tooltipClass) return tooltipClass;
|
||||
return null;
|
||||
};
|
||||
|
||||
const classExists = function(sideOrColor) {
|
||||
return getClass(sideOrColor) ? true : false;
|
||||
};
|
||||
|
||||
const toPx = function(value) {
|
||||
return `${value}px`;
|
||||
};
|
||||
|
||||
export default class EmulatedTooltip {
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
* @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on
|
||||
* @param {string} tip - string to show in 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.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.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.
|
||||
*/
|
||||
constructor(node, text, options = {}) {
|
||||
const {style = "black", side = "top", preventFlip = false, disabled = false, attachEvents = true} = options;
|
||||
this.node = node instanceof jQuery ? node[0] : node;
|
||||
this.label = text;
|
||||
this.style = style.toLowerCase();
|
||||
this.side = side.toLowerCase();
|
||||
this.preventFlip = preventFlip;
|
||||
this.disabled = disabled;
|
||||
|
||||
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.`);
|
||||
|
||||
this.element = document.createElement("div");
|
||||
this.element.className = getTooltipLayers().layer + " " + getTooltipLayers().disabledPointerEvents;
|
||||
|
||||
this.tooltipElement = document.createElement("div");
|
||||
this.tooltipElement.className = `${getTooltipClasses().tooltip} ${getClass(this.style)}`;
|
||||
|
||||
this.labelElement = document.createElement("div");
|
||||
this.labelElement.className = getTooltipClasses().tooltipContent
|
||||
|
||||
const pointerElement = document.createElement("div");
|
||||
pointerElement.className = getTooltipClasses().tooltipPointer;
|
||||
|
||||
this.tooltipElement.append(pointerElement);
|
||||
this.tooltipElement.append(this.labelElement);
|
||||
this.element.append(this.tooltipElement);
|
||||
|
||||
if(attachEvents){
|
||||
this.node.addEventListener("mouseenter", () => {
|
||||
if (this.disabled) return;
|
||||
this.show();
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
const nodes = Array.from(mutation.removedNodes);
|
||||
const directMatch = nodes.indexOf(this.node) > -1;
|
||||
const parentMatch = nodes.some(parent => parent.contains(this.node));
|
||||
if (directMatch || parentMatch) {
|
||||
this.hide();
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, {subtree: true, childList: true});
|
||||
});
|
||||
|
||||
this.node.addEventListener("mouseleave", () => {
|
||||
this.hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Container where the tooltip will be appended. */
|
||||
get container() {
|
||||
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 */
|
||||
get canShowAbove() { return this.node.getBoundingClientRect().top - this.element.offsetHeight >= 0; }
|
||||
/** 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; }
|
||||
/** 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; }
|
||||
/** 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; }
|
||||
|
||||
/** Hides the tooltip. Automatically called on mouseleave. */
|
||||
hide() {
|
||||
this.element.remove();
|
||||
this.tooltipElement.className = this._className;
|
||||
}
|
||||
|
||||
/** Shows the tooltip. Automatically called on mouseenter. Will attempt to flip if position was wrong. */
|
||||
show() {
|
||||
this.tooltipElement.className = `${getTooltipClasses().tooltip} ${getClass(this.style)}`;
|
||||
this.labelElement.textContent = this.label;
|
||||
this.container.append(this.element);
|
||||
|
||||
if (this.side == "top") {
|
||||
if (this.canShowAbove || (!this.canShowAbove && this.preventFlip)) this.showAbove();
|
||||
else this.showBelow();
|
||||
}
|
||||
|
||||
if (this.side == "bottom") {
|
||||
if (this.canShowBelow || (!this.canShowBelow && this.preventFlip)) this.showBelow();
|
||||
else this.showAbove();
|
||||
}
|
||||
|
||||
if (this.side == "left") {
|
||||
if (this.canShowLeft || (!this.canShowLeft && this.preventFlip)) this.showLeft();
|
||||
else this.showRight();
|
||||
}
|
||||
|
||||
if (this.side == "right") {
|
||||
if (this.canShowRight || (!this.canShowRight && this.preventFlip)) this.showRight();
|
||||
else this.showLeft();
|
||||
}
|
||||
}
|
||||
|
||||
/** Force showing the tooltip above the node. */
|
||||
showAbove() {
|
||||
this.tooltipElement.classList.add(getClass("top"));
|
||||
this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top - this.element.offsetHeight - 10));
|
||||
this.centerHorizontally();
|
||||
}
|
||||
|
||||
/** Force showing the tooltip below the node. */
|
||||
showBelow() {
|
||||
this.tooltipElement.classList.add(getClass("bottom"));
|
||||
this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top + this.node.offsetHeight + 10));
|
||||
this.centerHorizontally();
|
||||
}
|
||||
|
||||
/** Force showing the tooltip to the left of the node. */
|
||||
showLeft() {
|
||||
this.tooltipElement.classList.add(getClass("left"));
|
||||
this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left - this.element.offsetWidth - 10));
|
||||
this.centerVertically();
|
||||
}
|
||||
|
||||
/** Force showing the tooltip to the right of the node. */
|
||||
showRight() {
|
||||
this.tooltipElement.classList.add(getClass("right"));
|
||||
this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left + this.node.offsetWidth + 10));
|
||||
this.centerVertically();
|
||||
}
|
||||
|
||||
centerHorizontally() {
|
||||
const nodecenter = this.node.getBoundingClientRect().left + (this.node.offsetWidth / 2);
|
||||
this.element.style.setProperty("left", toPx(nodecenter - (this.element.offsetWidth / 2)));
|
||||
}
|
||||
|
||||
centerVertically() {
|
||||
const nodecenter = this.node.getBoundingClientRect().top + (this.node.offsetHeight / 2);
|
||||
this.element.style.setProperty("top", toPx(nodecenter - (this.element.offsetHeight / 2)));
|
||||
}
|
||||
}
|
|
@ -1,23 +1,23 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
import Tooltip from "./tooltip";
|
||||
|
||||
export default class extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const {style = "black", side = "top", text = ""} = this.props;
|
||||
this.node = BDV2.reactDom.findDOMNode(this);
|
||||
this.tooltip = new Tooltip(this.node, text, {style, side});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.tooltip.hide();
|
||||
delete this.tooltip;
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.children;
|
||||
}
|
||||
import BDV2 from "../modules/v2";
|
||||
import Tooltip from "./tooltip";
|
||||
|
||||
export default class extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const {style = "black", side = "top", text = ""} = this.props;
|
||||
this.node = BDV2.reactDom.findDOMNode(this);
|
||||
this.tooltip = new Tooltip(this.node, text, {style, side});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.tooltip.hide();
|
||||
delete this.tooltip;
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
|
@ -1,76 +1,76 @@
|
|||
const path = require("path");
|
||||
const CircularDependencyPlugin = require("circular-dependency-plugin");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const child_process = require("child_process")
|
||||
|
||||
module.exports = {
|
||||
mode: "development",
|
||||
target: "node",
|
||||
devtool: "inline-source-map",
|
||||
entry: "./src/index.js",
|
||||
output: {
|
||||
filename: "index.js",
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
library: "BetterDiscord",
|
||||
libraryTarget: "commonjs2"
|
||||
},
|
||||
externals: {
|
||||
electron: `electron`,
|
||||
fs: `fs`,
|
||||
path: `path`,
|
||||
events: `events`,
|
||||
rimraf: `rimraf`,
|
||||
yauzl: `yauzl`,
|
||||
mkdirp: `mkdirp`,
|
||||
request: `request`,
|
||||
"node-fetch": "node-fetch"
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".js", ".jsx"],
|
||||
modules: [
|
||||
path.resolve("src", "builtins"),
|
||||
path.resolve("src", "modules")
|
||||
],
|
||||
alias: {
|
||||
react$: path.resolve(__dirname, "src", "react.js"),
|
||||
"react-dom$": path.resolve(__dirname, "src", "react-dom.js")
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
loader: "babel-loader",
|
||||
exclude: /node_modules/,
|
||||
query: {
|
||||
presets: [["@babel/env", {
|
||||
targets: {
|
||||
node: "12.8.1",
|
||||
chrome: "78"
|
||||
}
|
||||
}], "@babel/react"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new CircularDependencyPlugin({
|
||||
// exclude detection of files based on a RegExp
|
||||
exclude: /a\.js|node_modules/,
|
||||
// add errors to webpack instead of warnings
|
||||
// failOnError: true,
|
||||
// set the current working directory for displaying module paths
|
||||
cwd: process.cwd(),
|
||||
})
|
||||
],
|
||||
optimization: {
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
compress: {drop_debugger:false}
|
||||
},
|
||||
sourceMap: true
|
||||
})
|
||||
]
|
||||
}
|
||||
const path = require("path");
|
||||
const CircularDependencyPlugin = require("circular-dependency-plugin");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const child_process = require("child_process")
|
||||
|
||||
module.exports = {
|
||||
mode: "development",
|
||||
target: "node",
|
||||
devtool: "inline-source-map",
|
||||
entry: "./src/index.js",
|
||||
output: {
|
||||
filename: "index.js",
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
library: "BetterDiscord",
|
||||
libraryTarget: "commonjs2"
|
||||
},
|
||||
externals: {
|
||||
electron: `electron`,
|
||||
fs: `fs`,
|
||||
path: `path`,
|
||||
events: `events`,
|
||||
rimraf: `rimraf`,
|
||||
yauzl: `yauzl`,
|
||||
mkdirp: `mkdirp`,
|
||||
request: `request`,
|
||||
"node-fetch": "node-fetch"
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".js", ".jsx"],
|
||||
modules: [
|
||||
path.resolve("src", "builtins"),
|
||||
path.resolve("src", "modules")
|
||||
],
|
||||
alias: {
|
||||
react$: path.resolve(__dirname, "src", "react.js"),
|
||||
"react-dom$": path.resolve(__dirname, "src", "react-dom.js")
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
loader: "babel-loader",
|
||||
exclude: /node_modules/,
|
||||
query: {
|
||||
presets: [["@babel/env", {
|
||||
targets: {
|
||||
node: "12.8.1",
|
||||
chrome: "78"
|
||||
}
|
||||
}], "@babel/react"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new CircularDependencyPlugin({
|
||||
// exclude detection of files based on a RegExp
|
||||
exclude: /a\.js|node_modules/,
|
||||
// add errors to webpack instead of warnings
|
||||
// failOnError: true,
|
||||
// set the current working directory for displaying module paths
|
||||
cwd: process.cwd(),
|
||||
})
|
||||
],
|
||||
optimization: {
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
compress: {drop_debugger:false}
|
||||
},
|
||||
sourceMap: true
|
||||
})
|
||||
]
|
||||
}
|
||||
};
|
|
@ -1,334 +1,334 @@
|
|||
import { Snowflake, Channel } from ".."
|
||||
import { DiscordGuild, channelsModule, guildModule, UserSettingsModule, ConstantsModule, CdnModule, AckModule } from "../util/DiscordToModules"
|
||||
import BaseStructure from "./BaseStructure"
|
||||
import { createChannel, createGuildMember, createRole, UserResolvable, resolveUserID, ChannelData, ChannelCreationOverwrites } from "../util/util"
|
||||
import Collection from "@discordjs/collection"
|
||||
import SnowflakeUtil from "../util/Snowflake"
|
||||
import GuildMember from "./GuildMember"
|
||||
import { MessageNotificationType, ChannelTypes } from "../util/Constants"
|
||||
import Role from "./Role"
|
||||
import DiscordJSError from "../util/DiscordJSError"
|
||||
import PermissionOverwrites from "./PermissionOverwrites"
|
||||
|
||||
export default class Guild extends BaseStructure {
|
||||
DiscordGuild:DiscordGuild
|
||||
constructor(data:DiscordGuild){
|
||||
super()
|
||||
this.DiscordGuild = data
|
||||
}
|
||||
|
||||
get id(): Snowflake{
|
||||
return this.DiscordGuild.id
|
||||
}
|
||||
|
||||
get afkChannel():Channel{
|
||||
if(!this.afkChannelID)return null
|
||||
return createChannel(channelsModule.getChannel(this.afkChannelID))
|
||||
}
|
||||
|
||||
get afkChannelID():Snowflake{
|
||||
return this.DiscordGuild.afkChannelId
|
||||
}
|
||||
|
||||
get afkTimeout():number{
|
||||
return this.DiscordGuild.afkTimeout
|
||||
}
|
||||
|
||||
get applicationID():Snowflake{
|
||||
return this.DiscordGuild.application_id
|
||||
}
|
||||
|
||||
get available():boolean{
|
||||
return true
|
||||
}
|
||||
|
||||
get channels():Collection<Snowflake, Channel>{{
|
||||
return this.client.channels.filter(channel => channel.guild_id === this.id)
|
||||
}}
|
||||
|
||||
get createdAt():Date{
|
||||
return SnowflakeUtil.deconstruct(this.id).date
|
||||
}
|
||||
|
||||
get createdTimestamp():number{
|
||||
return this.createdAt.getTime()
|
||||
}
|
||||
|
||||
get defaultChannel(){
|
||||
return this.channels.get(this.id)
|
||||
}
|
||||
|
||||
get defaultMessageNotifications():number{
|
||||
return this.DiscordGuild.defaultMessageNotifications
|
||||
}
|
||||
|
||||
deleted:boolean = false
|
||||
|
||||
get embedEnabled(){
|
||||
return true
|
||||
}
|
||||
|
||||
get emojis(){
|
||||
return this.client.emojis.filter(e => e.guild_id === this.id)
|
||||
}
|
||||
|
||||
get explicitContentFilter(){
|
||||
return this.DiscordGuild.explicitContentFilter
|
||||
}
|
||||
|
||||
get features(){
|
||||
return Array.from(this.DiscordGuild.features)
|
||||
}
|
||||
|
||||
get icon(){
|
||||
return this.DiscordGuild.icon
|
||||
}
|
||||
|
||||
get iconURL(){
|
||||
return this.DiscordGuild.getIconURL().replace(".webp", ".jpg")
|
||||
}
|
||||
|
||||
get joinedAt(){
|
||||
return new Date(this.DiscordGuild.joinedAt)
|
||||
}
|
||||
|
||||
get joinedTimestamp(){
|
||||
return this.DiscordGuild.joinedAt.getTime()
|
||||
}
|
||||
|
||||
get large(){
|
||||
return false
|
||||
}
|
||||
|
||||
get me(){
|
||||
return this.members.find(member => member.id === this.client.user.id)
|
||||
}
|
||||
|
||||
get memberCount(){
|
||||
return guildModule.getMemberCount(this.id)
|
||||
}
|
||||
|
||||
get members(){
|
||||
return new Collection<Snowflake, GuildMember>(guildModule.getMembers(this.id).map(member => [member.userId, createGuildMember(member)]))
|
||||
}
|
||||
|
||||
get messageNotifications():MessageNotificationType{
|
||||
return MessageNotificationType[guildModule.getMessageNotifications(this.id)] as unknown as MessageNotificationType
|
||||
}
|
||||
|
||||
get mfaLevel(){
|
||||
return this.DiscordGuild.mfaLevel
|
||||
}
|
||||
|
||||
get mobilePush():boolean{
|
||||
return guildModule.getNotificationsState().userGuildSettings[this.id].mobile_push
|
||||
}
|
||||
|
||||
get muted(){
|
||||
return guildModule.getNotificationsState().userGuildSettings[this.id].muted
|
||||
}
|
||||
|
||||
get name(){
|
||||
return this.DiscordGuild.name
|
||||
}
|
||||
|
||||
get nameAcronym(){
|
||||
return this.DiscordGuild.acronym
|
||||
}
|
||||
|
||||
get owner(){
|
||||
return this.members.get(this.ownerID)
|
||||
}
|
||||
|
||||
get ownerID(){
|
||||
return this.DiscordGuild.ownerId
|
||||
}
|
||||
|
||||
get position(){
|
||||
let guildPositions = UserSettingsModule.getAllSettings().guildPositions
|
||||
if(!guildPositions)return 0
|
||||
return guildPositions.indexOf(this.id)
|
||||
}
|
||||
|
||||
get presences(){ // TODO: Add activities.
|
||||
return new Collection()
|
||||
}
|
||||
|
||||
get region(){
|
||||
return this.DiscordGuild.region
|
||||
}
|
||||
|
||||
get roles(){
|
||||
return new Collection<Snowflake, Role>(Object.values(this.DiscordGuild.roles).map(role => [role.id, createRole(role)]))
|
||||
}
|
||||
|
||||
get splash(){
|
||||
return this.DiscordGuild.splash
|
||||
}
|
||||
|
||||
get splashURL(){
|
||||
return CdnModule.getGuildSplashURL({
|
||||
id: this.id,
|
||||
splash: this.splash,
|
||||
size: ConstantsModule.SPLASH_SIZE
|
||||
})
|
||||
}
|
||||
|
||||
get suppressEveryone(){
|
||||
return guildModule.getNotificationsState().userGuildSettings[this.id].suppress_everyone
|
||||
}
|
||||
|
||||
get systemChannel(){
|
||||
return this.client.channels.get(this.systemChannelID)
|
||||
}
|
||||
|
||||
get systemChannelID(){
|
||||
return this.DiscordGuild.systemChannelId
|
||||
}
|
||||
|
||||
get verificationLevel(){
|
||||
return this.DiscordGuild.verificationLevel
|
||||
}
|
||||
|
||||
get 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.
|
||||
return null
|
||||
}
|
||||
|
||||
get banner(){
|
||||
return this.DiscordGuild.banner
|
||||
}
|
||||
|
||||
get bannerURL(){
|
||||
return CdnModule.getGuildBannerURL({
|
||||
id: this.id,
|
||||
banner: this.banner
|
||||
})
|
||||
}
|
||||
|
||||
get description(){
|
||||
return this.DiscordGuild.description
|
||||
}
|
||||
|
||||
get embedChannel(){ // TODO: Implement embedChannel
|
||||
return null
|
||||
}
|
||||
|
||||
get embedChannelID(){ // TODO: Implement embedChannelID
|
||||
return null
|
||||
}
|
||||
|
||||
get maximumMembers(){ // TODO: Correctly Implement maximumMembers if possible
|
||||
return 250000
|
||||
}
|
||||
|
||||
get maximumPresences(){ // TODO: Correctly Implement maximumPresences if possible
|
||||
return 5000
|
||||
}
|
||||
|
||||
get widgetEnabled(){ // TODO: Correctly Implement widgetEnabled if possible
|
||||
return false
|
||||
}
|
||||
|
||||
get widgetChannelID(){ // TODO: Correctly Implement widgetChannelID if possible
|
||||
return null
|
||||
}
|
||||
|
||||
get widgetChannel(){ // TODO: Correctly Implement widgetChannel if possible
|
||||
return null
|
||||
}
|
||||
|
||||
get vanityURLCode(){
|
||||
return this.DiscordGuild.vanityURLCode
|
||||
}
|
||||
/** FUNCTIONS */
|
||||
async acknowledge(){
|
||||
AckModule.bulkAck(this.channels.filter(e => e.type === "text").map(e => {
|
||||
return {
|
||||
channelId: e.id,
|
||||
messageId: channelsModule.lastMessageId(e.id)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
addMember(...args:any):Promise<GuildMember>{
|
||||
return Promise.reject(new DiscordJSError("This method is not available on Lightcord."))
|
||||
}
|
||||
|
||||
allowDMs(allow:boolean){
|
||||
let restricted = UserSettingsModule.getAllSettings().restrictedGuilds
|
||||
if(allow){
|
||||
if(!restricted.includes(this.id))return Promise.resolve(this)
|
||||
restricted = restricted.filter(e => e !== this.id)
|
||||
}else{
|
||||
if(restricted.includes(this.id))return Promise.resolve(this)
|
||||
restricted.push(this.id)
|
||||
}
|
||||
return UserSettingsModule.updateRemoteSettings({
|
||||
restrictedGuilds: restricted
|
||||
}).then(() => this)
|
||||
}
|
||||
|
||||
async ban(user:UserResolvable, {
|
||||
days = 0,
|
||||
reason = null
|
||||
}: {
|
||||
days?: number,
|
||||
reason?: string
|
||||
} = {}):Promise<Snowflake>{ // always returning a snowflake
|
||||
let id = resolveUserID(user)
|
||||
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)
|
||||
if(result instanceof Error || result.status !== 204){
|
||||
let message = result.body
|
||||
if(Array.isArray(message)){
|
||||
message = message[0]
|
||||
}else{
|
||||
if(message.user_id){
|
||||
message = "User: "+ message.user_id[0]
|
||||
}else if(message.delete_message_days){
|
||||
message = "Days: "+ message.delete_message_days[0]
|
||||
}else if(message.reason){
|
||||
message = "Reason: "+ message.reason[0]
|
||||
}else{
|
||||
message = result.text
|
||||
}
|
||||
}
|
||||
throw new DiscordJSError(message)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
createChannel(name:string, typeOrOptions:ChannelTypes|ChannelData = ChannelTypes.TEXT, permissionOverwrites?: ChannelCreationOverwrites[] | Collection<Snowflake, PermissionOverwrites>, reason?: string){
|
||||
const [opts, type] = typeof typeOrOptions === "string" ? [
|
||||
{},
|
||||
typeOrOptions
|
||||
] : [
|
||||
typeOrOptions,
|
||||
typeOrOptions.type
|
||||
]
|
||||
const options = {
|
||||
name: name || "Unknown Channel",
|
||||
type: type || "text"
|
||||
}
|
||||
//TODO: CreateGuildChannels and handle permissions
|
||||
//channelsModule.createGuildChannel(this.id, options.type, optinos.name)
|
||||
}
|
||||
|
||||
fetch():Promise<Guild>{ // Guild is synced by Discord. Only refreshing from cache.
|
||||
let guild = guildModule.getGuild(this.id)
|
||||
if(!guild){
|
||||
this.deleted = true
|
||||
return Promise.resolve(this)
|
||||
}
|
||||
this.DiscordGuild = guild
|
||||
return Promise.resolve(this)
|
||||
}
|
||||
|
||||
|
||||
|
||||
import { Snowflake, Channel } from ".."
|
||||
import { DiscordGuild, channelsModule, guildModule, UserSettingsModule, ConstantsModule, CdnModule, AckModule } from "../util/DiscordToModules"
|
||||
import BaseStructure from "./BaseStructure"
|
||||
import { createChannel, createGuildMember, createRole, UserResolvable, resolveUserID, ChannelData, ChannelCreationOverwrites } from "../util/util"
|
||||
import Collection from "@discordjs/collection"
|
||||
import SnowflakeUtil from "../util/Snowflake"
|
||||
import GuildMember from "./GuildMember"
|
||||
import { MessageNotificationType, ChannelTypes } from "../util/Constants"
|
||||
import Role from "./Role"
|
||||
import DiscordJSError from "../util/DiscordJSError"
|
||||
import PermissionOverwrites from "./PermissionOverwrites"
|
||||
|
||||
export default class Guild extends BaseStructure {
|
||||
DiscordGuild:DiscordGuild
|
||||
constructor(data:DiscordGuild){
|
||||
super()
|
||||
this.DiscordGuild = data
|
||||
}
|
||||
|
||||
get id(): Snowflake{
|
||||
return this.DiscordGuild.id
|
||||
}
|
||||
|
||||
get afkChannel():Channel{
|
||||
if(!this.afkChannelID)return null
|
||||
return createChannel(channelsModule.getChannel(this.afkChannelID))
|
||||
}
|
||||
|
||||
get afkChannelID():Snowflake{
|
||||
return this.DiscordGuild.afkChannelId
|
||||
}
|
||||
|
||||
get afkTimeout():number{
|
||||
return this.DiscordGuild.afkTimeout
|
||||
}
|
||||
|
||||
get applicationID():Snowflake{
|
||||
return this.DiscordGuild.application_id
|
||||
}
|
||||
|
||||
get available():boolean{
|
||||
return true
|
||||
}
|
||||
|
||||
get channels():Collection<Snowflake, Channel>{{
|
||||
return this.client.channels.filter(channel => channel.guild_id === this.id)
|
||||
}}
|
||||
|
||||
get createdAt():Date{
|
||||
return SnowflakeUtil.deconstruct(this.id).date
|
||||
}
|
||||
|
||||
get createdTimestamp():number{
|
||||
return this.createdAt.getTime()
|
||||
}
|
||||
|
||||
get defaultChannel(){
|
||||
return this.channels.get(this.id)
|
||||
}
|
||||
|
||||
get defaultMessageNotifications():number{
|
||||
return this.DiscordGuild.defaultMessageNotifications
|
||||
}
|
||||
|
||||
deleted:boolean = false
|
||||
|
||||
get embedEnabled(){
|
||||
return true
|
||||
}
|
||||
|
||||
get emojis(){
|
||||
return this.client.emojis.filter(e => e.guild_id === this.id)
|
||||
}
|
||||
|
||||
get explicitContentFilter(){
|
||||
return this.DiscordGuild.explicitContentFilter
|
||||
}
|
||||
|
||||
get features(){
|
||||
return Array.from(this.DiscordGuild.features)
|
||||
}
|
||||
|
||||
get icon(){
|
||||
return this.DiscordGuild.icon
|
||||
}
|
||||
|
||||
get iconURL(){
|
||||
return this.DiscordGuild.getIconURL().replace(".webp", ".jpg")
|
||||
}
|
||||
|
||||
get joinedAt(){
|
||||
return new Date(this.DiscordGuild.joinedAt)
|
||||
}
|
||||
|
||||
get joinedTimestamp(){
|
||||
return this.DiscordGuild.joinedAt.getTime()
|
||||
}
|
||||
|
||||
get large(){
|
||||
return false
|
||||
}
|
||||
|
||||
get me(){
|
||||
return this.members.find(member => member.id === this.client.user.id)
|
||||
}
|
||||
|
||||
get memberCount(){
|
||||
return guildModule.getMemberCount(this.id)
|
||||
}
|
||||
|
||||
get members(){
|
||||
return new Collection<Snowflake, GuildMember>(guildModule.getMembers(this.id).map(member => [member.userId, createGuildMember(member)]))
|
||||
}
|
||||
|
||||
get messageNotifications():MessageNotificationType{
|
||||
return MessageNotificationType[guildModule.getMessageNotifications(this.id)] as unknown as MessageNotificationType
|
||||
}
|
||||
|
||||
get mfaLevel(){
|
||||
return this.DiscordGuild.mfaLevel
|
||||
}
|
||||
|
||||
get mobilePush():boolean{
|
||||
return guildModule.getNotificationsState().userGuildSettings[this.id].mobile_push
|
||||
}
|
||||
|
||||
get muted(){
|
||||
return guildModule.getNotificationsState().userGuildSettings[this.id].muted
|
||||
}
|
||||
|
||||
get name(){
|
||||
return this.DiscordGuild.name
|
||||
}
|
||||
|
||||
get nameAcronym(){
|
||||
return this.DiscordGuild.acronym
|
||||
}
|
||||
|
||||
get owner(){
|
||||
return this.members.get(this.ownerID)
|
||||
}
|
||||
|
||||
get ownerID(){
|
||||
return this.DiscordGuild.ownerId
|
||||
}
|
||||
|
||||
get position(){
|
||||
let guildPositions = UserSettingsModule.getAllSettings().guildPositions
|
||||
if(!guildPositions)return 0
|
||||
return guildPositions.indexOf(this.id)
|
||||
}
|
||||
|
||||
get presences(){ // TODO: Add activities.
|
||||
return new Collection()
|
||||
}
|
||||
|
||||
get region(){
|
||||
return this.DiscordGuild.region
|
||||
}
|
||||
|
||||
get roles(){
|
||||
return new Collection<Snowflake, Role>(Object.values(this.DiscordGuild.roles).map(role => [role.id, createRole(role)]))
|
||||
}
|
||||
|
||||
get splash(){
|
||||
return this.DiscordGuild.splash
|
||||
}
|
||||
|
||||
get splashURL(){
|
||||
return CdnModule.getGuildSplashURL({
|
||||
id: this.id,
|
||||
splash: this.splash,
|
||||
size: ConstantsModule.SPLASH_SIZE
|
||||
})
|
||||
}
|
||||
|
||||
get suppressEveryone(){
|
||||
return guildModule.getNotificationsState().userGuildSettings[this.id].suppress_everyone
|
||||
}
|
||||
|
||||
get systemChannel(){
|
||||
return this.client.channels.get(this.systemChannelID)
|
||||
}
|
||||
|
||||
get systemChannelID(){
|
||||
return this.DiscordGuild.systemChannelId
|
||||
}
|
||||
|
||||
get verificationLevel(){
|
||||
return this.DiscordGuild.verificationLevel
|
||||
}
|
||||
|
||||
get 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.
|
||||
return null
|
||||
}
|
||||
|
||||
get banner(){
|
||||
return this.DiscordGuild.banner
|
||||
}
|
||||
|
||||
get bannerURL(){
|
||||
return CdnModule.getGuildBannerURL({
|
||||
id: this.id,
|
||||
banner: this.banner
|
||||
})
|
||||
}
|
||||
|
||||
get description(){
|
||||
return this.DiscordGuild.description
|
||||
}
|
||||
|
||||
get embedChannel(){ // TODO: Implement embedChannel
|
||||
return null
|
||||
}
|
||||
|
||||
get embedChannelID(){ // TODO: Implement embedChannelID
|
||||
return null
|
||||
}
|
||||
|
||||
get maximumMembers(){ // TODO: Correctly Implement maximumMembers if possible
|
||||
return 250000
|
||||
}
|
||||
|
||||
get maximumPresences(){ // TODO: Correctly Implement maximumPresences if possible
|
||||
return 5000
|
||||
}
|
||||
|
||||
get widgetEnabled(){ // TODO: Correctly Implement widgetEnabled if possible
|
||||
return false
|
||||
}
|
||||
|
||||
get widgetChannelID(){ // TODO: Correctly Implement widgetChannelID if possible
|
||||
return null
|
||||
}
|
||||
|
||||
get widgetChannel(){ // TODO: Correctly Implement widgetChannel if possible
|
||||
return null
|
||||
}
|
||||
|
||||
get vanityURLCode(){
|
||||
return this.DiscordGuild.vanityURLCode
|
||||
}
|
||||
/** FUNCTIONS */
|
||||
async acknowledge(){
|
||||
AckModule.bulkAck(this.channels.filter(e => e.type === "text").map(e => {
|
||||
return {
|
||||
channelId: e.id,
|
||||
messageId: channelsModule.lastMessageId(e.id)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
addMember(...args:any):Promise<GuildMember>{
|
||||
return Promise.reject(new DiscordJSError("This method is not available on Lightcord."))
|
||||
}
|
||||
|
||||
allowDMs(allow:boolean){
|
||||
let restricted = UserSettingsModule.getAllSettings().restrictedGuilds
|
||||
if(allow){
|
||||
if(!restricted.includes(this.id))return Promise.resolve(this)
|
||||
restricted = restricted.filter(e => e !== this.id)
|
||||
}else{
|
||||
if(restricted.includes(this.id))return Promise.resolve(this)
|
||||
restricted.push(this.id)
|
||||
}
|
||||
return UserSettingsModule.updateRemoteSettings({
|
||||
restrictedGuilds: restricted
|
||||
}).then(() => this)
|
||||
}
|
||||
|
||||
async ban(user:UserResolvable, {
|
||||
days = 0,
|
||||
reason = null
|
||||
}: {
|
||||
days?: number,
|
||||
reason?: string
|
||||
} = {}):Promise<Snowflake>{ // always returning a snowflake
|
||||
let id = resolveUserID(user)
|
||||
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)
|
||||
if(result instanceof Error || result.status !== 204){
|
||||
let message = result.body
|
||||
if(Array.isArray(message)){
|
||||
message = message[0]
|
||||
}else{
|
||||
if(message.user_id){
|
||||
message = "User: "+ message.user_id[0]
|
||||
}else if(message.delete_message_days){
|
||||
message = "Days: "+ message.delete_message_days[0]
|
||||
}else if(message.reason){
|
||||
message = "Reason: "+ message.reason[0]
|
||||
}else{
|
||||
message = result.text
|
||||
}
|
||||
}
|
||||
throw new DiscordJSError(message)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
createChannel(name:string, typeOrOptions:ChannelTypes|ChannelData = ChannelTypes.TEXT, permissionOverwrites?: ChannelCreationOverwrites[] | Collection<Snowflake, PermissionOverwrites>, reason?: string){
|
||||
const [opts, type] = typeof typeOrOptions === "string" ? [
|
||||
{},
|
||||
typeOrOptions
|
||||
] : [
|
||||
typeOrOptions,
|
||||
typeOrOptions.type
|
||||
]
|
||||
const options = {
|
||||
name: name || "Unknown Channel",
|
||||
type: type || "text"
|
||||
}
|
||||
//TODO: CreateGuildChannels and handle permissions
|
||||
//channelsModule.createGuildChannel(this.id, options.type, optinos.name)
|
||||
}
|
||||
|
||||
fetch():Promise<Guild>{ // Guild is synced by Discord. Only refreshing from cache.
|
||||
let guild = guildModule.getGuild(this.id)
|
||||
if(!guild){
|
||||
this.deleted = true
|
||||
return Promise.resolve(this)
|
||||
}
|
||||
this.DiscordGuild = guild
|
||||
return Promise.resolve(this)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,61 +1,61 @@
|
|||
import Permissions from '../util/Permissions';
|
||||
import GuildChannel from './GuildChannel';
|
||||
import { Snowflake } from '..';
|
||||
|
||||
/**
|
||||
* Represents a permission overwrite for a role or member in a guild channel.
|
||||
*/
|
||||
export default class PermissionOverwrites {
|
||||
id: Snowflake;
|
||||
type: "role"|"member";
|
||||
deny: number;
|
||||
allow: number;
|
||||
channel: GuildChannel;
|
||||
constructor(guildChannel:GuildChannel, data) {
|
||||
/**
|
||||
* The GuildChannel this overwrite is for
|
||||
* @name PermissionOverwrites#channel
|
||||
* @type {GuildChannel}
|
||||
* @readonly
|
||||
*/
|
||||
Object.defineProperty(this, 'channel', { value: guildChannel });
|
||||
|
||||
if (data) this.setup(data);
|
||||
}
|
||||
|
||||
setup(data) {
|
||||
/**
|
||||
* The ID of this overwrite, either a user ID or a role ID
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.id = data.id;
|
||||
|
||||
/**
|
||||
* The type of this overwrite
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = data.type;
|
||||
|
||||
/**
|
||||
* The permissions that are denied for the user or role as a bitfield.
|
||||
* @type {number}
|
||||
*/
|
||||
this.deny = data.deny;
|
||||
|
||||
/**
|
||||
* The permissions that are allowed for the user or role as a bitfield.
|
||||
* @type {number}
|
||||
*/
|
||||
this.allow = data.allow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete this Permission Overwrite.
|
||||
* @param {string} [reason] Reason for deleting this overwrite
|
||||
* @returns {Promise<PermissionOverwrites>}
|
||||
*/
|
||||
delete(reason) {
|
||||
// TODO: Delete permission overwrites
|
||||
//return this.channel.client.rest.methods.deletePermissionOverwrites(this, reason);
|
||||
}
|
||||
import Permissions from '../util/Permissions';
|
||||
import GuildChannel from './GuildChannel';
|
||||
import { Snowflake } from '..';
|
||||
|
||||
/**
|
||||
* Represents a permission overwrite for a role or member in a guild channel.
|
||||
*/
|
||||
export default class PermissionOverwrites {
|
||||
id: Snowflake;
|
||||
type: "role"|"member";
|
||||
deny: number;
|
||||
allow: number;
|
||||
channel: GuildChannel;
|
||||
constructor(guildChannel:GuildChannel, data) {
|
||||
/**
|
||||
* The GuildChannel this overwrite is for
|
||||
* @name PermissionOverwrites#channel
|
||||
* @type {GuildChannel}
|
||||
* @readonly
|
||||
*/
|
||||
Object.defineProperty(this, 'channel', { value: guildChannel });
|
||||
|
||||
if (data) this.setup(data);
|
||||
}
|
||||
|
||||
setup(data) {
|
||||
/**
|
||||
* The ID of this overwrite, either a user ID or a role ID
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.id = data.id;
|
||||
|
||||
/**
|
||||
* The type of this overwrite
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = data.type;
|
||||
|
||||
/**
|
||||
* The permissions that are denied for the user or role as a bitfield.
|
||||
* @type {number}
|
||||
*/
|
||||
this.deny = data.deny;
|
||||
|
||||
/**
|
||||
* The permissions that are allowed for the user or role as a bitfield.
|
||||
* @type {number}
|
||||
*/
|
||||
this.allow = data.allow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete this Permission Overwrite.
|
||||
* @param {string} [reason] Reason for deleting this overwrite
|
||||
* @returns {Promise<PermissionOverwrites>}
|
||||
*/
|
||||
delete(reason) {
|
||||
// TODO: Delete permission overwrites
|
||||
//return this.channel.client.rest.methods.deletePermissionOverwrites(this, reason);
|
||||
}
|
||||
}
|
|
@ -1,32 +1,32 @@
|
|||
import Permissions from '../../util/Permissions';
|
||||
import Collection from '@discordjs/collection';
|
||||
import Guild from '../Guild';
|
||||
import { ChannelCreationOverwrites } from '../../util/util';
|
||||
import { Snowflake } from '../..';
|
||||
import PermissionOverwrites from '../PermissionOverwrites';
|
||||
|
||||
module.exports = function resolvePermissions(overwrites: ChannelCreationOverwrites[] | Collection<Snowflake, PermissionOverwrites>, guild:Guild) {
|
||||
if (overwrites instanceof Collection || overwrites instanceof Array) {
|
||||
//TODO: Remove ts-ignore
|
||||
// @ts-ignore
|
||||
overwrites = (Array.isArray(overwrites) ? overwrites : overwrites.array()).map(overwrite => {
|
||||
const role = this.client.resolver.resolveRole(guild, overwrite.id);
|
||||
if (role) {
|
||||
overwrite.id = role.id;
|
||||
overwrite.type = 'role';
|
||||
} else {
|
||||
overwrite.id = this.client.resolver.resolveUserID(overwrite.id);
|
||||
overwrite.type = 'member';
|
||||
}
|
||||
|
||||
return {
|
||||
allow: Permissions.resolve(overwrite.allow || overwrite.allowed || 0),
|
||||
deny: Permissions.resolve(overwrite.deny || overwrite.denied || 0),
|
||||
type: overwrite.type,
|
||||
id: overwrite.id,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return overwrites;
|
||||
import Permissions from '../../util/Permissions';
|
||||
import Collection from '@discordjs/collection';
|
||||
import Guild from '../Guild';
|
||||
import { ChannelCreationOverwrites } from '../../util/util';
|
||||
import { Snowflake } from '../..';
|
||||
import PermissionOverwrites from '../PermissionOverwrites';
|
||||
|
||||
module.exports = function resolvePermissions(overwrites: ChannelCreationOverwrites[] | Collection<Snowflake, PermissionOverwrites>, guild:Guild) {
|
||||
if (overwrites instanceof Collection || overwrites instanceof Array) {
|
||||
//TODO: Remove ts-ignore
|
||||
// @ts-ignore
|
||||
overwrites = (Array.isArray(overwrites) ? overwrites : overwrites.array()).map(overwrite => {
|
||||
const role = this.client.resolver.resolveRole(guild, overwrite.id);
|
||||
if (role) {
|
||||
overwrite.id = role.id;
|
||||
overwrite.type = 'role';
|
||||
} else {
|
||||
overwrite.id = this.client.resolver.resolveUserID(overwrite.id);
|
||||
overwrite.type = 'member';
|
||||
}
|
||||
|
||||
return {
|
||||
allow: Permissions.resolve(overwrite.allow || overwrite.allowed || 0),
|
||||
deny: Permissions.resolve(overwrite.deny || overwrite.denied || 0),
|
||||
type: overwrite.type,
|
||||
id: overwrite.id,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return overwrites;
|
||||
};
|
|
@ -1,210 +1,210 @@
|
|||
import BitField from "./BitField";
|
||||
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
|
||||
* permissions in their guild, and each channel in the guild may also have {@link PermissionOverwrites} for the member
|
||||
* that override their default permissions.
|
||||
* @extends {BitField}
|
||||
*/
|
||||
export default class Permissions extends BitField {
|
||||
/**
|
||||
* @param {number|PermissionResolvable} permissions Permissions or bitfield to read from
|
||||
*/
|
||||
constructor(permissions: number | PermissionResolvable) {
|
||||
super(permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitfield of the packed permissions
|
||||
* @type {number}
|
||||
* @see {@link Permissions#bitfield}
|
||||
* @deprecated
|
||||
* @readonly
|
||||
*/
|
||||
get raw() {
|
||||
return this.bitfield;
|
||||
}
|
||||
|
||||
set raw(raw) {
|
||||
this.bitfield = raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the bitfield has a permission, or any of multiple permissions.
|
||||
* @param {PermissionResolvable} permission Permission(s) to check for
|
||||
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
|
||||
* @returns {boolean}
|
||||
*/
|
||||
any(permission: PermissionResolvable, checkAdmin: boolean = true): boolean {
|
||||
return (
|
||||
(checkAdmin && super.has((this.constructor as any).FLAGS.ADMINISTRATOR)) ||
|
||||
super.any(permission)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the bitfield has a permission, or multiple permissions.
|
||||
* @param {PermissionResolvable} permission Permission(s) to check for
|
||||
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
|
||||
* @returns {boolean}
|
||||
*/
|
||||
has(permission: PermissionResolvable, checkAdmin: boolean = true): boolean {
|
||||
return (
|
||||
(checkAdmin && super.has((this.constructor as any).FLAGS.ADMINISTRATOR)) ||
|
||||
super.has(permission)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Numeric permission flags. All available properties:
|
||||
* - `ADMINISTRATOR` (implicitly has *all* permissions, and bypasses all channel overwrites)
|
||||
* - `CREATE_INSTANT_INVITE` (create invitations to the guild)
|
||||
* - `KICK_MEMBERS`
|
||||
* - `BAN_MEMBERS`
|
||||
* - `MANAGE_CHANNELS` (edit and reorder channels)
|
||||
* - `MANAGE_GUILD` (edit the guild information, region, etc.)
|
||||
* - `ADD_REACTIONS` (add new reactions to messages)
|
||||
* - `VIEW_AUDIT_LOG`
|
||||
* - `PRIORITY_SPEAKER`
|
||||
* - `STREAM`
|
||||
* - `VIEW_CHANNEL`
|
||||
* - `READ_MESSAGES` **(deprecated)**
|
||||
* - `SEND_MESSAGES`
|
||||
* - `SEND_TTS_MESSAGES`
|
||||
* - `MANAGE_MESSAGES` (delete messages and reactions)
|
||||
* - `EMBED_LINKS` (links posted will have a preview embedded)
|
||||
* - `ATTACH_FILES`
|
||||
* - `READ_MESSAGE_HISTORY` (view messages that were posted prior to opening Discord)
|
||||
* - `MENTION_EVERYONE`
|
||||
* - `USE_EXTERNAL_EMOJIS` (use emojis from different guilds)
|
||||
* - `EXTERNAL_EMOJIS` **(deprecated)**
|
||||
* - `CONNECT` (connect to a voice channel)
|
||||
* - `SPEAK` (speak in a voice channel)
|
||||
* - `MUTE_MEMBERS` (mute members across all voice channels)
|
||||
* - `DEAFEN_MEMBERS` (deafen members across all voice channels)
|
||||
* - `MOVE_MEMBERS` (move members between voice channels)
|
||||
* - `USE_VAD` (use voice activity detection)
|
||||
* - `CHANGE_NICKNAME`
|
||||
* - `MANAGE_NICKNAMES` (change other members' nicknames)
|
||||
* - `MANAGE_ROLES`
|
||||
* - `MANAGE_ROLES_OR_PERMISSIONS` **(deprecated)**
|
||||
* - `MANAGE_WEBHOOKS`
|
||||
* - `MANAGE_EMOJIS`
|
||||
* @type {Object}
|
||||
* @see {@link https://discordapp.com/developers/docs/topics/permissions}
|
||||
*/
|
||||
static FLAGS:{
|
||||
CREATE_INSTANT_INVITE: number,
|
||||
KICK_MEMBERS: number,
|
||||
BAN_MEMBERS: number,
|
||||
ADMINISTRATOR: number,
|
||||
MANAGE_CHANNELS: number,
|
||||
MANAGE_GUILD: number,
|
||||
ADD_REACTIONS: number,
|
||||
VIEW_AUDIT_LOG: number,
|
||||
PRIORITY_SPEAKER: number,
|
||||
STREAM: number,
|
||||
|
||||
VIEW_CHANNEL: number,
|
||||
READ_MESSAGES: number,
|
||||
SEND_MESSAGES: number,
|
||||
SEND_TTS_MESSAGES: number,
|
||||
MANAGE_MESSAGES: number,
|
||||
EMBED_LINKS: number,
|
||||
ATTACH_FILES: number,
|
||||
READ_MESSAGE_HISTORY: number,
|
||||
MENTION_EVERYONE: number,
|
||||
EXTERNAL_EMOJIS: number,
|
||||
USE_EXTERNAL_EMOJIS: number,
|
||||
|
||||
CONNECT: number,
|
||||
SPEAK: number,
|
||||
MUTE_MEMBERS: number,
|
||||
DEAFEN_MEMBERS: number,
|
||||
MOVE_MEMBERS: number,
|
||||
USE_VAD: number,
|
||||
|
||||
CHANGE_NICKNAME: number,
|
||||
MANAGE_NICKNAMES: number,
|
||||
MANAGE_ROLES: number,
|
||||
MANAGE_ROLES_OR_PERMISSIONS: number,
|
||||
MANAGE_WEBHOOKS: number,
|
||||
MANAGE_EMOJIS: number,
|
||||
} = {
|
||||
CREATE_INSTANT_INVITE: 1 << 0,
|
||||
KICK_MEMBERS: 1 << 1,
|
||||
BAN_MEMBERS: 1 << 2,
|
||||
ADMINISTRATOR: 1 << 3,
|
||||
MANAGE_CHANNELS: 1 << 4,
|
||||
MANAGE_GUILD: 1 << 5,
|
||||
ADD_REACTIONS: 1 << 6,
|
||||
VIEW_AUDIT_LOG: 1 << 7,
|
||||
PRIORITY_SPEAKER: 1 << 8,
|
||||
STREAM: 1 << 9,
|
||||
|
||||
VIEW_CHANNEL: 1 << 10,
|
||||
READ_MESSAGES: 1 << 10,
|
||||
SEND_MESSAGES: 1 << 11,
|
||||
SEND_TTS_MESSAGES: 1 << 12,
|
||||
MANAGE_MESSAGES: 1 << 13,
|
||||
EMBED_LINKS: 1 << 14,
|
||||
ATTACH_FILES: 1 << 15,
|
||||
READ_MESSAGE_HISTORY: 1 << 16,
|
||||
MENTION_EVERYONE: 1 << 17,
|
||||
EXTERNAL_EMOJIS: 1 << 18,
|
||||
USE_EXTERNAL_EMOJIS: 1 << 18,
|
||||
|
||||
CONNECT: 1 << 20,
|
||||
SPEAK: 1 << 21,
|
||||
MUTE_MEMBERS: 1 << 22,
|
||||
DEAFEN_MEMBERS: 1 << 23,
|
||||
MOVE_MEMBERS: 1 << 24,
|
||||
USE_VAD: 1 << 25,
|
||||
|
||||
CHANGE_NICKNAME: 1 << 26,
|
||||
MANAGE_NICKNAMES: 1 << 27,
|
||||
MANAGE_ROLES: 1 << 28,
|
||||
MANAGE_ROLES_OR_PERMISSIONS: 1 << 28,
|
||||
MANAGE_WEBHOOKS: 1 << 29,
|
||||
MANAGE_EMOJIS: 1 << 30,
|
||||
};
|
||||
|
||||
/**
|
||||
* Bitfield representing every permission combined
|
||||
* @type {number}
|
||||
*/
|
||||
static ALL: number = Object.keys(Permissions.FLAGS).reduce(
|
||||
(all, p) => all | Permissions.FLAGS[p],
|
||||
0
|
||||
);
|
||||
|
||||
/**
|
||||
* Bitfield representing the default permissions for users
|
||||
* @type {number}
|
||||
*/
|
||||
static DEFAULT: number = 104324673;
|
||||
}
|
||||
|
||||
Object.defineProperty(Permissions.prototype, "raw", {
|
||||
get: util.deprecate(
|
||||
Object.getOwnPropertyDescriptor(Permissions.prototype, "raw").get,
|
||||
"EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead"
|
||||
),
|
||||
set: util.deprecate(
|
||||
Object.getOwnPropertyDescriptor(Permissions.prototype, "raw").set,
|
||||
"EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead"
|
||||
),
|
||||
});
|
||||
|
||||
/**
|
||||
* Data that can be resolved to give a permission number. This can be:
|
||||
* * A string (see {@link Permissions.FLAGS})
|
||||
* * A permission number
|
||||
* @typedef {string|number|Permissions|PermissionResolvable[]} PermissionResolvable
|
||||
*/
|
||||
export type PermissionResolvable =
|
||||
| keyof typeof Permissions.FLAGS
|
||||
| number
|
||||
| Permissions
|
||||
| PermissionResolvable[];
|
||||
import BitField from "./BitField";
|
||||
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
|
||||
* permissions in their guild, and each channel in the guild may also have {@link PermissionOverwrites} for the member
|
||||
* that override their default permissions.
|
||||
* @extends {BitField}
|
||||
*/
|
||||
export default class Permissions extends BitField {
|
||||
/**
|
||||
* @param {number|PermissionResolvable} permissions Permissions or bitfield to read from
|
||||
*/
|
||||
constructor(permissions: number | PermissionResolvable) {
|
||||
super(permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitfield of the packed permissions
|
||||
* @type {number}
|
||||
* @see {@link Permissions#bitfield}
|
||||
* @deprecated
|
||||
* @readonly
|
||||
*/
|
||||
get raw() {
|
||||
return this.bitfield;
|
||||
}
|
||||
|
||||
set raw(raw) {
|
||||
this.bitfield = raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the bitfield has a permission, or any of multiple permissions.
|
||||
* @param {PermissionResolvable} permission Permission(s) to check for
|
||||
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
|
||||
* @returns {boolean}
|
||||
*/
|
||||
any(permission: PermissionResolvable, checkAdmin: boolean = true): boolean {
|
||||
return (
|
||||
(checkAdmin && super.has((this.constructor as any).FLAGS.ADMINISTRATOR)) ||
|
||||
super.any(permission)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the bitfield has a permission, or multiple permissions.
|
||||
* @param {PermissionResolvable} permission Permission(s) to check for
|
||||
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
|
||||
* @returns {boolean}
|
||||
*/
|
||||
has(permission: PermissionResolvable, checkAdmin: boolean = true): boolean {
|
||||
return (
|
||||
(checkAdmin && super.has((this.constructor as any).FLAGS.ADMINISTRATOR)) ||
|
||||
super.has(permission)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Numeric permission flags. All available properties:
|
||||
* - `ADMINISTRATOR` (implicitly has *all* permissions, and bypasses all channel overwrites)
|
||||
* - `CREATE_INSTANT_INVITE` (create invitations to the guild)
|
||||
* - `KICK_MEMBERS`
|
||||
* - `BAN_MEMBERS`
|
||||
* - `MANAGE_CHANNELS` (edit and reorder channels)
|
||||
* - `MANAGE_GUILD` (edit the guild information, region, etc.)
|
||||
* - `ADD_REACTIONS` (add new reactions to messages)
|
||||
* - `VIEW_AUDIT_LOG`
|
||||
* - `PRIORITY_SPEAKER`
|
||||
* - `STREAM`
|
||||
* - `VIEW_CHANNEL`
|
||||
* - `READ_MESSAGES` **(deprecated)**
|
||||
* - `SEND_MESSAGES`
|
||||
* - `SEND_TTS_MESSAGES`
|
||||
* - `MANAGE_MESSAGES` (delete messages and reactions)
|
||||
* - `EMBED_LINKS` (links posted will have a preview embedded)
|
||||
* - `ATTACH_FILES`
|
||||
* - `READ_MESSAGE_HISTORY` (view messages that were posted prior to opening Discord)
|
||||
* - `MENTION_EVERYONE`
|
||||
* - `USE_EXTERNAL_EMOJIS` (use emojis from different guilds)
|
||||
* - `EXTERNAL_EMOJIS` **(deprecated)**
|
||||
* - `CONNECT` (connect to a voice channel)
|
||||
* - `SPEAK` (speak in a voice channel)
|
||||
* - `MUTE_MEMBERS` (mute members across all voice channels)
|
||||
* - `DEAFEN_MEMBERS` (deafen members across all voice channels)
|
||||
* - `MOVE_MEMBERS` (move members between voice channels)
|
||||
* - `USE_VAD` (use voice activity detection)
|
||||
* - `CHANGE_NICKNAME`
|
||||
* - `MANAGE_NICKNAMES` (change other members' nicknames)
|
||||
* - `MANAGE_ROLES`
|
||||
* - `MANAGE_ROLES_OR_PERMISSIONS` **(deprecated)**
|
||||
* - `MANAGE_WEBHOOKS`
|
||||
* - `MANAGE_EMOJIS`
|
||||
* @type {Object}
|
||||
* @see {@link https://discordapp.com/developers/docs/topics/permissions}
|
||||
*/
|
||||
static FLAGS:{
|
||||
CREATE_INSTANT_INVITE: number,
|
||||
KICK_MEMBERS: number,
|
||||
BAN_MEMBERS: number,
|
||||
ADMINISTRATOR: number,
|
||||
MANAGE_CHANNELS: number,
|
||||
MANAGE_GUILD: number,
|
||||
ADD_REACTIONS: number,
|
||||
VIEW_AUDIT_LOG: number,
|
||||
PRIORITY_SPEAKER: number,
|
||||
STREAM: number,
|
||||
|
||||
VIEW_CHANNEL: number,
|
||||
READ_MESSAGES: number,
|
||||
SEND_MESSAGES: number,
|
||||
SEND_TTS_MESSAGES: number,
|
||||
MANAGE_MESSAGES: number,
|
||||
EMBED_LINKS: number,
|
||||
ATTACH_FILES: number,
|
||||
READ_MESSAGE_HISTORY: number,
|
||||
MENTION_EVERYONE: number,
|
||||
EXTERNAL_EMOJIS: number,
|
||||
USE_EXTERNAL_EMOJIS: number,
|
||||
|
||||
CONNECT: number,
|
||||
SPEAK: number,
|
||||
MUTE_MEMBERS: number,
|
||||
DEAFEN_MEMBERS: number,
|
||||
MOVE_MEMBERS: number,
|
||||
USE_VAD: number,
|
||||
|
||||
CHANGE_NICKNAME: number,
|
||||
MANAGE_NICKNAMES: number,
|
||||
MANAGE_ROLES: number,
|
||||
MANAGE_ROLES_OR_PERMISSIONS: number,
|
||||
MANAGE_WEBHOOKS: number,
|
||||
MANAGE_EMOJIS: number,
|
||||
} = {
|
||||
CREATE_INSTANT_INVITE: 1 << 0,
|
||||
KICK_MEMBERS: 1 << 1,
|
||||
BAN_MEMBERS: 1 << 2,
|
||||
ADMINISTRATOR: 1 << 3,
|
||||
MANAGE_CHANNELS: 1 << 4,
|
||||
MANAGE_GUILD: 1 << 5,
|
||||
ADD_REACTIONS: 1 << 6,
|
||||
VIEW_AUDIT_LOG: 1 << 7,
|
||||
PRIORITY_SPEAKER: 1 << 8,
|
||||
STREAM: 1 << 9,
|
||||
|
||||
VIEW_CHANNEL: 1 << 10,
|
||||
READ_MESSAGES: 1 << 10,
|
||||
SEND_MESSAGES: 1 << 11,
|
||||
SEND_TTS_MESSAGES: 1 << 12,
|
||||
MANAGE_MESSAGES: 1 << 13,
|
||||
EMBED_LINKS: 1 << 14,
|
||||
ATTACH_FILES: 1 << 15,
|
||||
READ_MESSAGE_HISTORY: 1 << 16,
|
||||
MENTION_EVERYONE: 1 << 17,
|
||||
EXTERNAL_EMOJIS: 1 << 18,
|
||||
USE_EXTERNAL_EMOJIS: 1 << 18,
|
||||
|
||||
CONNECT: 1 << 20,
|
||||
SPEAK: 1 << 21,
|
||||
MUTE_MEMBERS: 1 << 22,
|
||||
DEAFEN_MEMBERS: 1 << 23,
|
||||
MOVE_MEMBERS: 1 << 24,
|
||||
USE_VAD: 1 << 25,
|
||||
|
||||
CHANGE_NICKNAME: 1 << 26,
|
||||
MANAGE_NICKNAMES: 1 << 27,
|
||||
MANAGE_ROLES: 1 << 28,
|
||||
MANAGE_ROLES_OR_PERMISSIONS: 1 << 28,
|
||||
MANAGE_WEBHOOKS: 1 << 29,
|
||||
MANAGE_EMOJIS: 1 << 30,
|
||||
};
|
||||
|
||||
/**
|
||||
* Bitfield representing every permission combined
|
||||
* @type {number}
|
||||
*/
|
||||
static ALL: number = Object.keys(Permissions.FLAGS).reduce(
|
||||
(all, p) => all | Permissions.FLAGS[p],
|
||||
0
|
||||
);
|
||||
|
||||
/**
|
||||
* Bitfield representing the default permissions for users
|
||||
* @type {number}
|
||||
*/
|
||||
static DEFAULT: number = 104324673;
|
||||
}
|
||||
|
||||
Object.defineProperty(Permissions.prototype, "raw", {
|
||||
get: util.deprecate(
|
||||
Object.getOwnPropertyDescriptor(Permissions.prototype, "raw").get,
|
||||
"EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead"
|
||||
),
|
||||
set: util.deprecate(
|
||||
Object.getOwnPropertyDescriptor(Permissions.prototype, "raw").set,
|
||||
"EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead"
|
||||
),
|
||||
});
|
||||
|
||||
/**
|
||||
* Data that can be resolved to give a permission number. This can be:
|
||||
* * A string (see {@link Permissions.FLAGS})
|
||||
* * A permission number
|
||||
* @typedef {string|number|Permissions|PermissionResolvable[]} PermissionResolvable
|
||||
*/
|
||||
export type PermissionResolvable =
|
||||
| keyof typeof Permissions.FLAGS
|
||||
| number
|
||||
| Permissions
|
||||
| PermissionResolvable[];
|
||||
|
|
|
@ -1,69 +1,69 @@
|
|||
import {requireModule, DiscordChannel} from "../DiscordToModules"
|
||||
import { Snowflake } from "../.."
|
||||
|
||||
let channelsModuleInternal1:{
|
||||
getChannel(id:Snowflake): DiscordChannel,
|
||||
getChannels(): {
|
||||
[k:string]: DiscordChannel
|
||||
},
|
||||
getDMFromUserId(id:string):Snowflake,
|
||||
getDMUserIds():Snowflake[],
|
||||
getFollowerStatsForChannel(id: Snowflake):{
|
||||
loadingStatus: "succeeded"|"failed",
|
||||
lastFetched: number,
|
||||
channelsFollowing: number,
|
||||
guildMembers: number,
|
||||
guildsFollowing: number,
|
||||
usersSeenEver: number,
|
||||
subscribersGainedSinceLastPost: number,
|
||||
subscribersLostSinceLastPost: number
|
||||
},
|
||||
getGDMsForRecipients(recipients: Snowflake[]):Set<Snowflake>
|
||||
} = requireModule(e => e.default && e.default.getChannels && e.default.getChannel)
|
||||
|
||||
let channelsModuleInternal2:{
|
||||
deleteChannel(id: Snowflake):void
|
||||
}
|
||||
|
||||
let channelsModuleInternal4:{
|
||||
createChannel(guildId:Snowflake, type:string, name:string,
|
||||
permissionOverwrites: {
|
||||
id: Snowflake,
|
||||
type: "role"|"member",
|
||||
allow: number,
|
||||
deny: number
|
||||
}[], bitrate:number, userLimit:number, parentId:Snowflake, skuId:Snowflake, branchId:Snowflake):void
|
||||
} = requireModule(e => e.default && e.default.createChannel)
|
||||
|
||||
let channelsModuleInternal3:{
|
||||
hasUnread(channel_id:Snowflake):boolean,
|
||||
hasCategoryUnread(channel_id:Snowflake):boolean,
|
||||
getUnreadCount(channel_id:Snowflake):number,
|
||||
getMentionCount(channel_id:Snowflake):number,
|
||||
ackMessageId(channel_id:Snowflake):Snowflake,
|
||||
lastMessageId(channel_id:Snowflake):Snowflake,
|
||||
getOldestUnreadMessageId(channel_id:Snowflake):Snowflake,
|
||||
getOldestUnreadTimestamp(channel_id:Snowflake):number,
|
||||
isEstimated(channel_id:Snowflake):boolean,
|
||||
hasUnreadPins(channel_id:Snowflake):boolean,
|
||||
getAllReadStates()
|
||||
} = requireModule(e => e.default && e.default.lastMessageId)
|
||||
|
||||
function set3(){
|
||||
if(channelsModuleInternal3)return
|
||||
channelsModuleInternal3 = requireModule(e => e.default && e.default.lastMessageId)
|
||||
}
|
||||
|
||||
export = {
|
||||
getChannel: channelsModuleInternal1.getChannel,
|
||||
getAllChannels: channelsModuleInternal1.getChannels,
|
||||
get delete():typeof channelsModuleInternal2.deleteChannel{
|
||||
return channelsModuleInternal2 ? channelsModuleInternal2.deleteChannel : (channelsModuleInternal2 = requireModule(e => e.default && e.default.deleteChannel), channelsModuleInternal2.deleteChannel)
|
||||
},
|
||||
|
||||
get lastMessageId(){
|
||||
set3()
|
||||
return channelsModuleInternal3.lastMessageId
|
||||
},
|
||||
createGuildChannel: channelsModuleInternal4.createChannel
|
||||
import {requireModule, DiscordChannel} from "../DiscordToModules"
|
||||
import { Snowflake } from "../.."
|
||||
|
||||
let channelsModuleInternal1:{
|
||||
getChannel(id:Snowflake): DiscordChannel,
|
||||
getChannels(): {
|
||||
[k:string]: DiscordChannel
|
||||
},
|
||||
getDMFromUserId(id:string):Snowflake,
|
||||
getDMUserIds():Snowflake[],
|
||||
getFollowerStatsForChannel(id: Snowflake):{
|
||||
loadingStatus: "succeeded"|"failed",
|
||||
lastFetched: number,
|
||||
channelsFollowing: number,
|
||||
guildMembers: number,
|
||||
guildsFollowing: number,
|
||||
usersSeenEver: number,
|
||||
subscribersGainedSinceLastPost: number,
|
||||
subscribersLostSinceLastPost: number
|
||||
},
|
||||
getGDMsForRecipients(recipients: Snowflake[]):Set<Snowflake>
|
||||
} = requireModule(e => e.default && e.default.getChannels && e.default.getChannel)
|
||||
|
||||
let channelsModuleInternal2:{
|
||||
deleteChannel(id: Snowflake):void
|
||||
}
|
||||
|
||||
let channelsModuleInternal4:{
|
||||
createChannel(guildId:Snowflake, type:string, name:string,
|
||||
permissionOverwrites: {
|
||||
id: Snowflake,
|
||||
type: "role"|"member",
|
||||
allow: number,
|
||||
deny: number
|
||||
}[], bitrate:number, userLimit:number, parentId:Snowflake, skuId:Snowflake, branchId:Snowflake):void
|
||||
} = requireModule(e => e.default && e.default.createChannel)
|
||||
|
||||
let channelsModuleInternal3:{
|
||||
hasUnread(channel_id:Snowflake):boolean,
|
||||
hasCategoryUnread(channel_id:Snowflake):boolean,
|
||||
getUnreadCount(channel_id:Snowflake):number,
|
||||
getMentionCount(channel_id:Snowflake):number,
|
||||
ackMessageId(channel_id:Snowflake):Snowflake,
|
||||
lastMessageId(channel_id:Snowflake):Snowflake,
|
||||
getOldestUnreadMessageId(channel_id:Snowflake):Snowflake,
|
||||
getOldestUnreadTimestamp(channel_id:Snowflake):number,
|
||||
isEstimated(channel_id:Snowflake):boolean,
|
||||
hasUnreadPins(channel_id:Snowflake):boolean,
|
||||
getAllReadStates()
|
||||
} = requireModule(e => e.default && e.default.lastMessageId)
|
||||
|
||||
function set3(){
|
||||
if(channelsModuleInternal3)return
|
||||
channelsModuleInternal3 = requireModule(e => e.default && e.default.lastMessageId)
|
||||
}
|
||||
|
||||
export = {
|
||||
getChannel: channelsModuleInternal1.getChannel,
|
||||
getAllChannels: channelsModuleInternal1.getChannels,
|
||||
get delete():typeof channelsModuleInternal2.deleteChannel{
|
||||
return channelsModuleInternal2 ? channelsModuleInternal2.deleteChannel : (channelsModuleInternal2 = requireModule(e => e.default && e.default.deleteChannel), channelsModuleInternal2.deleteChannel)
|
||||
},
|
||||
|
||||
get lastMessageId(){
|
||||
set3()
|
||||
return channelsModuleInternal3.lastMessageId
|
||||
},
|
||||
createGuildChannel: channelsModuleInternal4.createChannel
|
||||
}
|
|
@ -1,149 +1,149 @@
|
|||
import { DiscordChannel, DiscordGuild, DiscordGuildMember, DiscordRole, DiscordMessage, DiscordUser } from "./DiscordToModules"
|
||||
import { Channel, Snowflake } from "..";
|
||||
import BaseChannel from "../structures/BaseChannel";
|
||||
import Guild from "../structures/Guild";
|
||||
import TextChannel from "../structures/TextChannel";
|
||||
import GuildMember from "../structures/GuildMember";
|
||||
import Role from "../structures/Role";
|
||||
import User from "../structures/User";
|
||||
import Message from "../structures/Message";
|
||||
import { ChannelTypes } from "./Constants";
|
||||
import CategoryChannel from "../structures/CategoryChannel";
|
||||
import Collection from "@discordjs/collection";
|
||||
import Permissions, { PermissionResolvable } from "./Permissions";
|
||||
import PermissionOverwrites from "../structures/PermissionOverwrites";
|
||||
|
||||
export function createChannel(channel:DiscordChannel):Channel{
|
||||
let constructor = channels[channel.type] || BaseChannel
|
||||
return new constructor(channel)
|
||||
}
|
||||
|
||||
const channels:(new(channel:DiscordChannel) => Channel)[] = [
|
||||
TextChannel
|
||||
]
|
||||
|
||||
export function createGuild(guild:DiscordGuild):Guild{
|
||||
return new Guild(guild)
|
||||
}
|
||||
|
||||
export function createGuildMember(member:DiscordGuildMember):GuildMember{
|
||||
return new GuildMember(member)
|
||||
}
|
||||
|
||||
export function createRole(role:DiscordRole):Role{
|
||||
return new Role(role)
|
||||
}
|
||||
|
||||
export function createMessage(message:DiscordMessage):Message{
|
||||
return new Message(message)
|
||||
}
|
||||
|
||||
export function createUser(user:DiscordUser):User{
|
||||
return new User(user)
|
||||
}
|
||||
|
||||
export function applyMixins(derivedCtor: any, baseCtors: any[]) {
|
||||
baseCtors.forEach(baseCtor => {
|
||||
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
|
||||
if (name !== 'constructor') {
|
||||
derivedCtor.prototype[name] = baseCtor.prototype[name];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a snowflake from a decimal string to a bit string.
|
||||
* @param {string} num Snowflake to be transformed
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
export function idToBinary(num:string):string{
|
||||
let bin = '';
|
||||
let high = parseInt(num.slice(0, -10)) || 0;
|
||||
let low = parseInt(num.slice(-10));
|
||||
while (low > 0 || high > 0) {
|
||||
bin = String(low & 1) + bin;
|
||||
low = Math.floor(low / 2);
|
||||
if (high > 0) {
|
||||
low += 5000000000 * (high % 2);
|
||||
high = Math.floor(high / 2);
|
||||
}
|
||||
}
|
||||
return bin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a snowflake from a bit string to a decimal string.
|
||||
* @param {string} num Bit string to be transformed
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
export function binaryToID(num:string):string {
|
||||
let dec = '';
|
||||
|
||||
while (num.length > 50) {
|
||||
const high = parseInt(num.slice(0, -32), 2);
|
||||
const low = parseInt((high % 10).toString(2) + num.slice(-32), 2);
|
||||
|
||||
dec = (low % 10).toString() + dec;
|
||||
num = Math.floor(high / 10).toString(2) +
|
||||
Math.floor(low / 10)
|
||||
.toString(2)
|
||||
.padStart(32, '0');
|
||||
}
|
||||
|
||||
let num2 = parseInt(num, 2);
|
||||
while (num2 > 0) {
|
||||
dec = (num2 % 10).toString() + dec;
|
||||
num2 = Math.floor(num2 / 10);
|
||||
}
|
||||
|
||||
return dec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export type UserResolvable = User | Snowflake | Message | Guild | GuildMember
|
||||
|
||||
export function resolveUserID(user:UserResolvable){
|
||||
if(typeof user === "string")return user // ID
|
||||
if(user instanceof User)return user.id // User
|
||||
if(user instanceof Message)return user.author.id // Message Author
|
||||
if(user instanceof Guild)return user.ownerID // Guild
|
||||
if(user instanceof GuildMember)return user.id // GuildMember
|
||||
return null
|
||||
}
|
||||
|
||||
export type ChannelData = {
|
||||
type?: ChannelTypes,
|
||||
name?: string,
|
||||
position?: number,
|
||||
topic?: string,
|
||||
nsfw?: boolean,
|
||||
bitrate?: number,
|
||||
userLimit?: number,
|
||||
parent?: CategoryChannel|Snowflake,
|
||||
permissionOverwrites?: ChannelCreationOverwrites[] | Collection<Snowflake, PermissionOverwrites>,
|
||||
rateLimitPerUser?: number
|
||||
}
|
||||
|
||||
export type ChannelCreationOverwrites = {
|
||||
allow?: PermissionResolvable,
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
allowed?: PermissionResolvable,
|
||||
deny?: PermissionResolvable,
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
denied?: PermissionResolvable,
|
||||
id?: GuildMemberResolvable | RoleResolvable
|
||||
}
|
||||
|
||||
export {BitFieldResolvable} from "./BitField"
|
||||
|
||||
export type GuildMemberResolvable = GuildMember | User
|
||||
|
||||
import { DiscordChannel, DiscordGuild, DiscordGuildMember, DiscordRole, DiscordMessage, DiscordUser } from "./DiscordToModules"
|
||||
import { Channel, Snowflake } from "..";
|
||||
import BaseChannel from "../structures/BaseChannel";
|
||||
import Guild from "../structures/Guild";
|
||||
import TextChannel from "../structures/TextChannel";
|
||||
import GuildMember from "../structures/GuildMember";
|
||||
import Role from "../structures/Role";
|
||||
import User from "../structures/User";
|
||||
import Message from "../structures/Message";
|
||||
import { ChannelTypes } from "./Constants";
|
||||
import CategoryChannel from "../structures/CategoryChannel";
|
||||
import Collection from "@discordjs/collection";
|
||||
import Permissions, { PermissionResolvable } from "./Permissions";
|
||||
import PermissionOverwrites from "../structures/PermissionOverwrites";
|
||||
|
||||
export function createChannel(channel:DiscordChannel):Channel{
|
||||
let constructor = channels[channel.type] || BaseChannel
|
||||
return new constructor(channel)
|
||||
}
|
||||
|
||||
const channels:(new(channel:DiscordChannel) => Channel)[] = [
|
||||
TextChannel
|
||||
]
|
||||
|
||||
export function createGuild(guild:DiscordGuild):Guild{
|
||||
return new Guild(guild)
|
||||
}
|
||||
|
||||
export function createGuildMember(member:DiscordGuildMember):GuildMember{
|
||||
return new GuildMember(member)
|
||||
}
|
||||
|
||||
export function createRole(role:DiscordRole):Role{
|
||||
return new Role(role)
|
||||
}
|
||||
|
||||
export function createMessage(message:DiscordMessage):Message{
|
||||
return new Message(message)
|
||||
}
|
||||
|
||||
export function createUser(user:DiscordUser):User{
|
||||
return new User(user)
|
||||
}
|
||||
|
||||
export function applyMixins(derivedCtor: any, baseCtors: any[]) {
|
||||
baseCtors.forEach(baseCtor => {
|
||||
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
|
||||
if (name !== 'constructor') {
|
||||
derivedCtor.prototype[name] = baseCtor.prototype[name];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a snowflake from a decimal string to a bit string.
|
||||
* @param {string} num Snowflake to be transformed
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
export function idToBinary(num:string):string{
|
||||
let bin = '';
|
||||
let high = parseInt(num.slice(0, -10)) || 0;
|
||||
let low = parseInt(num.slice(-10));
|
||||
while (low > 0 || high > 0) {
|
||||
bin = String(low & 1) + bin;
|
||||
low = Math.floor(low / 2);
|
||||
if (high > 0) {
|
||||
low += 5000000000 * (high % 2);
|
||||
high = Math.floor(high / 2);
|
||||
}
|
||||
}
|
||||
return bin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a snowflake from a bit string to a decimal string.
|
||||
* @param {string} num Bit string to be transformed
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
export function binaryToID(num:string):string {
|
||||
let dec = '';
|
||||
|
||||
while (num.length > 50) {
|
||||
const high = parseInt(num.slice(0, -32), 2);
|
||||
const low = parseInt((high % 10).toString(2) + num.slice(-32), 2);
|
||||
|
||||
dec = (low % 10).toString() + dec;
|
||||
num = Math.floor(high / 10).toString(2) +
|
||||
Math.floor(low / 10)
|
||||
.toString(2)
|
||||
.padStart(32, '0');
|
||||
}
|
||||
|
||||
let num2 = parseInt(num, 2);
|
||||
while (num2 > 0) {
|
||||
dec = (num2 % 10).toString() + dec;
|
||||
num2 = Math.floor(num2 / 10);
|
||||
}
|
||||
|
||||
return dec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export type UserResolvable = User | Snowflake | Message | Guild | GuildMember
|
||||
|
||||
export function resolveUserID(user:UserResolvable){
|
||||
if(typeof user === "string")return user // ID
|
||||
if(user instanceof User)return user.id // User
|
||||
if(user instanceof Message)return user.author.id // Message Author
|
||||
if(user instanceof Guild)return user.ownerID // Guild
|
||||
if(user instanceof GuildMember)return user.id // GuildMember
|
||||
return null
|
||||
}
|
||||
|
||||
export type ChannelData = {
|
||||
type?: ChannelTypes,
|
||||
name?: string,
|
||||
position?: number,
|
||||
topic?: string,
|
||||
nsfw?: boolean,
|
||||
bitrate?: number,
|
||||
userLimit?: number,
|
||||
parent?: CategoryChannel|Snowflake,
|
||||
permissionOverwrites?: ChannelCreationOverwrites[] | Collection<Snowflake, PermissionOverwrites>,
|
||||
rateLimitPerUser?: number
|
||||
}
|
||||
|
||||
export type ChannelCreationOverwrites = {
|
||||
allow?: PermissionResolvable,
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
allowed?: PermissionResolvable,
|
||||
deny?: PermissionResolvable,
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
denied?: PermissionResolvable,
|
||||
id?: GuildMemberResolvable | RoleResolvable
|
||||
}
|
||||
|
||||
export {BitFieldResolvable} from "./BitField"
|
||||
|
||||
export type GuildMemberResolvable = GuildMember | User
|
||||
|
||||
export type RoleResolvable = Role | Snowflake
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
const config = require("./webpack.config.js")
|
||||
|
||||
config.mode = "production"
|
||||
config.devtool = "source-map"
|
||||
config.output.filename = "main.min.js"
|
||||
|
||||
const config = require("./webpack.config.js")
|
||||
|
||||
config.mode = "production"
|
||||
config.devtool = "source-map"
|
||||
config.output.filename = "main.min.js"
|
||||
|
||||
module.exports = config
|
|
@ -1,26 +1,26 @@
|
|||
// bait typescript into thinking this is not reactDOM so no circular dependency.
|
||||
window.ReactDOM = (window["Reac"+"tDOM"] || // If in Lightcord
|
||||
(()=>{ // If in Standard BetterDiscord
|
||||
try{
|
||||
return window.BdApi.ReactDOM
|
||||
}catch(e){
|
||||
return null
|
||||
}
|
||||
})() ||
|
||||
(()=>{ // If in Powercord
|
||||
try{
|
||||
const webpack = require("powercord/webpack")
|
||||
return webpack.ReactDOM
|
||||
}catch(e){
|
||||
return null
|
||||
}
|
||||
})() ||
|
||||
(()=>{ // If in EnhancedDiscord
|
||||
try{
|
||||
return window.EDApi.ReactDOM
|
||||
}catch(e){
|
||||
return null
|
||||
}
|
||||
})())
|
||||
|
||||
// bait typescript into thinking this is not reactDOM so no circular dependency.
|
||||
window.ReactDOM = (window["Reac"+"tDOM"] || // If in Lightcord
|
||||
(()=>{ // If in Standard BetterDiscord
|
||||
try{
|
||||
return window.BdApi.ReactDOM
|
||||
}catch(e){
|
||||
return null
|
||||
}
|
||||
})() ||
|
||||
(()=>{ // If in Powercord
|
||||
try{
|
||||
const webpack = require("powercord/webpack")
|
||||
return webpack.ReactDOM
|
||||
}catch(e){
|
||||
return null
|
||||
}
|
||||
})() ||
|
||||
(()=>{ // If in EnhancedDiscord
|
||||
try{
|
||||
return window.EDApi.ReactDOM
|
||||
}catch(e){
|
||||
return null
|
||||
}
|
||||
})())
|
||||
|
||||
module.exports = window.ReactDOM
|
|
@ -1,25 +1,25 @@
|
|||
window.React = (window.React || // If in Lightcord
|
||||
(()=>{ // If in Standard BetterDiscord
|
||||
try{
|
||||
return window.BdApi.React
|
||||
}catch(e){
|
||||
return null
|
||||
}
|
||||
})() ||
|
||||
(()=>{ // If in Powercord
|
||||
try{
|
||||
const webpack = require("powercord/webpack")
|
||||
return webpack.React
|
||||
}catch(e){
|
||||
return null
|
||||
}
|
||||
})() ||
|
||||
(()=>{ // If in EnhancedDiscord
|
||||
try{
|
||||
return window.EDApi.React
|
||||
}catch(e){
|
||||
return null
|
||||
}
|
||||
})())
|
||||
|
||||
window.React = (window.React || // If in Lightcord
|
||||
(()=>{ // If in Standard BetterDiscord
|
||||
try{
|
||||
return window.BdApi.React
|
||||
}catch(e){
|
||||
return null
|
||||
}
|
||||
})() ||
|
||||
(()=>{ // If in Powercord
|
||||
try{
|
||||
const webpack = require("powercord/webpack")
|
||||
return webpack.React
|
||||
}catch(e){
|
||||
return null
|
||||
}
|
||||
})() ||
|
||||
(()=>{ // If in EnhancedDiscord
|
||||
try{
|
||||
return window.EDApi.React
|
||||
}catch(e){
|
||||
return null
|
||||
}
|
||||
})())
|
||||
|
||||
module.exports = window.React
|
|
@ -1,49 +1,49 @@
|
|||
import DiscordButton from "./inputs/Button"
|
||||
import Switch from "./inputs/Switch"
|
||||
import RadioGroup from "./inputs/RadioGroup"
|
||||
import TextArea from "./inputs/TextArea"
|
||||
import TextInput from "./inputs/TextInput"
|
||||
import Dropdown from "./inputs/Dropdown"
|
||||
import Title from "./general/Title"
|
||||
import SettingsTitle from "./general/SettingsTitle"
|
||||
import Tabs, { Tab } from "./general/Tabs"
|
||||
import SettingSubTitle from "./general/SettingSubTitle"
|
||||
import CodeBlock from "./general/CodeBlock"
|
||||
import cloneNullProto from "../modules/cloneNullProto"
|
||||
import Tooltip from "./general/Tooltip"
|
||||
import ColorPicker from "./inputs/ColorPicker"
|
||||
import AlertBox from "./general/AlertBox"
|
||||
import ErrorCatcher, { createProxyErrorCatcherClass } from "./general/ErrorCatcher"
|
||||
import Flex, { FlexChild } from "./general/Flex"
|
||||
import Text from "./general/Text"
|
||||
import DateRange from "./inputs/DateRange"
|
||||
import DateInput from "./inputs/DateInput"
|
||||
|
||||
const RadioGroupProxied = createProxyErrorCatcherClass(RadioGroup)
|
||||
export default cloneNullProto({
|
||||
inputs: cloneNullProto({
|
||||
Button: createProxyErrorCatcherClass(DiscordButton),
|
||||
Switch: createProxyErrorCatcherClass(Switch),
|
||||
Choices: RadioGroupProxied,
|
||||
RadioGroup: RadioGroupProxied,
|
||||
TextArea: createProxyErrorCatcherClass(TextArea),
|
||||
TextInput: createProxyErrorCatcherClass(TextInput),
|
||||
Dropdown: createProxyErrorCatcherClass(Dropdown),
|
||||
ColorPicker: createProxyErrorCatcherClass(ColorPicker),
|
||||
DateRange: createProxyErrorCatcherClass(DateRange),
|
||||
DateInput: createProxyErrorCatcherClass(DateInput)
|
||||
}),
|
||||
general: cloneNullProto({
|
||||
Title: createProxyErrorCatcherClass(Title),
|
||||
SettingsTitle: createProxyErrorCatcherClass(SettingsTitle),
|
||||
SettingSubTitle: createProxyErrorCatcherClass(SettingSubTitle),
|
||||
Tabs: createProxyErrorCatcherClass(Tabs),
|
||||
CodeBlock: createProxyErrorCatcherClass(CodeBlock),
|
||||
Tooltip: createProxyErrorCatcherClass(Tooltip),
|
||||
AlertBox: createProxyErrorCatcherClass(AlertBox),
|
||||
Flex: createProxyErrorCatcherClass(Flex),
|
||||
FlexChild: createProxyErrorCatcherClass(FlexChild),
|
||||
ErrorCatcher: ErrorCatcher,
|
||||
Text: Text
|
||||
})
|
||||
import DiscordButton from "./inputs/Button"
|
||||
import Switch from "./inputs/Switch"
|
||||
import RadioGroup from "./inputs/RadioGroup"
|
||||
import TextArea from "./inputs/TextArea"
|
||||
import TextInput from "./inputs/TextInput"
|
||||
import Dropdown from "./inputs/Dropdown"
|
||||
import Title from "./general/Title"
|
||||
import SettingsTitle from "./general/SettingsTitle"
|
||||
import Tabs, { Tab } from "./general/Tabs"
|
||||
import SettingSubTitle from "./general/SettingSubTitle"
|
||||
import CodeBlock from "./general/CodeBlock"
|
||||
import cloneNullProto from "../modules/cloneNullProto"
|
||||
import Tooltip from "./general/Tooltip"
|
||||
import ColorPicker from "./inputs/ColorPicker"
|
||||
import AlertBox from "./general/AlertBox"
|
||||
import ErrorCatcher, { createProxyErrorCatcherClass } from "./general/ErrorCatcher"
|
||||
import Flex, { FlexChild } from "./general/Flex"
|
||||
import Text from "./general/Text"
|
||||
import DateRange from "./inputs/DateRange"
|
||||
import DateInput from "./inputs/DateInput"
|
||||
|
||||
const RadioGroupProxied = createProxyErrorCatcherClass(RadioGroup)
|
||||
export default cloneNullProto({
|
||||
inputs: cloneNullProto({
|
||||
Button: createProxyErrorCatcherClass(DiscordButton),
|
||||
Switch: createProxyErrorCatcherClass(Switch),
|
||||
Choices: RadioGroupProxied,
|
||||
RadioGroup: RadioGroupProxied,
|
||||
TextArea: createProxyErrorCatcherClass(TextArea),
|
||||
TextInput: createProxyErrorCatcherClass(TextInput),
|
||||
Dropdown: createProxyErrorCatcherClass(Dropdown),
|
||||
ColorPicker: createProxyErrorCatcherClass(ColorPicker),
|
||||
DateRange: createProxyErrorCatcherClass(DateRange),
|
||||
DateInput: createProxyErrorCatcherClass(DateInput)
|
||||
}),
|
||||
general: cloneNullProto({
|
||||
Title: createProxyErrorCatcherClass(Title),
|
||||
SettingsTitle: createProxyErrorCatcherClass(SettingsTitle),
|
||||
SettingSubTitle: createProxyErrorCatcherClass(SettingSubTitle),
|
||||
Tabs: createProxyErrorCatcherClass(Tabs),
|
||||
CodeBlock: createProxyErrorCatcherClass(CodeBlock),
|
||||
Tooltip: createProxyErrorCatcherClass(Tooltip),
|
||||
AlertBox: createProxyErrorCatcherClass(AlertBox),
|
||||
Flex: createProxyErrorCatcherClass(Flex),
|
||||
FlexChild: createProxyErrorCatcherClass(FlexChild),
|
||||
ErrorCatcher: ErrorCatcher,
|
||||
Text: Text
|
||||
})
|
||||
})
|
|
@ -1,95 +1,95 @@
|
|||
import * as React from "react"
|
||||
import Utils, { ColorName } from "../../modules/Utils"
|
||||
|
||||
|
||||
export type TextProps = {
|
||||
weight?: TextWeight,
|
||||
color?: TextColor|string,
|
||||
textCase?: TextCase,
|
||||
size?: TextSize,
|
||||
selectable?: boolean,
|
||||
family?: TextFamily,
|
||||
children: string,
|
||||
tag?: string,
|
||||
className?: string,
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
export default class Text extends React.Component<TextProps> {
|
||||
render(){
|
||||
let classList = this.props.className ? this.props.className.split(" ") : []
|
||||
let style = this.props.style ? {...this.props.style} : {}
|
||||
style.flexGrow = 0
|
||||
if(this.props.weight){
|
||||
classList.push(`lc-text-weight${Utils.firstLetterUppercase(this.props.weight)}`)
|
||||
}
|
||||
if(this.props.color){
|
||||
style.color = Utils.getColor(this.props.color as TextColor) || this.props.color
|
||||
}
|
||||
if(this.props.textCase){
|
||||
classList.push(`lc-text-cases${Utils.firstLetterUppercase(this.props.textCase)}`)
|
||||
}
|
||||
if(this.props.size){
|
||||
classList.push(...Constants.sizes[this.props.size])
|
||||
}
|
||||
if(this.props.family){
|
||||
classList.push(`lc-text-${this.props.family}`)
|
||||
}
|
||||
if(this.props.selectable){
|
||||
classList.push("lc-text-selectable")
|
||||
}
|
||||
|
||||
return React.createElement(this.props.tag, {
|
||||
style,
|
||||
className: classList.join(" ")
|
||||
}, this.props.children)
|
||||
}
|
||||
|
||||
static defaultProps:TextProps = {
|
||||
tag: "div",
|
||||
children: null
|
||||
}
|
||||
|
||||
static get AllPreviews(){
|
||||
return AllPreviews || (AllPreviews = [
|
||||
this.weights.map(e => ({weight: e})),
|
||||
this.colors.map(e => ({color: e})),
|
||||
this.textCases.map(e => ({textCase: e})),
|
||||
this.sizes.map(e => ({size: e})),
|
||||
this.familys.map(e => ({family: e})),
|
||||
[
|
||||
{
|
||||
children: "Test Text"
|
||||
}
|
||||
],
|
||||
["div","p",...Utils.executeXTimes((index) => {
|
||||
return "h"+(index+1)
|
||||
}, 6)].map(e => ({tag: e})),
|
||||
[true, false].map(e => ({selectable: e}))
|
||||
])
|
||||
}
|
||||
|
||||
static weights:TextWeight[] = ["light", "normal", "medium", "semibold", "bold"]
|
||||
static get colors():TextColor[]{
|
||||
return Object.keys(Lightcord.DiscordModules.constants.Colors).map(e => e.toLowerCase()) as TextColor[]
|
||||
}
|
||||
static textCases:TextCase[] = ["lowercase", "uppercase"]
|
||||
static sizes:TextSize[] = ["small", "medium", "medium_small", "medium_large", "large"]
|
||||
static familys:TextFamily[] = ["primary", "code"]
|
||||
}
|
||||
let AllPreviews
|
||||
|
||||
export type TextWeight = "light"|"normal"|"medium"|"semibold"|"bold"
|
||||
export type TextColor = ColorName
|
||||
export type TextCase = "lowercase"|"uppercase"
|
||||
export type TextSize = "small"|"medium"|"medium_small"|"medium_large"|"large"
|
||||
export type TextFamily = "primary"|"code"
|
||||
|
||||
export const Constants = {
|
||||
sizes: {
|
||||
small: ["lc-text-size12", "lc-text-height16"],
|
||||
medium_small: ["lc-text-size14", "lc-text-height16"],
|
||||
medium: ["lc-text-size16", "lc-text-height20"],
|
||||
medium_large: ["lc-text-size20", "lc-text-height26"],
|
||||
large: ["lc-text-size28", "lc-text-height34"]
|
||||
}
|
||||
import * as React from "react"
|
||||
import Utils, { ColorName } from "../../modules/Utils"
|
||||
|
||||
|
||||
export type TextProps = {
|
||||
weight?: TextWeight,
|
||||
color?: TextColor|string,
|
||||
textCase?: TextCase,
|
||||
size?: TextSize,
|
||||
selectable?: boolean,
|
||||
family?: TextFamily,
|
||||
children: string,
|
||||
tag?: string,
|
||||
className?: string,
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
export default class Text extends React.Component<TextProps> {
|
||||
render(){
|
||||
let classList = this.props.className ? this.props.className.split(" ") : []
|
||||
let style = this.props.style ? {...this.props.style} : {}
|
||||
style.flexGrow = 0
|
||||
if(this.props.weight){
|
||||
classList.push(`lc-text-weight${Utils.firstLetterUppercase(this.props.weight)}`)
|
||||
}
|
||||
if(this.props.color){
|
||||
style.color = Utils.getColor(this.props.color as TextColor) || this.props.color
|
||||
}
|
||||
if(this.props.textCase){
|
||||
classList.push(`lc-text-cases${Utils.firstLetterUppercase(this.props.textCase)}`)
|
||||
}
|
||||
if(this.props.size){
|
||||
classList.push(...Constants.sizes[this.props.size])
|
||||
}
|
||||
if(this.props.family){
|
||||
classList.push(`lc-text-${this.props.family}`)
|
||||
}
|
||||
if(this.props.selectable){
|
||||
classList.push("lc-text-selectable")
|
||||
}
|
||||
|
||||
return React.createElement(this.props.tag, {
|
||||
style,
|
||||
className: classList.join(" ")
|
||||
}, this.props.children)
|
||||
}
|
||||
|
||||
static defaultProps:TextProps = {
|
||||
tag: "div",
|
||||
children: null
|
||||
}
|
||||
|
||||
static get AllPreviews(){
|
||||
return AllPreviews || (AllPreviews = [
|
||||
this.weights.map(e => ({weight: e})),
|
||||
this.colors.map(e => ({color: e})),
|
||||
this.textCases.map(e => ({textCase: e})),
|
||||
this.sizes.map(e => ({size: e})),
|
||||
this.familys.map(e => ({family: e})),
|
||||
[
|
||||
{
|
||||
children: "Test Text"
|
||||
}
|
||||
],
|
||||
["div","p",...Utils.executeXTimes((index) => {
|
||||
return "h"+(index+1)
|
||||
}, 6)].map(e => ({tag: e})),
|
||||
[true, false].map(e => ({selectable: e}))
|
||||
])
|
||||
}
|
||||
|
||||
static weights:TextWeight[] = ["light", "normal", "medium", "semibold", "bold"]
|
||||
static get colors():TextColor[]{
|
||||
return Object.keys(Lightcord.DiscordModules.constants.Colors).map(e => e.toLowerCase()) as TextColor[]
|
||||
}
|
||||
static textCases:TextCase[] = ["lowercase", "uppercase"]
|
||||
static sizes:TextSize[] = ["small", "medium", "medium_small", "medium_large", "large"]
|
||||
static familys:TextFamily[] = ["primary", "code"]
|
||||
}
|
||||
let AllPreviews
|
||||
|
||||
export type TextWeight = "light"|"normal"|"medium"|"semibold"|"bold"
|
||||
export type TextColor = ColorName
|
||||
export type TextCase = "lowercase"|"uppercase"
|
||||
export type TextSize = "small"|"medium"|"medium_small"|"medium_large"|"large"
|
||||
export type TextFamily = "primary"|"code"
|
||||
|
||||
export const Constants = {
|
||||
sizes: {
|
||||
small: ["lc-text-size12", "lc-text-height16"],
|
||||
medium_small: ["lc-text-size14", "lc-text-height16"],
|
||||
medium: ["lc-text-size16", "lc-text-height20"],
|
||||
medium_large: ["lc-text-size20", "lc-text-height26"],
|
||||
large: ["lc-text-size28", "lc-text-height34"]
|
||||
}
|
||||
}
|
|
@ -1,104 +1,104 @@
|
|||
import WebpackLoader from "./modules/WebpackLoader"
|
||||
import Components from "./components/components"
|
||||
import uuid from "./modules/uuid"
|
||||
import Utils from "./modules/Utils"
|
||||
import DiscordTools from "./modules/DiscordTools"
|
||||
import * as patchers from "./modules/patchers"
|
||||
import excludeProperties from "./modules/excludeProperties"
|
||||
import cloneNullProto from "./modules/cloneNullProto"
|
||||
import NOOP from "./modules/noop"
|
||||
import unfreeze from "./modules/Unfreeze"
|
||||
import { isNative, isImported } from "./modules/environnement"
|
||||
import * as bandagedbdApi from "@bandagedbd/bdapi"
|
||||
import "./alias/react"
|
||||
import "./alias/react-dom"
|
||||
import { LazyLoad } from "./modules/lazyLoader"
|
||||
patchers.patch()
|
||||
|
||||
const LightcordApi = {
|
||||
WebpackLoader: WebpackLoader,
|
||||
Components: Components,
|
||||
uuid: uuid,
|
||||
Utils: Utils,
|
||||
DiscordTools: DiscordTools,
|
||||
_: {
|
||||
excludeProperties: excludeProperties,
|
||||
cloneNullProto: cloneNullProto,
|
||||
NOOP: NOOP,
|
||||
unfreeze: unfreeze
|
||||
},
|
||||
get isNative(){return isNative},
|
||||
get isImported(){return isImported},
|
||||
LazyLoad: LazyLoad
|
||||
}
|
||||
|
||||
declare global {
|
||||
var React:typeof import("react")
|
||||
var ReactDOM:typeof import("../node_modules/@types/react-dom")
|
||||
interface Window {
|
||||
/**
|
||||
* Lightcord is only availlaible in Lightcord (native)
|
||||
*/
|
||||
Lightcord: LightcordGlobal,
|
||||
/**
|
||||
* BDModules is only availlaible in Lightcord (native)
|
||||
*/
|
||||
BDModules: {
|
||||
modules:any[],
|
||||
get(filter:(mod:any)=>boolean, modules?:any[]):any[],
|
||||
get(id:number, modules?:any[]):any,
|
||||
get(ids: [number|((mod:any)=>boolean)], modules?:any[]):any
|
||||
},
|
||||
BdApi: typeof bandagedbdApi.BdApi,
|
||||
EDApi: typeof bandagedbdApi.BdApi,
|
||||
ReactDOM: typeof ReactDOM;
|
||||
React:typeof React
|
||||
}
|
||||
var Lightcord:LightcordGlobal
|
||||
var BdApi: typeof bandagedbdApi.BdApi
|
||||
var EDApi: typeof bandagedbdApi.BdApi
|
||||
}
|
||||
|
||||
export default LightcordApi
|
||||
|
||||
Object.assign(window.Lightcord.Api, LightcordApi)
|
||||
|
||||
/**
|
||||
* The main Lightcord exports. Can be accessed with `window.Lightcord`
|
||||
*/
|
||||
export interface LightcordGlobal {
|
||||
DiscordModules: {
|
||||
/**
|
||||
* Internal Discord's dispatcher - can be used to subscribe to gateway events / client events.
|
||||
*/
|
||||
dispatcher: import("./types/DiscordDispatcherTypes").default,
|
||||
constants: import("./types/DiscordConstantsTypes").default
|
||||
},
|
||||
Settings: {
|
||||
devMode: boolean,
|
||||
callRingingBeat: boolean
|
||||
},
|
||||
Api: LightcordApiGlobal,
|
||||
BetterDiscord: {
|
||||
BdApi: typeof bandagedbdApi.BdApi,
|
||||
[mod:string]:any
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main Api. Can be accessed with `window.Lightcord.Api`
|
||||
*/
|
||||
type LightcordApiGlobal = lightcordApiMainExports & typeof LightcordApi
|
||||
|
||||
type lightcordApiMainExports = {
|
||||
/**
|
||||
* Waits until the first module that match the filter gets exported
|
||||
* @param filter The filter that specifies the module to match.
|
||||
*/
|
||||
ensureExported(filter: (mod:any) => boolean):Promise<any>,
|
||||
/**
|
||||
* Recreate the object without the `__proto__` and `prototype` properties - usefull for better formatting in console.
|
||||
* @param obj The object to recreate
|
||||
*/
|
||||
cloneNullProto<Obj = any>(obj:Obj):Obj
|
||||
import WebpackLoader from "./modules/WebpackLoader"
|
||||
import Components from "./components/components"
|
||||
import uuid from "./modules/uuid"
|
||||
import Utils from "./modules/Utils"
|
||||
import DiscordTools from "./modules/DiscordTools"
|
||||
import * as patchers from "./modules/patchers"
|
||||
import excludeProperties from "./modules/excludeProperties"
|
||||
import cloneNullProto from "./modules/cloneNullProto"
|
||||
import NOOP from "./modules/noop"
|
||||
import unfreeze from "./modules/Unfreeze"
|
||||
import { isNative, isImported } from "./modules/environnement"
|
||||
import * as bandagedbdApi from "@bandagedbd/bdapi"
|
||||
import "./alias/react"
|
||||
import "./alias/react-dom"
|
||||
import { LazyLoad } from "./modules/lazyLoader"
|
||||
patchers.patch()
|
||||
|
||||
const LightcordApi = {
|
||||
WebpackLoader: WebpackLoader,
|
||||
Components: Components,
|
||||
uuid: uuid,
|
||||
Utils: Utils,
|
||||
DiscordTools: DiscordTools,
|
||||
_: {
|
||||
excludeProperties: excludeProperties,
|
||||
cloneNullProto: cloneNullProto,
|
||||
NOOP: NOOP,
|
||||
unfreeze: unfreeze
|
||||
},
|
||||
get isNative(){return isNative},
|
||||
get isImported(){return isImported},
|
||||
LazyLoad: LazyLoad
|
||||
}
|
||||
|
||||
declare global {
|
||||
var React:typeof import("react")
|
||||
var ReactDOM:typeof import("../node_modules/@types/react-dom")
|
||||
interface Window {
|
||||
/**
|
||||
* Lightcord is only availlaible in Lightcord (native)
|
||||
*/
|
||||
Lightcord: LightcordGlobal,
|
||||
/**
|
||||
* BDModules is only availlaible in Lightcord (native)
|
||||
*/
|
||||
BDModules: {
|
||||
modules:any[],
|
||||
get(filter:(mod:any)=>boolean, modules?:any[]):any[],
|
||||
get(id:number, modules?:any[]):any,
|
||||
get(ids: [number|((mod:any)=>boolean)], modules?:any[]):any
|
||||
},
|
||||
BdApi: typeof bandagedbdApi.BdApi,
|
||||
EDApi: typeof bandagedbdApi.BdApi,
|
||||
ReactDOM: typeof ReactDOM;
|
||||
React:typeof React
|
||||
}
|
||||
var Lightcord:LightcordGlobal
|
||||
var BdApi: typeof bandagedbdApi.BdApi
|
||||
var EDApi: typeof bandagedbdApi.BdApi
|
||||
}
|
||||
|
||||
export default LightcordApi
|
||||
|
||||
Object.assign(window.Lightcord.Api, LightcordApi)
|
||||
|
||||
/**
|
||||
* The main Lightcord exports. Can be accessed with `window.Lightcord`
|
||||
*/
|
||||
export interface LightcordGlobal {
|
||||
DiscordModules: {
|
||||
/**
|
||||
* Internal Discord's dispatcher - can be used to subscribe to gateway events / client events.
|
||||
*/
|
||||
dispatcher: import("./types/DiscordDispatcherTypes").default,
|
||||
constants: import("./types/DiscordConstantsTypes").default
|
||||
},
|
||||
Settings: {
|
||||
devMode: boolean,
|
||||
callRingingBeat: boolean
|
||||
},
|
||||
Api: LightcordApiGlobal,
|
||||
BetterDiscord: {
|
||||
BdApi: typeof bandagedbdApi.BdApi,
|
||||
[mod:string]:any
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main Api. Can be accessed with `window.Lightcord.Api`
|
||||
*/
|
||||
type LightcordApiGlobal = lightcordApiMainExports & typeof LightcordApi
|
||||
|
||||
type lightcordApiMainExports = {
|
||||
/**
|
||||
* Waits until the first module that match the filter gets exported
|
||||
* @param filter The filter that specifies the module to match.
|
||||
*/
|
||||
ensureExported(filter: (mod:any) => boolean):Promise<any>,
|
||||
/**
|
||||
* Recreate the object without the `__proto__` and `prototype` properties - usefull for better formatting in console.
|
||||
* @param obj The object to recreate
|
||||
*/
|
||||
cloneNullProto<Obj = any>(obj:Obj):Obj
|
||||
}
|
|
@ -1,82 +1,82 @@
|
|||
let req
|
||||
setReq()
|
||||
function filterDangerous(mods){
|
||||
return mods.map(e => {
|
||||
return protect(e)
|
||||
})
|
||||
}
|
||||
function protect(exports){
|
||||
let theModule = exports.exports
|
||||
let mod = theModule.default
|
||||
if(!mod)return exports
|
||||
if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null;
|
||||
if (!mod.getToken && !mod.getEmail && !mod.showToken)return exports
|
||||
|
||||
const proxy = new Proxy(mod, {
|
||||
getOwnPropertyDescriptor: function(obj, prop) {
|
||||
if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined;
|
||||
return Object.getOwnPropertyDescriptor(obj, prop);
|
||||
},
|
||||
get: function(obj, func) {
|
||||
if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa";
|
||||
if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com";
|
||||
if (func == "showToken" && obj.showToken) return () => true;
|
||||
if (func == "__proto__" && obj.__proto__) return proxy;
|
||||
|
||||
return obj[func];
|
||||
}
|
||||
});
|
||||
|
||||
return Object.assign({}, exports, {exports: Object.assign({}, theModule, {default: proxy})})
|
||||
}
|
||||
|
||||
class Webpackloader {
|
||||
get modules(){
|
||||
if(req){
|
||||
return filterDangerous(Object.values(req.c).filter((e:any) => e && e.exports))
|
||||
}else{
|
||||
setReq()
|
||||
if(req){
|
||||
return filterDangerous(Object.values(req.c).filter((e:any) => e && e.exports))
|
||||
}else{
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
get(ids, modules){
|
||||
if(typeof ids === "function"){
|
||||
return (modules || this.modules).map((mdl) => {
|
||||
if(mdl && typeof mdl.exports !== "undefined"){
|
||||
return mdl.exports
|
||||
}else{
|
||||
return null
|
||||
}
|
||||
}).filter(e => e).filter(ids)
|
||||
}else if(Array.isArray(ids)){
|
||||
modules = modules || this.modules
|
||||
return ids.map(id => this.get(id, modules))
|
||||
}else{
|
||||
modules = modules || this.modules
|
||||
let module = modules.filter(e => !!e).find(e => e.i === ids)
|
||||
if(!module)return undefined
|
||||
return module.exports
|
||||
}
|
||||
}
|
||||
get default(){
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
export default new Webpackloader()
|
||||
|
||||
function setReq(){
|
||||
try{
|
||||
req = window["webpackJsonp"].push([[], {__extra_id__: (mdl, exports, req) => mdl.exports = req}, [["__extra_id__"]]]);
|
||||
if(req){
|
||||
delete req.m.__extra_id__;
|
||||
delete req.c.__extra_id__;
|
||||
}
|
||||
}catch(e){
|
||||
req = undefined
|
||||
}
|
||||
let req
|
||||
setReq()
|
||||
function filterDangerous(mods){
|
||||
return mods.map(e => {
|
||||
return protect(e)
|
||||
})
|
||||
}
|
||||
function protect(exports){
|
||||
let theModule = exports.exports
|
||||
let mod = theModule.default
|
||||
if(!mod)return exports
|
||||
if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null;
|
||||
if (!mod.getToken && !mod.getEmail && !mod.showToken)return exports
|
||||
|
||||
const proxy = new Proxy(mod, {
|
||||
getOwnPropertyDescriptor: function(obj, prop) {
|
||||
if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined;
|
||||
return Object.getOwnPropertyDescriptor(obj, prop);
|
||||
},
|
||||
get: function(obj, func) {
|
||||
if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa";
|
||||
if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com";
|
||||
if (func == "showToken" && obj.showToken) return () => true;
|
||||
if (func == "__proto__" && obj.__proto__) return proxy;
|
||||
|
||||
return obj[func];
|
||||
}
|
||||
});
|
||||
|
||||
return Object.assign({}, exports, {exports: Object.assign({}, theModule, {default: proxy})})
|
||||
}
|
||||
|
||||
class Webpackloader {
|
||||
get modules(){
|
||||
if(req){
|
||||
return filterDangerous(Object.values(req.c).filter((e:any) => e && e.exports))
|
||||
}else{
|
||||
setReq()
|
||||
if(req){
|
||||
return filterDangerous(Object.values(req.c).filter((e:any) => e && e.exports))
|
||||
}else{
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
get(ids, modules){
|
||||
if(typeof ids === "function"){
|
||||
return (modules || this.modules).map((mdl) => {
|
||||
if(mdl && typeof mdl.exports !== "undefined"){
|
||||
return mdl.exports
|
||||
}else{
|
||||
return null
|
||||
}
|
||||
}).filter(e => e).filter(ids)
|
||||
}else if(Array.isArray(ids)){
|
||||
modules = modules || this.modules
|
||||
return ids.map(id => this.get(id, modules))
|
||||
}else{
|
||||
modules = modules || this.modules
|
||||
let module = modules.filter(e => !!e).find(e => e.i === ids)
|
||||
if(!module)return undefined
|
||||
return module.exports
|
||||
}
|
||||
}
|
||||
get default(){
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
export default new Webpackloader()
|
||||
|
||||
function setReq(){
|
||||
try{
|
||||
req = window["webpackJsonp"].push([[], {__extra_id__: (mdl, exports, req) => mdl.exports = req}, [["__extra_id__"]]]);
|
||||
if(req){
|
||||
delete req.m.__extra_id__;
|
||||
delete req.c.__extra_id__;
|
||||
}
|
||||
}catch(e){
|
||||
req = undefined
|
||||
}
|
||||
}
|
|
@ -1,228 +1,228 @@
|
|||
import { notices, noticeWithoutID, notice, events as noticeEvents } from "../components/private/Notices";
|
||||
import Utils from "./Utils";
|
||||
import uuid from "./uuid";
|
||||
import cloneNullProto from "./cloneNullProto";
|
||||
import { EventEmitter } from "events";
|
||||
import { defaultNotice } from "../components/private/Notice";
|
||||
import excludeProperties from "./excludeProperties";
|
||||
import NOOP from "./noop";
|
||||
import WebpackLoader, { WebpackLoaderError } from "./WebpackLoader";
|
||||
|
||||
let soundModule
|
||||
export default new class DiscordTools {
|
||||
showNotice(data:NoticeData):Notice{
|
||||
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
|
||||
newData.id = uuid()
|
||||
notices.push(newData)
|
||||
noticeEvents.emit("noticeUpdate")
|
||||
const notice = new Notice(newData)
|
||||
return notice
|
||||
}
|
||||
|
||||
get notices():Notice[]{
|
||||
return notices.map(data => new Notice(data))
|
||||
}
|
||||
|
||||
/**
|
||||
* Quickly send notification (Even when no focused.)
|
||||
* @param data The notification. Be sure to include all properties except functions cause they're optional.
|
||||
* Notifications have a timeout of 3-5 seconds.
|
||||
* They look like this: https://i.imgur.com/jzuxKKu.png
|
||||
*/
|
||||
showNotification(data:NotificationData):Notification{
|
||||
const notification = new window.Notification(data.title, excludeProperties(data, [
|
||||
"title",
|
||||
"onClick",
|
||||
"onClose",
|
||||
"onShow"
|
||||
]))
|
||||
notification.onclick = data.onClick || NOOP
|
||||
notification.onshow = data.onShow || NOOP
|
||||
notification.onclose = data.onClose || NOOP
|
||||
return notification
|
||||
}
|
||||
|
||||
createSound(sound:Sound){
|
||||
soundModule = soundModule || WebpackLoader.findByUniqueProperties(["createSound"])
|
||||
if(!soundModule)throw new WebpackLoaderError("Couldn't find soundModule here.")
|
||||
const created = soundModule.createSound(sound)
|
||||
return created
|
||||
}
|
||||
|
||||
playSound(sound:Sound){
|
||||
const created = this.createSound(sound)
|
||||
created.play()
|
||||
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 NotificationData = {
|
||||
title: string,
|
||||
body: string,
|
||||
icon: string,
|
||||
onShow?: () => void,
|
||||
onClick?: () => void,
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
export type NoticeData = noticeWithoutID
|
||||
|
||||
const EventHandler = function(){
|
||||
if(this.removed !== this.state.removed){
|
||||
if(this.removed){
|
||||
this.emit("removed")
|
||||
}
|
||||
}
|
||||
if(this.showing !== this.state.showing){
|
||||
if(this.showing){
|
||||
this.emit("showing", true)
|
||||
}else{
|
||||
this.emit("showing", false)
|
||||
}
|
||||
}
|
||||
if(this.index !== this.state.index){
|
||||
this.emit("index", this.index)
|
||||
}
|
||||
}
|
||||
|
||||
/** A notice interface for modifying it and subscribing to events. */
|
||||
export class Notice extends EventEmitter {
|
||||
constructor(data){
|
||||
super()
|
||||
this.data = data
|
||||
|
||||
this.state = {
|
||||
removed: this.removed,
|
||||
showing: this.showing,
|
||||
index: this.index
|
||||
}
|
||||
|
||||
let eventFunc = EventHandler.bind(this)
|
||||
noticeEvents.on("noticeUpdate", eventFunc)
|
||||
this.on("removed", () => {
|
||||
noticeEvents.off("noticeUpdate", eventFunc)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be called whem the notice is removed.
|
||||
*/
|
||||
on(event: "removed", listener: () => void):this
|
||||
/**
|
||||
* Will be called when the notice is visible or not.
|
||||
*/
|
||||
on(event: "showing", listener: (isShowing:boolean) => void):this
|
||||
/**
|
||||
* Will be called when the notice queue changes.
|
||||
*/
|
||||
on(event: "index", listener: (index:number) => void):this
|
||||
on(event: string, listener: (...args:any[]) => void){
|
||||
return super.on(event, listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be called whem the notice is removed.
|
||||
*/
|
||||
once(event: "removed", listener: () => void):this
|
||||
/**
|
||||
* Will be called when the notice is visible or not.
|
||||
*/
|
||||
once(event: "showing", listener: (isShowing:boolean) => void):this
|
||||
/**
|
||||
* Will be called when the notice queue changes.
|
||||
*/
|
||||
once(event: "index", listener: (index:number) => void):this
|
||||
once(event: string, listener: (...args:any[]) => void){
|
||||
return super.once(event, listener)
|
||||
}
|
||||
|
||||
off(event: "removed", listener: () => void):this
|
||||
off(event: "showing", listener: (isShowing:boolean) => void):this
|
||||
off(event: "index", listener: (index:number) => void):this
|
||||
off(event: string, listener: (...args:any[]) => void){
|
||||
return super.off(event, listener)
|
||||
}
|
||||
|
||||
state:{
|
||||
removed:boolean,
|
||||
showing:boolean,
|
||||
index:number
|
||||
}
|
||||
|
||||
private nextTickRefresh:boolean = false
|
||||
|
||||
get removed():boolean{
|
||||
return !notices.find(e => e.id === this.id)
|
||||
}
|
||||
get showing():boolean{
|
||||
return this.index === 0
|
||||
}
|
||||
|
||||
get index():number{
|
||||
return notices.findIndex(e => e.id === this.id)
|
||||
}
|
||||
get id(){
|
||||
return this.data.id
|
||||
}
|
||||
|
||||
update(data: Partial<notice>){
|
||||
for(let key in data){
|
||||
if(key === "id")continue
|
||||
this.data[key] = data[key]
|
||||
}
|
||||
|
||||
if(!this.nextTickRefresh){
|
||||
this.nextTickRefresh = true
|
||||
process.nextTick(() => {
|
||||
this.nextTickRefresh = false
|
||||
noticeEvents.emit("noticeUpdate")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
get text(){
|
||||
return this.data.text
|
||||
}
|
||||
set text(text){
|
||||
this.update({
|
||||
text
|
||||
})
|
||||
}
|
||||
|
||||
get type(){
|
||||
return this.data.type
|
||||
}
|
||||
set type(type){
|
||||
this.update({
|
||||
type
|
||||
})
|
||||
}
|
||||
|
||||
get buttonText(){
|
||||
return this.data.buttonText
|
||||
}
|
||||
set buttonText(buttonText:string){
|
||||
this.update({
|
||||
buttonText
|
||||
})
|
||||
}
|
||||
|
||||
get onClick(){
|
||||
return this.data.onClick
|
||||
}
|
||||
set onClick(onClick){
|
||||
this.update({
|
||||
onClick
|
||||
})
|
||||
}
|
||||
|
||||
remove(){
|
||||
if(this.removed)return
|
||||
notices.splice(this.index, 1)
|
||||
noticeEvents.emit("noticeUpdate")
|
||||
}
|
||||
data:notice
|
||||
import { notices, noticeWithoutID, notice, events as noticeEvents } from "../components/private/Notices";
|
||||
import Utils from "./Utils";
|
||||
import uuid from "./uuid";
|
||||
import cloneNullProto from "./cloneNullProto";
|
||||
import { EventEmitter } from "events";
|
||||
import { defaultNotice } from "../components/private/Notice";
|
||||
import excludeProperties from "./excludeProperties";
|
||||
import NOOP from "./noop";
|
||||
import WebpackLoader, { WebpackLoaderError } from "./WebpackLoader";
|
||||
|
||||
let soundModule
|
||||
export default new class DiscordTools {
|
||||
showNotice(data:NoticeData):Notice{
|
||||
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
|
||||
newData.id = uuid()
|
||||
notices.push(newData)
|
||||
noticeEvents.emit("noticeUpdate")
|
||||
const notice = new Notice(newData)
|
||||
return notice
|
||||
}
|
||||
|
||||
get notices():Notice[]{
|
||||
return notices.map(data => new Notice(data))
|
||||
}
|
||||
|
||||
/**
|
||||
* Quickly send notification (Even when no focused.)
|
||||
* @param data The notification. Be sure to include all properties except functions cause they're optional.
|
||||
* Notifications have a timeout of 3-5 seconds.
|
||||
* They look like this: https://i.imgur.com/jzuxKKu.png
|
||||
*/
|
||||
showNotification(data:NotificationData):Notification{
|
||||
const notification = new window.Notification(data.title, excludeProperties(data, [
|
||||
"title",
|
||||
"onClick",
|
||||
"onClose",
|
||||
"onShow"
|
||||
]))
|
||||
notification.onclick = data.onClick || NOOP
|
||||
notification.onshow = data.onShow || NOOP
|
||||
notification.onclose = data.onClose || NOOP
|
||||
return notification
|
||||
}
|
||||
|
||||
createSound(sound:Sound){
|
||||
soundModule = soundModule || WebpackLoader.findByUniqueProperties(["createSound"])
|
||||
if(!soundModule)throw new WebpackLoaderError("Couldn't find soundModule here.")
|
||||
const created = soundModule.createSound(sound)
|
||||
return created
|
||||
}
|
||||
|
||||
playSound(sound:Sound){
|
||||
const created = this.createSound(sound)
|
||||
created.play()
|
||||
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 NotificationData = {
|
||||
title: string,
|
||||
body: string,
|
||||
icon: string,
|
||||
onShow?: () => void,
|
||||
onClick?: () => void,
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
export type NoticeData = noticeWithoutID
|
||||
|
||||
const EventHandler = function(){
|
||||
if(this.removed !== this.state.removed){
|
||||
if(this.removed){
|
||||
this.emit("removed")
|
||||
}
|
||||
}
|
||||
if(this.showing !== this.state.showing){
|
||||
if(this.showing){
|
||||
this.emit("showing", true)
|
||||
}else{
|
||||
this.emit("showing", false)
|
||||
}
|
||||
}
|
||||
if(this.index !== this.state.index){
|
||||
this.emit("index", this.index)
|
||||
}
|
||||
}
|
||||
|
||||
/** A notice interface for modifying it and subscribing to events. */
|
||||
export class Notice extends EventEmitter {
|
||||
constructor(data){
|
||||
super()
|
||||
this.data = data
|
||||
|
||||
this.state = {
|
||||
removed: this.removed,
|
||||
showing: this.showing,
|
||||
index: this.index
|
||||
}
|
||||
|
||||
let eventFunc = EventHandler.bind(this)
|
||||
noticeEvents.on("noticeUpdate", eventFunc)
|
||||
this.on("removed", () => {
|
||||
noticeEvents.off("noticeUpdate", eventFunc)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be called whem the notice is removed.
|
||||
*/
|
||||
on(event: "removed", listener: () => void):this
|
||||
/**
|
||||
* Will be called when the notice is visible or not.
|
||||
*/
|
||||
on(event: "showing", listener: (isShowing:boolean) => void):this
|
||||
/**
|
||||
* Will be called when the notice queue changes.
|
||||
*/
|
||||
on(event: "index", listener: (index:number) => void):this
|
||||
on(event: string, listener: (...args:any[]) => void){
|
||||
return super.on(event, listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be called whem the notice is removed.
|
||||
*/
|
||||
once(event: "removed", listener: () => void):this
|
||||
/**
|
||||
* Will be called when the notice is visible or not.
|
||||
*/
|
||||
once(event: "showing", listener: (isShowing:boolean) => void):this
|
||||
/**
|
||||
* Will be called when the notice queue changes.
|
||||
*/
|
||||
once(event: "index", listener: (index:number) => void):this
|
||||
once(event: string, listener: (...args:any[]) => void){
|
||||
return super.once(event, listener)
|
||||
}
|
||||
|
||||
off(event: "removed", listener: () => void):this
|
||||
off(event: "showing", listener: (isShowing:boolean) => void):this
|
||||
off(event: "index", listener: (index:number) => void):this
|
||||
off(event: string, listener: (...args:any[]) => void){
|
||||
return super.off(event, listener)
|
||||
}
|
||||
|
||||
state:{
|
||||
removed:boolean,
|
||||
showing:boolean,
|
||||
index:number
|
||||
}
|
||||
|
||||
private nextTickRefresh:boolean = false
|
||||
|
||||
get removed():boolean{
|
||||
return !notices.find(e => e.id === this.id)
|
||||
}
|
||||
get showing():boolean{
|
||||
return this.index === 0
|
||||
}
|
||||
|
||||
get index():number{
|
||||
return notices.findIndex(e => e.id === this.id)
|
||||
}
|
||||
get id(){
|
||||
return this.data.id
|
||||
}
|
||||
|
||||
update(data: Partial<notice>){
|
||||
for(let key in data){
|
||||
if(key === "id")continue
|
||||
this.data[key] = data[key]
|
||||
}
|
||||
|
||||
if(!this.nextTickRefresh){
|
||||
this.nextTickRefresh = true
|
||||
process.nextTick(() => {
|
||||
this.nextTickRefresh = false
|
||||
noticeEvents.emit("noticeUpdate")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
get text(){
|
||||
return this.data.text
|
||||
}
|
||||
set text(text){
|
||||
this.update({
|
||||
text
|
||||
})
|
||||
}
|
||||
|
||||
get type(){
|
||||
return this.data.type
|
||||
}
|
||||
set type(type){
|
||||
this.update({
|
||||
type
|
||||
})
|
||||
}
|
||||
|
||||
get buttonText(){
|
||||
return this.data.buttonText
|
||||
}
|
||||
set buttonText(buttonText:string){
|
||||
this.update({
|
||||
buttonText
|
||||
})
|
||||
}
|
||||
|
||||
get onClick(){
|
||||
return this.data.onClick
|
||||
}
|
||||
set onClick(onClick){
|
||||
this.update({
|
||||
onClick
|
||||
})
|
||||
}
|
||||
|
||||
remove(){
|
||||
if(this.removed)return
|
||||
notices.splice(this.index, 1)
|
||||
noticeEvents.emit("noticeUpdate")
|
||||
}
|
||||
data:notice
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -1,57 +1,57 @@
|
|||
const BDModules:typeof window.BDModules = window.BDModules || require("./BDModules")
|
||||
|
||||
export default new class WebpackLoader {
|
||||
constructor(){}
|
||||
|
||||
get(id: number):any{
|
||||
return BDModules.get(id)
|
||||
}
|
||||
find(filter: (mod:any) => boolean):any{
|
||||
let result = BDModules.get(filter)[0]
|
||||
if(!result){
|
||||
console.warn(filter, "couldn't find the module.")
|
||||
}
|
||||
return result
|
||||
}
|
||||
findByUniqueProperties(props:(string|number)[]):any{
|
||||
return BDModules.get((mod) => {
|
||||
if(mod.__esModule && ("default" in mod)){
|
||||
let doesMatch = true
|
||||
for(let prop of props){
|
||||
if(!Object.prototype.hasOwnProperty.call(mod.default, prop))doesMatch = false
|
||||
}
|
||||
if(doesMatch)return true
|
||||
}
|
||||
for(let prop of props){
|
||||
if(!Object.prototype.hasOwnProperty.call(mod, prop))return false
|
||||
}
|
||||
return true
|
||||
})[0]
|
||||
}
|
||||
filter(filter: (mod:any) => boolean):any[]{
|
||||
return BDModules.get(filter)
|
||||
}
|
||||
filterByUniqueProperties(props:(string|number)[]):any{
|
||||
return BDModules.get((mod) => {
|
||||
if(mod.__esModule && ("default" in mod)){
|
||||
let doesMatch = true
|
||||
for(let prop of props){
|
||||
if(!Object.prototype.hasOwnProperty.call(mod.default, prop))doesMatch = false
|
||||
}
|
||||
if(doesMatch)return true
|
||||
}
|
||||
for(let prop of props){
|
||||
if(!Object.prototype.hasOwnProperty.call(mod, prop))return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class WebpackLoaderError extends Error {
|
||||
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."
|
||||
super(message)
|
||||
this.name = "WebpackLoaderError"
|
||||
}
|
||||
const BDModules:typeof window.BDModules = window.BDModules || require("./BDModules")
|
||||
|
||||
export default new class WebpackLoader {
|
||||
constructor(){}
|
||||
|
||||
get(id: number):any{
|
||||
return BDModules.get(id)
|
||||
}
|
||||
find(filter: (mod:any) => boolean):any{
|
||||
let result = BDModules.get(filter)[0]
|
||||
if(!result){
|
||||
console.warn(filter, "couldn't find the module.")
|
||||
}
|
||||
return result
|
||||
}
|
||||
findByUniqueProperties(props:(string|number)[]):any{
|
||||
return BDModules.get((mod) => {
|
||||
if(mod.__esModule && ("default" in mod)){
|
||||
let doesMatch = true
|
||||
for(let prop of props){
|
||||
if(!Object.prototype.hasOwnProperty.call(mod.default, prop))doesMatch = false
|
||||
}
|
||||
if(doesMatch)return true
|
||||
}
|
||||
for(let prop of props){
|
||||
if(!Object.prototype.hasOwnProperty.call(mod, prop))return false
|
||||
}
|
||||
return true
|
||||
})[0]
|
||||
}
|
||||
filter(filter: (mod:any) => boolean):any[]{
|
||||
return BDModules.get(filter)
|
||||
}
|
||||
filterByUniqueProperties(props:(string|number)[]):any{
|
||||
return BDModules.get((mod) => {
|
||||
if(mod.__esModule && ("default" in mod)){
|
||||
let doesMatch = true
|
||||
for(let prop of props){
|
||||
if(!Object.prototype.hasOwnProperty.call(mod.default, prop))doesMatch = false
|
||||
}
|
||||
if(doesMatch)return true
|
||||
}
|
||||
for(let prop of props){
|
||||
if(!Object.prototype.hasOwnProperty.call(mod, prop))return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class WebpackLoaderError extends Error {
|
||||
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."
|
||||
super(message)
|
||||
this.name = "WebpackLoaderError"
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
/**
|
||||
* Recreate the given object without the __proto__. Useful for better formatting when output in console.
|
||||
* @param obj The object to recreate
|
||||
*/
|
||||
export default function cloneNullProto<Obj=any>(obj:Obj):Obj{
|
||||
let o = Object.create(null)
|
||||
Object.keys(obj).forEach(k => {
|
||||
o[k] = obj[k]
|
||||
})
|
||||
return o
|
||||
/**
|
||||
* Recreate the given object without the __proto__. Useful for better formatting when output in console.
|
||||
* @param obj The object to recreate
|
||||
*/
|
||||
export default function cloneNullProto<Obj=any>(obj:Obj):Obj{
|
||||
let o = Object.create(null)
|
||||
Object.keys(obj).forEach(k => {
|
||||
o[k] = obj[k]
|
||||
})
|
||||
return o
|
||||
}
|
|
@ -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"
|
|
@ -1,84 +1,84 @@
|
|||
let cache = new Map()
|
||||
|
||||
export function LazyLoad<T>(getObject: () => T):T{
|
||||
if(cache.has(getObject))return cache.get(getObject)
|
||||
|
||||
let mdl = null
|
||||
let setModule = () => {
|
||||
if(mdl)return
|
||||
mdl = getObject()
|
||||
}
|
||||
let handler:ProxyHandler<{}> = {
|
||||
get(target, prop){
|
||||
setModule()
|
||||
return mdl[prop]
|
||||
},
|
||||
set(target, prop, value){
|
||||
setModule()
|
||||
mdl[prop] = value
|
||||
return true
|
||||
},
|
||||
apply(target, thisArg, args){
|
||||
setModule()
|
||||
mdl.apply(this, args)
|
||||
},
|
||||
construct(target, args){
|
||||
setModule()
|
||||
const prototype = Object.create(mdl.prototype)
|
||||
handler.apply(target, prototype, args)
|
||||
return prototype
|
||||
},
|
||||
deleteProperty(target, prop){
|
||||
setModule()
|
||||
if(!(prop in mdl))return false
|
||||
delete mdl[prop]
|
||||
return true
|
||||
},
|
||||
enumerate(target){
|
||||
setModule()
|
||||
return Object.keys(mdl)
|
||||
},
|
||||
ownKeys(target) {
|
||||
setModule()
|
||||
return Object.keys(mdl)
|
||||
},
|
||||
has(target, prop){
|
||||
setModule()
|
||||
return prop in mdl
|
||||
},
|
||||
defineProperty(target, prop, attributes){
|
||||
setModule()
|
||||
return Object.defineProperty(mdl, prop, attributes)
|
||||
},
|
||||
getOwnPropertyDescriptor(target, prop){
|
||||
setModule()
|
||||
return Object.getOwnPropertyDescriptor(mdl, prop)
|
||||
},
|
||||
getPrototypeOf(target){
|
||||
setModule()
|
||||
return Object.getPrototypeOf(mdl)
|
||||
},
|
||||
setPrototypeOf(target, proto){
|
||||
setModule()
|
||||
try{
|
||||
Object.setPrototypeOf(mdl, proto)
|
||||
return true
|
||||
}catch(e){
|
||||
return false
|
||||
}
|
||||
},
|
||||
isExtensible(target){
|
||||
setModule()
|
||||
return Object.isExtensible(mdl)
|
||||
},
|
||||
preventExtensions(target){
|
||||
setModule()
|
||||
Object.preventExtensions(mdl)
|
||||
return true
|
||||
}
|
||||
}
|
||||
const proxy = new Proxy({}, handler)
|
||||
|
||||
cache.set(getObject, proxy)
|
||||
return proxy as T
|
||||
let cache = new Map()
|
||||
|
||||
export function LazyLoad<T>(getObject: () => T):T{
|
||||
if(cache.has(getObject))return cache.get(getObject)
|
||||
|
||||
let mdl = null
|
||||
let setModule = () => {
|
||||
if(mdl)return
|
||||
mdl = getObject()
|
||||
}
|
||||
let handler:ProxyHandler<{}> = {
|
||||
get(target, prop){
|
||||
setModule()
|
||||
return mdl[prop]
|
||||
},
|
||||
set(target, prop, value){
|
||||
setModule()
|
||||
mdl[prop] = value
|
||||
return true
|
||||
},
|
||||
apply(target, thisArg, args){
|
||||
setModule()
|
||||
mdl.apply(this, args)
|
||||
},
|
||||
construct(target, args){
|
||||
setModule()
|
||||
const prototype = Object.create(mdl.prototype)
|
||||
handler.apply(target, prototype, args)
|
||||
return prototype
|
||||
},
|
||||
deleteProperty(target, prop){
|
||||
setModule()
|
||||
if(!(prop in mdl))return false
|
||||
delete mdl[prop]
|
||||
return true
|
||||
},
|
||||
enumerate(target){
|
||||
setModule()
|
||||
return Object.keys(mdl)
|
||||
},
|
||||
ownKeys(target) {
|
||||
setModule()
|
||||
return Object.keys(mdl)
|
||||
},
|
||||
has(target, prop){
|
||||
setModule()
|
||||
return prop in mdl
|
||||
},
|
||||
defineProperty(target, prop, attributes){
|
||||
setModule()
|
||||
return Object.defineProperty(mdl, prop, attributes)
|
||||
},
|
||||
getOwnPropertyDescriptor(target, prop){
|
||||
setModule()
|
||||
return Object.getOwnPropertyDescriptor(mdl, prop)
|
||||
},
|
||||
getPrototypeOf(target){
|
||||
setModule()
|
||||
return Object.getPrototypeOf(mdl)
|
||||
},
|
||||
setPrototypeOf(target, proto){
|
||||
setModule()
|
||||
try{
|
||||
Object.setPrototypeOf(mdl, proto)
|
||||
return true
|
||||
}catch(e){
|
||||
return false
|
||||
}
|
||||
},
|
||||
isExtensible(target){
|
||||
setModule()
|
||||
return Object.isExtensible(mdl)
|
||||
},
|
||||
preventExtensions(target){
|
||||
setModule()
|
||||
Object.preventExtensions(mdl)
|
||||
return true
|
||||
}
|
||||
}
|
||||
const proxy = new Proxy({}, handler)
|
||||
|
||||
cache.set(getObject, proxy)
|
||||
return proxy as T
|
||||
}
|
|
@ -1,232 +1,232 @@
|
|||
import Utils from "./Utils"
|
||||
import Notices, { notices } from "../components/private/Notices"
|
||||
import { isNative } from "./environnement";
|
||||
import WebpackLoader from "./WebpackLoader";
|
||||
|
||||
export function patch(){
|
||||
/** START NOTICE */
|
||||
getModule(e => e.default && e.default.displayName === "ConnectedAppView")
|
||||
.then(async (mod) => {
|
||||
const appClasses = await getModule(e => e.hasNotice);
|
||||
const buildRender = original => {
|
||||
return function render(){
|
||||
const returnValue = original.call(this, ...arguments)
|
||||
const newchildren = []
|
||||
let children = returnValue.props.children[1].props.children
|
||||
if(!Array.isArray(children))children = [children]
|
||||
|
||||
newchildren.push(children[0])
|
||||
newchildren.push(React.createElement(Notices, {container: this}))
|
||||
newchildren.push(children[1])
|
||||
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)
|
||||
|
||||
return returnValue
|
||||
}
|
||||
}
|
||||
const buildRenderChannelSidebar = original => {
|
||||
return function renderChannelSidebar(){
|
||||
const returnValue = original.call(this, ...arguments)
|
||||
|
||||
const hasNotice = notices.length > 0
|
||||
if(!hasNotice)return returnValue
|
||||
if(!Utils.hasClass(returnValue.props.className, appClasses.hasNotice)){
|
||||
returnValue.props.className += " "+Utils.removeDa(appClasses.hasNotice)
|
||||
}
|
||||
|
||||
return returnValue
|
||||
}
|
||||
}
|
||||
mod.default.prototype.render = buildRender(mod.default.prototype.render);
|
||||
(async function(){
|
||||
const base = document.querySelector("."+Utils.removeDa(appClasses.base))
|
||||
if(!base)throw new Error(`Could not find base here`)
|
||||
const elem = Utils.FindReact(base) as any
|
||||
elem.render = buildRender(elem.render)
|
||||
elem.forceUpdate()
|
||||
})()
|
||||
})
|
||||
/** END NOTICE */
|
||||
|
||||
if(isNative){
|
||||
/** START USERPOPOUT PATCH */
|
||||
awaitLogin()
|
||||
.then(async () => {
|
||||
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 render1 = new UserPopout.default({userId: userModule.default.getCurrentUser().id, guildId: null, channelId: null, disableUserProfileLink: true}).render()
|
||||
const PopoutProps = render1.props
|
||||
const render2 = render1.type.render(PopoutProps, null)
|
||||
const render3 = new render2.type(render2.props).render()
|
||||
const UserPopoutComponent = render3.type
|
||||
if(!UserPopoutComponent)throw new Error(`Couldn't find the UserPopoutComponent component.`)
|
||||
|
||||
const render = UserPopoutComponent.prototype.render
|
||||
UserPopoutComponent.prototype.render = function(){
|
||||
const returnValue = render.call(this, ...arguments)
|
||||
try{
|
||||
returnValue.props.children.props["data-user-id"] = this.props.user.id
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
}
|
||||
return returnValue
|
||||
}
|
||||
})
|
||||
/** END USERPOPOUT PATCH*/
|
||||
|
||||
/** START USERPROFILE PATCH */
|
||||
awaitLogin()
|
||||
.then(async () => {
|
||||
let UserProfile = await getModule(e => e.default && e.default.displayName === "UserProfile")
|
||||
const userModule = await getModule(e => e.default && e.default.getCurrentUser)
|
||||
const render1 = new UserProfile.default({
|
||||
user: userModule.default.getCurrentUser()
|
||||
}).render()
|
||||
const render2 = new render1.type(render1.props).render()
|
||||
const render3 = render2.type.render(render2.props, null)
|
||||
const render4 = new render3.type(render3.props).render()
|
||||
const UserProfileComponent = render4.type
|
||||
if(!UserProfileComponent)throw new Error(`Couldn't find the UserProfileComponent component.`)
|
||||
|
||||
const render = UserProfileComponent.prototype.render
|
||||
UserProfileComponent.prototype.render = function(){
|
||||
const returnValue = render.call(this, ...arguments)
|
||||
console.log(returnValue)
|
||||
try{
|
||||
returnValue.props.children.props["data-user-id"] = this.props.user.id
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
}
|
||||
return returnValue
|
||||
}
|
||||
})
|
||||
/** END USERPROFILE PATCH */
|
||||
|
||||
/** START WEBHOOK PATCH */
|
||||
/*
|
||||
let usedWebhooks = {}
|
||||
|
||||
getModule(e => e && e.Request && e.Request.prototype && e.Request.prototype.end)
|
||||
.then(RequestModule => {
|
||||
const end = RequestModule.Request.prototype.end
|
||||
RequestModule.Request.prototype.end = function(){
|
||||
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]
|
||||
|
||||
if(usedWebhooks[channelId]){ // webhook is availlable
|
||||
let webhook = usedWebhooks[channelId]
|
||||
let url = `/webhooks/${webhook.id}/${webhook.token}?wait=true`
|
||||
this.url = url
|
||||
}
|
||||
}
|
||||
|
||||
return end.call(this, ...arguments)
|
||||
}
|
||||
})
|
||||
getModule(e => e.default && e.default.displayName === "Webhook")
|
||||
.then(webhookComponent => {
|
||||
const renderEdit = webhookComponent.default.prototype.renderEdit
|
||||
webhookComponent.default.prototype.renderEdit = function(){
|
||||
const webhook = this.props.webhook
|
||||
let returnValue = renderEdit.call(this, ...arguments)
|
||||
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"
|
||||
|
||||
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){
|
||||
delete usedWebhooks[webhook.channel_id]
|
||||
}else{
|
||||
usedWebhooks[webhook.channel_id] = {
|
||||
id: webhook.id,
|
||||
token: webhook.token
|
||||
}
|
||||
}
|
||||
webhookPanels.forEach(e => e())
|
||||
}}, message))
|
||||
|
||||
return returnValue
|
||||
}
|
||||
})
|
||||
|
||||
let webhookPanels = []
|
||||
let getComp = (comp) => {
|
||||
class SettingsWebhooks extends React.PureComponent {
|
||||
constructor(props){
|
||||
super(props)
|
||||
}
|
||||
|
||||
componentWillMount(){
|
||||
this.id = uuid()
|
||||
this.component = new comp(this.props)
|
||||
let func = () => {
|
||||
this.component.forceUpdate()
|
||||
}
|
||||
func.id = this.id
|
||||
webhookPanels.push(func)
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
this.component = null
|
||||
webhookPanels = webhookPanels.filter(e => e.id !== this.id)
|
||||
}
|
||||
|
||||
render(){
|
||||
return this.component.render()
|
||||
}
|
||||
|
||||
static displayName = "SettingsWebhooks"
|
||||
}
|
||||
|
||||
return SettingsWebhooks
|
||||
}
|
||||
getModule(e => e.default && e.default.displayName === "FluxContainer(SettingsWebhooks)")
|
||||
.then(webhooksComponents => {
|
||||
let comp = webhooksComponents.default
|
||||
|
||||
webhooksComponents.default = getComp(comp)
|
||||
|
||||
WebpackLoader.find(e => e.default && e.default.displayName === "FluxContainer(FluxContainer(SettingsWebhooks))")
|
||||
.forEach(mod => {
|
||||
mod.default = getComp(mod.default)
|
||||
})
|
||||
})*/
|
||||
/** END WEBHOOK PATCH */
|
||||
}
|
||||
|
||||
// TODO: Add in app-notifications / confirmations.
|
||||
/** START IN-APP NOTIFICATIONS */
|
||||
//getModule(e => true)
|
||||
/** END IN-APP NOTIFICATIONS */
|
||||
}
|
||||
|
||||
function getModule(filter: (mod:any) => boolean):Promise<any>{
|
||||
return new Promise((resolve) => {
|
||||
window.Lightcord.Api.ensureExported(filter)
|
||||
.then(resolve)
|
||||
.catch(err => {
|
||||
console.error("[LIGHTCORD]", err, filter)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let hasCompletedLogin = false
|
||||
let loginPromise:Promise<void>
|
||||
function awaitLogin():Promise<void>{
|
||||
if(hasCompletedLogin)return Promise.resolve()
|
||||
if(loginPromise)return loginPromise
|
||||
|
||||
return loginPromise = new Promise((resolve) => {
|
||||
let isResolved = false
|
||||
window.Lightcord.DiscordModules.dispatcher.subscribe("CONNECTION_OPEN", (ev) => {
|
||||
if(isResolved)return
|
||||
hasCompletedLogin = true
|
||||
resolve()
|
||||
isResolved = true
|
||||
})
|
||||
})
|
||||
}
|
||||
window.Lightcord.DiscordModules.dispatcher.subscribe("LOGOUT", (ev) => {
|
||||
hasCompletedLogin = false
|
||||
loginPromise = undefined
|
||||
import Utils from "./Utils"
|
||||
import Notices, { notices } from "../components/private/Notices"
|
||||
import { isNative } from "./environnement";
|
||||
import WebpackLoader from "./WebpackLoader";
|
||||
|
||||
export function patch(){
|
||||
/** START NOTICE */
|
||||
getModule(e => e.default && e.default.displayName === "ConnectedAppView")
|
||||
.then(async (mod) => {
|
||||
const appClasses = await getModule(e => e.hasNotice);
|
||||
const buildRender = original => {
|
||||
return function render(){
|
||||
const returnValue = original.call(this, ...arguments)
|
||||
const newchildren = []
|
||||
let children = returnValue.props.children[1].props.children
|
||||
if(!Array.isArray(children))children = [children]
|
||||
|
||||
newchildren.push(children[0])
|
||||
newchildren.push(React.createElement(Notices, {container: this}))
|
||||
newchildren.push(children[1])
|
||||
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)
|
||||
|
||||
return returnValue
|
||||
}
|
||||
}
|
||||
const buildRenderChannelSidebar = original => {
|
||||
return function renderChannelSidebar(){
|
||||
const returnValue = original.call(this, ...arguments)
|
||||
|
||||
const hasNotice = notices.length > 0
|
||||
if(!hasNotice)return returnValue
|
||||
if(!Utils.hasClass(returnValue.props.className, appClasses.hasNotice)){
|
||||
returnValue.props.className += " "+Utils.removeDa(appClasses.hasNotice)
|
||||
}
|
||||
|
||||
return returnValue
|
||||
}
|
||||
}
|
||||
mod.default.prototype.render = buildRender(mod.default.prototype.render);
|
||||
(async function(){
|
||||
const base = document.querySelector("."+Utils.removeDa(appClasses.base))
|
||||
if(!base)throw new Error(`Could not find base here`)
|
||||
const elem = Utils.FindReact(base) as any
|
||||
elem.render = buildRender(elem.render)
|
||||
elem.forceUpdate()
|
||||
})()
|
||||
})
|
||||
/** END NOTICE */
|
||||
|
||||
if(isNative){
|
||||
/** START USERPOPOUT PATCH */
|
||||
awaitLogin()
|
||||
.then(async () => {
|
||||
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 render1 = new UserPopout.default({userId: userModule.default.getCurrentUser().id, guildId: null, channelId: null, disableUserProfileLink: true}).render()
|
||||
const PopoutProps = render1.props
|
||||
const render2 = render1.type.render(PopoutProps, null)
|
||||
const render3 = new render2.type(render2.props).render()
|
||||
const UserPopoutComponent = render3.type
|
||||
if(!UserPopoutComponent)throw new Error(`Couldn't find the UserPopoutComponent component.`)
|
||||
|
||||
const render = UserPopoutComponent.prototype.render
|
||||
UserPopoutComponent.prototype.render = function(){
|
||||
const returnValue = render.call(this, ...arguments)
|
||||
try{
|
||||
returnValue.props.children.props["data-user-id"] = this.props.user.id
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
}
|
||||
return returnValue
|
||||
}
|
||||
})
|
||||
/** END USERPOPOUT PATCH*/
|
||||
|
||||
/** START USERPROFILE PATCH */
|
||||
awaitLogin()
|
||||
.then(async () => {
|
||||
let UserProfile = await getModule(e => e.default && e.default.displayName === "UserProfile")
|
||||
const userModule = await getModule(e => e.default && e.default.getCurrentUser)
|
||||
const render1 = new UserProfile.default({
|
||||
user: userModule.default.getCurrentUser()
|
||||
}).render()
|
||||
const render2 = new render1.type(render1.props).render()
|
||||
const render3 = render2.type.render(render2.props, null)
|
||||
const render4 = new render3.type(render3.props).render()
|
||||
const UserProfileComponent = render4.type
|
||||
if(!UserProfileComponent)throw new Error(`Couldn't find the UserProfileComponent component.`)
|
||||
|
||||
const render = UserProfileComponent.prototype.render
|
||||
UserProfileComponent.prototype.render = function(){
|
||||
const returnValue = render.call(this, ...arguments)
|
||||
console.log(returnValue)
|
||||
try{
|
||||
returnValue.props.children.props["data-user-id"] = this.props.user.id
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
}
|
||||
return returnValue
|
||||
}
|
||||
})
|
||||
/** END USERPROFILE PATCH */
|
||||
|
||||
/** START WEBHOOK PATCH */
|
||||
/*
|
||||
let usedWebhooks = {}
|
||||
|
||||
getModule(e => e && e.Request && e.Request.prototype && e.Request.prototype.end)
|
||||
.then(RequestModule => {
|
||||
const end = RequestModule.Request.prototype.end
|
||||
RequestModule.Request.prototype.end = function(){
|
||||
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]
|
||||
|
||||
if(usedWebhooks[channelId]){ // webhook is availlable
|
||||
let webhook = usedWebhooks[channelId]
|
||||
let url = `/webhooks/${webhook.id}/${webhook.token}?wait=true`
|
||||
this.url = url
|
||||
}
|
||||
}
|
||||
|
||||
return end.call(this, ...arguments)
|
||||
}
|
||||
})
|
||||
getModule(e => e.default && e.default.displayName === "Webhook")
|
||||
.then(webhookComponent => {
|
||||
const renderEdit = webhookComponent.default.prototype.renderEdit
|
||||
webhookComponent.default.prototype.renderEdit = function(){
|
||||
const webhook = this.props.webhook
|
||||
let returnValue = renderEdit.call(this, ...arguments)
|
||||
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"
|
||||
|
||||
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){
|
||||
delete usedWebhooks[webhook.channel_id]
|
||||
}else{
|
||||
usedWebhooks[webhook.channel_id] = {
|
||||
id: webhook.id,
|
||||
token: webhook.token
|
||||
}
|
||||
}
|
||||
webhookPanels.forEach(e => e())
|
||||
}}, message))
|
||||
|
||||
return returnValue
|
||||
}
|
||||
})
|
||||
|
||||
let webhookPanels = []
|
||||
let getComp = (comp) => {
|
||||
class SettingsWebhooks extends React.PureComponent {
|
||||
constructor(props){
|
||||
super(props)
|
||||
}
|
||||
|
||||
componentWillMount(){
|
||||
this.id = uuid()
|
||||
this.component = new comp(this.props)
|
||||
let func = () => {
|
||||
this.component.forceUpdate()
|
||||
}
|
||||
func.id = this.id
|
||||
webhookPanels.push(func)
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
this.component = null
|
||||
webhookPanels = webhookPanels.filter(e => e.id !== this.id)
|
||||
}
|
||||
|
||||
render(){
|
||||
return this.component.render()
|
||||
}
|
||||
|
||||
static displayName = "SettingsWebhooks"
|
||||
}
|
||||
|
||||
return SettingsWebhooks
|
||||
}
|
||||
getModule(e => e.default && e.default.displayName === "FluxContainer(SettingsWebhooks)")
|
||||
.then(webhooksComponents => {
|
||||
let comp = webhooksComponents.default
|
||||
|
||||
webhooksComponents.default = getComp(comp)
|
||||
|
||||
WebpackLoader.find(e => e.default && e.default.displayName === "FluxContainer(FluxContainer(SettingsWebhooks))")
|
||||
.forEach(mod => {
|
||||
mod.default = getComp(mod.default)
|
||||
})
|
||||
})*/
|
||||
/** END WEBHOOK PATCH */
|
||||
}
|
||||
|
||||
// TODO: Add in app-notifications / confirmations.
|
||||
/** START IN-APP NOTIFICATIONS */
|
||||
//getModule(e => true)
|
||||
/** END IN-APP NOTIFICATIONS */
|
||||
}
|
||||
|
||||
function getModule(filter: (mod:any) => boolean):Promise<any>{
|
||||
return new Promise((resolve) => {
|
||||
window.Lightcord.Api.ensureExported(filter)
|
||||
.then(resolve)
|
||||
.catch(err => {
|
||||
console.error("[LIGHTCORD]", err, filter)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let hasCompletedLogin = false
|
||||
let loginPromise:Promise<void>
|
||||
function awaitLogin():Promise<void>{
|
||||
if(hasCompletedLogin)return Promise.resolve()
|
||||
if(loginPromise)return loginPromise
|
||||
|
||||
return loginPromise = new Promise((resolve) => {
|
||||
let isResolved = false
|
||||
window.Lightcord.DiscordModules.dispatcher.subscribe("CONNECTION_OPEN", (ev) => {
|
||||
if(isResolved)return
|
||||
hasCompletedLogin = true
|
||||
resolve()
|
||||
isResolved = true
|
||||
})
|
||||
})
|
||||
}
|
||||
window.Lightcord.DiscordModules.dispatcher.subscribe("LOGOUT", (ev) => {
|
||||
hasCompletedLogin = false
|
||||
loginPromise = undefined
|
||||
})
|
|
@ -1,21 +1,21 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": false,
|
||||
"module": "CommonJS",
|
||||
"target": "ES2017",
|
||||
"jsx": "react",
|
||||
"outDir": "./dist",
|
||||
"resolveJsonModule": true,
|
||||
"rootDir": "./src",
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"allowJs": true
|
||||
},
|
||||
"exclude": [
|
||||
"./js/**",
|
||||
"./webpack.config.js",
|
||||
"./dist/**",
|
||||
"./prod.config.js",
|
||||
"./docs"
|
||||
]
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": false,
|
||||
"module": "CommonJS",
|
||||
"target": "ES2017",
|
||||
"jsx": "react",
|
||||
"outDir": "./dist",
|
||||
"resolveJsonModule": true,
|
||||
"rootDir": "./src",
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"allowJs": true
|
||||
},
|
||||
"exclude": [
|
||||
"./js/**",
|
||||
"./webpack.config.js",
|
||||
"./dist/**",
|
||||
"./prod.config.js",
|
||||
"./docs"
|
||||
]
|
||||
}
|
|
@ -1,72 +1,72 @@
|
|||
const path = require("path");
|
||||
const TerserPlugin = require("terser-webpack-plugin")
|
||||
const child_process = require("child_process")
|
||||
|
||||
module.exports = {
|
||||
mode: "development",
|
||||
target: "node",
|
||||
devtool: "inline-source-map",
|
||||
entry: "./src/index.ts",
|
||||
output: {
|
||||
filename: "main.js",
|
||||
path: path.resolve(__dirname, "js"),
|
||||
library: "LightcordApi",
|
||||
libraryTarget: "commonjs2"
|
||||
},
|
||||
externals: {
|
||||
electron: `electron`,
|
||||
fs: `fs`,
|
||||
path: `path`,
|
||||
events: `events`,
|
||||
rimraf: `rimraf`,
|
||||
yauzl: `yauzl`,
|
||||
mkdirp: `mkdirp`,
|
||||
request: `request`,
|
||||
"node-fetch": "node-fetch",
|
||||
"uuid/v1": "uuid/v1",
|
||||
"uuid/v4": "uuid/v4",
|
||||
"powercord/webpack": "powercord/webpack"
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".js", ".jsx", ".json", ".ts", ".tsx"],
|
||||
alias: {
|
||||
"react$": path.resolve(__dirname, "src", "alias", "react.js"),
|
||||
"react-dom$": path.resolve(__dirname, "src", "alias", "react-dom.js")
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.jsx?$/,
|
||||
loader: "babel-loader",
|
||||
exclude: /node_modules/,
|
||||
query: {
|
||||
presets: [
|
||||
["@babel/env", {
|
||||
targets: {
|
||||
node: "12.8.1",
|
||||
chrome: "78"
|
||||
}
|
||||
}], "@babel/react"
|
||||
]
|
||||
}
|
||||
}, {
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
}]
|
||||
},
|
||||
optimization: {
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
cache: true,
|
||||
parallel: true,
|
||||
sourceMap: true,
|
||||
terserOptions: {
|
||||
mangle: false,
|
||||
keep_classnames: true,
|
||||
keep_fnames: true
|
||||
}
|
||||
}),
|
||||
]
|
||||
}
|
||||
const path = require("path");
|
||||
const TerserPlugin = require("terser-webpack-plugin")
|
||||
const child_process = require("child_process")
|
||||
|
||||
module.exports = {
|
||||
mode: "development",
|
||||
target: "node",
|
||||
devtool: "inline-source-map",
|
||||
entry: "./src/index.ts",
|
||||
output: {
|
||||
filename: "main.js",
|
||||
path: path.resolve(__dirname, "js"),
|
||||
library: "LightcordApi",
|
||||
libraryTarget: "commonjs2"
|
||||
},
|
||||
externals: {
|
||||
electron: `electron`,
|
||||
fs: `fs`,
|
||||
path: `path`,
|
||||
events: `events`,
|
||||
rimraf: `rimraf`,
|
||||
yauzl: `yauzl`,
|
||||
mkdirp: `mkdirp`,
|
||||
request: `request`,
|
||||
"node-fetch": "node-fetch",
|
||||
"uuid/v1": "uuid/v1",
|
||||
"uuid/v4": "uuid/v4",
|
||||
"powercord/webpack": "powercord/webpack"
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".js", ".jsx", ".json", ".ts", ".tsx"],
|
||||
alias: {
|
||||
"react$": path.resolve(__dirname, "src", "alias", "react.js"),
|
||||
"react-dom$": path.resolve(__dirname, "src", "alias", "react-dom.js")
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.jsx?$/,
|
||||
loader: "babel-loader",
|
||||
exclude: /node_modules/,
|
||||
query: {
|
||||
presets: [
|
||||
["@babel/env", {
|
||||
targets: {
|
||||
node: "12.8.1",
|
||||
chrome: "78"
|
||||
}
|
||||
}], "@babel/react"
|
||||
]
|
||||
}
|
||||
}, {
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
}]
|
||||
},
|
||||
optimization: {
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
cache: true,
|
||||
parallel: true,
|
||||
sourceMap: true,
|
||||
terserOptions: {
|
||||
mangle: false,
|
||||
keep_classnames: true,
|
||||
keep_fnames: true
|
||||
}
|
||||
}),
|
||||
]
|
||||
}
|
||||
};
|
126
afterbuild.js
126
afterbuild.js
|
@ -1,64 +1,64 @@
|
|||
const fs = require("fs")
|
||||
const fsAsync = fs.promises
|
||||
const yazl = require("yazl")
|
||||
const __path = require("path")
|
||||
|
||||
const buildsPaths = __path.join(__dirname, "builds")
|
||||
const folders = [
|
||||
"lightcord-win32-ia32",
|
||||
"lightcord-linux-x64",
|
||||
"lightcord-darwin-x64"
|
||||
]
|
||||
folders.forEach(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`)
|
||||
const zipPath = __path.join(buildsPaths, folder+".zip")
|
||||
if(fs.existsSync(zipPath)){
|
||||
console.warn(`Deleting ${zipPath}.`)
|
||||
fs.unlinkSync(zipPath)
|
||||
}
|
||||
const zip = new yazl.ZipFile()
|
||||
zip.outputStream.pipe(fs.createWriteStream(zipPath))
|
||||
|
||||
const platform = folder.split("-")[1]
|
||||
processNextDir(path, zip, platform)
|
||||
.then(() => {
|
||||
console.log(`Zipped ${platform}.`)
|
||||
zip.end()
|
||||
})
|
||||
})
|
||||
|
||||
async function processNextDir(dir, zip, platform, bpath = dir){
|
||||
if(dir.replace(bpath, ""))zip.addEmptyDirectory(dir.replace(bpath, "").slice(1))
|
||||
await Promise.all(fs.readdirSync(dir, {withFileTypes: true})
|
||||
.map(async file => {
|
||||
let path = __path.join(dir, file.name)
|
||||
if(file.isDirectory()){
|
||||
return await processNextDir(path, zip, platform, bpath)
|
||||
}else if(file.isFile()){
|
||||
if(!path.includes("node_modules")){
|
||||
if(platform === "win32"){
|
||||
if(file.name.endsWith("_linux.node"))return
|
||||
if(file.name.endsWith("_darwin.node"))return
|
||||
if(file.name.endsWith(".dylib"))return
|
||||
if(file.name.endsWith(".so.4"))return
|
||||
}else if(platform === "linux"){
|
||||
if(file.name.endsWith("_win32.node"))return
|
||||
if(file.name.endsWith("_darwin.node"))return
|
||||
if(file.name.endsWith(".dylib"))return
|
||||
if(file.name.endsWith(".dll"))return
|
||||
}else if(platform === "darwin"){
|
||||
if(file.name.endsWith("_linux.node"))return
|
||||
if(file.name.endsWith("_win32.node"))return
|
||||
if(file.name.endsWith(".dll"))return
|
||||
if(file.name.endsWith(".so.4"))return
|
||||
}
|
||||
}
|
||||
let stat = fs.statSync(path)
|
||||
zip.addBuffer(await fsAsync.readFile(path), __path.relative(bpath, path), {
|
||||
mode: stat.mode,
|
||||
mtime: stat.mtime
|
||||
})
|
||||
}
|
||||
}))
|
||||
const fs = require("fs")
|
||||
const fsAsync = fs.promises
|
||||
const yazl = require("yazl")
|
||||
const __path = require("path")
|
||||
|
||||
const buildsPaths = __path.join(__dirname, "builds")
|
||||
const folders = [
|
||||
"lightcord-win32-ia32",
|
||||
"lightcord-linux-x64",
|
||||
"lightcord-darwin-x64"
|
||||
]
|
||||
folders.forEach(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`)
|
||||
const zipPath = __path.join(buildsPaths, folder+".zip")
|
||||
if(fs.existsSync(zipPath)){
|
||||
console.warn(`Deleting ${zipPath}.`)
|
||||
fs.unlinkSync(zipPath)
|
||||
}
|
||||
const zip = new yazl.ZipFile()
|
||||
zip.outputStream.pipe(fs.createWriteStream(zipPath))
|
||||
|
||||
const platform = folder.split("-")[1]
|
||||
processNextDir(path, zip, platform)
|
||||
.then(() => {
|
||||
console.log(`Zipped ${platform}.`)
|
||||
zip.end()
|
||||
})
|
||||
})
|
||||
|
||||
async function processNextDir(dir, zip, platform, bpath = dir){
|
||||
if(dir.replace(bpath, ""))zip.addEmptyDirectory(dir.replace(bpath, "").slice(1))
|
||||
await Promise.all(fs.readdirSync(dir, {withFileTypes: true})
|
||||
.map(async file => {
|
||||
let path = __path.join(dir, file.name)
|
||||
if(file.isDirectory()){
|
||||
return await processNextDir(path, zip, platform, bpath)
|
||||
}else if(file.isFile()){
|
||||
if(!path.includes("node_modules")){
|
||||
if(platform === "win32"){
|
||||
if(file.name.endsWith("_linux.node"))return
|
||||
if(file.name.endsWith("_darwin.node"))return
|
||||
if(file.name.endsWith(".dylib"))return
|
||||
if(file.name.endsWith(".so.4"))return
|
||||
}else if(platform === "linux"){
|
||||
if(file.name.endsWith("_win32.node"))return
|
||||
if(file.name.endsWith("_darwin.node"))return
|
||||
if(file.name.endsWith(".dylib"))return
|
||||
if(file.name.endsWith(".dll"))return
|
||||
}else if(platform === "darwin"){
|
||||
if(file.name.endsWith("_linux.node"))return
|
||||
if(file.name.endsWith("_win32.node"))return
|
||||
if(file.name.endsWith(".dll"))return
|
||||
if(file.name.endsWith(".so.4"))return
|
||||
}
|
||||
}
|
||||
let stat = fs.statSync(path)
|
||||
zip.addBuffer(await fsAsync.readFile(path), __path.relative(bpath, path), {
|
||||
mode: stat.mode,
|
||||
mtime: stat.mtime
|
||||
})
|
||||
}
|
||||
}))
|
||||
}
|
548
build.js
548
build.js
|
@ -1,275 +1,275 @@
|
|||
const child_process = require("child_process")
|
||||
const path = require("path")
|
||||
const terser = require("terser")
|
||||
const util = require("util")
|
||||
|
||||
const production = true
|
||||
const includeSourcesMaps = true
|
||||
|
||||
let fs = require("fs")
|
||||
|
||||
console.log = (...args) => {
|
||||
process.stdout.write(Buffer.from(util.formatWithOptions({colors: true}, ...args)+"\n", "binary").toString("utf8"))
|
||||
}
|
||||
console.info = (...args) => {
|
||||
console.log(`\x1b[34m[INFO]\x1b[0m`, ...args)
|
||||
}
|
||||
let commit = child_process.execSync("git rev-parse HEAD").toString().split("\n")[0].trim()
|
||||
console.info(`Obtained commit ${commit} for the build`)
|
||||
|
||||
async function processNextDir(folder, folders, predicate, compile, ignoreModules){
|
||||
if(typeof ignoreModules === "undefined")ignoreModules = false
|
||||
let files = fs.readdirSync(folder, {withFileTypes: true})
|
||||
for(let file of files){
|
||||
if(file.isFile()){
|
||||
let isMinified = file.name.endsWith(".min.js") || file.name.endsWith(".min.css")
|
||||
let filepath = path.join(folder, file.name)
|
||||
let type = file.name.split(".").pop().toLowerCase()
|
||||
if(type === file.name)type = ""
|
||||
if([
|
||||
"ts",
|
||||
"md",
|
||||
"gitignore",
|
||||
"map"
|
||||
].includes(type)){
|
||||
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of type ${type}\x1b[0m`)
|
||||
continue
|
||||
}
|
||||
if([
|
||||
"tsconfig.json",
|
||||
"webpack.config.js"
|
||||
].includes(file.name)){
|
||||
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of name ${file.name}\x1b[0m`)
|
||||
continue
|
||||
}
|
||||
if(folders.exclude && folders.exclude.test(filepath)){
|
||||
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because regex\x1b[0m`)
|
||||
continue
|
||||
}
|
||||
let hasMinifiedVersion = (type === "js" || type === "css") && !isMinified && files.find(f => {
|
||||
return f.name === file.name.split(".").slice(0, -1).join(".")+".min."+type
|
||||
})
|
||||
if(hasMinifiedVersion){
|
||||
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because it has a minified version.\x1b[0m`)
|
||||
continue
|
||||
}
|
||||
if(!isMinified && predicate(filepath) && filepath.split(/[\\/]+/).reverse()[1] !== "js"){
|
||||
await compile(filepath, path.join(filepath.replace(folders.startDir, folders.newDir)), "..")
|
||||
}else{
|
||||
if(["js", "css"].includes(type)){
|
||||
if(!includeSourcesMaps){
|
||||
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))
|
||||
}
|
||||
let fileContent = (await fs.promises.readFile(filepath, "utf8"))
|
||||
let sourceMap = fileContent.split(/[\n\r]+/g).pop()
|
||||
if(!sourceMap || !sourceMap.startsWith("//# sourceMappingURL=")){
|
||||
console.log(`This file doesn't have sourcemap. ${file.name}.`)
|
||||
await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
|
||||
continue
|
||||
}
|
||||
let sourceMapContent
|
||||
if(sourceMap.slice(21).startsWith("data:")){
|
||||
console.log(`Extracting sourcemap from data uri. From file ${file.name}.`)
|
||||
sourceMapContent = Buffer.from(sourceMap.split("=").slice(1).join("="), "base64").toString("utf-8")
|
||||
}else{
|
||||
console.log(`Extracting sourcemap from file ${file.name}.map.`)
|
||||
await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
|
||||
sourceMapContent = await fs.promises.readFile(path.join(folder, sourceMap.slice(21)), "utf8")
|
||||
}
|
||||
sourceMapContent = JSON.parse(sourceMapContent)
|
||||
sourceMapContent.sourcesContent = []
|
||||
let sourceMapPath = filepath + ".map"
|
||||
fileContent = fileContent
|
||||
// source 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)+".map", JSON.stringify(sourceMapContent))
|
||||
}else{
|
||||
await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
|
||||
}
|
||||
}
|
||||
}else if(file.isDirectory()){
|
||||
if(ignoreModules && file.name === "node_modules")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 processNextDir(path.join(folder, file.name), ...Array.from(arguments).slice(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function main(){
|
||||
let startTimestamp = Date.now()
|
||||
console.info("Starting build")
|
||||
|
||||
console.info("Reseting existent directory...")
|
||||
await fs.promises.rmdir("./distApp", {"recursive": true})
|
||||
await fs.promises.mkdir(__dirname+"/distApp/dist", {"recursive": true})
|
||||
|
||||
console.info("Executing command `npm run compile`")
|
||||
child_process.execSync("npm run compile", {
|
||||
encoding: "binary",
|
||||
stdio: "inherit"
|
||||
})
|
||||
|
||||
let startDir = path.join(__dirname, "./dist")
|
||||
let newDir = path.join(__dirname, "./distApp/dist")
|
||||
console.info("No error detected. Copying files from "+startDir+".")
|
||||
await fs.promises.mkdir(startDir, {recursive: true})
|
||||
|
||||
await processNextDir(startDir, {
|
||||
startDir,
|
||||
newDir
|
||||
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
|
||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
||||
|
||||
if(filepath.endsWith("git.js")){
|
||||
await fs.promises.writeFile(newpath, terser.minify(fs.readFileSync(filepath, "utf8").replace(/"{commit}"/g, `"${commit}"`)).code, "utf8")
|
||||
}else{
|
||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
||||
}
|
||||
}, true).then(() => {
|
||||
console.info(`Copied files and minified them from ${startDir}.`)
|
||||
})
|
||||
|
||||
await processNextDir(path.join(__dirname, "modules"), {
|
||||
startDir: path.join(__dirname, "modules"),
|
||||
newDir: path.join(__dirname, "distApp", "modules"),
|
||||
exclude: /discord_spellcheck/g
|
||||
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
|
||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
||||
}, true).then(() => {
|
||||
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 => {
|
||||
let dir = path.join(__dirname, "distApp", "modules", mdl)
|
||||
|
||||
if(!fs.existsSync(path.join(dir, "package.json"))){
|
||||
if(mdl === "discord_desktop_core"){
|
||||
dir = path.join(dir, "core")
|
||||
}else{
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
console.info(`Installing modules for ${mdl}`)
|
||||
child_process.execSync("npm install --only=prod", {
|
||||
encoding: "binary",
|
||||
cwd: dir,
|
||||
stdio: "inherit"
|
||||
})
|
||||
}))
|
||||
|
||||
await fs.promises.mkdir(path.join(__dirname, "distApp", "modules", "discord_spellcheck"), {recursive: true})
|
||||
await processNextDir(path.join(__dirname, "modules", "discord_spellcheck"), {
|
||||
startDir: path.join(__dirname, "modules", "discord_spellcheck"),
|
||||
newDir: path.join(__dirname, "distApp", "modules", "discord_spellcheck")
|
||||
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
|
||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
||||
}, false).then(() => {
|
||||
console.info(`Copied files and minified them from ${path.join(__dirname, "modules")}.`)
|
||||
})
|
||||
|
||||
await processNextDir(path.join(__dirname, "LightcordApi"), {
|
||||
startDir: path.join(__dirname, "LightcordApi"),
|
||||
newDir: path.join(__dirname, "distApp", "LightcordApi"),
|
||||
exclude: /(src|webpack\.config\.js|tsconfig\.json|dist|docs)/g
|
||||
}, ((filepath) => filepath.endsWith(".js") && (!production ? !filepath.includes("node_modules") : true)), async (filepath, newpath) => {
|
||||
await fs.promises.copyFile(filepath, newpath)
|
||||
}, true).then(() => {
|
||||
console.info(`Copied files and minified them from ${path.join(__dirname, "LightcordApi")}.`)
|
||||
})
|
||||
|
||||
child_process.execSync("npm install --only=prod", {
|
||||
encoding: "binary",
|
||||
cwd: path.join(__dirname, "distApp", "LightcordApi"),
|
||||
stdio: "inherit"
|
||||
})
|
||||
|
||||
function processDJS(dir){
|
||||
fs.mkdirSync(path.join(__dirname, "distApp", "DiscordJS", dir), {recursive: true})
|
||||
return processNextDir(path.join(__dirname, "DiscordJS", dir), {
|
||||
startDir: path.join(__dirname, "DiscordJS", dir),
|
||||
newDir: path.join(__dirname, "distApp", "DiscordJS", dir),
|
||||
exclude: /node_modules/g
|
||||
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
|
||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
||||
}).then(() => {
|
||||
console.info(`Copied files and minified them from ${path.join(__dirname, "DiscordJS", dir)}.`)
|
||||
})
|
||||
}
|
||||
async function copyFileDJS(file){
|
||||
await fs.promises.writeFile(path.join(__dirname, "distApp", "DiscordJS", file), await fs.promises.readFile(path.join(__dirname, "DiscordJS", file)))
|
||||
}
|
||||
|
||||
await processDJS("dist")
|
||||
await copyFileDJS("package.json")
|
||||
|
||||
child_process.execSync("npm install --only=prod", {
|
||||
encoding: "binary",
|
||||
cwd: path.join(__dirname, "distApp", "DiscordJS"),
|
||||
stdio: "inherit"
|
||||
})
|
||||
|
||||
fs.mkdirSync(path.join(__dirname, "distApp", "BetterDiscordApp", "dist"), {recursive: true})
|
||||
const BDPackageJSON = require("./BetterDiscordApp/package.json")
|
||||
fs.writeFileSync(path.join(__dirname, "distApp", "BetterDiscordApp", "package.json"), JSON.stringify(BDPackageJSON), "utf8")
|
||||
const files = [
|
||||
"index.min.js",
|
||||
"style.min.css"
|
||||
]
|
||||
files.forEach(e => {
|
||||
files.push(e + ".map")
|
||||
})
|
||||
files.forEach(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(e.endsWith(".map")){
|
||||
const data = JSON.parse(fs.readFileSync(pth, "utf8"))
|
||||
data.sourcesContent = []
|
||||
fs.writeFileSync(path.join(__dirname, "distApp", "BetterDiscordApp", "dist", e), JSON.stringify(data))
|
||||
}else{
|
||||
fs.copyFileSync(pth, path.join(__dirname, "distApp", "BetterDiscordApp", "dist", e))
|
||||
}
|
||||
})
|
||||
|
||||
await fs.promises.mkdir(path.join(__dirname, "distApp", "splash", "videos"), {recursive: true})
|
||||
await processNextDir(path.join(__dirname, "splash"), {
|
||||
startDir: path.join(__dirname, "splash"),
|
||||
newDir: path.join(__dirname, "distApp", "splash"),
|
||||
exclude: /node_modules/g
|
||||
}, (filepath) => {
|
||||
if(filepath.endsWith(".js"))return true
|
||||
return false
|
||||
}, async (filepath, newpath) => {
|
||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
||||
}).then(() => {
|
||||
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")))
|
||||
|
||||
let packageJSON = require("./package.json")
|
||||
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_darwin"] = packageJSON.scripts["build:electron_darwin"].replace("./distApp", ".")
|
||||
|
||||
fs.writeFileSync(path.join(__dirname, "distApp", "package.json"), JSON.stringify(packageJSON), "utf8")
|
||||
|
||||
console.info(`Installing ${Object.keys(packageJSON.dependencies).length} packages...`)
|
||||
child_process.execSync("npm install --only=prod", {
|
||||
encoding: "binary",
|
||||
cwd: path.join(__dirname, "distApp"),
|
||||
stdio: "inherit"
|
||||
})
|
||||
console.info("Build took "+(Date.now() - startTimestamp) +"ms.")
|
||||
}
|
||||
main()
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
const child_process = require("child_process")
|
||||
const path = require("path")
|
||||
const terser = require("terser")
|
||||
const util = require("util")
|
||||
|
||||
const production = true
|
||||
const includeSourcesMaps = true
|
||||
|
||||
let fs = require("fs")
|
||||
|
||||
console.log = (...args) => {
|
||||
process.stdout.write(Buffer.from(util.formatWithOptions({colors: true}, ...args)+"\n", "binary").toString("utf8"))
|
||||
}
|
||||
console.info = (...args) => {
|
||||
console.log(`\x1b[34m[INFO]\x1b[0m`, ...args)
|
||||
}
|
||||
let commit = child_process.execSync("git rev-parse HEAD").toString().split("\n")[0].trim()
|
||||
console.info(`Obtained commit ${commit} for the build`)
|
||||
|
||||
async function processNextDir(folder, folders, predicate, compile, ignoreModules){
|
||||
if(typeof ignoreModules === "undefined")ignoreModules = false
|
||||
let files = fs.readdirSync(folder, {withFileTypes: true})
|
||||
for(let file of files){
|
||||
if(file.isFile()){
|
||||
let isMinified = file.name.endsWith(".min.js") || file.name.endsWith(".min.css")
|
||||
let filepath = path.join(folder, file.name)
|
||||
let type = file.name.split(".").pop().toLowerCase()
|
||||
if(type === file.name)type = ""
|
||||
if([
|
||||
"ts",
|
||||
"md",
|
||||
"gitignore",
|
||||
"map"
|
||||
].includes(type)){
|
||||
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of type ${type}\x1b[0m`)
|
||||
continue
|
||||
}
|
||||
if([
|
||||
"tsconfig.json",
|
||||
"webpack.config.js"
|
||||
].includes(file.name)){
|
||||
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of name ${file.name}\x1b[0m`)
|
||||
continue
|
||||
}
|
||||
if(folders.exclude && folders.exclude.test(filepath)){
|
||||
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because regex\x1b[0m`)
|
||||
continue
|
||||
}
|
||||
let hasMinifiedVersion = (type === "js" || type === "css") && !isMinified && files.find(f => {
|
||||
return f.name === file.name.split(".").slice(0, -1).join(".")+".min."+type
|
||||
})
|
||||
if(hasMinifiedVersion){
|
||||
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because it has a minified version.\x1b[0m`)
|
||||
continue
|
||||
}
|
||||
if(!isMinified && predicate(filepath) && filepath.split(/[\\/]+/).reverse()[1] !== "js"){
|
||||
await compile(filepath, path.join(filepath.replace(folders.startDir, folders.newDir)), "..")
|
||||
}else{
|
||||
if(["js", "css"].includes(type)){
|
||||
if(!includeSourcesMaps){
|
||||
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))
|
||||
}
|
||||
let fileContent = (await fs.promises.readFile(filepath, "utf8"))
|
||||
let sourceMap = fileContent.split(/[\n\r]+/g).pop()
|
||||
if(!sourceMap || !sourceMap.startsWith("//# sourceMappingURL=")){
|
||||
console.log(`This file doesn't have sourcemap. ${file.name}.`)
|
||||
await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
|
||||
continue
|
||||
}
|
||||
let sourceMapContent
|
||||
if(sourceMap.slice(21).startsWith("data:")){
|
||||
console.log(`Extracting sourcemap from data uri. From file ${file.name}.`)
|
||||
sourceMapContent = Buffer.from(sourceMap.split("=").slice(1).join("="), "base64").toString("utf-8")
|
||||
}else{
|
||||
console.log(`Extracting sourcemap from file ${file.name}.map.`)
|
||||
await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
|
||||
sourceMapContent = await fs.promises.readFile(path.join(folder, sourceMap.slice(21)), "utf8")
|
||||
}
|
||||
sourceMapContent = JSON.parse(sourceMapContent)
|
||||
sourceMapContent.sourcesContent = []
|
||||
let sourceMapPath = filepath + ".map"
|
||||
fileContent = fileContent
|
||||
// source 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)+".map", JSON.stringify(sourceMapContent))
|
||||
}else{
|
||||
await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
|
||||
}
|
||||
}
|
||||
}else if(file.isDirectory()){
|
||||
if(ignoreModules && file.name === "node_modules")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 processNextDir(path.join(folder, file.name), ...Array.from(arguments).slice(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function main(){
|
||||
let startTimestamp = Date.now()
|
||||
console.info("Starting build")
|
||||
|
||||
console.info("Reseting existent directory...")
|
||||
await fs.promises.rmdir("./distApp", {"recursive": true})
|
||||
await fs.promises.mkdir(__dirname+"/distApp/dist", {"recursive": true})
|
||||
|
||||
console.info("Executing command `npm run compile`")
|
||||
child_process.execSync("npm run compile", {
|
||||
encoding: "binary",
|
||||
stdio: "inherit"
|
||||
})
|
||||
|
||||
let startDir = path.join(__dirname, "./dist")
|
||||
let newDir = path.join(__dirname, "./distApp/dist")
|
||||
console.info("No error detected. Copying files from "+startDir+".")
|
||||
await fs.promises.mkdir(startDir, {recursive: true})
|
||||
|
||||
await processNextDir(startDir, {
|
||||
startDir,
|
||||
newDir
|
||||
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
|
||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
||||
|
||||
if(filepath.endsWith("git.js")){
|
||||
await fs.promises.writeFile(newpath, terser.minify(fs.readFileSync(filepath, "utf8").replace(/"{commit}"/g, `"${commit}"`)).code, "utf8")
|
||||
}else{
|
||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
||||
}
|
||||
}, true).then(() => {
|
||||
console.info(`Copied files and minified them from ${startDir}.`)
|
||||
})
|
||||
|
||||
await processNextDir(path.join(__dirname, "modules"), {
|
||||
startDir: path.join(__dirname, "modules"),
|
||||
newDir: path.join(__dirname, "distApp", "modules"),
|
||||
exclude: /discord_spellcheck/g
|
||||
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
|
||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
||||
}, true).then(() => {
|
||||
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 => {
|
||||
let dir = path.join(__dirname, "distApp", "modules", mdl)
|
||||
|
||||
if(!fs.existsSync(path.join(dir, "package.json"))){
|
||||
if(mdl === "discord_desktop_core"){
|
||||
dir = path.join(dir, "core")
|
||||
}else{
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
console.info(`Installing modules for ${mdl}`)
|
||||
child_process.execSync("npm install --only=prod", {
|
||||
encoding: "binary",
|
||||
cwd: dir,
|
||||
stdio: "inherit"
|
||||
})
|
||||
}))
|
||||
|
||||
await fs.promises.mkdir(path.join(__dirname, "distApp", "modules", "discord_spellcheck"), {recursive: true})
|
||||
await processNextDir(path.join(__dirname, "modules", "discord_spellcheck"), {
|
||||
startDir: path.join(__dirname, "modules", "discord_spellcheck"),
|
||||
newDir: path.join(__dirname, "distApp", "modules", "discord_spellcheck")
|
||||
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
|
||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
||||
}, false).then(() => {
|
||||
console.info(`Copied files and minified them from ${path.join(__dirname, "modules")}.`)
|
||||
})
|
||||
|
||||
await processNextDir(path.join(__dirname, "LightcordApi"), {
|
||||
startDir: path.join(__dirname, "LightcordApi"),
|
||||
newDir: path.join(__dirname, "distApp", "LightcordApi"),
|
||||
exclude: /(src|webpack\.config\.js|tsconfig\.json|dist|docs)/g
|
||||
}, ((filepath) => filepath.endsWith(".js") && (!production ? !filepath.includes("node_modules") : true)), async (filepath, newpath) => {
|
||||
await fs.promises.copyFile(filepath, newpath)
|
||||
}, true).then(() => {
|
||||
console.info(`Copied files and minified them from ${path.join(__dirname, "LightcordApi")}.`)
|
||||
})
|
||||
|
||||
child_process.execSync("npm install --only=prod", {
|
||||
encoding: "binary",
|
||||
cwd: path.join(__dirname, "distApp", "LightcordApi"),
|
||||
stdio: "inherit"
|
||||
})
|
||||
|
||||
function processDJS(dir){
|
||||
fs.mkdirSync(path.join(__dirname, "distApp", "DiscordJS", dir), {recursive: true})
|
||||
return processNextDir(path.join(__dirname, "DiscordJS", dir), {
|
||||
startDir: path.join(__dirname, "DiscordJS", dir),
|
||||
newDir: path.join(__dirname, "distApp", "DiscordJS", dir),
|
||||
exclude: /node_modules/g
|
||||
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
|
||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
||||
}).then(() => {
|
||||
console.info(`Copied files and minified them from ${path.join(__dirname, "DiscordJS", dir)}.`)
|
||||
})
|
||||
}
|
||||
async function copyFileDJS(file){
|
||||
await fs.promises.writeFile(path.join(__dirname, "distApp", "DiscordJS", file), await fs.promises.readFile(path.join(__dirname, "DiscordJS", file)))
|
||||
}
|
||||
|
||||
await processDJS("dist")
|
||||
await copyFileDJS("package.json")
|
||||
|
||||
child_process.execSync("npm install --only=prod", {
|
||||
encoding: "binary",
|
||||
cwd: path.join(__dirname, "distApp", "DiscordJS"),
|
||||
stdio: "inherit"
|
||||
})
|
||||
|
||||
fs.mkdirSync(path.join(__dirname, "distApp", "BetterDiscordApp", "dist"), {recursive: true})
|
||||
const BDPackageJSON = require("./BetterDiscordApp/package.json")
|
||||
fs.writeFileSync(path.join(__dirname, "distApp", "BetterDiscordApp", "package.json"), JSON.stringify(BDPackageJSON), "utf8")
|
||||
const files = [
|
||||
"index.min.js",
|
||||
"style.min.css"
|
||||
]
|
||||
files.forEach(e => {
|
||||
files.push(e + ".map")
|
||||
})
|
||||
files.forEach(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(e.endsWith(".map")){
|
||||
const data = JSON.parse(fs.readFileSync(pth, "utf8"))
|
||||
data.sourcesContent = []
|
||||
fs.writeFileSync(path.join(__dirname, "distApp", "BetterDiscordApp", "dist", e), JSON.stringify(data))
|
||||
}else{
|
||||
fs.copyFileSync(pth, path.join(__dirname, "distApp", "BetterDiscordApp", "dist", e))
|
||||
}
|
||||
})
|
||||
|
||||
await fs.promises.mkdir(path.join(__dirname, "distApp", "splash", "videos"), {recursive: true})
|
||||
await processNextDir(path.join(__dirname, "splash"), {
|
||||
startDir: path.join(__dirname, "splash"),
|
||||
newDir: path.join(__dirname, "distApp", "splash"),
|
||||
exclude: /node_modules/g
|
||||
}, (filepath) => {
|
||||
if(filepath.endsWith(".js"))return true
|
||||
return false
|
||||
}, async (filepath, newpath) => {
|
||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
||||
}).then(() => {
|
||||
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")))
|
||||
|
||||
let packageJSON = require("./package.json")
|
||||
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_darwin"] = packageJSON.scripts["build:electron_darwin"].replace("./distApp", ".")
|
||||
|
||||
fs.writeFileSync(path.join(__dirname, "distApp", "package.json"), JSON.stringify(packageJSON), "utf8")
|
||||
|
||||
console.info(`Installing ${Object.keys(packageJSON.dependencies).length} packages...`)
|
||||
child_process.execSync("npm install --only=prod", {
|
||||
encoding: "binary",
|
||||
cwd: path.join(__dirname, "distApp"),
|
||||
stdio: "inherit"
|
||||
})
|
||||
console.info("Build took "+(Date.now() - startTimestamp) +"ms.")
|
||||
}
|
||||
main()
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
|
@ -1,119 +1,119 @@
|
|||
const spawn = require("cross-spawn")
|
||||
const path = require("path")
|
||||
const { existsSync, promises: fsPromises } = require("fs")
|
||||
|
||||
const supportedPlatforms = []
|
||||
const Platforms = {
|
||||
linux: {
|
||||
name: "linux",
|
||||
run: () => {
|
||||
return awaitExec("npm", ["run", "build:electron_linux"])
|
||||
}
|
||||
},
|
||||
win: {
|
||||
name: "win",
|
||||
run: () => {
|
||||
return awaitExec("npm", ["run", "build:electron_win"])
|
||||
}
|
||||
},
|
||||
mac: {
|
||||
name: "mac",
|
||||
run: () => {
|
||||
return awaitExec("npm", ["run", "build:electron_darwin"])
|
||||
}
|
||||
},
|
||||
mac_experimental: {
|
||||
name: "mac",
|
||||
experimental: true,
|
||||
run: async () => {
|
||||
const basePath = path.join(__dirname, "..", "lightcord-darwin-x64")
|
||||
const nextPath = path.join(__dirname, "builds", "lightcord-darwin-x64")
|
||||
if(existsSync(nextPath)){
|
||||
console.log(`Cleaning ${nextPath}.`)
|
||||
await fsPromises.rmdir(nextPath, {recursive: true})
|
||||
}
|
||||
console.log(`Copying files from ${basePath}.`)
|
||||
let nextDir = async (pth) => {
|
||||
const newPath = pth.replace(basePath, nextPath)
|
||||
await fsPromises.mkdir(newPath)
|
||||
for(let file of await fsPromises.readdir(pth, {withFileTypes: true})){
|
||||
const filePath = path.join(pth, file.name)
|
||||
const newFilePath = path.join(newPath, file.name)
|
||||
if(file.isFile()){
|
||||
await fsPromises.copyFile(filePath, newFilePath)
|
||||
}else if(file.isDirectory()){
|
||||
await nextDir(filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
await nextDir(basePath)
|
||||
console.log(`Files are copied. Erasing current bundle if existing.`)
|
||||
const asarPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "app.asar")
|
||||
if(existsSync(asarPath))await fsPromises.unlink(asarPath)
|
||||
const asarUnpackPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "app.asar.unpacked")
|
||||
if(existsSync(asarUnpackPath))await fsPromises.rmdir(asarUnpackPath, {recursive: true})
|
||||
const asar = require("asar")
|
||||
await asar.createPackageWithOptions(path.join(__dirname, "distApp"), asarPath, {
|
||||
unpack: "*.{node,dylib,so.4,dll}",
|
||||
unpackDir: asarUnpackPath
|
||||
})
|
||||
const iconPath = path.join(__dirname, "app_icon_darwin.icns")
|
||||
if(existsSync(iconPath)){
|
||||
console.log(`Setting icon.`)
|
||||
const newIconPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "electron.icns")
|
||||
await fsPromises.copyFile(iconPath, newIconPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(process.platform){
|
||||
case "win32":
|
||||
supportedPlatforms.push(Platforms.win)
|
||||
supportedPlatforms.push(Platforms.linux)
|
||||
if(existsSync(path.join(__dirname, "..", "lightcord-darwin-x64"))){
|
||||
supportedPlatforms.push(Platforms.mac_experimental)
|
||||
}
|
||||
break
|
||||
case "linux":
|
||||
supportedPlatforms.push(Platforms.linux)
|
||||
if(existsSync(path.join(__dirname, "..", "lightcord-darwin-x64"))){
|
||||
supportedPlatforms.push(Platforms.mac_experimental)
|
||||
}
|
||||
break
|
||||
case "darwin":
|
||||
supportedPlatforms.push(Platforms.mac)
|
||||
supportedPlatforms.push(Platforms.linux)
|
||||
break
|
||||
}
|
||||
|
||||
(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`)
|
||||
for(let platform of supportedPlatforms){
|
||||
console.log(`[\x1b[33mINFO\x1b[0m] Building platform ${platform.name}`)
|
||||
if(platform.experimental)console.warn(`[\x1b[33mWARN\x1b[0m] This platform is experimental`)
|
||||
await platform.run()
|
||||
}
|
||||
})().catch(err => {
|
||||
console.error(`Couldn't package app for electrons. Error: ${err}`)
|
||||
})
|
||||
|
||||
function awaitExec(command, args = []){
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn.spawn(command, args, {
|
||||
env: process.env,
|
||||
cwd: process.cwd(),
|
||||
stdio: "inherit"
|
||||
})
|
||||
child.on("close", (code) => {
|
||||
console.log()
|
||||
console.log()
|
||||
console.log(`Command ${command}${args.length > 0 ? " " + args.join(" ") : ""} ended with code ${code}.`)
|
||||
if(code !== 0){
|
||||
console.error("\x1b[31mFAILURE\x1b[0m Command failed. See logs above.")
|
||||
return reject(code)
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
const spawn = require("cross-spawn")
|
||||
const path = require("path")
|
||||
const { existsSync, promises: fsPromises } = require("fs")
|
||||
|
||||
const supportedPlatforms = []
|
||||
const Platforms = {
|
||||
linux: {
|
||||
name: "linux",
|
||||
run: () => {
|
||||
return awaitExec("npm", ["run", "build:electron_linux"])
|
||||
}
|
||||
},
|
||||
win: {
|
||||
name: "win",
|
||||
run: () => {
|
||||
return awaitExec("npm", ["run", "build:electron_win"])
|
||||
}
|
||||
},
|
||||
mac: {
|
||||
name: "mac",
|
||||
run: () => {
|
||||
return awaitExec("npm", ["run", "build:electron_darwin"])
|
||||
}
|
||||
},
|
||||
mac_experimental: {
|
||||
name: "mac",
|
||||
experimental: true,
|
||||
run: async () => {
|
||||
const basePath = path.join(__dirname, "..", "lightcord-darwin-x64")
|
||||
const nextPath = path.join(__dirname, "builds", "lightcord-darwin-x64")
|
||||
if(existsSync(nextPath)){
|
||||
console.log(`Cleaning ${nextPath}.`)
|
||||
await fsPromises.rmdir(nextPath, {recursive: true})
|
||||
}
|
||||
console.log(`Copying files from ${basePath}.`)
|
||||
let nextDir = async (pth) => {
|
||||
const newPath = pth.replace(basePath, nextPath)
|
||||
await fsPromises.mkdir(newPath)
|
||||
for(let file of await fsPromises.readdir(pth, {withFileTypes: true})){
|
||||
const filePath = path.join(pth, file.name)
|
||||
const newFilePath = path.join(newPath, file.name)
|
||||
if(file.isFile()){
|
||||
await fsPromises.copyFile(filePath, newFilePath)
|
||||
}else if(file.isDirectory()){
|
||||
await nextDir(filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
await nextDir(basePath)
|
||||
console.log(`Files are copied. Erasing current bundle if existing.`)
|
||||
const asarPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "app.asar")
|
||||
if(existsSync(asarPath))await fsPromises.unlink(asarPath)
|
||||
const asarUnpackPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "app.asar.unpacked")
|
||||
if(existsSync(asarUnpackPath))await fsPromises.rmdir(asarUnpackPath, {recursive: true})
|
||||
const asar = require("asar")
|
||||
await asar.createPackageWithOptions(path.join(__dirname, "distApp"), asarPath, {
|
||||
unpack: "*.{node,dylib,so.4,dll}",
|
||||
unpackDir: asarUnpackPath
|
||||
})
|
||||
const iconPath = path.join(__dirname, "app_icon_darwin.icns")
|
||||
if(existsSync(iconPath)){
|
||||
console.log(`Setting icon.`)
|
||||
const newIconPath = path.join(nextPath, "lightcord.app", "Contents", "Resources", "electron.icns")
|
||||
await fsPromises.copyFile(iconPath, newIconPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(process.platform){
|
||||
case "win32":
|
||||
supportedPlatforms.push(Platforms.win)
|
||||
supportedPlatforms.push(Platforms.linux)
|
||||
if(existsSync(path.join(__dirname, "..", "lightcord-darwin-x64"))){
|
||||
supportedPlatforms.push(Platforms.mac_experimental)
|
||||
}
|
||||
break
|
||||
case "linux":
|
||||
supportedPlatforms.push(Platforms.linux)
|
||||
if(existsSync(path.join(__dirname, "..", "lightcord-darwin-x64"))){
|
||||
supportedPlatforms.push(Platforms.mac_experimental)
|
||||
}
|
||||
break
|
||||
case "darwin":
|
||||
supportedPlatforms.push(Platforms.mac)
|
||||
supportedPlatforms.push(Platforms.linux)
|
||||
break
|
||||
}
|
||||
|
||||
(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`)
|
||||
for(let platform of supportedPlatforms){
|
||||
console.log(`[\x1b[33mINFO\x1b[0m] Building platform ${platform.name}`)
|
||||
if(platform.experimental)console.warn(`[\x1b[33mWARN\x1b[0m] This platform is experimental`)
|
||||
await platform.run()
|
||||
}
|
||||
})().catch(err => {
|
||||
console.error(`Couldn't package app for electrons. Error: ${err}`)
|
||||
})
|
||||
|
||||
function awaitExec(command, args = []){
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn.spawn(command, args, {
|
||||
env: process.env,
|
||||
cwd: process.cwd(),
|
||||
stdio: "inherit"
|
||||
})
|
||||
child.on("close", (code) => {
|
||||
console.log()
|
||||
console.log()
|
||||
console.log(`Command ${command}${args.length > 0 ? " " + args.join(" ") : ""} ended with code ${code}.`)
|
||||
if(code !== 0){
|
||||
console.error("\x1b[31mFAILURE\x1b[0m Command failed. See logs above.")
|
||||
return reject(code)
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
58
compile.js
58
compile.js
|
@ -1,30 +1,30 @@
|
|||
const spawn = require("cross-spawn")
|
||||
const { join } = require("path")
|
||||
|
||||
/** Main Project */
|
||||
spawnSync("tsc")
|
||||
/** BetterDiscord */
|
||||
spawnSync("npm run build", join(__dirname, "BetterDiscordApp"))
|
||||
spawnSync("npm run build-prod", join(__dirname, "BetterDiscordApp"))
|
||||
spawnSync("npm run minify-css", join(__dirname, "BetterDiscordApp"))
|
||||
/** DiscordJS */
|
||||
spawnSync("npm run build", join(__dirname, "DiscordJS"))
|
||||
/** LightcordApi */
|
||||
spawnSync("npm run build", join(__dirname, "LightcordApi"))
|
||||
spawnSync("npm run build-prod", join(__dirname, "LightcordApi"))
|
||||
spawnSync("tsc", join(__dirname, "LightcordApi"))
|
||||
|
||||
|
||||
function spawnSync(args, cwd){
|
||||
args = args.split(" ")
|
||||
let command = args.shift()
|
||||
return spawn(command, args, {
|
||||
cwd: cwd || process.cwd(),
|
||||
env: process.env,
|
||||
stdio: "inherit"
|
||||
})
|
||||
}
|
||||
|
||||
process.on("beforeExit", () => {
|
||||
console.log(`Exiting compilation`)
|
||||
const spawn = require("cross-spawn")
|
||||
const { join } = require("path")
|
||||
|
||||
/** Main Project */
|
||||
spawnSync("tsc")
|
||||
/** BetterDiscord */
|
||||
spawnSync("npm run build", join(__dirname, "BetterDiscordApp"))
|
||||
spawnSync("npm run build-prod", join(__dirname, "BetterDiscordApp"))
|
||||
spawnSync("npm run minify-css", join(__dirname, "BetterDiscordApp"))
|
||||
/** DiscordJS */
|
||||
spawnSync("npm run build", join(__dirname, "DiscordJS"))
|
||||
/** LightcordApi */
|
||||
spawnSync("npm run build", join(__dirname, "LightcordApi"))
|
||||
spawnSync("npm run build-prod", join(__dirname, "LightcordApi"))
|
||||
spawnSync("tsc", join(__dirname, "LightcordApi"))
|
||||
|
||||
|
||||
function spawnSync(args, cwd){
|
||||
args = args.split(" ")
|
||||
let command = args.shift()
|
||||
return spawn(command, args, {
|
||||
cwd: cwd || process.cwd(),
|
||||
env: process.env,
|
||||
stdio: "inherit"
|
||||
})
|
||||
}
|
||||
|
||||
process.on("beforeExit", () => {
|
||||
console.log(`Exiting compilation`)
|
||||
})
|
|
@ -1,164 +1,164 @@
|
|||
const fetch = require("node-fetch")
|
||||
const yauzl = require("yauzl")
|
||||
const tmp = require("tmp")
|
||||
const fs = require("fs")
|
||||
const util = require('util')
|
||||
const { join, dirname } = require("path")
|
||||
const streamPipeline = util.promisify(require('stream').pipeline)
|
||||
|
||||
const API_URL = "https://discord.com/api"
|
||||
const branch = "stable"
|
||||
const platforms = ["win", "linux", "osx"]
|
||||
const platformsNormalized = {
|
||||
win: "win32",
|
||||
linux: "linux",
|
||||
osx: "darwin"
|
||||
}
|
||||
|
||||
const manuallyDownloads = []
|
||||
const patchedJS = []
|
||||
|
||||
;(async function(){
|
||||
for(let platform of platforms){
|
||||
console.log(`[\x1b[32mINFO\x1b[0m] Downloading modules for ${platform} (${branch})`)
|
||||
const version = await fetch(`${API_URL}/updates/${branch}?platform=${platform}`)
|
||||
.then(res => res.json()).then(res => res.name)
|
||||
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}`)
|
||||
.then(res => res.json())
|
||||
console.log(`[\x1b[32mINFO\x1b[0m] Downloading ${Object.keys(modules).length} modules.`)
|
||||
for(const module in modules){
|
||||
if(["discord_desktop_core"].includes(module))continue
|
||||
const moduleVersion = modules[module]
|
||||
console.log(`[\x1b[32mINFO\x1b[0m] Downloading ${module} v${moduleVersion}.`)
|
||||
const file = tmp.fileSync({
|
||||
prefix: module+"-",
|
||||
postfix: ".zip"
|
||||
})
|
||||
const res = await fetch(`${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`, {
|
||||
redirect: "follow"
|
||||
})
|
||||
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))
|
||||
/**
|
||||
* @type {yauzl.ZipFile}
|
||||
*/
|
||||
const zipFile = await new Promise((resolve, reject) => {
|
||||
yauzl.open(file.name, {lazyEntries: true, autoClose: true}, (err, zip) => {
|
||||
if(err)return reject(err)
|
||||
resolve(zip)
|
||||
})
|
||||
})
|
||||
const modulePath = join(__dirname, "modules", module)
|
||||
const exists = fs.existsSync(modulePath)
|
||||
let hasNode = false
|
||||
let hasBinaries = ["discord_hook", "discord_modules"].includes(module)
|
||||
let hasNativeDependencies = ["discord_voice", "discord_krisp"].includes(module)
|
||||
if(!exists){
|
||||
console.warn(`[\x1b[33mWARN\x1b[0m] Downloading whole module because it doesn't exists in your files.`)
|
||||
fs.mkdirSync(modulePath)
|
||||
}
|
||||
await new Promise((resolve) => {
|
||||
zipFile.readEntry()
|
||||
zipFile.on("entry", function(entry) {
|
||||
const fileName = entry.fileName.toString("utf8")
|
||||
if(fileName.endsWith("/")){
|
||||
const folderPath = join(modulePath, fileName)
|
||||
if(!fs.existsSync(folderPath))fs.mkdirSync(folderPath, {recursive: true})
|
||||
zipFile.readEntry();
|
||||
}else{
|
||||
if(!exists){
|
||||
const filePath = join(modulePath, fileName)
|
||||
zipFile.openReadStream(entry, function(err, readStream) {
|
||||
if (err) throw err;
|
||||
readStream.on("end", function() {
|
||||
let content = fs.readFileSync(filePath, "utf-8")
|
||||
if(content.includes(module+".node")){
|
||||
content = content.replace(`${module}.node`, `${module}_'+process.platform+'.node`)
|
||||
fs.writeFileSync(filePath, content)
|
||||
}
|
||||
zipFile.readEntry();
|
||||
});
|
||||
readStream.pipe(fs.createWriteStream(filePath));
|
||||
});
|
||||
}else{
|
||||
let filePath = join(modulePath, fileName)
|
||||
if(filePath.endsWith(".asar"))return zipFile.readEntry();
|
||||
if(fileName.endsWith(".node")){
|
||||
hasNode = true
|
||||
filePath = filePath.replace(".node", "_"+platformsNormalized[platform]+".node")
|
||||
extractFile()
|
||||
}else if(join(filePath, "..") === modulePath && filePath.endsWith(".js")){
|
||||
if(patchedJS.includes(filePath))return zipFile.readEntry();
|
||||
patchedJS.push(filePath)
|
||||
extractFile(() => {
|
||||
setTimeout(() => {
|
||||
let content = fs.readFileSync(filePath, "utf-8")
|
||||
if(content.includes(module+".node")){
|
||||
content = content.replace(`${module}.node`, `${module}_'+process.platform+'.node`)
|
||||
fs.writeFileSync(filePath, content)
|
||||
}
|
||||
}, 10);
|
||||
})
|
||||
}else if(hasBinaries && ["exe", "dll"].map(e => {
|
||||
return filePath.endsWith("."+e)
|
||||
}).includes(true)){ // binaries
|
||||
extractFile()
|
||||
}else if(hasNativeDependencies && ["so.4", "dll", "dylib", "thw"].map(e => {
|
||||
return filePath.endsWith("."+e)
|
||||
}).includes(true)){
|
||||
extractFile()
|
||||
}else{
|
||||
zipFile.readEntry();
|
||||
}
|
||||
async function extractFile(onEnd){
|
||||
const dir = dirname(filePath)
|
||||
if(!fs.existsSync(dir))await fs.promises.mkdir(dir, {recursive: true})
|
||||
zipFile.openReadStream(entry, function(err, readStream) {
|
||||
if(err)throw err;
|
||||
readStream.on("end", function() {
|
||||
if(onEnd)onEnd()
|
||||
zipFile.readEntry();
|
||||
}).on("error", (err) => {
|
||||
zipFile.close()
|
||||
console.error(err)
|
||||
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}`)
|
||||
resolve()
|
||||
zipFile.readEntry();
|
||||
manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`)
|
||||
})
|
||||
try{
|
||||
readStream.pipe(fs.createWriteStream(filePath));
|
||||
}catch(err){
|
||||
zipFile.close()
|
||||
console.error(err)
|
||||
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}`)
|
||||
resolve()
|
||||
zipFile.readEntry();
|
||||
manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}).on("end", () => resolve())
|
||||
.on("error", (err) => {
|
||||
zipFile.close()
|
||||
console.error(err)
|
||||
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}`)
|
||||
manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`)
|
||||
})
|
||||
})
|
||||
if(hasNode){
|
||||
console.log(`[\x1b[32mINFO\x1b[0m] .node files are now in ${module}.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
if(manuallyDownloads.length > 0){
|
||||
console.error(`[\x1b[31mERROR\x1b[0m] Couldn't download some modules. Manual links:`)
|
||||
manuallyDownloads.forEach(console.log)
|
||||
}
|
||||
const fetch = require("node-fetch")
|
||||
const yauzl = require("yauzl")
|
||||
const tmp = require("tmp")
|
||||
const fs = require("fs")
|
||||
const util = require('util')
|
||||
const { join, dirname } = require("path")
|
||||
const streamPipeline = util.promisify(require('stream').pipeline)
|
||||
|
||||
const API_URL = "https://discord.com/api"
|
||||
const branch = "stable"
|
||||
const platforms = ["win", "linux", "osx"]
|
||||
const platformsNormalized = {
|
||||
win: "win32",
|
||||
linux: "linux",
|
||||
osx: "darwin"
|
||||
}
|
||||
|
||||
const manuallyDownloads = []
|
||||
const patchedJS = []
|
||||
|
||||
;(async function(){
|
||||
for(let platform of platforms){
|
||||
console.log(`[\x1b[32mINFO\x1b[0m] Downloading modules for ${platform} (${branch})`)
|
||||
const version = await fetch(`${API_URL}/updates/${branch}?platform=${platform}`)
|
||||
.then(res => res.json()).then(res => res.name)
|
||||
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}`)
|
||||
.then(res => res.json())
|
||||
console.log(`[\x1b[32mINFO\x1b[0m] Downloading ${Object.keys(modules).length} modules.`)
|
||||
for(const module in modules){
|
||||
if(["discord_desktop_core"].includes(module))continue
|
||||
const moduleVersion = modules[module]
|
||||
console.log(`[\x1b[32mINFO\x1b[0m] Downloading ${module} v${moduleVersion}.`)
|
||||
const file = tmp.fileSync({
|
||||
prefix: module+"-",
|
||||
postfix: ".zip"
|
||||
})
|
||||
const res = await fetch(`${API_URL}/modules/${branch}/${module}/${moduleVersion}?platform=${platform}&host_version=${version}`, {
|
||||
redirect: "follow"
|
||||
})
|
||||
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))
|
||||
/**
|
||||
* @type {yauzl.ZipFile}
|
||||
*/
|
||||
const zipFile = await new Promise((resolve, reject) => {
|
||||
yauzl.open(file.name, {lazyEntries: true, autoClose: true}, (err, zip) => {
|
||||
if(err)return reject(err)
|
||||
resolve(zip)
|
||||
})
|
||||
})
|
||||
const modulePath = join(__dirname, "modules", module)
|
||||
const exists = fs.existsSync(modulePath)
|
||||
let hasNode = false
|
||||
let hasBinaries = ["discord_hook", "discord_modules"].includes(module)
|
||||
let hasNativeDependencies = ["discord_voice", "discord_krisp"].includes(module)
|
||||
if(!exists){
|
||||
console.warn(`[\x1b[33mWARN\x1b[0m] Downloading whole module because it doesn't exists in your files.`)
|
||||
fs.mkdirSync(modulePath)
|
||||
}
|
||||
await new Promise((resolve) => {
|
||||
zipFile.readEntry()
|
||||
zipFile.on("entry", function(entry) {
|
||||
const fileName = entry.fileName.toString("utf8")
|
||||
if(fileName.endsWith("/")){
|
||||
const folderPath = join(modulePath, fileName)
|
||||
if(!fs.existsSync(folderPath))fs.mkdirSync(folderPath, {recursive: true})
|
||||
zipFile.readEntry();
|
||||
}else{
|
||||
if(!exists){
|
||||
const filePath = join(modulePath, fileName)
|
||||
zipFile.openReadStream(entry, function(err, readStream) {
|
||||
if (err) throw err;
|
||||
readStream.on("end", function() {
|
||||
let content = fs.readFileSync(filePath, "utf-8")
|
||||
if(content.includes(module+".node")){
|
||||
content = content.replace(`${module}.node`, `${module}_'+process.platform+'.node`)
|
||||
fs.writeFileSync(filePath, content)
|
||||
}
|
||||
zipFile.readEntry();
|
||||
});
|
||||
readStream.pipe(fs.createWriteStream(filePath));
|
||||
});
|
||||
}else{
|
||||
let filePath = join(modulePath, fileName)
|
||||
if(filePath.endsWith(".asar"))return zipFile.readEntry();
|
||||
if(fileName.endsWith(".node")){
|
||||
hasNode = true
|
||||
filePath = filePath.replace(".node", "_"+platformsNormalized[platform]+".node")
|
||||
extractFile()
|
||||
}else if(join(filePath, "..") === modulePath && filePath.endsWith(".js")){
|
||||
if(patchedJS.includes(filePath))return zipFile.readEntry();
|
||||
patchedJS.push(filePath)
|
||||
extractFile(() => {
|
||||
setTimeout(() => {
|
||||
let content = fs.readFileSync(filePath, "utf-8")
|
||||
if(content.includes(module+".node")){
|
||||
content = content.replace(`${module}.node`, `${module}_'+process.platform+'.node`)
|
||||
fs.writeFileSync(filePath, content)
|
||||
}
|
||||
}, 10);
|
||||
})
|
||||
}else if(hasBinaries && ["exe", "dll"].map(e => {
|
||||
return filePath.endsWith("."+e)
|
||||
}).includes(true)){ // binaries
|
||||
extractFile()
|
||||
}else if(hasNativeDependencies && ["so.4", "dll", "dylib", "thw"].map(e => {
|
||||
return filePath.endsWith("."+e)
|
||||
}).includes(true)){
|
||||
extractFile()
|
||||
}else{
|
||||
zipFile.readEntry();
|
||||
}
|
||||
async function extractFile(onEnd){
|
||||
const dir = dirname(filePath)
|
||||
if(!fs.existsSync(dir))await fs.promises.mkdir(dir, {recursive: true})
|
||||
zipFile.openReadStream(entry, function(err, readStream) {
|
||||
if(err)throw err;
|
||||
readStream.on("end", function() {
|
||||
if(onEnd)onEnd()
|
||||
zipFile.readEntry();
|
||||
}).on("error", (err) => {
|
||||
zipFile.close()
|
||||
console.error(err)
|
||||
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}`)
|
||||
resolve()
|
||||
zipFile.readEntry();
|
||||
manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`)
|
||||
})
|
||||
try{
|
||||
readStream.pipe(fs.createWriteStream(filePath));
|
||||
}catch(err){
|
||||
zipFile.close()
|
||||
console.error(err)
|
||||
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}`)
|
||||
resolve()
|
||||
zipFile.readEntry();
|
||||
manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}).on("end", () => resolve())
|
||||
.on("error", (err) => {
|
||||
zipFile.close()
|
||||
console.error(err)
|
||||
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}`)
|
||||
manuallyDownloads.push(`${module}/${moduleVersion}?platform=${platform}&host_version=${version}`)
|
||||
})
|
||||
})
|
||||
if(hasNode){
|
||||
console.log(`[\x1b[32mINFO\x1b[0m] .node files are now in ${module}.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
if(manuallyDownloads.length > 0){
|
||||
console.error(`[\x1b[31mERROR\x1b[0m] Couldn't download some modules. Manual links:`)
|
||||
manuallyDownloads.forEach(console.log)
|
||||
}
|
||||
})()
|
|
@ -1,54 +1,54 @@
|
|||
const child_process = require("child_process")
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
|
||||
const MODULES_DIRNAME = path.join(__dirname, "modules")
|
||||
|
||||
fs.readdirSync(MODULES_DIRNAME, {withFileTypes: true})
|
||||
.forEach(e => {
|
||||
if(!e.isDirectory())return
|
||||
const MODULE_DIRNAME = path.join(MODULES_DIRNAME, e.name)
|
||||
if(!fs.existsSync(path.join(MODULE_DIRNAME, "package.json")))return
|
||||
if(e.name === "discord_spellcheck")return
|
||||
|
||||
console.log(`Installing modules in ${e.name}.`)
|
||||
|
||||
child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], {
|
||||
cwd: MODULE_DIRNAME,
|
||||
env: process.env,
|
||||
stdio: "inherit"
|
||||
}).on("error", (err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const MODULE_DIRNAME = path.join(__dirname, "modules", "discord_desktop_core", "core")
|
||||
const BETTERDISCORD_DIRNAME = path.join(__dirname, "BetterDiscordApp")
|
||||
const DISCORDJS_DIRNAME = path.join(__dirname, "DiscordJS")
|
||||
|
||||
child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], {
|
||||
cwd: MODULE_DIRNAME,
|
||||
env: process.env,
|
||||
stdio: "inherit"
|
||||
}).on("error", (err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], {
|
||||
cwd: BETTERDISCORD_DIRNAME,
|
||||
env: process.env,
|
||||
stdio: "inherit"
|
||||
}).on("error", (err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
|
||||
child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], {
|
||||
cwd: DISCORDJS_DIRNAME,
|
||||
env: process.env,
|
||||
stdio: "inherit"
|
||||
const child_process = require("child_process")
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
|
||||
const MODULES_DIRNAME = path.join(__dirname, "modules")
|
||||
|
||||
fs.readdirSync(MODULES_DIRNAME, {withFileTypes: true})
|
||||
.forEach(e => {
|
||||
if(!e.isDirectory())return
|
||||
const MODULE_DIRNAME = path.join(MODULES_DIRNAME, e.name)
|
||||
if(!fs.existsSync(path.join(MODULE_DIRNAME, "package.json")))return
|
||||
if(e.name === "discord_spellcheck")return
|
||||
|
||||
console.log(`Installing modules in ${e.name}.`)
|
||||
|
||||
child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], {
|
||||
cwd: MODULE_DIRNAME,
|
||||
env: process.env,
|
||||
stdio: "inherit"
|
||||
}).on("error", (err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const MODULE_DIRNAME = path.join(__dirname, "modules", "discord_desktop_core", "core")
|
||||
const BETTERDISCORD_DIRNAME = path.join(__dirname, "BetterDiscordApp")
|
||||
const DISCORDJS_DIRNAME = path.join(__dirname, "DiscordJS")
|
||||
|
||||
child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], {
|
||||
cwd: MODULE_DIRNAME,
|
||||
env: process.env,
|
||||
stdio: "inherit"
|
||||
}).on("error", (err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], {
|
||||
cwd: BETTERDISCORD_DIRNAME,
|
||||
env: process.env,
|
||||
stdio: "inherit"
|
||||
}).on("error", (err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
|
||||
child_process.spawn((process.platform === "win32" ? "npm.cmd" : "npm"), ["i"], {
|
||||
cwd: DISCORDJS_DIRNAME,
|
||||
env: process.env,
|
||||
stdio: "inherit"
|
||||
})
|
|
@ -1,36 +1,36 @@
|
|||
const EventEmitter = require('events');
|
||||
const {CloudSync: CloudSyncNative} = require('./discord_cloudsync_'+process.platform+'.node');
|
||||
|
||||
function makeCallback(resolve, reject) {
|
||||
return (err, result) => {
|
||||
if (err != null && err !== '') {
|
||||
reject(new Error(JSON.parse(err)));
|
||||
} else {
|
||||
resolve(result != null && result !== '' ? JSON.parse(result) : null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class CloudSync extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._cloudSync = new CloudSyncNative(state => this.emit('state', JSON.parse(state)));
|
||||
}
|
||||
|
||||
sync(id, config) {
|
||||
return new Promise((resolve, reject) =>
|
||||
this._cloudSync.command(JSON.stringify({type: 'SYNC', id, config}), makeCallback(resolve, reject))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function cloudSyncConstructor() {
|
||||
const instance = new CloudSync();
|
||||
return {
|
||||
on: instance.on.bind(instance),
|
||||
sync: instance.sync.bind(instance),
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = cloudSyncConstructor;
|
||||
const EventEmitter = require('events');
|
||||
const {CloudSync: CloudSyncNative} = require('./discord_cloudsync_'+process.platform+'.node');
|
||||
|
||||
function makeCallback(resolve, reject) {
|
||||
return (err, result) => {
|
||||
if (err != null && err !== '') {
|
||||
reject(new Error(JSON.parse(err)));
|
||||
} else {
|
||||
resolve(result != null && result !== '' ? JSON.parse(result) : null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class CloudSync extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._cloudSync = new CloudSyncNative(state => this.emit('state', JSON.parse(state)));
|
||||
}
|
||||
|
||||
sync(id, config) {
|
||||
return new Promise((resolve, reject) =>
|
||||
this._cloudSync.command(JSON.stringify({type: 'SYNC', id, config}), makeCallback(resolve, reject))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function cloudSyncConstructor() {
|
||||
const instance = new CloudSync();
|
||||
return {
|
||||
on: instance.on.bind(instance),
|
||||
sync: instance.sync.bind(instance),
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = cloudSyncConstructor;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"files": [
|
||||
"discord_cloudsync.node",
|
||||
"index.js",
|
||||
"manifest.json"
|
||||
]
|
||||
{
|
||||
"files": [
|
||||
"discord_cloudsync.node",
|
||||
"index.js",
|
||||
"manifest.json"
|
||||
]
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
const electron = require("electron")
|
||||
const { useShim } = require("./patchNotifications");
|
||||
const appSettings = electron.remote.getGlobal("appSettings")
|
||||
|
||||
module.exports = {
|
||||
NotificationsUseShim: (value) => {
|
||||
if(![true, false].includes(value))return
|
||||
appSettings.set("DEFAULT_NOTIFICATIONS", !value)
|
||||
appSettings.save()
|
||||
useShim(value)
|
||||
}
|
||||
const electron = require("electron")
|
||||
const { useShim } = require("./patchNotifications");
|
||||
const appSettings = electron.remote.getGlobal("appSettings")
|
||||
|
||||
module.exports = {
|
||||
NotificationsUseShim: (value) => {
|
||||
if(![true, false].includes(value))return
|
||||
appSettings.set("DEFAULT_NOTIFICATIONS", !value)
|
||||
appSettings.save()
|
||||
useShim(value)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,253 +1,253 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018, Nick Gavrilov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* 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
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* From https://github.com/ilearnio/module-alias
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
var BuiltinModule = require('module')
|
||||
|
||||
// Guard against poorly mocked module constructors
|
||||
var Module = module.constructor.length > 1
|
||||
? module.constructor
|
||||
: BuiltinModule
|
||||
|
||||
var nodePath = require('path')
|
||||
|
||||
var modulePaths = []
|
||||
var moduleAliases = {}
|
||||
var moduleAliasNames = []
|
||||
|
||||
var oldNodeModulePaths = Module._nodeModulePaths
|
||||
Module._nodeModulePaths = function (from) {
|
||||
var paths = oldNodeModulePaths.call(this, from)
|
||||
|
||||
// Only include the module path for top-level modules
|
||||
// that were not installed:
|
||||
if (from.indexOf('node_modules') === -1) {
|
||||
paths = modulePaths.concat(paths)
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
var oldResolveFilename = Module._resolveFilename
|
||||
Module._resolveFilename = function (request, parentModule, isMain, options) {
|
||||
for (var i = moduleAliasNames.length; i-- > 0;) {
|
||||
var alias = moduleAliasNames[i]
|
||||
if (isPathMatchesAlias(request, alias)) {
|
||||
var aliasTarget = moduleAliases[alias]
|
||||
// Custom function handler
|
||||
if (typeof moduleAliases[alias] === 'function') {
|
||||
var fromPath = parentModule.filename
|
||||
aliasTarget = moduleAliases[alias](fromPath, request, alias)
|
||||
if (!aliasTarget || typeof aliasTarget !== 'string') {
|
||||
throw new Error('[module-alias] Expecting custom handler function to return path.')
|
||||
}
|
||||
}
|
||||
request = nodePath.join(aliasTarget, request.substr(alias.length))
|
||||
// Only use the first match
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return oldResolveFilename.call(this, request, parentModule, isMain, options)
|
||||
}
|
||||
|
||||
function isPathMatchesAlias (path, alias) {
|
||||
// Matching /^alias(\/|$)/
|
||||
if (path.indexOf(alias) === 0) {
|
||||
if (path.length === alias.length) return true
|
||||
if (path[alias.length] === '/') return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function addPathHelper (path, targetArray) {
|
||||
path = nodePath.normalize(path)
|
||||
if (targetArray && targetArray.indexOf(path) === -1) {
|
||||
targetArray.unshift(path)
|
||||
}
|
||||
}
|
||||
|
||||
function removePathHelper (path, targetArray) {
|
||||
if (targetArray) {
|
||||
var index = targetArray.indexOf(path)
|
||||
if (index !== -1) {
|
||||
targetArray.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addPath (path) {
|
||||
var parent
|
||||
path = nodePath.normalize(path)
|
||||
|
||||
if (modulePaths.indexOf(path) === -1) {
|
||||
modulePaths.push(path)
|
||||
// Enable the search path for the current top-level module
|
||||
var mainModule = getMainModule()
|
||||
if (mainModule) {
|
||||
addPathHelper(path, mainModule.paths)
|
||||
}
|
||||
parent = module.parent
|
||||
|
||||
// Also modify the paths of the module that was used to load the
|
||||
// app-module-paths module and all of it's parents
|
||||
while (parent && parent !== mainModule) {
|
||||
addPathHelper(path, parent.paths)
|
||||
parent = parent.parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addAliases (aliases) {
|
||||
for (var alias in aliases) {
|
||||
addAlias(alias, aliases[alias])
|
||||
}
|
||||
}
|
||||
|
||||
function addAlias (alias, target) {
|
||||
moduleAliases[alias] = target
|
||||
// Cost of sorting is lower here than during resolution
|
||||
moduleAliasNames = Object.keys(moduleAliases)
|
||||
moduleAliasNames.sort()
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset any changes maded (resets all registered aliases
|
||||
* and custom module directories)
|
||||
* The function is undocumented and for testing purposes only
|
||||
*/
|
||||
function reset () {
|
||||
var mainModule = getMainModule()
|
||||
|
||||
// Reset all changes in paths caused by addPath function
|
||||
modulePaths.forEach(function (path) {
|
||||
if (mainModule) {
|
||||
removePathHelper(path, mainModule.paths)
|
||||
}
|
||||
|
||||
// Delete from require.cache if the module has been required before.
|
||||
// This is required for node >= 11
|
||||
Object.getOwnPropertyNames(require.cache).forEach(function (name) {
|
||||
if (name.indexOf(path) !== -1) {
|
||||
delete require.cache[name]
|
||||
}
|
||||
})
|
||||
|
||||
var parent = module.parent
|
||||
while (parent && parent !== mainModule) {
|
||||
removePathHelper(path, parent.paths)
|
||||
parent = parent.parent
|
||||
}
|
||||
})
|
||||
|
||||
modulePaths = []
|
||||
moduleAliases = {}
|
||||
moduleAliasNames = []
|
||||
}
|
||||
|
||||
/**
|
||||
* Import aliases from package.json
|
||||
* @param {object} options
|
||||
*/
|
||||
function init (options) {
|
||||
if (typeof options === 'string') {
|
||||
options = { base: options }
|
||||
}
|
||||
|
||||
options = options || {}
|
||||
|
||||
var candidatePackagePaths
|
||||
if (options.base) {
|
||||
candidatePackagePaths = [nodePath.resolve(options.base.replace(/\/package\.json$/, ''))]
|
||||
} else {
|
||||
// There is probably 99% chance that the project root directory in located
|
||||
// above the node_modules directory,
|
||||
// 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`)
|
||||
candidatePackagePaths = [nodePath.join(__dirname, '../..'), process.cwd()]
|
||||
}
|
||||
|
||||
var npmPackage
|
||||
var base
|
||||
for (var i in candidatePackagePaths) {
|
||||
try {
|
||||
base = candidatePackagePaths[i]
|
||||
|
||||
npmPackage = require(nodePath.join(base, 'package.json'))
|
||||
break
|
||||
} catch (e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof npmPackage !== 'object') {
|
||||
var pathString = candidatePackagePaths.join(',\n')
|
||||
throw new Error('Unable to find package.json in any of:\n[' + pathString + ']')
|
||||
}
|
||||
|
||||
//
|
||||
// Import aliases
|
||||
//
|
||||
|
||||
var aliases = npmPackage._moduleAliases || {}
|
||||
|
||||
for (var alias in aliases) {
|
||||
if (aliases[alias][0] !== '/') {
|
||||
aliases[alias] = nodePath.join(base, aliases[alias])
|
||||
}
|
||||
}
|
||||
|
||||
addAliases(aliases)
|
||||
|
||||
//
|
||||
// Register custom module directories (like node_modules)
|
||||
//
|
||||
|
||||
if (npmPackage._moduleDirectories instanceof Array) {
|
||||
npmPackage._moduleDirectories.forEach(function (dir) {
|
||||
if (dir === 'node_modules') return
|
||||
|
||||
var modulePath = nodePath.join(base, dir)
|
||||
addPath(modulePath)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function getMainModule () {
|
||||
return require.main._simulateRepl ? undefined : require.main
|
||||
}
|
||||
|
||||
module.exports = init
|
||||
module.exports.addPath = addPath
|
||||
module.exports.addAlias = addAlias
|
||||
module.exports.addAliases = addAliases
|
||||
module.exports.isPathMatchesAlias = isPathMatchesAlias
|
||||
module.exports.reset = reset
|
||||
module.exports.setMain = function(main){
|
||||
require.main = main
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018, Nick Gavrilov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* 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
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* From https://github.com/ilearnio/module-alias
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
var BuiltinModule = require('module')
|
||||
|
||||
// Guard against poorly mocked module constructors
|
||||
var Module = module.constructor.length > 1
|
||||
? module.constructor
|
||||
: BuiltinModule
|
||||
|
||||
var nodePath = require('path')
|
||||
|
||||
var modulePaths = []
|
||||
var moduleAliases = {}
|
||||
var moduleAliasNames = []
|
||||
|
||||
var oldNodeModulePaths = Module._nodeModulePaths
|
||||
Module._nodeModulePaths = function (from) {
|
||||
var paths = oldNodeModulePaths.call(this, from)
|
||||
|
||||
// Only include the module path for top-level modules
|
||||
// that were not installed:
|
||||
if (from.indexOf('node_modules') === -1) {
|
||||
paths = modulePaths.concat(paths)
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
var oldResolveFilename = Module._resolveFilename
|
||||
Module._resolveFilename = function (request, parentModule, isMain, options) {
|
||||
for (var i = moduleAliasNames.length; i-- > 0;) {
|
||||
var alias = moduleAliasNames[i]
|
||||
if (isPathMatchesAlias(request, alias)) {
|
||||
var aliasTarget = moduleAliases[alias]
|
||||
// Custom function handler
|
||||
if (typeof moduleAliases[alias] === 'function') {
|
||||
var fromPath = parentModule.filename
|
||||
aliasTarget = moduleAliases[alias](fromPath, request, alias)
|
||||
if (!aliasTarget || typeof aliasTarget !== 'string') {
|
||||
throw new Error('[module-alias] Expecting custom handler function to return path.')
|
||||
}
|
||||
}
|
||||
request = nodePath.join(aliasTarget, request.substr(alias.length))
|
||||
// Only use the first match
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return oldResolveFilename.call(this, request, parentModule, isMain, options)
|
||||
}
|
||||
|
||||
function isPathMatchesAlias (path, alias) {
|
||||
// Matching /^alias(\/|$)/
|
||||
if (path.indexOf(alias) === 0) {
|
||||
if (path.length === alias.length) return true
|
||||
if (path[alias.length] === '/') return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function addPathHelper (path, targetArray) {
|
||||
path = nodePath.normalize(path)
|
||||
if (targetArray && targetArray.indexOf(path) === -1) {
|
||||
targetArray.unshift(path)
|
||||
}
|
||||
}
|
||||
|
||||
function removePathHelper (path, targetArray) {
|
||||
if (targetArray) {
|
||||
var index = targetArray.indexOf(path)
|
||||
if (index !== -1) {
|
||||
targetArray.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addPath (path) {
|
||||
var parent
|
||||
path = nodePath.normalize(path)
|
||||
|
||||
if (modulePaths.indexOf(path) === -1) {
|
||||
modulePaths.push(path)
|
||||
// Enable the search path for the current top-level module
|
||||
var mainModule = getMainModule()
|
||||
if (mainModule) {
|
||||
addPathHelper(path, mainModule.paths)
|
||||
}
|
||||
parent = module.parent
|
||||
|
||||
// Also modify the paths of the module that was used to load the
|
||||
// app-module-paths module and all of it's parents
|
||||
while (parent && parent !== mainModule) {
|
||||
addPathHelper(path, parent.paths)
|
||||
parent = parent.parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addAliases (aliases) {
|
||||
for (var alias in aliases) {
|
||||
addAlias(alias, aliases[alias])
|
||||
}
|
||||
}
|
||||
|
||||
function addAlias (alias, target) {
|
||||
moduleAliases[alias] = target
|
||||
// Cost of sorting is lower here than during resolution
|
||||
moduleAliasNames = Object.keys(moduleAliases)
|
||||
moduleAliasNames.sort()
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset any changes maded (resets all registered aliases
|
||||
* and custom module directories)
|
||||
* The function is undocumented and for testing purposes only
|
||||
*/
|
||||
function reset () {
|
||||
var mainModule = getMainModule()
|
||||
|
||||
// Reset all changes in paths caused by addPath function
|
||||
modulePaths.forEach(function (path) {
|
||||
if (mainModule) {
|
||||
removePathHelper(path, mainModule.paths)
|
||||
}
|
||||
|
||||
// Delete from require.cache if the module has been required before.
|
||||
// This is required for node >= 11
|
||||
Object.getOwnPropertyNames(require.cache).forEach(function (name) {
|
||||
if (name.indexOf(path) !== -1) {
|
||||
delete require.cache[name]
|
||||
}
|
||||
})
|
||||
|
||||
var parent = module.parent
|
||||
while (parent && parent !== mainModule) {
|
||||
removePathHelper(path, parent.paths)
|
||||
parent = parent.parent
|
||||
}
|
||||
})
|
||||
|
||||
modulePaths = []
|
||||
moduleAliases = {}
|
||||
moduleAliasNames = []
|
||||
}
|
||||
|
||||
/**
|
||||
* Import aliases from package.json
|
||||
* @param {object} options
|
||||
*/
|
||||
function init (options) {
|
||||
if (typeof options === 'string') {
|
||||
options = { base: options }
|
||||
}
|
||||
|
||||
options = options || {}
|
||||
|
||||
var candidatePackagePaths
|
||||
if (options.base) {
|
||||
candidatePackagePaths = [nodePath.resolve(options.base.replace(/\/package\.json$/, ''))]
|
||||
} else {
|
||||
// There is probably 99% chance that the project root directory in located
|
||||
// above the node_modules directory,
|
||||
// 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`)
|
||||
candidatePackagePaths = [nodePath.join(__dirname, '../..'), process.cwd()]
|
||||
}
|
||||
|
||||
var npmPackage
|
||||
var base
|
||||
for (var i in candidatePackagePaths) {
|
||||
try {
|
||||
base = candidatePackagePaths[i]
|
||||
|
||||
npmPackage = require(nodePath.join(base, 'package.json'))
|
||||
break
|
||||
} catch (e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof npmPackage !== 'object') {
|
||||
var pathString = candidatePackagePaths.join(',\n')
|
||||
throw new Error('Unable to find package.json in any of:\n[' + pathString + ']')
|
||||
}
|
||||
|
||||
//
|
||||
// Import aliases
|
||||
//
|
||||
|
||||
var aliases = npmPackage._moduleAliases || {}
|
||||
|
||||
for (var alias in aliases) {
|
||||
if (aliases[alias][0] !== '/') {
|
||||
aliases[alias] = nodePath.join(base, aliases[alias])
|
||||
}
|
||||
}
|
||||
|
||||
addAliases(aliases)
|
||||
|
||||
//
|
||||
// Register custom module directories (like node_modules)
|
||||
//
|
||||
|
||||
if (npmPackage._moduleDirectories instanceof Array) {
|
||||
npmPackage._moduleDirectories.forEach(function (dir) {
|
||||
if (dir === 'node_modules') return
|
||||
|
||||
var modulePath = nodePath.join(base, dir)
|
||||
addPath(modulePath)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function getMainModule () {
|
||||
return require.main._simulateRepl ? undefined : require.main
|
||||
}
|
||||
|
||||
module.exports = init
|
||||
module.exports.addPath = addPath
|
||||
module.exports.addAlias = addAlias
|
||||
module.exports.addAliases = addAliases
|
||||
module.exports.isPathMatchesAlias = isPathMatchesAlias
|
||||
module.exports.reset = reset
|
||||
module.exports.setMain = function(main){
|
||||
require.main = main
|
||||
}
|
|
@ -1,148 +1,148 @@
|
|||
let req
|
||||
setReq()
|
||||
|
||||
|
||||
class DangerousWebpackloader {
|
||||
get modules(){
|
||||
if(req){
|
||||
return Object.values(req.c).filter(e => e && e.exports)
|
||||
}else{
|
||||
setReq()
|
||||
if(req){
|
||||
return Object.values(req.c).filter(e => e && e.exports)
|
||||
}else{
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
get(ids, modules){
|
||||
if(typeof ids === "function"){
|
||||
return (modules || this.modules).map((mdl) => {
|
||||
if(mdl && typeof mdl.exports !== "undefined"){
|
||||
return mdl.exports
|
||||
}else{
|
||||
return null
|
||||
}
|
||||
}).filter(e => e).filter(ids)
|
||||
}else if(Array.isArray(ids)){
|
||||
modules = modules || this.modules
|
||||
return ids.map(id => this.get(id, modules))
|
||||
}else{
|
||||
modules = modules || this.modules
|
||||
let module = modules.filter(e => !!e).find(e => e.i === ids)
|
||||
if(!module)return undefined
|
||||
return module.exports
|
||||
}
|
||||
}
|
||||
get default(){
|
||||
return this
|
||||
}
|
||||
}
|
||||
function filterDangerous(mods){
|
||||
return mods.map(e => {
|
||||
return protect(e)
|
||||
})
|
||||
}
|
||||
function protect(exports){
|
||||
let theModule = exports.exports
|
||||
let mod = theModule.default
|
||||
if(!mod)return exports
|
||||
if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null;
|
||||
if (!mod.getToken && !mod.getEmail && !mod.showToken)return exports
|
||||
|
||||
const proxy = new Proxy(mod, {
|
||||
getOwnPropertyDescriptor: function(obj, prop) {
|
||||
if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined;
|
||||
return Object.getOwnPropertyDescriptor(obj, prop);
|
||||
},
|
||||
get: function(obj, func) {
|
||||
if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa";
|
||||
if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com";
|
||||
if (func == "showToken" && obj.showToken) return () => true;
|
||||
if (func == "__proto__" && obj.__proto__) return proxy;
|
||||
|
||||
return obj[func];
|
||||
}
|
||||
});
|
||||
|
||||
return Object.assign({}, exports, {exports: Object.assign({}, theModule, {default: proxy})})
|
||||
}
|
||||
class Webpackloader {
|
||||
get modules(){
|
||||
if(req){
|
||||
return filterDangerous(Object.values(req.c).filter(e => e && e.exports))
|
||||
}else{
|
||||
setReq()
|
||||
if(req){
|
||||
return filterDangerous(Object.values(req.c).filter(e => e && e.exports))
|
||||
}else{
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
get(ids, modules){
|
||||
if(typeof ids === "function"){
|
||||
return (modules || this.modules).map((mdl) => {
|
||||
if(mdl && typeof mdl.exports !== "undefined"){
|
||||
return mdl.exports
|
||||
}else{
|
||||
return null
|
||||
}
|
||||
}).filter(e => e).filter(ids)
|
||||
}else if(Array.isArray(ids)){
|
||||
modules = modules || this.modules
|
||||
return ids.map(id => this.get(id, modules))
|
||||
}else{
|
||||
modules = modules || this.modules
|
||||
let module = modules.filter(e => !!e).find(e => e.i === ids)
|
||||
if(!module)return undefined
|
||||
return module.exports
|
||||
}
|
||||
}
|
||||
get default(){
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new DangerousWebpackloader()
|
||||
|
||||
global.BDModules = new Webpackloader()
|
||||
|
||||
function setReq(){
|
||||
try{
|
||||
req = webpackJsonp.push([[], {__extra_id__: (mdl, exports, req) => mdl.exports = req}, [["__extra_id__"]]]);
|
||||
if(req){
|
||||
delete req.m.__extra_id__;
|
||||
delete req.c.__extra_id__;/*
|
||||
Object.defineProperty(req.c, "0", {
|
||||
get: () => ({
|
||||
i: 0,
|
||||
l: true,
|
||||
exports: require("react")
|
||||
}),
|
||||
configurable: false,
|
||||
enumerable: true
|
||||
})
|
||||
Object.defineProperty(req.c, "856", {
|
||||
get: () => ({
|
||||
i: 856,
|
||||
l: true,
|
||||
exports: require("react")
|
||||
}),
|
||||
configurable: false,
|
||||
enumerable: true
|
||||
})
|
||||
Object.defineProperty(req.c, "71", {
|
||||
get: () => ({
|
||||
i: 71,
|
||||
l: true,
|
||||
exports: require("react-dom")
|
||||
}),
|
||||
configurable: false,
|
||||
enumerable: true
|
||||
})*/
|
||||
}
|
||||
}catch(e){
|
||||
req = undefined
|
||||
}
|
||||
let req
|
||||
setReq()
|
||||
|
||||
|
||||
class DangerousWebpackloader {
|
||||
get modules(){
|
||||
if(req){
|
||||
return Object.values(req.c).filter(e => e && e.exports)
|
||||
}else{
|
||||
setReq()
|
||||
if(req){
|
||||
return Object.values(req.c).filter(e => e && e.exports)
|
||||
}else{
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
get(ids, modules){
|
||||
if(typeof ids === "function"){
|
||||
return (modules || this.modules).map((mdl) => {
|
||||
if(mdl && typeof mdl.exports !== "undefined"){
|
||||
return mdl.exports
|
||||
}else{
|
||||
return null
|
||||
}
|
||||
}).filter(e => e).filter(ids)
|
||||
}else if(Array.isArray(ids)){
|
||||
modules = modules || this.modules
|
||||
return ids.map(id => this.get(id, modules))
|
||||
}else{
|
||||
modules = modules || this.modules
|
||||
let module = modules.filter(e => !!e).find(e => e.i === ids)
|
||||
if(!module)return undefined
|
||||
return module.exports
|
||||
}
|
||||
}
|
||||
get default(){
|
||||
return this
|
||||
}
|
||||
}
|
||||
function filterDangerous(mods){
|
||||
return mods.map(e => {
|
||||
return protect(e)
|
||||
})
|
||||
}
|
||||
function protect(exports){
|
||||
let theModule = exports.exports
|
||||
let mod = theModule.default
|
||||
if(!mod)return exports
|
||||
if (mod.remove && mod.set && mod.clear && mod.get && !mod.sort) return null;
|
||||
if (!mod.getToken && !mod.getEmail && !mod.showToken)return exports
|
||||
|
||||
const proxy = new Proxy(mod, {
|
||||
getOwnPropertyDescriptor: function(obj, prop) {
|
||||
if (prop === "getToken" || prop === "getEmail" || prop === "showToken") return undefined;
|
||||
return Object.getOwnPropertyDescriptor(obj, prop);
|
||||
},
|
||||
get: function(obj, func) {
|
||||
if (func == "getToken" && obj.getToken) return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa";
|
||||
if (func == "getEmail" && obj.getEmail) return () => "puppet11112@gmail.com";
|
||||
if (func == "showToken" && obj.showToken) return () => true;
|
||||
if (func == "__proto__" && obj.__proto__) return proxy;
|
||||
|
||||
return obj[func];
|
||||
}
|
||||
});
|
||||
|
||||
return Object.assign({}, exports, {exports: Object.assign({}, theModule, {default: proxy})})
|
||||
}
|
||||
class Webpackloader {
|
||||
get modules(){
|
||||
if(req){
|
||||
return filterDangerous(Object.values(req.c).filter(e => e && e.exports))
|
||||
}else{
|
||||
setReq()
|
||||
if(req){
|
||||
return filterDangerous(Object.values(req.c).filter(e => e && e.exports))
|
||||
}else{
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
get(ids, modules){
|
||||
if(typeof ids === "function"){
|
||||
return (modules || this.modules).map((mdl) => {
|
||||
if(mdl && typeof mdl.exports !== "undefined"){
|
||||
return mdl.exports
|
||||
}else{
|
||||
return null
|
||||
}
|
||||
}).filter(e => e).filter(ids)
|
||||
}else if(Array.isArray(ids)){
|
||||
modules = modules || this.modules
|
||||
return ids.map(id => this.get(id, modules))
|
||||
}else{
|
||||
modules = modules || this.modules
|
||||
let module = modules.filter(e => !!e).find(e => e.i === ids)
|
||||
if(!module)return undefined
|
||||
return module.exports
|
||||
}
|
||||
}
|
||||
get default(){
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new DangerousWebpackloader()
|
||||
|
||||
global.BDModules = new Webpackloader()
|
||||
|
||||
function setReq(){
|
||||
try{
|
||||
req = webpackJsonp.push([[], {__extra_id__: (mdl, exports, req) => mdl.exports = req}, [["__extra_id__"]]]);
|
||||
if(req){
|
||||
delete req.m.__extra_id__;
|
||||
delete req.c.__extra_id__;/*
|
||||
Object.defineProperty(req.c, "0", {
|
||||
get: () => ({
|
||||
i: 0,
|
||||
l: true,
|
||||
exports: require("react")
|
||||
}),
|
||||
configurable: false,
|
||||
enumerable: true
|
||||
})
|
||||
Object.defineProperty(req.c, "856", {
|
||||
get: () => ({
|
||||
i: 856,
|
||||
l: true,
|
||||
exports: require("react")
|
||||
}),
|
||||
configurable: false,
|
||||
enumerable: true
|
||||
})
|
||||
Object.defineProperty(req.c, "71", {
|
||||
get: () => ({
|
||||
i: 71,
|
||||
l: true,
|
||||
exports: require("react-dom")
|
||||
}),
|
||||
configurable: false,
|
||||
enumerable: true
|
||||
})*/
|
||||
}
|
||||
}catch(e){
|
||||
req = undefined
|
||||
}
|
||||
}
|
|
@ -1,102 +1,102 @@
|
|||
const ipcRenderer = require("../discord_native/renderer/ipc")
|
||||
|
||||
|
||||
if(process.platform === "win32"){
|
||||
let useShim = false
|
||||
|
||||
const originalNotification = window.Notification
|
||||
|
||||
ipcRenderer.send("NOTIFICATIONS_CLEAR")
|
||||
|
||||
const notifications = {}
|
||||
ipcRenderer.on("NOTIFICATION_CLICK", (e, id) => {
|
||||
var notification = notifications[id];
|
||||
if(notification){
|
||||
notification.onclick()
|
||||
notification.close()
|
||||
}
|
||||
})
|
||||
class LightcordNotification {
|
||||
constructor(title, data){
|
||||
this.id = LightcordNotification._id++
|
||||
this.onshow = function() {}
|
||||
this.onclick = function() {}
|
||||
this.onclose = function() {}
|
||||
this.title = title
|
||||
this.body = data.body
|
||||
this.icon = data.icon
|
||||
setTimeout(() => {
|
||||
return this.onshow()
|
||||
}, 0)
|
||||
notifications[this.id] = this
|
||||
ipcRenderer.send("NOTIFICATION_SHOW", {
|
||||
id: this.id,
|
||||
title: this.title,
|
||||
body: this.body,
|
||||
icon: this.icon,
|
||||
theme: settingStore ? settingStore.default.theme : "dark"
|
||||
})
|
||||
}
|
||||
|
||||
static _id = 0
|
||||
|
||||
static requestPermission(callback){
|
||||
callback()
|
||||
}
|
||||
|
||||
close(){
|
||||
if(!notifications[this.id])return
|
||||
|
||||
delete notifications[this.id]
|
||||
ipcRenderer.send("NOTIFICATION_CLOSE", this.id)
|
||||
this.onclose()
|
||||
}
|
||||
}
|
||||
|
||||
LightcordNotification.permission = "granted"
|
||||
|
||||
function Notification(){
|
||||
if(useShim)return new LightcordNotification(...arguments)
|
||||
return new originalNotification(...arguments)
|
||||
}
|
||||
Object.defineProperties(Notification, {
|
||||
permission: {
|
||||
get(){
|
||||
if(useShim)return LightcordNotification.permission
|
||||
return originalNotification.permission
|
||||
}
|
||||
},
|
||||
requestPermission: {
|
||||
get(){
|
||||
if(useShim)return LightcordNotification.requestPermission
|
||||
return originalNotification.requestPermission
|
||||
}
|
||||
},
|
||||
_id: {
|
||||
get(){
|
||||
if(useShim)return LightcordNotification._id
|
||||
return originalNotification._id
|
||||
}
|
||||
}
|
||||
})
|
||||
window.Notification = Notification
|
||||
|
||||
|
||||
module.exports = {
|
||||
useShim(use){
|
||||
useShim = !!use
|
||||
}
|
||||
}
|
||||
}else{
|
||||
module.exports = {
|
||||
useShim(){}
|
||||
}
|
||||
}
|
||||
|
||||
let settingStore
|
||||
|
||||
ensureExported((e => e.default && e.default.theme))
|
||||
.then(themeStore => {
|
||||
settingStore = themeStore
|
||||
ipcRenderer.send("UPDATE_THEME", themeStore.default.theme)
|
||||
const ipcRenderer = require("../discord_native/renderer/ipc")
|
||||
|
||||
|
||||
if(process.platform === "win32"){
|
||||
let useShim = false
|
||||
|
||||
const originalNotification = window.Notification
|
||||
|
||||
ipcRenderer.send("NOTIFICATIONS_CLEAR")
|
||||
|
||||
const notifications = {}
|
||||
ipcRenderer.on("NOTIFICATION_CLICK", (e, id) => {
|
||||
var notification = notifications[id];
|
||||
if(notification){
|
||||
notification.onclick()
|
||||
notification.close()
|
||||
}
|
||||
})
|
||||
class LightcordNotification {
|
||||
constructor(title, data){
|
||||
this.id = LightcordNotification._id++
|
||||
this.onshow = function() {}
|
||||
this.onclick = function() {}
|
||||
this.onclose = function() {}
|
||||
this.title = title
|
||||
this.body = data.body
|
||||
this.icon = data.icon
|
||||
setTimeout(() => {
|
||||
return this.onshow()
|
||||
}, 0)
|
||||
notifications[this.id] = this
|
||||
ipcRenderer.send("NOTIFICATION_SHOW", {
|
||||
id: this.id,
|
||||
title: this.title,
|
||||
body: this.body,
|
||||
icon: this.icon,
|
||||
theme: settingStore ? settingStore.default.theme : "dark"
|
||||
})
|
||||
}
|
||||
|
||||
static _id = 0
|
||||
|
||||
static requestPermission(callback){
|
||||
callback()
|
||||
}
|
||||
|
||||
close(){
|
||||
if(!notifications[this.id])return
|
||||
|
||||
delete notifications[this.id]
|
||||
ipcRenderer.send("NOTIFICATION_CLOSE", this.id)
|
||||
this.onclose()
|
||||
}
|
||||
}
|
||||
|
||||
LightcordNotification.permission = "granted"
|
||||
|
||||
function Notification(){
|
||||
if(useShim)return new LightcordNotification(...arguments)
|
||||
return new originalNotification(...arguments)
|
||||
}
|
||||
Object.defineProperties(Notification, {
|
||||
permission: {
|
||||
get(){
|
||||
if(useShim)return LightcordNotification.permission
|
||||
return originalNotification.permission
|
||||
}
|
||||
},
|
||||
requestPermission: {
|
||||
get(){
|
||||
if(useShim)return LightcordNotification.requestPermission
|
||||
return originalNotification.requestPermission
|
||||
}
|
||||
},
|
||||
_id: {
|
||||
get(){
|
||||
if(useShim)return LightcordNotification._id
|
||||
return originalNotification._id
|
||||
}
|
||||
}
|
||||
})
|
||||
window.Notification = Notification
|
||||
|
||||
|
||||
module.exports = {
|
||||
useShim(use){
|
||||
useShim = !!use
|
||||
}
|
||||
}
|
||||
}else{
|
||||
module.exports = {
|
||||
useShim(){}
|
||||
}
|
||||
}
|
||||
|
||||
let settingStore
|
||||
|
||||
ensureExported((e => e.default && e.default.theme))
|
||||
.then(themeStore => {
|
||||
settingStore = themeStore
|
||||
ipcRenderer.send("UPDATE_THEME", themeStore.default.theme)
|
||||
}).catch(console.error)
|
|
@ -1,97 +1,97 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
const loginContainerModule = BDModules.get(e => e.mainLoginContainer)[0];
|
||||
const colors = BDModules.get(e => e.colorHeaderPrimary)[0];
|
||||
const sizes = BDModules.get(e => e.size24)[0];
|
||||
const authBoxModule = BDModules.get(e => e.title && typeof e.title === "string" && e.authBoxPadding)[0];
|
||||
const marginModule = BDModules.get(e => e.marginBottom8)[0];
|
||||
const titleModule = BDModules.get(e => e.h5)[0];
|
||||
const inputModule = BDModules.get(e => e.inputWrapper)[0];
|
||||
const contentModule = BDModules.get(e => e.contents)[0];
|
||||
const verticalSeparatorModule = BDModules.get(e => e.verticalSeparator)[0];
|
||||
const loginModule = BDModules.get(e => e.default && e.default.loginToken)[0].default;
|
||||
|
||||
class TokenLogin extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return [/*#__PURE__*/React.createElement("div", {
|
||||
class: verticalSeparatorModule.verticalSeparator
|
||||
}), /*#__PURE__*/React.createElement("div", {
|
||||
className: loginContainerModule.mainLoginContainer
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: `${colors.colorHeaderPrimary} ${sizes.size24} ${authBoxModule.title} ${marginModule.marginBottom8}`
|
||||
}, "Connect with Token"), /*#__PURE__*/React.createElement("div", {
|
||||
className: `${colors.colorHeaderSecondary} ${sizes.size16}`
|
||||
}, "Input your token below"), /*#__PURE__*/React.createElement("div", {
|
||||
className: `${authBoxModule.block} ${marginModule.marginTop20}`
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: marginModule.marginBottom20
|
||||
}, /*#__PURE__*/React.createElement(TokenInput, {
|
||||
ref: "input"
|
||||
})), /*#__PURE__*/React.createElement("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) {
|
||||
this.refs.input.setState({
|
||||
error: "This field is necessary"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
loginModule.loginToken(this.refs.input.state.value);
|
||||
ev.stopPropagation();
|
||||
}
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: contentModule.contents
|
||||
}, "Login"))))];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exports.default = TokenLogin;
|
||||
|
||||
class TokenInput extends React.Component {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = {
|
||||
value: "",
|
||||
error: null
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return [/*#__PURE__*/React.createElement("h5", {
|
||||
className: `${colors.colorStandard} ${sizes.size14} ${titleModule.h5} ${titleModule.defaultMarginh5}${this.state.error ? " " + titleModule.error : ""}`
|
||||
}, "Token", this.state.error ? /*#__PURE__*/React.createElement("span", {
|
||||
class: titleModule.errorMessage
|
||||
}, /*#__PURE__*/React.createElement("span", {
|
||||
class: titleModule.errorSeparator
|
||||
}, "-"), this.state.error) : null), /*#__PURE__*/React.createElement("div", {
|
||||
className: inputModule.inputWrapper
|
||||
}, /*#__PURE__*/React.createElement("input", {
|
||||
className: `${inputModule.inputDefault}${this.state.error ? " " + inputModule.inputError : ""}`,
|
||||
name: "token",
|
||||
type: "token",
|
||||
placeholder: true,
|
||||
"aria-label": "Token",
|
||||
autoComplete: "off",
|
||||
maxLength: 999,
|
||||
spellCheck: "false",
|
||||
value: this.state.value,
|
||||
onChange: ev => {
|
||||
this.setState({
|
||||
value: ev.target.value
|
||||
});
|
||||
}
|
||||
}))];
|
||||
}
|
||||
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
const loginContainerModule = BDModules.get(e => e.mainLoginContainer)[0];
|
||||
const colors = BDModules.get(e => e.colorHeaderPrimary)[0];
|
||||
const sizes = BDModules.get(e => e.size24)[0];
|
||||
const authBoxModule = BDModules.get(e => e.title && typeof e.title === "string" && e.authBoxPadding)[0];
|
||||
const marginModule = BDModules.get(e => e.marginBottom8)[0];
|
||||
const titleModule = BDModules.get(e => e.h5)[0];
|
||||
const inputModule = BDModules.get(e => e.inputWrapper)[0];
|
||||
const contentModule = BDModules.get(e => e.contents)[0];
|
||||
const verticalSeparatorModule = BDModules.get(e => e.verticalSeparator)[0];
|
||||
const loginModule = BDModules.get(e => e.default && e.default.loginToken)[0].default;
|
||||
|
||||
class TokenLogin extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return [/*#__PURE__*/React.createElement("div", {
|
||||
class: verticalSeparatorModule.verticalSeparator
|
||||
}), /*#__PURE__*/React.createElement("div", {
|
||||
className: loginContainerModule.mainLoginContainer
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: `${colors.colorHeaderPrimary} ${sizes.size24} ${authBoxModule.title} ${marginModule.marginBottom8}`
|
||||
}, "Connect with Token"), /*#__PURE__*/React.createElement("div", {
|
||||
className: `${colors.colorHeaderSecondary} ${sizes.size16}`
|
||||
}, "Input your token below"), /*#__PURE__*/React.createElement("div", {
|
||||
className: `${authBoxModule.block} ${marginModule.marginTop20}`
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: marginModule.marginBottom20
|
||||
}, /*#__PURE__*/React.createElement(TokenInput, {
|
||||
ref: "input"
|
||||
})), /*#__PURE__*/React.createElement("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) {
|
||||
this.refs.input.setState({
|
||||
error: "This field is necessary"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
loginModule.loginToken(this.refs.input.state.value);
|
||||
ev.stopPropagation();
|
||||
}
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: contentModule.contents
|
||||
}, "Login"))))];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exports.default = TokenLogin;
|
||||
|
||||
class TokenInput extends React.Component {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = {
|
||||
value: "",
|
||||
error: null
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return [/*#__PURE__*/React.createElement("h5", {
|
||||
className: `${colors.colorStandard} ${sizes.size14} ${titleModule.h5} ${titleModule.defaultMarginh5}${this.state.error ? " " + titleModule.error : ""}`
|
||||
}, "Token", this.state.error ? /*#__PURE__*/React.createElement("span", {
|
||||
class: titleModule.errorMessage
|
||||
}, /*#__PURE__*/React.createElement("span", {
|
||||
class: titleModule.errorSeparator
|
||||
}, "-"), this.state.error) : null), /*#__PURE__*/React.createElement("div", {
|
||||
className: inputModule.inputWrapper
|
||||
}, /*#__PURE__*/React.createElement("input", {
|
||||
className: `${inputModule.inputDefault}${this.state.error ? " " + inputModule.inputError : ""}`,
|
||||
name: "token",
|
||||
type: "token",
|
||||
placeholder: true,
|
||||
"aria-label": "Token",
|
||||
autoComplete: "off",
|
||||
maxLength: 999,
|
||||
spellCheck: "false",
|
||||
value: this.state.value,
|
||||
onChange: ev => {
|
||||
this.setState({
|
||||
value: ev.target.value
|
||||
});
|
||||
}
|
||||
}))];
|
||||
}
|
||||
|
||||
}
|
|
@ -1,80 +1,80 @@
|
|||
const loginContainerModule = BDModules.get(e => e.mainLoginContainer)[0]
|
||||
const colors = BDModules.get(e => e.colorHeaderPrimary)[0]
|
||||
const sizes = BDModules.get(e => e.size24)[0]
|
||||
const authBoxModule = BDModules.get(e => e.title && typeof e.title === "string" && e.authBoxPadding)[0]
|
||||
const marginModule = BDModules.get(e => e.marginBottom8)[0]
|
||||
const titleModule = BDModules.get(e => e.h5)[0]
|
||||
const inputModule = BDModules.get(e => e.inputWrapper)[0]
|
||||
const contentModule = BDModules.get(e => e.contents)[0]
|
||||
const verticalSeparatorModule = BDModules.get(e => e.verticalSeparator)[0]
|
||||
|
||||
const loginModule = BDModules.get(e => e.default && e.default.loginToken)[0].default
|
||||
|
||||
export default class TokenLogin extends React.Component {
|
||||
constructor(props){
|
||||
super(props)
|
||||
}
|
||||
|
||||
render(){
|
||||
return ([
|
||||
<div class={verticalSeparatorModule.verticalSeparator}></div>,
|
||||
<div className={loginContainerModule.mainLoginContainer}>
|
||||
<div className={`${colors.colorHeaderPrimary} ${sizes.size24} ${authBoxModule.title} ${marginModule.marginBottom8}`}>
|
||||
Connect with Token
|
||||
</div>
|
||||
<div className={`${colors.colorHeaderSecondary} ${sizes.size16}`}>
|
||||
Input your token below
|
||||
</div>
|
||||
<div className={`${authBoxModule.block} ${marginModule.marginTop20}`}>
|
||||
<div className={marginModule.marginBottom20}>
|
||||
<TokenInput ref="input"/>
|
||||
</div>
|
||||
<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){
|
||||
this.refs.input.setState({
|
||||
error: "This field is necessary"
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
loginModule.loginToken(this.refs.input.state.value)
|
||||
ev.stopPropagation()
|
||||
}}>
|
||||
<div className={contentModule.contents}>
|
||||
Login
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
class TokenInput extends React.Component {
|
||||
constructor(){
|
||||
super(...arguments)
|
||||
|
||||
this.state = {
|
||||
value: "",
|
||||
error: null
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
return [
|
||||
<h5 className={`${colors.colorStandard} ${sizes.size14} ${titleModule.h5} ${titleModule.defaultMarginh5}${this.state.error ? " "+titleModule.error : ""}`}>
|
||||
Token
|
||||
{this.state.error ? <span class={titleModule.errorMessage}>
|
||||
<span class={titleModule.errorSeparator}>-</span>{this.state.error}
|
||||
</span> : null}
|
||||
</h5>,
|
||||
<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) => {
|
||||
this.setState({
|
||||
value: ev.target.value
|
||||
})
|
||||
}}/>
|
||||
</div>
|
||||
]
|
||||
}
|
||||
const loginContainerModule = BDModules.get(e => e.mainLoginContainer)[0]
|
||||
const colors = BDModules.get(e => e.colorHeaderPrimary)[0]
|
||||
const sizes = BDModules.get(e => e.size24)[0]
|
||||
const authBoxModule = BDModules.get(e => e.title && typeof e.title === "string" && e.authBoxPadding)[0]
|
||||
const marginModule = BDModules.get(e => e.marginBottom8)[0]
|
||||
const titleModule = BDModules.get(e => e.h5)[0]
|
||||
const inputModule = BDModules.get(e => e.inputWrapper)[0]
|
||||
const contentModule = BDModules.get(e => e.contents)[0]
|
||||
const verticalSeparatorModule = BDModules.get(e => e.verticalSeparator)[0]
|
||||
|
||||
const loginModule = BDModules.get(e => e.default && e.default.loginToken)[0].default
|
||||
|
||||
export default class TokenLogin extends React.Component {
|
||||
constructor(props){
|
||||
super(props)
|
||||
}
|
||||
|
||||
render(){
|
||||
return ([
|
||||
<div class={verticalSeparatorModule.verticalSeparator}></div>,
|
||||
<div className={loginContainerModule.mainLoginContainer}>
|
||||
<div className={`${colors.colorHeaderPrimary} ${sizes.size24} ${authBoxModule.title} ${marginModule.marginBottom8}`}>
|
||||
Connect with Token
|
||||
</div>
|
||||
<div className={`${colors.colorHeaderSecondary} ${sizes.size16}`}>
|
||||
Input your token below
|
||||
</div>
|
||||
<div className={`${authBoxModule.block} ${marginModule.marginTop20}`}>
|
||||
<div className={marginModule.marginBottom20}>
|
||||
<TokenInput ref="input"/>
|
||||
</div>
|
||||
<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){
|
||||
this.refs.input.setState({
|
||||
error: "This field is necessary"
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
loginModule.loginToken(this.refs.input.state.value)
|
||||
ev.stopPropagation()
|
||||
}}>
|
||||
<div className={contentModule.contents}>
|
||||
Login
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
class TokenInput extends React.Component {
|
||||
constructor(){
|
||||
super(...arguments)
|
||||
|
||||
this.state = {
|
||||
value: "",
|
||||
error: null
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
return [
|
||||
<h5 className={`${colors.colorStandard} ${sizes.size14} ${titleModule.h5} ${titleModule.defaultMarginh5}${this.state.error ? " "+titleModule.error : ""}`}>
|
||||
Token
|
||||
{this.state.error ? <span class={titleModule.errorMessage}>
|
||||
<span class={titleModule.errorSeparator}>-</span>{this.state.error}
|
||||
</span> : null}
|
||||
</h5>,
|
||||
<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) => {
|
||||
this.setState({
|
||||
value: ev.target.value
|
||||
})
|
||||
}}/>
|
||||
</div>
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
/**
|
||||
* This file is for injections other than already present modules. mostly ipc thing
|
||||
*/
|
||||
|
||||
const electron = require("electron")
|
||||
|
||||
const UserAgent = electron.session.defaultSession.getUserAgent()
|
||||
electron.ipcMain.on("LIGHTCORD_GET_USER_AGENT", (event) => {
|
||||
event.returnValue = UserAgent
|
||||
/**
|
||||
* This file is for injections other than already present modules. mostly ipc thing
|
||||
*/
|
||||
|
||||
const electron = require("electron")
|
||||
|
||||
const UserAgent = electron.session.defaultSession.getUserAgent()
|
||||
electron.ipcMain.on("LIGHTCORD_GET_USER_AGENT", (event) => {
|
||||
event.returnValue = UserAgent
|
||||
})
|
|
@ -1,68 +1,68 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Lightcord Tabs</title>
|
||||
<script src="index.js"></script>
|
||||
<style>
|
||||
.documentFull {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
.discord-webview {
|
||||
margin: 0px;
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 38px;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.active-webview {
|
||||
display: inherit
|
||||
}
|
||||
body {
|
||||
margin: 0
|
||||
}
|
||||
.surface {
|
||||
-webkit-app-region: drag
|
||||
}
|
||||
.chrome-tab {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="surface">
|
||||
<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-bottom-bar"></div>
|
||||
<!-- Styles to prevent flash after JS initialization -->
|
||||
<style>
|
||||
.chrome-tabs .chrome-tab {
|
||||
width: 258px
|
||||
}
|
||||
|
||||
.chrome-tabs .chrome-tab:nth-child(1) {
|
||||
transform: translate3d(0px, 0, 0)
|
||||
}
|
||||
|
||||
.chrome-tabs .chrome-tab:nth-child(2) {
|
||||
transform: translate3d(239px, 0, 0)
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
<div class="chrome-tabs-optional-shadow-below-bottom-bar"></div>
|
||||
</div>
|
||||
<div class="documentFull"></div>
|
||||
</body>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Lightcord Tabs</title>
|
||||
<script src="index.js"></script>
|
||||
<style>
|
||||
.documentFull {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
.discord-webview {
|
||||
margin: 0px;
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 38px;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.active-webview {
|
||||
display: inherit
|
||||
}
|
||||
body {
|
||||
margin: 0
|
||||
}
|
||||
.surface {
|
||||
-webkit-app-region: drag
|
||||
}
|
||||
.chrome-tab {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="surface">
|
||||
<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-bottom-bar"></div>
|
||||
<!-- Styles to prevent flash after JS initialization -->
|
||||
<style>
|
||||
.chrome-tabs .chrome-tab {
|
||||
width: 258px
|
||||
}
|
||||
|
||||
.chrome-tabs .chrome-tab:nth-child(1) {
|
||||
transform: translate3d(0px, 0, 0)
|
||||
}
|
||||
|
||||
.chrome-tabs .chrome-tab:nth-child(2) {
|
||||
transform: translate3d(239px, 0, 0)
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
<div class="chrome-tabs-optional-shadow-below-bottom-bar"></div>
|
||||
</div>
|
||||
<div class="documentFull"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,182 +1,182 @@
|
|||
const fs = require("fs")
|
||||
const { join } = require("path")
|
||||
const { pathToFileURL } = require("url")
|
||||
const ipc = require("../discord_native/renderer/ipc")
|
||||
|
||||
let webviews = new Map()
|
||||
window.webviews = webviews
|
||||
|
||||
function forwardToCurrentWebview(event){
|
||||
return [event, async (...args) => {
|
||||
let webview = webviews.get(document.querySelector(".chrome-tab[active]"))
|
||||
if(!webview)return
|
||||
await webview.ready
|
||||
webview.send(event, ...args.slice(1))
|
||||
}]
|
||||
}
|
||||
|
||||
/** discord_desktop_core Stable */
|
||||
ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_FOCUS"))
|
||||
ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_BLUR"))
|
||||
ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_OPEN_VOICE_SETTINGS"))
|
||||
ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_TOGGLE_MUTE"))
|
||||
ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_TOGGLE_DEAFEN"))
|
||||
ipc.on(...forwardToCurrentWebview("LAUNCH_APPLICATION"))
|
||||
ipc.on(...forwardToCurrentWebview("SPELLCHECK_RESULT"))
|
||||
ipc.on(...forwardToCurrentWebview("WINDOW_DEVTOOLS_OPENED"))
|
||||
ipc.on(...forwardToCurrentWebview("WINDOW_DEVTOOLS_CLOSED"))
|
||||
ipc.on(...forwardToCurrentWebview("UPDATE_ERROR"))
|
||||
ipc.on(...forwardToCurrentWebview("UPDATE_NOT_AVAILABLE"))
|
||||
ipc.on(...forwardToCurrentWebview("UPDATE_MANUALLY"))
|
||||
ipc.on(...forwardToCurrentWebview("UPDATE_AVAILABLE"))
|
||||
ipc.on(...forwardToCurrentWebview("MODULE_INSTALL_PROGRESS"))
|
||||
ipc.on(...forwardToCurrentWebview("UPDATE_DOWNLOADED"))
|
||||
ipc.on(...forwardToCurrentWebview("MODULE_INSTALLED"))
|
||||
ipc.on(...forwardToCurrentWebview("CHECKING_FOR_UPDATES"))
|
||||
ipc.on(...forwardToCurrentWebview("UPDATER_HISTORY_RESPONSE"))
|
||||
ipc.on(...forwardToCurrentWebview("ACCESSIBILITY_SUPPORT_CHANGED"))
|
||||
ipc.on(...forwardToCurrentWebview("HELP_OPEN"))
|
||||
ipc.on(...forwardToCurrentWebview("USER_SETTINGS_OPEN"))
|
||||
ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_PATH"))
|
||||
/** discord_desktop_core Development */
|
||||
ipc.on(...forwardToCurrentWebview("NAVIGATE_BACK"))
|
||||
ipc.on(...forwardToCurrentWebview("NAVIGATE_FORWARD"))
|
||||
|
||||
ipc.on("RELOAD", () => {
|
||||
let webview = webviews.get(document.querySelector(".chrome-tab[active]"))
|
||||
if(!webview)return
|
||||
webview.reload()
|
||||
})
|
||||
ipc.on("NEW_TAB", () => {
|
||||
chromeTabs.addTab({
|
||||
title: 'Lightcord',
|
||||
favicon: faviconURL
|
||||
})
|
||||
})
|
||||
ipc.on("CLOSE_TAB", () => {
|
||||
let active = document.querySelector("div.chrome-tab[active]")
|
||||
if(!active)return
|
||||
chromeTabs.removeTab(active)
|
||||
})
|
||||
ipc.on("OPEN_DEVTOOLS", () => {
|
||||
let webview = webviews.get(document.querySelector(".chrome-tab[active]"))
|
||||
if(!webview)return
|
||||
webview.openDevTools()
|
||||
})
|
||||
|
||||
window.onload = () => {
|
||||
const ChromeTabs = require("chrome-tabs")
|
||||
require("chrome-tabs/css/chrome-tabs.css")
|
||||
require("chrome-tabs/css/chrome-tabs-dark-theme.css")
|
||||
require("./controls.css")
|
||||
|
||||
let tabs = document.querySelector(".chrome-tabs")
|
||||
let chromeTabs = new ChromeTabs()
|
||||
chromeTabs.init(tabs)
|
||||
|
||||
tabs.addEventListener('activeTabChange', ({detail}) => {
|
||||
let webview = webviews.get(detail.tabEl)
|
||||
if(!webview){
|
||||
chromeTabs.removeTab(detail.tabEl)
|
||||
return
|
||||
}
|
||||
let active = Array.from(webviews.values()).find(e => e.classList.contains("active-webview"))
|
||||
if(active)active.classList.remove("active-webview")
|
||||
webview.classList.add("active-webview")
|
||||
})
|
||||
tabs.addEventListener('tabAdd', ({detail}) => {
|
||||
chromeTabs.updateTab(detail.tabEl, {
|
||||
title: "Lightcord Loading...",
|
||||
favicon: faviconURL
|
||||
})
|
||||
let webview = document.createElement("webview")
|
||||
webview.src = "https://discord.com/app"
|
||||
webview.classList.add("discord-webview")
|
||||
webview.classList.add("webview-active")
|
||||
webview.setAttribute("preload", pathToFileURL(join(__dirname, "../mainScreenPreload.js")))
|
||||
webview.shadowRoot.childNodes.item(1).style.height = "100%"
|
||||
webview.enableremotemodule = true
|
||||
webview.nodeintegration = false
|
||||
webview.spellcheck = true
|
||||
webview.webpreferences = "nativeWindowOpen=yes"
|
||||
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) ?
|
||||
console.log(ev[0].channel)
|
||||
if(ev[0].channel === "DISCORD_NEW_TAB"){
|
||||
chromeTabs.addTab({
|
||||
title: 'Lightcord',
|
||||
favicon: faviconURL
|
||||
})
|
||||
return
|
||||
}
|
||||
ipc.send(ev[0].channel.replace("DISCORD_", ""), ev.slice(1))
|
||||
})
|
||||
webview.addEventListener('page-title-updated', () => {
|
||||
let el = Array.from(webviews.entries()).find(e => e[1] === webview)[0]
|
||||
if(!el)return
|
||||
chromeTabs.updateTab(el, {
|
||||
favicon: faviconURL,
|
||||
title: webview.getTitle()
|
||||
})
|
||||
})
|
||||
webviews.set(detail.tabEl, webview)
|
||||
document.querySelector(".documentFull").appendChild(webview)
|
||||
let r
|
||||
webview.ready = new Promise(resolve => (r = resolve))
|
||||
webview.addEventListener("dom-ready", () => {
|
||||
r()
|
||||
webview.send("DISCORD_IS_TAB")
|
||||
})
|
||||
webview.addEventListener("will-navigate", (e) => {
|
||||
e.preventDefault()
|
||||
console.log(e, e.url)
|
||||
})
|
||||
})
|
||||
tabs.addEventListener('tabRemove', ({detail}) => {
|
||||
let webview = webviews.get(detail.tabEl)
|
||||
if(!webview)return
|
||||
webview.remove()
|
||||
webviews.delete(detail.tabEl)
|
||||
if(document.querySelector(".chrome-tabs-content").childNodes.length === 0){
|
||||
window.close()
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener('keydown', (event) => {
|
||||
if(event.ctrlKey){
|
||||
if(event.key === 't'){
|
||||
chromeTabs.addTab({
|
||||
title: 'Lightcord',
|
||||
favicon: faviconURL
|
||||
})
|
||||
}else if(event.key === "w"){
|
||||
let active = document.querySelector("div.chrome-tab[active]")
|
||||
if(!active)return
|
||||
chromeTabs.removeTab(active)
|
||||
}
|
||||
}
|
||||
})
|
||||
setImmediate(() => {
|
||||
chromeTabs.addTab({
|
||||
title: 'Lightcord Loading...',
|
||||
favicon: faviconURL
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
require.extensions[".css"] = (m, filename) => {
|
||||
let content = fs.readFileSync(filename, "binary")
|
||||
let style = document.createElement("style")
|
||||
style.id = btoa(filename)
|
||||
style.innerHTML = content
|
||||
document.head.appendChild(style)
|
||||
m.exports = {
|
||||
id: style.id,
|
||||
remove(){
|
||||
return style.remove()
|
||||
}
|
||||
}
|
||||
return m.exports
|
||||
}
|
||||
|
||||
const fs = require("fs")
|
||||
const { join } = require("path")
|
||||
const { pathToFileURL } = require("url")
|
||||
const ipc = require("../discord_native/renderer/ipc")
|
||||
|
||||
let webviews = new Map()
|
||||
window.webviews = webviews
|
||||
|
||||
function forwardToCurrentWebview(event){
|
||||
return [event, async (...args) => {
|
||||
let webview = webviews.get(document.querySelector(".chrome-tab[active]"))
|
||||
if(!webview)return
|
||||
await webview.ready
|
||||
webview.send(event, ...args.slice(1))
|
||||
}]
|
||||
}
|
||||
|
||||
/** discord_desktop_core Stable */
|
||||
ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_FOCUS"))
|
||||
ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_BLUR"))
|
||||
ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_OPEN_VOICE_SETTINGS"))
|
||||
ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_TOGGLE_MUTE"))
|
||||
ipc.on(...forwardToCurrentWebview("SYSTEM_TRAY_TOGGLE_DEAFEN"))
|
||||
ipc.on(...forwardToCurrentWebview("LAUNCH_APPLICATION"))
|
||||
ipc.on(...forwardToCurrentWebview("SPELLCHECK_RESULT"))
|
||||
ipc.on(...forwardToCurrentWebview("WINDOW_DEVTOOLS_OPENED"))
|
||||
ipc.on(...forwardToCurrentWebview("WINDOW_DEVTOOLS_CLOSED"))
|
||||
ipc.on(...forwardToCurrentWebview("UPDATE_ERROR"))
|
||||
ipc.on(...forwardToCurrentWebview("UPDATE_NOT_AVAILABLE"))
|
||||
ipc.on(...forwardToCurrentWebview("UPDATE_MANUALLY"))
|
||||
ipc.on(...forwardToCurrentWebview("UPDATE_AVAILABLE"))
|
||||
ipc.on(...forwardToCurrentWebview("MODULE_INSTALL_PROGRESS"))
|
||||
ipc.on(...forwardToCurrentWebview("UPDATE_DOWNLOADED"))
|
||||
ipc.on(...forwardToCurrentWebview("MODULE_INSTALLED"))
|
||||
ipc.on(...forwardToCurrentWebview("CHECKING_FOR_UPDATES"))
|
||||
ipc.on(...forwardToCurrentWebview("UPDATER_HISTORY_RESPONSE"))
|
||||
ipc.on(...forwardToCurrentWebview("ACCESSIBILITY_SUPPORT_CHANGED"))
|
||||
ipc.on(...forwardToCurrentWebview("HELP_OPEN"))
|
||||
ipc.on(...forwardToCurrentWebview("USER_SETTINGS_OPEN"))
|
||||
ipc.on(...forwardToCurrentWebview("MAIN_WINDOW_PATH"))
|
||||
/** discord_desktop_core Development */
|
||||
ipc.on(...forwardToCurrentWebview("NAVIGATE_BACK"))
|
||||
ipc.on(...forwardToCurrentWebview("NAVIGATE_FORWARD"))
|
||||
|
||||
ipc.on("RELOAD", () => {
|
||||
let webview = webviews.get(document.querySelector(".chrome-tab[active]"))
|
||||
if(!webview)return
|
||||
webview.reload()
|
||||
})
|
||||
ipc.on("NEW_TAB", () => {
|
||||
chromeTabs.addTab({
|
||||
title: 'Lightcord',
|
||||
favicon: faviconURL
|
||||
})
|
||||
})
|
||||
ipc.on("CLOSE_TAB", () => {
|
||||
let active = document.querySelector("div.chrome-tab[active]")
|
||||
if(!active)return
|
||||
chromeTabs.removeTab(active)
|
||||
})
|
||||
ipc.on("OPEN_DEVTOOLS", () => {
|
||||
let webview = webviews.get(document.querySelector(".chrome-tab[active]"))
|
||||
if(!webview)return
|
||||
webview.openDevTools()
|
||||
})
|
||||
|
||||
window.onload = () => {
|
||||
const ChromeTabs = require("chrome-tabs")
|
||||
require("chrome-tabs/css/chrome-tabs.css")
|
||||
require("chrome-tabs/css/chrome-tabs-dark-theme.css")
|
||||
require("./controls.css")
|
||||
|
||||
let tabs = document.querySelector(".chrome-tabs")
|
||||
let chromeTabs = new ChromeTabs()
|
||||
chromeTabs.init(tabs)
|
||||
|
||||
tabs.addEventListener('activeTabChange', ({detail}) => {
|
||||
let webview = webviews.get(detail.tabEl)
|
||||
if(!webview){
|
||||
chromeTabs.removeTab(detail.tabEl)
|
||||
return
|
||||
}
|
||||
let active = Array.from(webviews.values()).find(e => e.classList.contains("active-webview"))
|
||||
if(active)active.classList.remove("active-webview")
|
||||
webview.classList.add("active-webview")
|
||||
})
|
||||
tabs.addEventListener('tabAdd', ({detail}) => {
|
||||
chromeTabs.updateTab(detail.tabEl, {
|
||||
title: "Lightcord Loading...",
|
||||
favicon: faviconURL
|
||||
})
|
||||
let webview = document.createElement("webview")
|
||||
webview.src = "https://discord.com/app"
|
||||
webview.classList.add("discord-webview")
|
||||
webview.classList.add("webview-active")
|
||||
webview.setAttribute("preload", pathToFileURL(join(__dirname, "../mainScreenPreload.js")))
|
||||
webview.shadowRoot.childNodes.item(1).style.height = "100%"
|
||||
webview.enableremotemodule = true
|
||||
webview.nodeintegration = false
|
||||
webview.spellcheck = true
|
||||
webview.webpreferences = "nativeWindowOpen=yes"
|
||||
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) ?
|
||||
console.log(ev[0].channel)
|
||||
if(ev[0].channel === "DISCORD_NEW_TAB"){
|
||||
chromeTabs.addTab({
|
||||
title: 'Lightcord',
|
||||
favicon: faviconURL
|
||||
})
|
||||
return
|
||||
}
|
||||
ipc.send(ev[0].channel.replace("DISCORD_", ""), ev.slice(1))
|
||||
})
|
||||
webview.addEventListener('page-title-updated', () => {
|
||||
let el = Array.from(webviews.entries()).find(e => e[1] === webview)[0]
|
||||
if(!el)return
|
||||
chromeTabs.updateTab(el, {
|
||||
favicon: faviconURL,
|
||||
title: webview.getTitle()
|
||||
})
|
||||
})
|
||||
webviews.set(detail.tabEl, webview)
|
||||
document.querySelector(".documentFull").appendChild(webview)
|
||||
let r
|
||||
webview.ready = new Promise(resolve => (r = resolve))
|
||||
webview.addEventListener("dom-ready", () => {
|
||||
r()
|
||||
webview.send("DISCORD_IS_TAB")
|
||||
})
|
||||
webview.addEventListener("will-navigate", (e) => {
|
||||
e.preventDefault()
|
||||
console.log(e, e.url)
|
||||
})
|
||||
})
|
||||
tabs.addEventListener('tabRemove', ({detail}) => {
|
||||
let webview = webviews.get(detail.tabEl)
|
||||
if(!webview)return
|
||||
webview.remove()
|
||||
webviews.delete(detail.tabEl)
|
||||
if(document.querySelector(".chrome-tabs-content").childNodes.length === 0){
|
||||
window.close()
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener('keydown', (event) => {
|
||||
if(event.ctrlKey){
|
||||
if(event.key === 't'){
|
||||
chromeTabs.addTab({
|
||||
title: 'Lightcord',
|
||||
favicon: faviconURL
|
||||
})
|
||||
}else if(event.key === "w"){
|
||||
let active = document.querySelector("div.chrome-tab[active]")
|
||||
if(!active)return
|
||||
chromeTabs.removeTab(active)
|
||||
}
|
||||
}
|
||||
})
|
||||
setImmediate(() => {
|
||||
chromeTabs.addTab({
|
||||
title: 'Lightcord Loading...',
|
||||
favicon: faviconURL
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
require.extensions[".css"] = (m, filename) => {
|
||||
let content = fs.readFileSync(filename, "binary")
|
||||
let style = document.createElement("style")
|
||||
style.id = btoa(filename)
|
||||
style.innerHTML = content
|
||||
document.head.appendChild(style)
|
||||
m.exports = {
|
||||
id: style.id,
|
||||
remove(){
|
||||
return style.remove()
|
||||
}
|
||||
}
|
||||
return m.exports
|
||||
}
|
||||
|
||||
const faviconURL = pathToFileURL(join(__dirname, "../images/discord.png"))
|
|
@ -1,16 +1,16 @@
|
|||
{
|
||||
"name": "discord_desktop_core",
|
||||
"description": "Discord Client for Desktop - Light version - Core App",
|
||||
"main": "app/index.js",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"chrome-tabs": "^5.4.0",
|
||||
"invariant": "2.2.4",
|
||||
"lodash": "4.17.19",
|
||||
"mkdirp": "^1.0.4",
|
||||
"promise.allsettled": "^1.0.0",
|
||||
"request": "2.88.0",
|
||||
"rimraf": "^2.6.3",
|
||||
"yauzl": "^2.10.0"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "discord_desktop_core",
|
||||
"description": "Discord Client for Desktop - Light version - Core App",
|
||||
"main": "app/index.js",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"chrome-tabs": "^5.4.0",
|
||||
"invariant": "2.2.4",
|
||||
"lodash": "4.17.19",
|
||||
"mkdirp": "^1.0.4",
|
||||
"promise.allsettled": "^1.0.0",
|
||||
"request": "2.88.0",
|
||||
"rimraf": "^2.6.3",
|
||||
"yauzl": "^2.10.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
const {Dispatch: DispatchNative} = require('./discord_dispatch_'+process.platform+'.node');
|
||||
|
||||
function dispatchConstructor(jsonConfigString, updateCallback, errorCallback, analyticsCallback) {
|
||||
const instance = new DispatchNative(jsonConfigString, updateCallback, errorCallback, analyticsCallback);
|
||||
return {
|
||||
command: instance.command.bind(instance),
|
||||
destroy: instance.destroy.bind(instance),
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {Dispatch: dispatchConstructor};
|
||||
const {Dispatch: DispatchNative} = require('./discord_dispatch_'+process.platform+'.node');
|
||||
|
||||
function dispatchConstructor(jsonConfigString, updateCallback, errorCallback, analyticsCallback) {
|
||||
const instance = new DispatchNative(jsonConfigString, updateCallback, errorCallback, analyticsCallback);
|
||||
return {
|
||||
command: instance.command.bind(instance),
|
||||
destroy: instance.destroy.bind(instance),
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {Dispatch: dispatchConstructor};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"files": [
|
||||
"discord_dispatch.node",
|
||||
"index.js",
|
||||
"manifest.json"
|
||||
]
|
||||
{
|
||||
"files": [
|
||||
"discord_dispatch.node",
|
||||
"index.js",
|
||||
"manifest.json"
|
||||
]
|
||||
}
|
|
@ -1 +1 @@
|
|||
module.exports = require('./discord_erlpack_'+process.platform+'.node');
|
||||
module.exports = require('./discord_erlpack_'+process.platform+'.node');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"files": [
|
||||
"discord_erlpack.node",
|
||||
"index.js",
|
||||
"manifest.json"
|
||||
]
|
||||
{
|
||||
"files": [
|
||||
"discord_erlpack.node",
|
||||
"index.js",
|
||||
"manifest.json"
|
||||
]
|
||||
}
|
|
@ -1 +1 @@
|
|||
module.exports = require('./discord_game_utils_'+process.platform+'.node');
|
||||
module.exports = require('./discord_game_utils_'+process.platform+'.node');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"files": [
|
||||
"discord_game_utils.node",
|
||||
"index.js",
|
||||
"manifest.json"
|
||||
]
|
||||
{
|
||||
"files": [
|
||||
"discord_game_utils.node",
|
||||
"index.js",
|
||||
"manifest.json"
|
||||
]
|
||||
}
|
|
@ -1 +1 @@
|
|||
module.exports = require('./discord_hook_'+process.platform+'.node');
|
||||
module.exports = require('./discord_hook_'+process.platform+'.node');
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
const KrispModule = require('./discord_krisp_'+process.platform+'.node');
|
||||
|
||||
KrispModule._initialize();
|
||||
|
||||
KrispModule.getNcModels = function() {
|
||||
return new Promise(resolve => {
|
||||
KrispModule._getNcModels(models => resolve(models));
|
||||
});
|
||||
};
|
||||
|
||||
KrispModule.getVadModels = function() {
|
||||
return new Promise(resolve => {
|
||||
KrispModule._getVadModels(models => resolve(models));
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = KrispModule;
|
||||
const KrispModule = require('./discord_krisp_'+process.platform+'.node');
|
||||
|
||||
KrispModule._initialize();
|
||||
|
||||
KrispModule.getNcModels = function() {
|
||||
return new Promise(resolve => {
|
||||
KrispModule._getNcModels(models => resolve(models));
|
||||
});
|
||||
};
|
||||
|
||||
KrispModule.getVadModels = function() {
|
||||
return new Promise(resolve => {
|
||||
KrispModule._getVadModels(models => resolve(models));
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = KrispModule;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"files": [
|
||||
"discord_krisp.node",
|
||||
"VAD_weight.thw",
|
||||
"NC_weight.thw",
|
||||
"NC_small_16k.thw",
|
||||
"c6.s.f.c55d28.thw",
|
||||
"c6.xs.f.ca03f4.thw",
|
||||
"index.js",
|
||||
"manifest.json"
|
||||
]
|
||||
{
|
||||
"files": [
|
||||
"discord_krisp.node",
|
||||
"VAD_weight.thw",
|
||||
"NC_weight.thw",
|
||||
"NC_small_16k.thw",
|
||||
"c6.s.f.c55d28.thw",
|
||||
"c6.xs.f.ca03f4.thw",
|
||||
"index.js",
|
||||
"manifest.json"
|
||||
]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
const native = require('./discord_media_'+process.platform+'.node');
|
||||
|
||||
module.exports = {
|
||||
getSystemAnalyticsBlob() {
|
||||
return new Promise(resolve => native.getSystemAnalyticsBlob(resolve));
|
||||
},
|
||||
};
|
||||
const native = require('./discord_media_'+process.platform+'.node');
|
||||
|
||||
module.exports = {
|
||||
getSystemAnalyticsBlob() {
|
||||
return new Promise(resolve => native.getSystemAnalyticsBlob(resolve));
|
||||
},
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue