Injector v3.0.0: Rewrite to use Parcel

pull/8/head
Oj18 4 years ago
parent 1e462ee71b
commit 990d0d21dd

2
.gitignore vendored

@ -0,0 +1,2 @@
.cache
node_modules

32
dist/index.js vendored

File diff suppressed because one or more lines are too long

1
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

6204
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,26 @@
{
"name": "injector",
"version": "3.0.0",
"description": "GooseMod injector",
"main": "index.js",
"scripts": {
"build": "parcel build src/index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/GooseMod/Injector.git"
},
"keywords": [
"goosemod",
"discord"
],
"author": "GooseMod",
"license": "GPL-3.0",
"bugs": {
"url": "https://github.com/GooseMod/Injector/issues"
},
"homepage": "https://github.com/GooseMod/Injector#readme",
"dependencies": {
"parcel-bundler": "^1.12.4"
}
}

@ -0,0 +1,183 @@
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
globalThis.modules = {};
globalThis.disabledModules = {};
console.log('pass 1');
import * as Logger from './util/logger';
globalThis.logger = Logger;
import * as Hash from './util/hash';
globalThis.version = '3.0.0';
Hash.sha512('foobar').then((hash) => {
globalThis.injectorHash = hash;
globalThis.logger.debug('import.version.goosemod', `${globalThis.version} (${globalThis.injectorHash})`);
});
console.log('pass 2');
if (window.DiscordNative !== undefined) globalThis.logger.debug('import.version.discord', `${DiscordNative.app.getReleaseChannel()} ${DiscordNative.app.getVersion()}`);
if (window.gmUntethered) {
globalThis.untetheredVersion = window.gmUntethered.slice();
// delete window.gmUntethered;
}
console.log('pass 3');
import WebpackModules from './util/discord/webpackModules';
globalThis.webpackModules = WebpackModules;
import fixLocalStorage from './util/discord/fixLocalStorage';
fixLocalStorage();
console.log('pass 4');
import showToast from './ui/toast';
globalThis.showToast = showToast;
import easterEggs from './ui/easterEggs';
globalThis.messageEasterEggs = easterEggs;
import confirmDialog from './ui/modals/confirm';
globalThis.confirmDialog = confirmDialog;
globalThis.messageEasterEggs.interval = setInterval(globalThis.messageEasterEggs.check, 1000);
import { startLoadingScreen, stopLoadingScreen, updateLoadingScreen } from './ui/loading';
Object.assign(globalThis, {
startLoadingScreen,
stopLoadingScreen,
updateLoadingScreen
});
console.log('pass 5');
import { removeModuleUI, isSettingsOpen, closeSettings, openSettings, openSettingItem, reopenSettings, injectInSettings, checkSettingsOpenInterval } from './ui/settings';
Object.assign(globalThis, {
removeModuleUI,
isSettingsOpen,
closeSettings,
openSettings,
openSettingItem,
reopenSettings,
injectInSettings,
checkSettingsOpenInterval
});
import cspBypasser from './util/discord/cspBypasser';
globalThis.cspBypasser = cspBypasser;
globalThis.cspBypasser.init();
console.log('pass 6');
import { importModule, importModules, bindHandlers, getModuleFiles, importModulesFull } from './moduleManager';
Object.assign(globalThis, {
importModule,
importModules,
bindHandlers,
getModuleFiles,
importModulesFull
});
import { saveModuleSettings, clearModuleSetting, clearSettings, loadSavedModuleSetting, loadSavedModuleSettings } from './moduleSettingsStore';
Object.assign(globalThis, {
saveModuleSettings,
clearModuleSetting,
clearSettings,
loadSavedModuleSetting,
loadSavedModuleSettings
});
import moduleStoreAPI from './moduleStore';
globalThis.moduleStoreAPI = moduleStoreAPI;
globalThis.saveInterval = setInterval(globalThis.saveModuleSettings, 3000);
globalThis.remove = () => {
clearInterval(globalThis.messageEasterEggs.interval);
clearInterval(globalThis.saveInterval);
clearInterval(globalThis.injectInSettings);
globalThis.clearSettings();
globalThis.moduleStoreAPI.jsCache.purgeCache();
globalThis.removed = true;
for (let p in globalThis.modules) {
if (globalThis.modules.hasOwnProperty(p) && globalThis.modules[p].remove !== undefined) {
globalThis.modules[p].remove();
}
}
};
const init = async () => {
globalThis.startLoadingScreen();
globalThis.updateLoadingScreen('Getting module index from Module Store...');
await globalThis.moduleStoreAPI.updateModules();
globalThis.moduleStoreAPI.updateStoreSetting();
globalThis.initialImport = true;
let toInstallModules = Object.keys(JSON.parse(localStorage.getItem('goosemodModules')) || {});
let toInstallIsDefault = false;
if (toInstallModules.length === 0) {
toInstallIsDefault = true;
toInstallModules = ['hardcodedColorFixer', 'draculaTheme', 'fucklytics', 'visualTweaks', 'wysiwygMessages', 'customSounds', 'devMode', 'twitchEmotes', 'noMessageDeletion'];
}
toInstallModules = toInstallModules.filter((m) => globalThis.moduleStoreAPI.modules.find((x) => x.filename === m) !== undefined);
let themeModule = toInstallModules.find((x) => x.toLowerCase().includes('theme'));
if (themeModule) {
toInstallModules.unshift(toInstallModules.splice(toInstallModules.indexOf(themeModule), 1)[0]);
}
let hardcodedColorFixerModule = toInstallModules.find((x) => x === 'hardcodedColorFixer');
if (hardcodedColorFixerModule) {
toInstallModules.unshift(toInstallModules.splice(toInstallModules.indexOf(hardcodedColorFixerModule), 1)[0]);
}
globalThis.updateLoadingScreen(`Importing default modules from Module Store... (${toInstallIsDefault ? '(Default)' : '(Last Installed)'})`);
for (let m of toInstallModules) {
globalThis.updateLoadingScreen(`${globalThis.moduleStoreAPI.modules.find((x) => x.filename === m).name} - ${toInstallModules.indexOf(m) + 1}/${toInstallModules.length}`)
//globalThis.updateLoadingScreen(`Importing default modules from Module Store...<br><br>${globalThis.moduleStoreAPI.modules.find((x) => x.filename === m).name}<br>${toInstallModules.indexOf(m) + 1}/${toInstallModules.length}<br>${toInstallIsDefault ? '(Default)' : '(Last Installed)'}`);
await globalThis.moduleStoreAPI.importModule(m);
}
delete globalThis.initialImport;
globalThis.updateLoadingScreen(`Loading saved module settings...`);
await globalThis.loadSavedModuleSettings();
globalThis.stopLoadingScreen();
if (globalThis.isSettingsOpen()) { // If settings are open, reopen to inject new GooseMod options
globalThis.reopenSettings();
} else {
// Only open settings (when not already open) if new user
if (!localStorage.getItem('goosemodModules')) {
globalThis.openSettings();
await sleep(200);
globalThis.openSettingItem('Module Store');
}
}
};
init();

File diff suppressed because it is too large Load Diff

