Saving Checkpoint before I'm doing *risky* things

This commit is contained in:
Jean Ouina 2020-07-28 03:02:29 +02:00
parent e47e1b0158
commit df6c9c4203
31 changed files with 1065 additions and 163 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +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
custom: ['https://paypal.me/jenwina'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

4
.gitignore vendored
View File

@ -110,3 +110,7 @@ builds
dist
distApp
# discord_voice debug
discord-last-webrtc_0
discord-webrtc_0

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -4186,7 +4186,7 @@
},
"kind-of": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
"resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
"integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=",
"dev": true
},
@ -6505,7 +6505,7 @@
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@ -6614,7 +6614,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {

View File

@ -76,6 +76,7 @@ export const settings = {
"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"},
@ -146,6 +147,7 @@ export const defaultCookie = {
"lightcord-6": true,
"lightcord-7": false,
"lightcord-8": false,
"lightcord-9": false,
"lightcord-10": false,
"no_window_bound": false,
};

View File

@ -143,6 +143,7 @@ Core.prototype.init = async function() {
Utils.suppressErrors(this.patchMessageHeader.bind(this), "BD Badge Chat Patch")();
Utils.suppressErrors(this.patchMemberList.bind(this), "BD Badge Member List Patch")();
Utils.suppressErrors(this.patchAttachment.bind(this), "LC Plugin Certifier Patch")();
Utils.suppressErrors(this.patchPopoutWindow.bind(this), "BD Popout Window Patch")();
if(bdConfig.haveInstalledDefault){
let alert = Utils.alert("First Installation", "As it is the first time you install Lightcord, We added two default themes and one default plugin in your plugin/theme folder. Check it in the Plugin/Theme settings.")
@ -161,6 +162,48 @@ Core.prototype.init = async function() {
}
};
Core.prototype.patchPopoutWindow = async function() {
let canceled = false
this.cancelPatchPopoutWindow = () => {
canceled = true
}
window.Lightcord.Api.ensureExported(e => e.default && e.default.getWindow)
.then(popoutModule => {
if(canceled)return
// Not a good idea to do it like that.
const interceptor = window.Lightcord.DiscordModules.dispatcher._interceptor
window.Lightcord.DiscordModules.dispatcher.setInterceptor(function(action){
if(action && action.type === "POPOUT_WINDOW_OPEN"){
const render = action.render
action.render = function(){
const render1 = render.call(this, ...arguments)
const type1 = render1.type
render1.type = function(props){
const render2 = type1(props)
console.log(props, render2)
return render2
}
console.log(render1)
return render1
}
}
return interceptor.call(this, action)
})
window.Lightcord.DiscordModules.dispatcher.subscribe("POPOUT_WINDOW_OPEN", (ev) => {
if(!settingsCookie["lightcord-9"])return
if(canceled)return
Utils.log("POPOUT THEME", "Popout opened, Adding theme")
setImmediate(() => {
console.log(ev)
const window = popoutModule.default.getWindow(ev.key)
console.log(window)
})
})
})
};
Core.prototype.patchAttributes = async function() {
let attribsPatchs = []
this.cancelPatchAttributes = function() {
@ -171,7 +214,7 @@ Core.prototype.patchAttributes = async function() {
// TODO: try to patch correctly the user popout on a next update
const Anchor = WebpackModules.find(m => m.displayName == "Anchor");
ensureExported(e => e.default && e.default.displayName === "DiscordTag")
window.Lightcord.Api.ensureExported(e => e.default && e.default.displayName === "DiscordTag")
.then(DiscordTag => {
let DiscordTagComp = DiscordTag.default
DiscordTag.default = function(props){
@ -282,6 +325,7 @@ Core.prototype.initSettings = function () {
settingModule.default.prototype.getPredicateSections = function(){
let result = getPredicateSections.call(this, ...arguments)
if(!result[1])return result
if(result[1].section === "My Account"){ // user settings, not guild settings
let poped = []

View File

@ -30,10 +30,13 @@ export default class ApiPreview extends React.PureComponent {
<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 by a plugin. 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://github.com/lightcord/lightcord/wiki/Apis")
remote.shell.openExternal("https://lightcord.deroku.xyz/LightcordApi/docs")
}} wrapper={false}>
Documentation
</Lightcord.Api.Components.inputs.Button>

View File

@ -572,7 +572,7 @@ class PresenceErrorCatcher extends React.Component {
return this.render()
}
}else{
emptyClasses = emptyClasses || BDV2.WebpackModules.find(e => e.emptyStateImage)
emptyClasses = emptyClasses || BDV2.WebpackModules.find(e => e.emptyStateImage && e.emptyState)
if(!emptyClasses){
Utils.showToast("An error occured. Please check the console for more informations.")
return null
@ -584,7 +584,7 @@ class PresenceErrorCatcher extends React.Component {
backgroundColor: "var(--background-primary)",
padding: "30px 30px",
borderRadius: "8px"
}} className="lc-tab-box-shadow">
}} className={`lc-tab-box-shadow ${emptyClasses.emptyState}`}>
<div className={emptyClasses.emptyStateImage} style={{
marginTop: "20px"
}}>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@
"build-prod": "webpack --progress --colors --mode production -o js/main.min.js --devtool none",
"watch-prod": "webpack --progress --colors --watch --mode production -o js/main.min.js --devtool none",
"test": "echo \"Error: no test specified\" && exit 1",
"docs": "typedoc --plugin ./node_modules/typedoc-plugin-markdown --out ../../LightcordApiDocs ./src"
"docs": "typedoc --out ../../LightcordApiDocs ./src"
},
"author": "",
"license": "ISC",

View File

@ -1,2 +1,28 @@
// bait typescript into thinking this is not reactDOM so no circular dependency.
export = window["Reac"+"tDOM"] as typeof import("react-dom")
import { ReactDOMSelector } from "./selectors"
// 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.findModule(ReactDOMSelector as any)
}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.findModule(ReactDOMSelector as any)
}catch(e){
return null
}
})()) as typeof import("react-dom")
export = (window.ReactDOM as any)

View File

@ -1 +1,27 @@
import { ReactSelector } from "./selectors";
window.React = (window.React || // If in Lightcord
(()=>{ // If in Standard BetterDiscord
try{
return window.BdApi.findModule(ReactSelector as any)
}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.findModule(ReactSelector as any)
}catch(e){
return null
}
})()) as typeof import("react")
export = window.React

View File

@ -0,0 +1,2 @@
export const ReactSelector = mod => !["Component", "PureComponent", "Children", "createElement", "cloneElement"].find(k => !mod[k])
export const ReactDOMSelector = mod => mod.findDOMNode

View File

@ -13,25 +13,27 @@ import cloneNullProto from "../modules/cloneNullProto"
import Tooltip from "./general/Tooltip"
import ColorPicker from "./inputs/ColorPicker"
import AlertBox from "./general/AlertBox"
import { createProxyErrorCatcherClass } from "./private/ErrorCatcher"
const RadioGroupProxied = createProxyErrorCatcherClass(RadioGroup)
export default cloneNullProto({
inputs: cloneNullProto({
Button: DiscordButton,
Switch: Switch,
Choices: RadioGroup,
RadioGroup: RadioGroup,
TextArea: TextArea,
TextInput: TextInput,
Dropdown: Dropdown,
ColorPicker: ColorPicker
Button: createProxyErrorCatcherClass(DiscordButton),
Switch: createProxyErrorCatcherClass(Switch),
Choices: RadioGroupProxied,
RadioGroup: RadioGroupProxied,
TextArea: createProxyErrorCatcherClass(TextArea),
TextInput: createProxyErrorCatcherClass(TextInput),
Dropdown: createProxyErrorCatcherClass(Dropdown),
ColorPicker: createProxyErrorCatcherClass(ColorPicker)
}),
general: cloneNullProto({
Title: Title,
SettingsTitle: SettingsTitle,
SettingSubTitle: SettingSubTitle,
Tabs: Tabs,
CodeBlock: CodeBlock,
Tooltip: Tooltip,
AlertBox: AlertBox
Title: createProxyErrorCatcherClass(Title),
SettingsTitle: createProxyErrorCatcherClass(SettingsTitle),
SettingSubTitle: createProxyErrorCatcherClass(SettingSubTitle),
Tabs: createProxyErrorCatcherClass(Tabs),
CodeBlock: createProxyErrorCatcherClass(CodeBlock),
Tooltip: createProxyErrorCatcherClass(Tooltip),
AlertBox: createProxyErrorCatcherClass(AlertBox)
})
})

View File

@ -2,6 +2,7 @@ import NOOP from "../../modules/noop"
import WebpackLoader from "../../modules/WebpackLoader"
import Tooltip from "../general/Tooltip"
import Utils from "../../modules/Utils"
import { createProxyErrorCatcherClass } from "../private/ErrorCatcher"
const Constants = {
defaultColor: 10070709,
@ -57,9 +58,13 @@ export default class ColorPicker extends React.PureComponent<ColorPickerProps, {
/** Preload the component. */
static preload(){
if(ColorPicker.prototype.modules[0])return
if(isFetching)return
new ColorPicker({}).render()
if(ColorPicker.prototype.modules[0])return // already loaded
if(isFetching)return // is fetching so don't double preload.
try{ // If we caught an error
new ColorPicker({}).render()
}catch(e){
console.error(e)
}
}
onChange(val){
@ -114,9 +119,10 @@ export default class ColorPicker extends React.PureComponent<ColorPickerProps, {
return {
id: null
}
}
},
renderHeader: GuildSettingsRoles.prototype.renderHeader
})
const GuildRoleSettings = settings.props.children.type
const GuildRoleSettings = settings.props.children[1].type
let children = GuildRoleSettings.prototype.renderColorPicker.call({
props: {
role: {
@ -128,6 +134,7 @@ export default class ColorPicker extends React.PureComponent<ColorPickerProps, {
}
}).props.children
children.type(children.props).props.children.type._ctor().then(c => {
ColorPickerModules = null
this.forceUpdate()
resolve()
})
@ -175,7 +182,7 @@ export default class ColorPicker extends React.PureComponent<ColorPickerProps, {
}
static help = {
info: "To convert hex colors to decimal, you can do `Lightcord.Api.Utils.HexColorToDecimal('#yourcolor')` and go back with `Lightcord.Api.Utils.DecimalColorToHex(7506394)`",
info: "To convert hex colors to decimal, you can do `Lightcord.Api.Utils.HexColorToDecimal('#7289DA')` and go back with `Lightcord.Api.Utils.DecimalColorToHex(7506394)`",
warn: "The component may not appear instantly. The component needs to be loaded, so you could experience 50-300ms loading time depending on your internet connection."
}
}

View File

@ -0,0 +1,97 @@
import { ReactNode } from "react";
import WebpackLoader from "../../modules/WebpackLoader";
import DiscordTools from "../../modules/DiscordTools";
import NOOP from "../../modules/noop";
import uuid from "../../modules/uuid";
type ErrorCatcherProps = {
children: ReactNode
}
let ErrorCatcherModules
export default class ErrorCatcher extends React.Component<ErrorCatcherProps, {error: boolean, hasSentNotification: boolean}> {
constructor(props:ErrorCatcherProps){
super(props)
this.state = {
error: false,
hasSentNotification: false
}
}
get modules():any[]{
return ErrorCatcherModules || (ErrorCatcherModules = [
WebpackLoader.find(e => e.emptyStateImage && e.emptyState)
])
}
defaultProps:ErrorCatcherProps = {
children: null
}
render(){
if(!this.state.error){
return this.props.children
}else{ // try to render a user-friendly interface.
const [
emptyClasses
] = this.modules
if(!emptyClasses){ // If we can't, render nothing and show a notification.
if(this.state.hasSentNotification)return null // If the notification was already sent, don't send one.
const notification = DiscordTools.showNotification({
body: "An error occured. Please check the console for more informations.",
icon: "https://github.com/lightcord.png",
onClick: NOOP,
onClose: NOOP,
onShow: NOOP,
title: "Lightcord Informations"
})
this.setState({
hasSentNotification: true
})
setTimeout(() => {
notification.close()
}, 2000)
return null
}
return <div className={emptyClasses.emptyState}>
<div className={emptyClasses.emptyStateImage} style={{
marginTop: "20px"
}}></div>
<div className={emptyClasses.emptyStateHeader}>An error occured</div>
<p className={emptyClasses.emptyStateSubtext}>
Please check the console for more informations. Join our ­support server for more help.
</p>
</div>
}
}
componentDidCatch(error, errorInfo){
console.error(errorInfo.componentStack)
this.setState({
error: true
})
}
}
export function createProxyErrorCatcherClass<base = Function>(Class:base):base & {
readonly original: base
}{
const ClassCopy = (class Proxied extends React.Component {
render(){
return <ErrorCatcher key={uuid()}>
{React.createElement(Class as any, {...this.props, key: uuid()})}
</ErrorCatcher>
}
static displayName = Class["displayName"] || Class["name"]
static get original(){
return Class
}
}) as any
Object.entries(Object.getOwnPropertyDescriptors(Class)).forEach(value => {
if(value[0] in ClassCopy)return
Object.defineProperty(ClassCopy, value[0], value[1])
})
return ClassCopy
}

View File

@ -8,6 +8,8 @@ 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"
patchers.patch()
const LightcordApi = {
@ -21,19 +23,29 @@ const LightcordApi = {
cloneNullProto: cloneNullProto,
NOOP: NOOP,
unfreeze: unfreeze
}
},
get isNative(){return isNative},
get isImported(){return isImported}
}
declare global {
var React:typeof import("react")
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
}
var Lightcord:LightcordGlobal
}
@ -59,7 +71,7 @@ export interface LightcordGlobal {
},
Api: LightcordApiGlobal,
BetterDiscord: {
BdApi: typeof import("@bandagedbd/bdapi").BdApi,
BdApi: typeof bandagedbdApi.BdApi,
[mod:string]:any
}
}