@ -0,0 +1,104 @@
const ab2str = (buf) => { // ArrayBuffer (UTF-8) -> String
return String.fromCharCode.apply(null, new Uint8Array(buf));
};
export const importModule = async (f) => {
let field = f.filename.split('.').slice(0, -1).join('.'); // Get name of module via filename (taking away the file extension)
globalThis.logger.debug('import', `Importing module: "${field}"`);
let settingItem = globalThis.settings.items.find((x) => x[1] === 'Manage Modules');
if (globalThis.modules[field] !== undefined) {
globalThis.logger.debug(`import.load.module.${field}`, 'Module already imported, removing then installing new version');
await globalThis.modules[field].remove();
settingItem[2].splice(settingItem[2].indexOf(settingItem[2].find((x) => x.text === `${globalThis.modules[field].name} (${globalThis.modules[field].version})`)), 1);
}
if (typeof f.data === 'object') { // ArrayBuffer (UTF-8) -> String
f.data = ab2str(f.data);
}
globalThis.modules[field] = eval(f.data.replace(/\bthis\./g, 'globalThis.')); // Set globalThis.modules.<module_name> to the return value of the module (an object containing handlers)
globalThis.logger.debug(`import.load.module.${field}`, `Evaled module JS`);
globalThis.bindHandlers(globalThis.modules[field]); // Bind all handlers to module parent / returned object from module code
globalThis.logger.debug(`import.load.module.${field}`, `Binded handlers`);
await globalThis.modules[field].onImport(); // Run the module's onImport handler
globalThis.logger.debug(`import.load.module.${field}`, `Ran onImport()`);
let toggleObj = {
type: 'text-and-danger-button',
text: `${globalThis.modules[field].name} <span class="description-3_Ncsb">by</span> ${globalThis.modules[field].author} <span class="description-3_Ncsb">(v${globalThis.modules[field].version})</span>`,
buttonText: 'Remove',
subtext: globalThis.modules[field].description,
onclick: (el) => {
el.textContent = 'Removing...';
globalThis.removeModuleUI(field, 'Manage Modules');
}
};
settingItem[2].push(toggleObj);
globalThis.logger.debug(`import.load.module.${field}`, `Added to Modules setting page`);
};
export const importModules = async (files) => {
globalThis.logger.debug('import', 'Looping through files');
for (let f of files) {
globalThis.importModule(f);
}
globalThis.logger.debug('import', 'Imported all files');
};
export const bindHandlers = (handlers) => {
for (let p in handlers) {
if (handlers.hasOwnProperty(p) && typeof handlers[p] === 'function') {
handlers[p] = handlers[p].bind(this);
}
}
return handlers;
};
export const getModuleFiles = async () => { // Ask for module files (one by one due to Discord restraint) until no file is chosen
globalThis.logger.debug('import.fileask', 'Asking for files');
let allFiles = [];
while (true) {
let files = await DiscordNative.fileManager.openFiles(); // Ask for file (singular)
if (files.length === 0) { // If no file, stop asking for files
break;
}
allFiles.push(files[0]); // Add file to files array
}
globalThis.logger.debug('import.fileask', 'Finished asking for files');
return allFiles;
};
export const importModulesFull = async () => {
if (window.DiscordNative === undefined) {
alert('Not supported in browser');
return [];
}
let files = await globalThis.getModuleFiles();
await globalThis.importModules(files);
return files;
};

@ -0,0 +1,54 @@
export const saveModuleSettings = async () => {
//globalThis.logger.debug('settings', 'Saving module settings...');
let settings = JSON.parse(localStorage.getItem('goosemodModules')) || {};
for (let p in globalThis.modules) {
if (globalThis.modules.hasOwnProperty(p)) {
settings[p] = await (globalThis.modules[p].getSettings || (async () => []))();
}
}
if (JSON.stringify(JSON.parse(localStorage.getItem('goosemodModules'))) !== JSON.stringify(settings)) {
localStorage.setItem('goosemodModules', JSON.stringify(settings));
globalThis.showToast('Settings saved');
}
//console.log(settings);
};
export const clearModuleSetting = (moduleName) => {
let settings = JSON.parse(localStorage.getItem('goosemodModules'));
delete settings[moduleName];
localStorage.setItem('goosemodModules', JSON.stringify(settings));
};
export const clearSettings = () => {
localStorage.removeItem('goosemodModules');
};
export const loadSavedModuleSetting = async (moduleName) => {
let settings = JSON.parse(localStorage.getItem('goosemodModules'));
await (globalThis.modules[moduleName].loadSettings || (async () => []))(settings[moduleName]);
};
export const loadSavedModuleSettings = async () => {
//globalThis.logger.debug('settings', 'Loading module settings...');
let settings = JSON.parse(localStorage.getItem('goosemodModules'));
if (!settings) return;
for (let p in globalThis.modules) {
if (globalThis.modules.hasOwnProperty(p) && settings.hasOwnProperty(p)) {
console.log(p, globalThis.modules[p].loadSettings, settings[p]);
await (globalThis.modules[p].loadSettings || (async () => []))(settings[p]);
}
}
return settings;
};

@ -0,0 +1,120 @@
export default {
modules: [],
apiBaseURL: 'https://goosemod-api.netlify.app',
jsCache: require('./jsCache'),
updateModules: async () => {
globalThis.moduleStoreAPI.modules = (await globalThis.cspBypasser.json(`${globalThis.moduleStoreAPI.apiBaseURL}/modules.json`, false)).sort((a, b) => a.name.localeCompare(b.name));
},
importModule: async (moduleName) => {
const moduleInfo = globalThis.moduleStoreAPI.modules.find((x) => x.filename === moduleName);
const jsCode = await globalThis.moduleStoreAPI.jsCache.getJSForModule(moduleName);
await globalThis.importModule({
filename: `${moduleInfo.filename}.js`,
data: jsCode
});
if (globalThis.modules[moduleName].onLoadingFinished !== undefined) {
await globalThis.modules[moduleName].onLoadingFinished();
}
let settingItem = globalThis.settings.items.find((x) => x[1] === 'Module Store');
let item = settingItem[2].find((x) => x.subtext === moduleInfo.description);
item.type = 'toggle-text-danger-button';
item.buttonText = 'Remove';
if (globalThis.isSettingsOpen() && !globalThis.initialImport) globalThis.settings.createFromItems();
},
moduleRemoved: async (m) => {
let item = globalThis.settings.items.find((x) => x[1] === 'Module Store')[2].find((x) => x.subtext === m.description);
if (item === undefined) return;
item.type = 'text-and-button';
item.buttonText = 'Import';
},
updateStoreSetting: () => {
let item = globalThis.settings.items.find((x) => x[1] === 'Module Store');
item[2] = item[2].slice(0, 2);
let sortedCategories = globalThis.moduleStoreAPI.modules.reduce((cats, o) => cats.includes(o.category) ? cats : cats.concat(o.category), []).sort((a, b) => a.localeCompare(b));
let arr = Object.entries(globalThis.moduleStoreAPI.modules.reduce((cats, o) => {
if (!cats[o.category]) cats[o.category]=[];
cats[o.category].push(o);
return cats;
},{})).sort((a, b) => a[0].localeCompare(b[0])).map(o => o[1]);
let funIndex = sortedCategories.indexOf('fun');
sortedCategories.push(sortedCategories.splice(funIndex, 1)[0]);
arr.push(arr.splice(funIndex, 1)[0]);
for (let i = 0; i < arr.length; i++) {
item[2].push({
type: 'header',
text: sortedCategories[i].replace(/\-/g, ' ')
});
for (let m of arr[i]) {
item[2].push({
type: globalThis.modules[m.filename] ? 'toggle-text-danger-button' : 'text-and-button',
text: `${m.name} <span class="description-3_Ncsb">by</span> ${m.author} <span class="description-3_Ncsb">(v${m.version})</span>`,
buttonText: globalThis.modules[m.filename] ? 'Remove' : 'Import',
subtext: m.description,
onclick: async (el) => {
if (globalThis.modules[m.filename]) {
el.textContent = 'Removing...';
globalThis.removeModuleUI(m.filename, 'Module Store');
return;
}
el.textContent = 'Importing...';
await globalThis.moduleStoreAPI.importModule(m.filename);
globalThis.settings.createFromItems();
globalThis.openSettingItem('Module Store');
},
isToggled: () => globalThis.modules[m.filename] !== undefined,
onToggle: async (checked) => {
if (checked) {
globalThis.modules[m.filename] = Object.assign({}, globalThis.disabledModules[m.filename]);
delete globalThis.disabledModules[m.filename];
await globalThis.modules[m.filename].onImport();
await globalThis.modules[m.filename].onLoadingFinished();
globalThis.loadSavedModuleSetting(m.filename);
} else {
globalThis.disabledModules[m.filename] = Object.assign({}, globalThis.modules[m.filename]);
globalThis.modules[m.filename].remove();
delete globalThis.modules[m.filename];
globalThis.settings.createFromItems();
globalThis.openSettingItem('Module Store');
}
globalThis.settings.createFromItems();
globalThis.openSettingItem('Module Store');
}
});
}
}
}
}

@ -0,0 +1,25 @@
export const getCache = () => JSON.parse(localStorage.getItem('goosemodJSCache') || '{}');
export const purgeCache = () => localStorage.removeItem('goosemodJSCache');
export const updateCache = (moduleName, version, js) => {
let cache = globalThis.moduleStoreAPI.jsCache.getCache();
cache[moduleName] = {version, js};
localStorage.setItem('goosemodJSCache', JSON.stringify(cache));
};
export const getJSForModule = async (moduleName) => {
const moduleInfo = globalThis.moduleStoreAPI.modules.find((x) => x.filename === moduleName);
const cache = globalThis.moduleStoreAPI.jsCache.getCache();
if (cache[moduleName] && moduleInfo.version === cache[moduleName].version) {
return cache[moduleName].js;
} else {
const js = await globalThis.cspBypasser.text(moduleInfo.codeURL, false);
globalThis.moduleStoreAPI.jsCache.updateCache(moduleName, moduleInfo.version, js);
return js;
}
};

@ -0,0 +1,49 @@
export default {
eggs: [
{
text: 'Goose Emoji',
message: 'Did you know there is no goose emoji? The most used one as a standin is a swan (\u{1F9A2}). Very sad.'
},
{
text: 'That\'s Numberwang!',
message: 'That\'s Wangernum!'
},
{
text: 'When does Atmosphere come out?',
message: 'June 15th!'
},
{
text: 'What is the meaning of life?',
message: '42, duh.'
},
{
text: 'Honk',
message: 'Honk'
},
{
text: 'GooseMod',
message: 'You talking about me? ;)'
}
],
interval: 0,
check: () => {
let el = document.getElementsByClassName('slateTextArea-1Mkdgw')[0];
if (!el) return;
for (let e of globalThis.messageEasterEggs.eggs) {
if (el.textContent === e.text) {
if (e.cooldown) {
e.cooldown -= 1;
continue;
}
globalThis.showToast(e.message);
e.cooldown = (e.cooldown || 6) - 1;
}
}
}
};

@ -0,0 +1,15 @@
let loadingToast = undefined;
export const startLoadingScreen = () => {
loadingToast = globalThis.showToast('Injecting GooseMod: Starting...', { timeout: 99999 });
};
export const updateLoadingScreen = (tip) => {
loadingToast.toastElem.innerHTML = `Injecting GooseMod: ${tip}`;
};
export const stopLoadingScreen = () => {
loadingToast.toastElem.innerHTML = `GooseMod has injected successfully`;
loadingToast.closeFn();
};

@ -0,0 +1,132 @@
export default (buttonText, title, description) => {
return new Promise((res) => {
//Making the div boxes to house the stuff
let confirmELContainer = document.createElement('div');
confirmELContainer.classList.add('layerContainer-yqaFcK');
let confirmELLayer = document.createElement('div');
confirmELLayer.classList.add('layer-2KE1M9');
let confirmEL = document.createElement('div');
confirmEL.classList.add("focusLock-Ns3yie");
confirmEL.setAttribute('role', 'dialog');
confirmEL.setAttribute('aria-label', title);
confirmEL.setAttribute('tabindex', '-1');
confirmEL.setAttribute('aria-model', 'true');
let confirmELRoot = document.createElement('div');
confirmELRoot.classList.add("root-1gCeng", "small-3iVZYw", "fullscreenOnMobile-1bD22y");
confirmELRoot.style.opacity = '1';
confirmELRoot.style.transform = 'scale(1)';
//Header stuff
let confirmELHeaderDiv = document.createElement('div');
confirmELHeaderDiv.classList.add('flex-1xMQg5', 'flex-1O1GKY', 'horizontal-1ae9ci', 'horizontal-2EEEnY', 'flex-1O1GKY', 'directionRow-3v3tfG', 'justifyStart-2NDFzi', 'alignCenter-1dQNNs', 'noWrap-3jynv6', 'header-1TKi98');
confirmELHeaderDiv.style.flex = '0 0 auto';
let confirmElHeaderH = document.createElement('h4');
confirmElHeaderH.classList.add("colorStandard-2KCXvj", "size14-e6ZScH", "h4-AQvcAz", "title-3sZWYQ", "defaultColor-1_ajX0", "defaultMarginh4-2vWMG5");
confirmElHeaderH.textContent = title;
//Body stuff
let confirmELBody = document.createElement('div');
confirmELBody.classList.add('content-1LAB8Z', 'content-mK72R6', 'thin-1ybCId', 'scrollerBase-289Jih');
confirmELBody.setAttribute('dir', 'ltr');
confirmELBody.style.overflow = 'hidden scroll';
confirmELBody.style.paddingRight = '8px';
let confirmELBodyText = document.createElement('div')
confirmELBodyText.classList.add('colorStandard-2KCXvj', 'size16-1P40sf')
confirmELBodyText.textContent = description;
let confirmELBodyWhitespace = document.createElement('div');
confirmELBodyWhitespace.setAttribute('aria-hidden', 'true');
confirmELBodyWhitespace.style.position = 'absolute';
confirmELBodyWhitespace.style.pointerEvents = 'none';
confirmELBodyWhitespace.style.minHeight = '0px';
confirmELBodyWhitespace.style.minWidth = '1px';
confirmELBodyWhitespace.style.flex = '0 0 auto';
confirmELBodyWhitespace.style.height = '20px';
//Button stuff
let confirmELButtonsDiv = document.createElement('div');
confirmELButtonsDiv.classList.add('flex-1xMQg5', 'flex-1O1GKY', 'horizontalReverse-2eTKWD', 'horizontalReverse-3tRjY7', 'flex-1O1GKY', 'directionRowReverse-m8IjIq', 'justifyStart-2NDFzi', 'alignStretch-DpGPf3', 'noWrap-3jynv6', 'footer-2gL1pp');
let confirmELButtonsSubmit = document.createElement('button');
confirmELButtonsSubmit.type = 'submit';
confirmELButtonsSubmit.classList.add('button-38aScr', 'lookFilled-1Gx00P', 'colorRed-1TFJan', 'sizeMedium-1AC_Sl', 'grow-q77ONN');
let confirmELButtonsSubmitDiv = document.createElement('div');
confirmELButtonsSubmitDiv.classList.add('contents-18-Yxp');
confirmELButtonsSubmitDiv.textContent = buttonText;
let confirmELButtonsCancel = document.createElement('button');
confirmELButtonsCancel.type = 'button';
confirmELButtonsCancel.classList.add('button-38aScr', 'lookLink-9FtZy-', 'colorPrimary-3b3xI6', 'sizeMedium-1AC_Sl', 'grow-q77ONN');
let confirmELButtonsCancelDiv = document.createElement('div');
confirmELButtonsCancelDiv.classList.add('contents-18-Yxp');
confirmELButtonsCancelDiv.textContent = 'Cancel';
//Misc
let confirmELDimBackgroundDiv = document.createElement('div');
confirmELDimBackgroundDiv.classList.add('backdropWithLayer-3_uhz4');
confirmELDimBackgroundDiv.style.opacity = '0.85';
confirmELDimBackgroundDiv.style.backgroundColor = 'rgb(0, 0, 0)';
confirmELDimBackgroundDiv.style.transform = 'translateZ(0px)';
//Add all the elements to the document
//Appending misc
confirmELContainer.appendChild(confirmELDimBackgroundDiv);
//Appending root elements
confirmELContainer.appendChild(confirmELLayer);
confirmELLayer.appendChild(confirmEL);
confirmEL.appendChild(confirmELRoot);
//Appending headers
confirmELRoot.appendChild(confirmELHeaderDiv);
confirmELHeaderDiv.appendChild(confirmElHeaderH);
//Appending body
confirmELRoot.appendChild(confirmELBody);
confirmELBody.appendChild(confirmELBodyText);
confirmELBody.appendChild(confirmELBodyWhitespace);
//Appending buttons
confirmELRoot.appendChild(confirmELButtonsDiv);
confirmELButtonsDiv.appendChild(confirmELButtonsSubmit);
confirmELButtonsDiv.appendChild(confirmELButtonsCancel);
confirmELButtonsSubmit.appendChild(confirmELButtonsSubmitDiv);
confirmELButtonsCancel.appendChild(confirmELButtonsCancelDiv);
//Inserting element into document
document.getElementById('app-mount').insertBefore(confirmELContainer, null);
//Making it function
confirmELButtonsSubmit.onclick = () => {
confirmELLayer.remove();
confirmELDimBackgroundDiv.remove();
res(true);
};
confirmELButtonsCancel.onclick = () => {
confirmELLayer.remove();
confirmELDimBackgroundDiv.remove();
res(false);
};
confirmELDimBackgroundDiv.onclick = () => {
confirmELLayer.remove();
confirmELDimBackgroundDiv.remove();
};
document.querySelector('div[aria-label="Close"]').onclick = () => {
confirmELLayer.remove();
confirmELDimBackgroundDiv.remove();
};
});
}