View File

@ -0,0 +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
}
}

View File

@ -152,6 +152,8 @@ export class Notice extends EventEmitter {
index:number
}
private nextTickRefresh:boolean = false
get removed():boolean{
return !notices.find(e => e.id === this.id)
}
@ -166,36 +168,55 @@ export class Notice extends EventEmitter {
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.data.text = text
noticeEvents.emit("noticeUpdate")
this.update({
text
})
}
get type(){
return this.data.type
}
set type(type){
this.data.type = type
noticeEvents.emit("noticeUpdate")
this.update({
type
})
}
get buttonText(){
return this.data.buttonText
}
set buttonText(buttonText:string){
this.data.buttonText = buttonText
noticeEvents.emit("noticeUpdate")
this.update({
buttonText
})
}
get onClick(){
return this.data.onClick
}
set onClick(onClick){
this.data.onClick = onClick
noticeEvents.emit("noticeUpdate")
this.update({
onClick
})
}
remove(){

View File

@ -1,4 +1,4 @@
const BDModules = window.BDModules
const BDModules:typeof window.BDModules = window.BDModules || require("./BDModules")
export default new class WebpackLoader {
constructor(){}
@ -50,7 +50,7 @@ export default new class WebpackLoader {
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. \n\tOpen an issue on https://github.com/Lightcord/Lightcord or in their discord server."
message += "\n\tThis error is related to Lightcord not being able to find a WebpackModule. \n\tPlease show this error and a few lines of logs above this error to the devs. \n\tOpen an issue on https://github.com/Lightcord/Lightcord or in our discord server."
super(message)
this.name = "WebpackLoaderError"
}

View File

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

View File

View File

@ -1,5 +1,6 @@
import Utils from "./Utils"
import Notices, { notices } from "../components/private/Notices"
import { isNative } from "./environnement";
export function patch(){
/** START NOTICE */
@ -47,6 +48,62 @@ export function patch(){
})
/** 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 */
}
// TODO: Add in app-notifications / confirmations.
/** START IN-APP NOTIFICATIONS */
//getModule(e => true)
/** END IN-APP NOTIFICATIONS */
@ -60,4 +117,25 @@ function getModule(filter: (mod:any) => boolean):Promise<any>{
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
})