@ -0,0 +1,763 @@
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
export const removeModuleUI = (field, where) => {
let settingItem = globalThis.settings.items.find((x) => x[1] === 'Manage Modules');
settingItem[2].splice(settingItem[2].indexOf(settingItem[2].find((x) => x.subtext === globalThis.modules[field].description)), 1);
globalThis.moduleStoreAPI.moduleRemoved(globalThis.modules[field]);
globalThis.modules[field].remove();
delete globalThis.modules[field];
globalThis.clearModuleSetting(field);
globalThis.settings.createFromItems();
globalThis.openSettingItem(where);
};
export const isSettingsOpen = () => {
return document.querySelector('div[aria-label="USER_SETTINGS"] div[aria-label="Close"]') !== null;
};
export const closeSettings = () => {
let closeEl = document.querySelector('div[aria-label="USER_SETTINGS"] div[aria-label="Close"]');
if (closeEl === null) return false;
closeEl.click(); // Close settings via clicking the close settings button
};
export const openSettings = () => {
settingsButtonEl.click();
};
export const openSettingItem = (name) => {
try {
[...settingsSidebarGooseModContainer.children].find((x) => x.textContent === name).click();
return true;
} catch (e) {
return false;
}
};
export const reopenSettings = async () => {
globalThis.closeSettings();
await sleep(1000);
globalThis.openSettings();
await sleep(200);
globalThis.openSettingItem('Module Store');
};
// Settings UI stuff
let settingsButtonEl;
(async function() {
settingsButtonEl = document.querySelector('button[aria-label="User Settings"]');
while (!settingsButtonEl) {
globalThis.showToast('Failed to get settings button, retrying');
settingsButtonEl = document.querySelector('button[aria-label="User Settings"]');
await sleep(1000);
}
settingsButtonEl.addEventListener('click', injectInSettings);
})();
let settingsLayerEl, settingsSidebarEl, settingsSidebarGooseModContainer, settingsMainEl, settingsClasses;
const settings = {
items: [],
createItem: (panelName, content, clickHandler, danger = false) => {
globalThis.settings.items.push(['item', panelName, content, clickHandler, danger]);
},
createHeading: (headingName) => {
globalThis.settings.items.push(['heading', headingName]);
},
createSeparator: () => {
globalThis.settings.items.push(['separator']);
},
createFromItems: () => {
settingsSidebarGooseModContainer.innerHTML = '';
for (let i of globalThis.settings.items) {
switch (i[0]) {
case 'item':
globalThis.settings._createItem(i[1], i[2], i[3], i[4]);
break;
case 'heading':
globalThis.settings._createHeading(i[1]);
break;
case 'separator':
globalThis.settings._createSeparator();
break;
}
}
},
_createItem: (panelName, content, clickHandler, danger = false) => {
let parentEl = document.createElement('div');
let headerEl = document.createElement('h2');
headerEl.textContent = `${panelName} ${content[0]}`;
headerEl.classList.add('colorStandard-2KCXvj', 'size14-e6ZScH', 'h2-2gWE-o', 'title-3sZWYQ', 'defaultColor-1_ajX0', 'defaultMarginh2-2LTaUL');
parentEl.appendChild(headerEl);
let contentEl = document.createElement('div');
contentEl.className = 'children-rWhLdy';
parentEl.appendChild(contentEl);
let i = 0;
for (let e of content.slice(1)) {
let el;
switch (e.type) {
case 'header':
el = document.createElement('h2');
if (i !== 0) {
el.classList.add('marginTop20-3TxNs6');
}
el.classList.add('colorStandard-2KCXvj', 'size14-e6ZScH', 'h5-18_1nd', 'title-3sZWYQ', 'marginBottom8-AtZOdT');
el.textContent = e.text;
break;
case 'toggle': {
el = document.createElement('div');
el.classList.add('marginBottom20-32qID7');
let txtEl = document.createElement('span');
txtEl.classList.add('titleDefault-a8-ZSr', 'title-31JmR4');
txtEl.style.float = 'left';
txtEl.innerHTML = e.text;
let checked = e.isToggled();
let toggleEl = document.createElement('div');
toggleEl.className = 'control-2BBjec';
let offHTML = '<div class="container-3auIfb" tabindex="-1" style="opacity: 1; background-color: rgb(114, 118, 125);"><svg class="slider-TkfMQL" viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet" style="left: -3px;"><rect fill="white" x="4" y="0" height="20" width="20" rx="10"></rect><svg viewBox="0 0 20 20" fill="none"><path fill="rgba(114, 118, 125, 1)" d="M5.13231 6.72963L6.7233 5.13864L14.855 13.2704L13.264 14.8614L5.13231 6.72963Z"></path><path fill="rgba(114, 118, 125, 1)" d="M13.2704 5.13864L14.8614 6.72963L6.72963 14.8614L5.13864 13.2704L13.2704 5.13864Z"></path></svg></svg><input id="uid_328" type="checkbox" class="input-rwLH4i" tabindex="0"></div>';
let onHTML = '<div class="container-3auIfb" tabindex="-1" style="opacity: 1; background-color: rgb(67, 181, 129);"><svg class="slider-TkfMQL" viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet" style="left: 12px;"><rect fill="white" x="4" y="0" height="20" width="20" rx="10"></rect><svg viewBox="0 0 20 20" fill="none"><path fill="rgba(67, 181, 129, 1)" d="M7.89561 14.8538L6.30462 13.2629L14.3099 5.25755L15.9009 6.84854L7.89561 14.8538Z"></path><path fill="rgba(67, 181, 129, 1)" d="M4.08643 11.0903L5.67742 9.49929L9.4485 13.2704L7.85751 14.8614L4.08643 11.0903Z"></path></svg></svg><input id="uid_328" type="checkbox" class="input-rwLH4i" tabindex="0"></div>';
let fn = () => {
checked = !checked;
if (checked) {
toggleEl.innerHTML = onHTML;
} else {
toggleEl.innerHTML = offHTML;
}
e.onToggle(checked, el);
};
toggleEl.onclick = fn;
txtEl.onclick = fn;
el.appendChild(txtEl);
el.appendChild(toggleEl);
toggleEl.innerHTML = checked ? onHTML : offHTML;
toggleEl.style.float = 'right';
if (e.subtext) {
let subtextEl = document.createElement('div');
subtextEl.classList.add('colorStandard-2KCXvj', 'size14-e6ZScH', 'description-3_Ncsb', 'formText-3fs7AJ', 'note-1V3kyJ', 'modeDefault-3a2Ph1');
subtextEl.textContent = e.subtext;
subtextEl.style.clear = 'both';
el.appendChild(subtextEl);
}
let dividerEl = document.createElement('div');
dividerEl.classList.add('divider-3573oO', 'dividerDefault-3rvLe-');
dividerEl.style.marginTop = e.subtext ? '20px' : '45px';
el.appendChild(dividerEl);
break;
}
case 'text':
el = document.createElement('div');
el.classList.add('marginBottom20-32qID7');
let textEl = document.createElement('span');
textEl.classList.add('titleDefault-a8-ZSr', 'title-31JmR4');
textEl.style.float = 'left';
textEl.innerHTML = e.text;
el.appendChild(textEl);
if (e.subtext) {
let subtextEl = document.createElement('div');
subtextEl.classList.add('colorStandard-2KCXvj', 'size14-e6ZScH', 'description-3_Ncsb', 'formText-3fs7AJ', 'note-1V3kyJ', 'modeDefault-3a2Ph1');
subtextEl.innerHTML = e.subtext;
subtextEl.style.clear = 'both';
el.appendChild(subtextEl);
}
let dividerEl_ = document.createElement('div');
dividerEl_.classList.add('divider-3573oO', 'dividerDefault-3rvLe-');
dividerEl_.style.marginTop = e.subtext ? '20px' : '45px';
el.appendChild(dividerEl_);
break;
case 'text-and-danger-button':
el = document.createElement('div');
el.classList.add('marginBottom20-32qID7');
let txtEl2 = document.createElement('span');
txtEl2.classList.add('titleDefault-a8-ZSr', 'title-31JmR4');
txtEl2.style.float = 'left';
txtEl2.innerHTML = e.text;
let buttonEl = document.createElement('div');
buttonEl.classList.add('button-38aScr', 'lookOutlined-3sRXeN', 'colorRed-1TFJan', 'sizeSmall-2cSMqn', 'grow-q77ONN');
buttonEl.onclick = () => {
e.onclick(buttonEl);
};
buttonEl.style.cursor = 'pointer';
buttonEl.style.float = 'right';
let contentsEl2 = document.createElement('div');
contentsEl2.classList.add('contents-18-Yxp');
contentsEl2.textContent = e.buttonText;
buttonEl.appendChild(contentsEl2);
el.appendChild(txtEl2);
el.appendChild(buttonEl);
if (e.subtext) {
let subtextEl = document.createElement('div');
subtextEl.classList.add('colorStandard-2KCXvj', 'size14-e6ZScH', 'description-3_Ncsb', 'formText-3fs7AJ', 'note-1V3kyJ', 'modeDefault-3a2Ph1');
subtextEl.textContent = e.subtext;
subtextEl.style.clear = 'both';
el.appendChild(subtextEl);
}
let dividerEl2 = document.createElement('div');
dividerEl2.classList.add('divider-3573oO', 'dividerDefault-3rvLe-');
dividerEl2.style.marginTop = e.subtext ? '20px' : '45px';
el.appendChild(dividerEl2);
break;
case 'text-and-button':
el = document.createElement('div');
el.classList.add('marginBottom20-32qID7');
let txtEl3 = document.createElement('span');
txtEl3.classList.add('titleDefault-a8-ZSr', 'title-31JmR4');
txtEl3.style.float = 'left';
txtEl3.innerHTML = e.text;
let buttonEl2 = document.createElement('div');
buttonEl2.classList.add('button-38aScr', 'lookFilled-1Gx00P', 'colorBrand-3pXr91', 'sizeSmall-2cSMqn', 'grow-q77ONN');
buttonEl2.onclick = () => {
e.onclick(buttonEl2);
};
buttonEl2.style.cursor = 'pointer';
buttonEl2.style.float = 'right';
let contentsEl3 = document.createElement('div');
contentsEl3.classList.add('contents-18-Yxp');
contentsEl3.textContent = e.buttonText;
buttonEl2.appendChild(contentsEl3);
el.appendChild(txtEl3);
el.appendChild(buttonEl2);
if (e.subtext) {
let subtextEl2 = document.createElement('div');
subtextEl2.classList.add('colorStandard-2KCXvj', 'size14-e6ZScH', 'description-3_Ncsb', 'formText-3fs7AJ', 'note-1V3kyJ', 'modeDefault-3a2Ph1');
subtextEl2.textContent = e.subtext;
subtextEl2.style.clear = 'both';
el.appendChild(subtextEl2);
}
let dividerEl3 = document.createElement('div');
dividerEl3.classList.add('divider-3573oO', 'dividerDefault-3rvLe-');
dividerEl3.style.marginTop = e.subtext ? '20px' : '45px';
el.appendChild(dividerEl3);
break;
case 'button':
el = document.createElement('button');
el.classList.add('button-38aScr', 'lookFilled-1Gx00P', 'colorBrand-3pXr91', 'sizeSmall-2cSMqn', 'grow-q77ONN');
let contentsEl = document.createElement('div');
contentsEl.classList.add('contents-18-Yxp');
contentsEl.textContent = e.text;
el.appendChild(contentsEl);
el.onclick = e.onclick;
break;
case 'toggle-text-button': {
el = document.createElement('div');
el.classList.add('marginBottom20-32qID7');
let checked = e.isToggled();
let toggleEl = document.createElement('div');
toggleEl.className = 'control-2BBjec';
let offHTML = '<div class="container-3auIfb" tabindex="-1" style="opacity: 1; background-color: rgb(114, 118, 125);"><svg class="slider-TkfMQL" viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet" style="left: -3px;"><rect fill="white" x="4" y="0" height="20" width="20" rx="10"></rect><svg viewBox="0 0 20 20" fill="none"><path fill="rgba(114, 118, 125, 1)" d="M5.13231 6.72963L6.7233 5.13864L14.855 13.2704L13.264 14.8614L5.13231 6.72963Z"></path><path fill="rgba(114, 118, 125, 1)" d="M13.2704 5.13864L14.8614 6.72963L6.72963 14.8614L5.13864 13.2704L13.2704 5.13864Z"></path></svg></svg><input id="uid_328" type="checkbox" class="input-rwLH4i" tabindex="0"></div>';
let onHTML = '<div class="container-3auIfb" tabindex="-1" style="opacity: 1; background-color: rgb(67, 181, 129);"><svg class="slider-TkfMQL" viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet" style="left: 12px;"><rect fill="white" x="4" y="0" height="20" width="20" rx="10"></rect><svg viewBox="0 0 20 20" fill="none"><path fill="rgba(67, 181, 129, 1)" d="M7.89561 14.8538L6.30462 13.2629L14.3099 5.25755L15.9009 6.84854L7.89561 14.8538Z"></path><path fill="rgba(67, 181, 129, 1)" d="M4.08643 11.0903L5.67742 9.49929L9.4485 13.2704L7.85751 14.8614L4.08643 11.0903Z"></path></svg></svg><input id="uid_328" type="checkbox" class="input-rwLH4i" tabindex="0"></div>';
toggleEl.innerHTML = checked ? onHTML : offHTML;
let fn = () => {
checked = !checked;
if (checked) {
toggleEl.innerHTML = onHTML;
} else {
toggleEl.innerHTML = offHTML;
}
e.onToggle(checked, el);
};
toggleEl.onclick = fn;
toggleEl.style.float = 'left';
toggleEl.style.marginRight = '8px';
el.appendChild(toggleEl);
let txtEl = document.createElement('span');
txtEl.onclick = fn;
txtEl.classList.add('titleDefault-a8-ZSr', 'title-31JmR4');
txtEl.style.float = 'left';
txtEl.innerHTML = e.text;
let buttonEl = document.createElement('div');
buttonEl.classList.add('button-38aScr', 'lookFilled-1Gx00P', 'colorBrand-3pXr91', 'sizeSmall-2cSMqn', 'grow-q77ONN');
buttonEl.onclick = () => {
e.onclick(buttonEl);
};
buttonEl.style.cursor = 'pointer';
buttonEl.style.float = 'right';
let contentsEl = document.createElement('div');
contentsEl.classList.add('contents-18-Yxp');
contentsEl.textContent = e.buttonText;
buttonEl.appendChild(contentsEl);
el.appendChild(txtEl);
el.appendChild(buttonEl);
if (e.subtext) {
let subtextEl = document.createElement('div');
subtextEl.classList.add('colorStandard-2KCXvj', 'size14-e6ZScH', 'description-3_Ncsb', 'formText-3fs7AJ', 'note-1V3kyJ', 'modeDefault-3a2Ph1');
subtextEl.textContent = e.subtext;
subtextEl.style.clear = 'both';
el.appendChild(subtextEl);
}
let dividerEl = document.createElement('div');
dividerEl.classList.add('divider-3573oO', 'dividerDefault-3rvLe-');
dividerEl.style.marginTop = e.subtext ? '20px' : '45px';
el.appendChild(dividerEl);
break;
}
case 'toggle-text-danger-button': {
el = document.createElement('div');
el.classList.add('marginBottom20-32qID7');
let checked = e.isToggled();
let toggleEl = document.createElement('div');
toggleEl.className = 'control-2BBjec';
let offHTML = '<div class="container-3auIfb" tabindex="-1" style="opacity: 1; background-color: rgb(114, 118, 125);"><svg class="slider-TkfMQL" viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet" style="left: -3px;"><rect fill="white" x="4" y="0" height="20" width="20" rx="10"></rect><svg viewBox="0 0 20 20" fill="none"><path fill="rgba(114, 118, 125, 1)" d="M5.13231 6.72963L6.7233 5.13864L14.855 13.2704L13.264 14.8614L5.13231 6.72963Z"></path><path fill="rgba(114, 118, 125, 1)" d="M13.2704 5.13864L14.8614 6.72963L6.72963 14.8614L5.13864 13.2704L13.2704 5.13864Z"></path></svg></svg><input id="uid_328" type="checkbox" class="input-rwLH4i" tabindex="0"></div>';
let onHTML = '<div class="container-3auIfb" tabindex="-1" style="opacity: 1; background-color: rgb(67, 181, 129);"><svg class="slider-TkfMQL" viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet" style="left: 12px;"><rect fill="white" x="4" y="0" height="20" width="20" rx="10"></rect><svg viewBox="0 0 20 20" fill="none"><path fill="rgba(67, 181, 129, 1)" d="M7.89561 14.8538L6.30462 13.2629L14.3099 5.25755L15.9009 6.84854L7.89561 14.8538Z"></path><path fill="rgba(67, 181, 129, 1)" d="M4.08643 11.0903L5.67742 9.49929L9.4485 13.2704L7.85751 14.8614L4.08643 11.0903Z"></path></svg></svg><input id="uid_328" type="checkbox" class="input-rwLH4i" tabindex="0"></div>';
toggleEl.innerHTML = checked ? onHTML : offHTML;
let fn = () => {
checked = !checked;
if (checked) {
toggleEl.innerHTML = onHTML;
} else {
toggleEl.innerHTML = offHTML;
}
e.onToggle(checked, el);
};
toggleEl.onclick = fn;
toggleEl.style.float = 'left';
toggleEl.style.marginRight = '8px';
el.appendChild(toggleEl);
let txtEl = document.createElement('span');
txtEl.onclick = fn;
txtEl.classList.add('titleDefault-a8-ZSr', 'title-31JmR4');
txtEl.style.float = 'left';
txtEl.innerHTML = e.text;
let buttonEl = document.createElement('div');
buttonEl.classList.add('button-38aScr', 'lookOutlined-3sRXeN', 'colorRed-1TFJan', 'sizeSmall-2cSMqn', 'grow-q77ONN');
buttonEl.onclick = () => {
e.onclick(buttonEl);
};
buttonEl.style.cursor = 'pointer';
buttonEl.style.float = 'right';
let contentsEl = document.createElement('div');
contentsEl.classList.add('contents-18-Yxp');
contentsEl.textContent = e.buttonText;
buttonEl.appendChild(contentsEl);
el.appendChild(txtEl);
el.appendChild(buttonEl);
if (e.subtext) {
let subtextEl = document.createElement('div');
subtextEl.classList.add('colorStandard-2KCXvj', 'size14-e6ZScH', 'description-3_Ncsb', 'formText-3fs7AJ', 'note-1V3kyJ', 'modeDefault-3a2Ph1');
subtextEl.textContent = e.subtext;
subtextEl.style.clear = 'both';
el.appendChild(subtextEl);
}
let dividerEl = document.createElement('div');
dividerEl.classList.add('divider-3573oO', 'dividerDefault-3rvLe-');
dividerEl.style.marginTop = e.subtext ? '20px' : '45px';
el.appendChild(dividerEl);
break;
}
}
contentEl.appendChild(el);
i++;
}
let el = document.createElement('div');
el.classList.add(settingsClasses['item']);
el.classList.add(settingsClasses['themed']);
if (danger) {
el.style.color = 'rgb(240, 71, 71)';
el.onmouseenter = () => {
el.style.backgroundColor = 'rgba(240, 71, 71, 0.1)';
};
el.onmouseleave = () => {
el.style.backgroundColor = 'unset';
};
}
el.setAttribute('tabindex', '0');
el.setAttribute('role', 'button');
el.innerText = panelName;
el.onclick = async () => {
if (clickHandler !== undefined) {
clickHandler();
return;
}
setTimeout(() => {
settingsMainEl.firstChild.innerHTML = '';
settingsMainEl.firstChild.appendChild(parentEl);
for (let e of settingsSidebarEl.children) {
e.classList.remove(settingsClasses['selected']);
}
el.classList.add(settingsClasses['selected']);
}, 10);
};
settingsSidebarEl.addEventListener('click', () => {
if (globalThis.removed === true) return;
el.classList.remove(settingsClasses['selected']);
});
if (panelName === 'Manage Modules' && window.DiscordNative === undefined) return;
settingsSidebarGooseModContainer.appendChild(el);
},
_createHeading: (headingName) => {
let el = document.createElement('div');
el.className = settingsClasses['header'];
el.setAttribute('tabindex', '0');
el.setAttribute('role', 'button');
el.innerText = headingName;
settingsSidebarGooseModContainer.appendChild(el);
},
_createSeparator: () => {
let el = document.createElement('div');
el.className = settingsClasses['separator'];
settingsSidebarGooseModContainer.appendChild(el);
}
};
globalThis.settings = settings;
let tryingToInject = false;
export const injectInSettings = async () => {
if (globalThis.removed) return;
if (tryingToInject) return;
tryingToInject = true;
settingsLayerEl = undefined;
while (!settingsLayerEl) {
settingsLayerEl = document.querySelector('div[aria-label="USER_SETTINGS"]');
await sleep(2);
}
settingsSidebarEl = settingsLayerEl.querySelector('nav > div');
if (settingsSidebarEl.classList.contains('goosemod-settings-injected')) return;
settingsSidebarEl.classList.add('goosemod-settings-injected');
settingsClasses = {};
for (let e of settingsSidebarEl.children) {
for (let c of e.classList) {
let name = c.split('-')[0];
if (settingsClasses[name] === undefined) {
settingsClasses[name] = c;
}
}
}
settingsSidebarGooseModContainer = document.createElement('div');
settingsSidebarEl.insertBefore(settingsSidebarGooseModContainer, settingsSidebarEl.childNodes[settingsSidebarEl.childElementCount - 4]);//settingsSidebarEl.querySelector(`.${settingsClasses.item}:not(${settingsClasses.themed}) ~ ${settingsClasses.item}:not(${settingsClasses.themed})`));
let el = document.createElement('div');
el.className = settingsClasses['separator'];
settingsSidebarEl.insertBefore(el, settingsSidebarGooseModContainer.nextSibling); //.insertBefore(settingsSidebarGooseModContainer, settingsSidebarEl.childNodes[settingsSidebarEl.childElementCount - 4]);//settingsSidebarEl.querySelector(`.${settingsClasses.item}:not(${settingsClasses.themed}) ~ ${settingsClasses.item}:not(${settingsClasses.themed})`));
let versionEl = document.createElement('div');
versionEl.classList.add('colorMuted-HdFt4q', 'size12-3cLvbJ');
versionEl.textContent = `GooseMod ${globalThis.version} (${globalThis.injectorHash.substring(0, 7)})`;
settingsSidebarEl.lastChild.appendChild(versionEl);
let versionElUntethered = document.createElement('div');
versionElUntethered.classList.add('colorMuted-HdFt4q', 'size12-3cLvbJ');
versionElUntethered.textContent = `GooseMod Untethered ${globalThis.untetheredVersion || 'N/A'}`;
settingsSidebarEl.lastChild.appendChild(versionElUntethered);
settingsMainEl = settingsLayerEl.querySelector('main');
globalThis.settings.createFromItems();
tryingToInject = false;
};
export const checkSettingsOpenInterval = setInterval(async () => {
if (tryingToInject) return;
let el = document.querySelector('div[aria-label="USER_SETTINGS"]');
if (el && !el.querySelector('nav > div').classList.contains('goosemod-settings-injected')) {
await globalThis.injectInSettings();
}
}, 100);
globalThis.settings.createHeading('GooseMod');
globalThis.settings.createItem('Manage Modules', ['',
{
type: 'button',
text: 'Import Local Modules',
onclick: async () => {
let files = await globalThis.importModulesFull();
for (let f of files) {
let n = f.filename.split('.').slice(0, -1).join('.');
if (globalThis.modules[n].onLoadingFinished !== undefined) {
await globalThis.modules[n].onLoadingFinished();
}
}
globalThis.settings.createFromItems();
globalThis.openSettingItem('Manage Modules');
},
},
{
type: 'header',
text: 'Imported Modules'
}
]);
globalThis.settings.createItem('Module Store', ['',
{
type: 'button',
text: 'Update Index',
onclick: async () => {
await globalThis.moduleStoreAPI.updateModules();
await globalThis.moduleStoreAPI.updateStoreSetting();
globalThis.settings.createFromItems();
globalThis.openSettingItem('Module Store');
},
}
]);
globalThis.settings.createSeparator();
globalThis.settings.createItem('Uninstall', [""], async () => {
if (await globalThis.confirmDialog('Uninstall', 'Uninstall GooseMod', 'Are you sure you want to uninstall GooseMod? This is a quick uninstall, it may leave some code behind but there should be no remaining noticable changes.')) {
globalThis.closeSettings();
globalThis.remove();
}
}, true);
if (window.DiscordNative !== undefined) {
globalThis.settings.createItem('Local Reinstall', [''], async () => {
if (await globalThis.confirmDialog('Reinstall', 'Reinstall GooseMod', 'Are you sure you want to reinstall GooseMod? This will uninstall GooseMod, then ask you for the inject.js file, then run it to reinstall.')) {
globalThis.closeSettings();
globalThis.remove();
eval(ab2str((await DiscordNative.fileManager.openFiles())[0].data));
}
}, true);
}
globalThis.settings.createSeparator();
globalThis.settings.createHeading('GooseMod Modules');

@ -0,0 +1,155 @@
/* Toasts from BBD, slightly modified to fit CSS variables and tweaked to liking - full credit and sources:
** CSS: https://github.com/rauenzi/BetterDiscordApp/blob/master/src/styles/index.css
** JS: https://github.com/rauenzi/BetterDiscordApp/blob/master/src/modules/utils.js
** Again huge thanks to Rauenzi / (B)BD for basing some ideas and code (especially related to webpack modules)
** (Classes renamed to not interfere with (B)BD installed alongside)
*/
const toastCSS = `.gm-toasts {
position: fixed;
display: flex;
top: 0;
flex-direction: column;
align-items: center;
justify-content: flex-end;
pointer-events: none;
z-index: 4000;
}
@keyframes gm-toast-up {
from {
transform: translateY(0);
opacity: 0;
}
}
.gm-toast {
animation: gm-toast-up 300ms ease;
transform: translateY(-10px);
background: var(--background-floating);
padding: 10px;
border-radius: 5px;
box-shadow: 0 0 0 1px rgba(32,34,37,.6), 0 2px 10px 0 rgba(0,0,0,.2);
font-weight: 500;
color: #fff;
user-select: text;
font-size: 14px;
opacity: 1;
margin-top: 10px;
pointer-events: none;
user-select: none;
text-align: center;
}
@keyframes gm-toast-down {
to {
transform: translateY(0px);
opacity: 0;
}
}
.gm-toast.closing {
animation: gm-toast-down 200ms ease;
animation-fill-mode: forwards;
opacity: 1;
transform: translateY(-10px);
}
.gm-toast.icon {
padding-left: 30px;
background-size: 20px 20px;
background-repeat: no-repeat;
background-position: 6px 50%;
}
.gm-toast.toast-info {
background-color: #4a90e2;
}
.gm-toast.toast-info.icon {
background-image: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPiAgICA8cGF0aCBkPSJNMTIgMkM2LjQ4IDIgMiA2LjQ4IDIgMTJzNC40OCAxMCAxMCAxMCAxMC00LjQ4IDEwLTEwUzE3LjUyIDIgMTIgMnptMSAxNWgtMnYtNmgydjZ6bTAtOGgtMlY3aDJ2MnoiLz48L3N2Zz4=);
}
.gm-toast.toast-success {
background-color: #43b581;
}
.gm-toast.toast-success.icon {
background-image: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPiAgICA8cGF0aCBkPSJNMTIgMkM2LjQ4IDIgMiA2LjQ4IDIgMTJzNC40OCAxMCAxMCAxMCAxMC00LjQ4IDEwLTEwUzE3LjUyIDIgMTIgMnptLTIgMTVsLTUtNSAxLjQxLTEuNDFMMTAgMTQuMTdsNy41OS03LjU5TDE5IDhsLTkgOXoiLz48L3N2Zz4=);
}
.gm-toast.toast-danger,
.gm-toast.toast-error {
background-color: #f04747;
}
.gm-toast.toast-danger.icon,
.gm-toast.toast-error.icon {
background-image: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gICAgPHBhdGggZD0iTTEyIDJDNi40NyAyIDIgNi40NyAyIDEyczQuNDcgMTAgMTAgMTAgMTAtNC40NyAxMC0xMFMxNy41MyAyIDEyIDJ6bTUgMTMuNTlMMTUuNTkgMTcgMTIgMTMuNDEgOC40MSAxNyA3IDE1LjU5IDEwLjU5IDEyIDcgOC40MSA4LjQxIDcgMTIgMTAuNTkgMTUuNTkgNyAxNyA4LjQxIDEzLjQxIDEyIDE3IDE1LjU5eiIvPiAgICA8cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PC9zdmc+);
}
.gm-toast.toast-warning,
.gm-toast.toast-warn {
background-color: #FFA600;
color: white;
}
.gm-toast.toast-warning.icon,
.gm-toast.toast-warn.icon {
background-image: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPiAgICA8cGF0aCBkPSJNMSAyMWgyMkwxMiAyIDEgMjF6bTEyLTNoLTJ2LTJoMnYyem0wLTRoLTJ2LTRoMnY0eiIvPjwvc3ZnPg==);
}`;
const styleSheet = document.createElement('style'); // Add CSS as stylesheet
styleSheet.textContent = toastCSS;
document.head.appendChild(styleSheet);
export default (text, options = {}) => {
if (!document.querySelector('.gm-toasts')) {
const container = document.querySelector('.sidebar-2K8pFh + div') || null;
const memberlist = container ? container.querySelector('.membersWrap-2h-GB4') : null;
const form = container ? container.querySelector('form') : null;
const left = container ? container.getBoundingClientRect().left : 310;
const right = memberlist ? memberlist.getBoundingClientRect().left : 0;
const width = right ? right - container.getBoundingClientRect().left : window.innerWidth - left - 240;
const bottom = form ? form.offsetHeight : 80;
const toastWrapper = document.createElement('div');
toastWrapper.classList.add('gm-toasts');
toastWrapper.style.setProperty('left', left + 'px');
toastWrapper.style.setProperty('width', width + 'px');
toastWrapper.style.setProperty('bottom', bottom + 'px');
document.querySelector('#app-mount').appendChild(toastWrapper);
}
const {type = '', icon = true, timeout = 3000} = options;
const toastElem = document.createElement('div');
toastElem.classList.add('gm-toast');
if (type) toastElem.classList.add('toast-' + type);
if (type && icon) toastElem.classList.add('icon');
toastElem.textContent = text;
document.querySelector('.gm-toasts').appendChild(toastElem);
let closeFn = () => {
toastElem.classList.add('closing');
setTimeout(() => {
toastElem.remove();
if (!document.querySelectorAll('.gm-toasts .gm-toast').length) document.querySelector('.gm-toasts').remove();
}, 300);
};
setTimeout(closeFn, timeout);
return { toastElem, closeFn };
};