View File

@ -1,70 +1,71 @@
const path = require("path");
const TerserPlugin = require("terser-webpack-plugin")
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"
},
resolve: {
extensions: [".js", ".jsx", ".json", ".ts", ".tsx"],
alias: {
"react$": path.resolve(__dirname, "src", "alias", "react.ts"),
"react-dom$": path.resolve(__dirname, "src", "alias", "react-dom.ts")
}
},
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")
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.ts"),
"react-dom$": path.resolve(__dirname, "src", "alias", "react-dom.ts")
}
},
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
}
}),
]
}
};

View File

@ -48,7 +48,7 @@ async function privateInit(){
ModuleLoader.get(e => e.getCurrentHub)[0].getCurrentHub().getClient().getOptions().enabled = false
// setting react in require cache
const React = ModuleLoader.get(e => !["Component", "PureComponent", "Children", "createElement", "cloneElement"].map(c => !!e[c]).includes(false))[0]
const React = ModuleLoader.get(e => !["Component", "PureComponent", "Children", "createElement", "cloneElement"].find(k => !e[k]))[0]
window.React = React
const ReactDOM = ModuleLoader.get(e => e.findDOMNode)[0]
@ -278,7 +278,7 @@ async function privateInit(){
DiscordNative.ipc.send("UPDATE_THEME", data.settings.theme)
})
require("lightcordapi/js/main.js")
require("lightcordapi/js/main.min.js")
/*
if(shouldShowPrompt){

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,3 @@
process.once('loaded', () => {
console.log("sltsv")
})

View File

@ -14,9 +14,11 @@ var _electron = require('electron');
var _appFeatures = require('./appFeatures');
var _mainScreen = require('./mainScreen');
const { join } = require('path');
const MIN_POPOUT_WIDTH = 320;
const MIN_POPOUT_HEIGHT = 180;
/** @type {Electron.BrowserWindowConstructorOptions} */
const DEFAULT_POPOUT_OPTIONS = {
title: 'Lightcord Popout',
backgroundColor: '#2f3136',
@ -29,7 +31,8 @@ const DEFAULT_POPOUT_OPTIONS = {
webPreferences: {
nodeIntegration: false,
nativeWindowOpen: true
}
},
icon: join(__dirname, 'discord.png')
};
const features = (0, _appFeatures.getFeatures)();