@ -0,0 +1,109 @@
export default {
frame: document.createElement('iframe'),
init: async () => {
globalThis.cspBypasser.frame.src = 'discord:';
document.body.appendChild(globalThis.cspBypasser.frame);
//await awaitIframe(globalThis.cspBypasser.frame);
let script = document.createElement('script');
script.type = 'text/javascript';
let code = `
window.addEventListener('message', async (e) => {
const {url, type, useCORSProxy} = e.data;
if (!url) return;
const proxyURL = useCORSProxy ? \`https://cors-anywhere.herokuapp.com/\${url}\` : url;
if (type === 'img') {
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
let img = new Image();
img.src = proxyURL;
img.crossOrigin = 'anonymous';
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
e.source.postMessage({ verify: url, data: canvas.toDataURL("image/png") });
};
return;
}
const req = await fetch(proxyURL, {
cache: 'no-store'
});
e.source.postMessage({ verify: url, data: await req[type]() });
});`;
script.appendChild(document.createTextNode(code));
globalThis.cspBypasser.frame.contentDocument.head.appendChild(script);
},
runCode: (code) => {
let script = document.createElement('script');
script.type = 'text/javascript';
script.appendChild(document.createTextNode(code));
globalThis.cspBypasser.frame.contentDocument.head.appendChild(script);
},
json: (url, useCORSProxy = true) => {
return new Promise((res) => {
globalThis.cspBypasser.frame.contentWindow.postMessage({url, type: 'json', useCORSProxy});
window.addEventListener('message', async (e) => {
if (e.data.verify !== url) return;
res(e.data.data);
});
});
},
text: (url, useCORSProxy = true) => {
return new Promise((res) => {
globalThis.cspBypasser.frame.contentWindow.postMessage({url, type: 'text', useCORSProxy});
window.addEventListener('message', async (e) => {
if (e.data.verify !== url) return;
res(e.data.data);
});
});
},
blob: (url, useCORSProxy = true) => {
return new Promise((res) => {
globalThis.cspBypasser.frame.contentWindow.postMessage({url, type: 'blob', useCORSProxy});
window.addEventListener('message', async (e) => {
if (e.data.verify !== url) return;
res(e.data.data);
});
});
},
image: (url, useCORSProxy = true) => {
return new Promise((res) => {
globalThis.cspBypasser.frame.contentWindow.postMessage({url, type: 'img', useCORSProxy});
window.addEventListener('message', async (e) => {
if (e.data.verify !== url) return;
res(e.data.data);
});
});
},
};