View File

@ -13,8 +13,8 @@
"build:electron_linux": "electron-packager ./distApp --ignore=\"(distApp|builds|\\.ts|\\.dll)\" --arch=x64 --protocol=discord --platform=\"linux\" --out=builds --icon=app.ico --executable-name=\"Lightcord\" --asar.unpack=*.{node,so.4} --overwrite",
"build:minify": "node build.js",
"build:after": "node afterbuild.js",
"devInstall": "npm i -g --arch=ia32 electron@8.4.0 && npm i -g typescript && npm i --save-dev @types/node@12.12.39 && npm i --save-dev --arch=ia32 electron@8.4.0 && node installSubModules.js && echo \"Everything is installed. You should be able to do `npm run test` to compile everything and launch.\"",
"devInstall:64": "npm i -g --arch=x64 electron@8.4.0 && npm i -g typescript && npm i --save-dev @types/node@12.12.39 && npm i --save-dev --arch=x64 electron@8.4.0 && node installSubModules.js && echo \"Everything is installed. You should be able to do `npm run test` to compile everything and launch.\""
"devInstall": "npm i --save-dev --arch=ia32 electron@8.4.0 && node installSubModules.js && echo \"Everything is installed. You should be able to do `npm run test` to compile everything and launch.\"",
"devInstall:64": "npm i --save-dev --arch=x64 electron@8.4.0 && node installSubModules.js && echo \"Everything is installed. You should be able to do `npm run test` to compile everything and launch.\""
},
"author": "",
"license": "ISC",
@ -34,7 +34,7 @@
"@types/auto-launch": "^5.0.1",
"@types/electron-devtools-installer": "^2.2.0",
"@types/mkdirp": "^1.0.0",
"@types/node": "^12.12.39",
"@types/node": "12.12.39",
"@types/rimraf": "^3.0.0",
"@types/uuid": "^8.0.0",
"@types/yauzl": "^2.9.1",