@ -0,0 +1,15 @@
// Bypass to get Local Storage (Discord block / remove it) - Source / credit: https://stackoverflow.com/questions/52509440/discord-window-localstorage-is-undefined-how-to-get-access-to-the-localstorage
function getLocalStoragePropertyDescriptor() {
const iframe = document.createElement('iframe');
document.head.append(iframe);
const pd = Object.getOwnPropertyDescriptor(iframe.contentWindow, 'localStorage');
iframe.remove();
return pd;
}
export default () => {
Object.defineProperty(window, 'localStorage', getLocalStoragePropertyDescriptor());
};

@ -0,0 +1,46 @@
const obj = { // https://github.com/rauenzi/BetterDiscordApp/blob/master/src/modules/webpackModules.js
req: undefined,
init: () => {
obj.req = window.webpackJsonp.push([[], {__extra_id__: (module, exports, req) => module.exports = req}, [["__extra_id__"]]]);
delete obj.req.m.__extra_id__;
delete obj.req.c.__extra_id__;
},
find: (filter) => {
for (const i in obj.req.c) {
if (obj.req.c.hasOwnProperty(i)) {
const m = obj.req.c[i].exports;
if (m && m.__esModule && m.default && filter(m.default)) return m.default;
if (m && filter(m)) return m;
}
}
// console.warn("Cannot find loaded module in cache");
return null;
},
findAll: (filter) => {
const modules = [];
for (const i in obj.req.c) {
if (obj.req.c.hasOwnProperty(i)) {
const m = obj.req.c[i].exports;
if (m && m.__esModule && m.default && filter(m.default)) modules.push(m.default);
else if (m && filter(m)) modules.push(m);
}
}
return modules;
},
findByProps: (...propNames) => obj.find(module => propNames.every(prop => module[prop] !== undefined)),
findByPropsAll: (...propNames) => obj.findAll(module => propNames.every(prop => module[prop] !== undefined)),
findByPrototypes: (...protoNames) => obj.find(module => module.prototype && protoNames.every(protoProp => module.prototype[protoProp] !== undefined)),
findByDisplayName: (displayName) => obj.find(module => module.displayName === displayName),
};
obj.init();
export default obj;

@ -0,0 +1,7 @@
const hash = async (str, algorithm) => {
const buf = await crypto.subtle.digest(algorithm, new TextEncoder('utf-8').encode(str));
return Array.prototype.map.call(new Uint8Array(buf), x => (('00' + x.toString(16)).slice(-2))).join('');
};
export const sha256 = (str) => hash(str, 'SHA-256');
export const sha512 = (str) => hash(str, 'SHA-512');

@ -0,0 +1,5 @@
export const debug = (region, ...args) => {
let parentRegion = region.split('.')[0];
console.log(`%cGooseMod%c %c${region}`, 'border: 1px solid white; padding: 2px; background-color: black; color: white', 'background-color: none', `border: 1px solid white; padding: 2px; background-color: ${(globalThis.modules[parentRegion] && globalThis.modules[parentRegion].logRegionColor) || 'rgb(100, 0, 0)'}; color: white`, ...(args));
};
Loading…
Cancel
Save