start adding a custom client mod
69
assets/public/custom/pushMessages.js
Normal file
@ -0,0 +1,69 @@
|
||||
function checkServiceWorkerSupport() {
|
||||
if (!"serviceWorker" in navigator || !"PushManager" in window)
|
||||
throw new Error("Your browser does not have Service Worker support")
|
||||
}
|
||||
|
||||
async function unregisterPush() {
|
||||
checkServiceWorkerSupport();
|
||||
|
||||
const registration = await navigator.serviceWorker.getRegistration();
|
||||
if (!registration) return;
|
||||
|
||||
const subscription = await reg.pushManager.getSubscription();
|
||||
if (!subscription) return;
|
||||
|
||||
await subscription.unsubscribe();
|
||||
}
|
||||
|
||||
async function registerPush(publicKey) {
|
||||
checkServiceWorkerSupport();
|
||||
|
||||
const registration = await navigator.serviceWorker.register("/assets/custom/serviceWorker.js");
|
||||
console.log("Service Worker registered");
|
||||
|
||||
let subscription = await registration.pushManager.getSubscription();
|
||||
|
||||
if (!subscription) {
|
||||
const applicationServerKey = urlBase64ToUint8Array(publicKey);
|
||||
subscription = await registration.pushManager.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey
|
||||
});
|
||||
console.log("Push subscription obtained");
|
||||
}
|
||||
|
||||
const response = await fetch(`${window.GLOBAL_ENV.API_ENDPOINT}/v9/users/@me/devices`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": localStorage.token
|
||||
},
|
||||
body: JSON.stringify({
|
||||
provider: "webpush",
|
||||
webpush_subscription: subscription
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
console.log("Device registered successfully");
|
||||
} else {
|
||||
const text = await response.text();
|
||||
throw new Error("Failed to register: ", text)
|
||||
}
|
||||
}
|
||||
|
||||
function urlBase64ToUint8Array(base64String) {
|
||||
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
|
||||
const base64 = (base64String + padding)
|
||||
.replace(/\-/g, "+")
|
||||
.replace(/_/g, "/");
|
||||
|
||||
const rawData = atob(base64);
|
||||
const outputArray = new Uint8Array(rawData.length);
|
||||
|
||||
for (let i = 0; i < rawData.length; i++) {
|
||||
outputArray[i] = rawData.charCodeAt(i);
|
||||
}
|
||||
|
||||
return outputArray;
|
||||
}
|
||||
67
assets/public/custom/rtcPatcher.js
Normal file
@ -0,0 +1,67 @@
|
||||
let fingerprint = null;
|
||||
|
||||
const OriginalRTC = window.RTCPeerConnection;
|
||||
const OriginalSend = WebSocket.prototype.send;
|
||||
|
||||
window.RTCPeerConnection = function (...args) {
|
||||
const pc = new OriginalRTC(...args);
|
||||
const originalCreateOffer = pc.createOffer.bind(pc);
|
||||
|
||||
pc.createOffer = async function (options) {
|
||||
const offer = await originalCreateOffer(options);
|
||||
|
||||
if (offer.sdp) {
|
||||
const lines = offer.sdp.split("\n");
|
||||
for (const line of lines) {
|
||||
if (line.includes("a=fingerprint")) {
|
||||
fingerprint = line.split("a=fingerprint:")[1]?.trim() ?? null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return offer;
|
||||
};
|
||||
|
||||
return pc;
|
||||
};
|
||||
|
||||
Object.setPrototypeOf(window.RTCPeerConnection, OriginalRTC);
|
||||
Object.setPrototypeOf(window.RTCPeerConnection.prototype, OriginalRTC.prototype);
|
||||
|
||||
WebSocket.prototype.send = function (data) {
|
||||
try {
|
||||
let parsed = JSON.parse(data);
|
||||
|
||||
if (parsed.d && typeof parsed.d === "object") {
|
||||
for (const [key, value] of Object.entries(parsed.d)) {
|
||||
if (typeof value === "string" && (value.includes("a=extmap") || value.includes("a=ice-") || value.includes("a=rtpmap"))) {
|
||||
if (!value.includes("a=fingerprint")) {
|
||||
parsed.d[key] = addFingerprintToSdp(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return OriginalSend.call(this, JSON.stringify(parsed));
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
return OriginalSend.call(this, data);
|
||||
};
|
||||
|
||||
function addFingerprintToSdp(sdp) {
|
||||
if (!fingerprint) return sdp;
|
||||
|
||||
const lines = sdp.split("\n");
|
||||
const result = [];
|
||||
let added = false;
|
||||
|
||||
for (const line of lines) {
|
||||
result.push(line);
|
||||
if (!added && line.includes("a=ice-pwd:")) {
|
||||
result.push(`a=fingerprint:${fingerprint}`);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result.join("\n");
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
self.addEventListener('push', (event) => {
|
||||
self.addEventListener("push", (event) => {
|
||||
if (!event.data) return;
|
||||
|
||||
const payload = event.data.json();
|
||||
@ -18,7 +18,7 @@ self.addEventListener('push', (event) => {
|
||||
event.waitUntil(self.registration.showNotification(title, options));
|
||||
});
|
||||
|
||||
self.addEventListener('notificationclick', (event) => {
|
||||
self.addEventListener("notificationclick", (event) => {
|
||||
event.notification.close();
|
||||
|
||||
const payload = event.notification.data;
|
||||
120
assets/public/custom/web/main.js
Normal file
@ -0,0 +1,120 @@
|
||||
(function () {
|
||||
const BUTTON_ID = "rscord-settings-btn";
|
||||
const CONTENT_CONTAINER_ID = "custom-content";
|
||||
const SWITCH_CONTAINER_ID = "switch-container";
|
||||
|
||||
const CSS_CLASSES = {
|
||||
tabItem: "item-3XjbnG",
|
||||
selected: "selected-g-kMVV",
|
||||
contentColumn: "contentColumn-1C7as6",
|
||||
settingsSidebar: "standardSidebarView-E9Pc3j",
|
||||
title: "colorStandard-1Xxp1s size14-k_3Hy4 h1-34Txb0 title-3hptVQ defaultColor-2cKwKo defaultMarginh1-EURXsm"
|
||||
};
|
||||
|
||||
const TAB_CONTENT = `
|
||||
<h2 class="${CSS_CLASSES.title}">RSCord Settings</h2>
|
||||
<div id="${SWITCH_CONTAINER_ID}"></div>
|
||||
`;
|
||||
|
||||
function addSwitches(container) {
|
||||
container.appendChild(createSwitch(
|
||||
"Push Notifications",
|
||||
"Sends you notifications even when you close the app",
|
||||
"push-notifications",
|
||||
false,
|
||||
async () => {
|
||||
const publicKey = window.GLOBAL_ENV.VAPID_KEY;
|
||||
if (!publicKey) {
|
||||
alert("Server has not enabled push notifications")
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
await registerPush(publicKey);
|
||||
return true;
|
||||
} catch(err) {
|
||||
alert(err.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
function addSettingsTab() {
|
||||
const advancedTab = document.querySelector(`.${CSS_CLASSES.tabItem}[aria-controls='advanced-tab']`);
|
||||
if (!advancedTab) return;
|
||||
if (document.getElementById(BUTTON_ID)) return;
|
||||
|
||||
const settingsBtn = document.createElement("div");
|
||||
settingsBtn.id = BUTTON_ID;
|
||||
settingsBtn.className = advancedTab.className;
|
||||
settingsBtn.role = "tab";
|
||||
settingsBtn.tabIndex = -1;
|
||||
settingsBtn.ariaSelected = "false";
|
||||
settingsBtn.textContent = "RSCord Settings";
|
||||
|
||||
settingsBtn.addEventListener("click", () => {
|
||||
const tabs = settingsBtn.parentElement.querySelectorAll('[role="tab"]');
|
||||
tabs.forEach(tab => tab.classList.remove(CSS_CLASSES.selected));
|
||||
tabs.forEach(tab => tab.setAttribute("aria-selected", "false"));
|
||||
settingsBtn.classList.add(CSS_CLASSES.selected);
|
||||
settingsBtn.setAttribute("aria-selected", "true");
|
||||
|
||||
let customContent = document.getElementById(CONTENT_CONTAINER_ID);
|
||||
if (!customContent) {
|
||||
customContent = document.createElement("div");
|
||||
customContent.id = CONTENT_CONTAINER_ID;
|
||||
customContent.innerHTML = TAB_CONTENT;
|
||||
|
||||
const switchContainer = customContent.querySelector(`#${SWITCH_CONTAINER_ID}`);
|
||||
addSwitches(switchContainer);
|
||||
|
||||
document.querySelector(`.${CSS_CLASSES.contentColumn}`).appendChild(customContent);
|
||||
}
|
||||
|
||||
const contentColumn = document.querySelector(`.${CSS_CLASSES.contentColumn}`);
|
||||
Array.from(contentColumn.children).forEach(child => {
|
||||
if (child.id !== CONTENT_CONTAINER_ID) child.style.display = "none";
|
||||
});
|
||||
customContent.style.display = "block";
|
||||
});
|
||||
|
||||
advancedTab.insertAdjacentElement("afterend", settingsBtn);
|
||||
return true;
|
||||
}
|
||||
|
||||
function startObserver() {
|
||||
const observer = new MutationObserver(() => {
|
||||
const sidebar = document.querySelector(`.${CSS_CLASSES.settingsSidebar}`);
|
||||
if (sidebar) addSettingsTab();
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
startObserver();
|
||||
|
||||
document.body.addEventListener("click", (e) => {
|
||||
const clickedTab = e.target.closest('[role="tab"]');
|
||||
if (!clickedTab) return;
|
||||
|
||||
const allTabs = document.querySelectorAll('[role="tab"]');
|
||||
allTabs.forEach(tab => tab.classList.remove(CSS_CLASSES.selected));
|
||||
allTabs.forEach(tab => tab.setAttribute("aria-selected", "false"));
|
||||
clickedTab.classList.add(CSS_CLASSES.selected);
|
||||
clickedTab.setAttribute("aria-selected", "true");
|
||||
|
||||
const contentColumn = document.querySelector(`.${CSS_CLASSES.contentColumn}`);
|
||||
if (!contentColumn) return;
|
||||
|
||||
if (clickedTab.id === BUTTON_ID) {
|
||||
Array.from(contentColumn.children).forEach(child => {
|
||||
if (child.id === CONTENT_CONTAINER_ID) child.style.display = "block";
|
||||
else child.style.display = "none";
|
||||
});
|
||||
} else {
|
||||
Array.from(contentColumn.children).forEach(child => {
|
||||
if (child.id !== CONTENT_CONTAINER_ID) child.style.display = "";
|
||||
else child.style.display = "none";
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
197
assets/public/custom/web/switch.js
Normal file
@ -0,0 +1,197 @@
|
||||
const CSS_CLASSES = {
|
||||
container: "container-1zDvAE",
|
||||
labelRow: "labelRow-2jl9gK",
|
||||
label: "title-2dsDLn",
|
||||
control: "control-1fl03-",
|
||||
switchControl: "container-2nx-BQ",
|
||||
switchControlChecked: "checked-25WXMf",
|
||||
input: "input-2XRLou",
|
||||
slider: "slider-32CRPX",
|
||||
note: "note-2C4pGr",
|
||||
description: "colorStandard-1Xxp1s size14-k_3Hy4 description-30xx7u formText-2ngGjI modeDefault-2fEh7a",
|
||||
divider: "divider-_0um2u dividerDefault-3C2-ws"
|
||||
};
|
||||
|
||||
const ICON_PATHS = {
|
||||
checked: [
|
||||
"M7.89561 14.8538L6.30462 13.2629L14.3099 5.25755L15.9009 6.84854L7.89561 14.8538Z",
|
||||
"M4.08643 11.0903L5.67742 9.49929L9.4485 13.2704L7.85751 14.8614L4.08643 11.0903Z"
|
||||
],
|
||||
unchecked: [
|
||||
"M5.13231 6.72963L6.7233 5.13864L14.855 13.2704L13.264 14.8614L5.13231 6.72963Z",
|
||||
"M13.2704 5.13864L14.8614 6.72963L6.72963 14.8614L5.13864 13.2704L13.2704 5.13864Z"
|
||||
]
|
||||
};
|
||||
|
||||
const CHECKED_COLOR = "hsl(139, 47.3%, 43.9%)";
|
||||
const UNCHECKED_COLOR = "hsl(218, 4.6%, 46.9%)";
|
||||
|
||||
function createSwitch(
|
||||
labelText,
|
||||
descriptionText,
|
||||
inputId,
|
||||
defaultChecked = false,
|
||||
onToggle = null
|
||||
) {
|
||||
const container = document.createElement("div");
|
||||
container.className = CSS_CLASSES.container;
|
||||
|
||||
const checkbox = document.createElement("input");
|
||||
checkbox.id = inputId;
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.className = CSS_CLASSES.input;
|
||||
checkbox.tabIndex = 0;
|
||||
checkbox.checked = defaultChecked;
|
||||
|
||||
const sliderSVG = createSliderSVG(defaultChecked);
|
||||
|
||||
const switchControl = document.createElement("div");
|
||||
switchControl.className = `${CSS_CLASSES.switchControl} ${defaultChecked ? CSS_CLASSES.switchControlChecked : ""}`;
|
||||
switchControl.style.opacity = "1";
|
||||
switchControl.style.backgroundColor = defaultChecked ? CHECKED_COLOR : UNCHECKED_COLOR;
|
||||
switchControl.style.cursor = "pointer";
|
||||
switchControl.tabIndex = -1;
|
||||
switchControl.appendChild(sliderSVG);
|
||||
switchControl.appendChild(checkbox);
|
||||
|
||||
const controlWrapper = document.createElement("div");
|
||||
controlWrapper.className = CSS_CLASSES.control;
|
||||
controlWrapper.appendChild(switchControl);
|
||||
|
||||
const label = document.createElement("label");
|
||||
label.htmlFor = inputId;
|
||||
label.className = CSS_CLASSES.label;
|
||||
label.textContent = labelText;
|
||||
|
||||
const labelRow = document.createElement("div");
|
||||
labelRow.className = CSS_CLASSES.labelRow;
|
||||
labelRow.appendChild(label);
|
||||
labelRow.appendChild(controlWrapper);
|
||||
|
||||
container.appendChild(labelRow);
|
||||
|
||||
if (descriptionText) {
|
||||
const description = document.createElement("div");
|
||||
description.className = CSS_CLASSES.description;
|
||||
description.textContent = descriptionText;
|
||||
|
||||
const noteWrapper = document.createElement("div");
|
||||
noteWrapper.className = CSS_CLASSES.note;
|
||||
noteWrapper.appendChild(description);
|
||||
container.appendChild(noteWrapper);
|
||||
}
|
||||
|
||||
const divider = document.createElement("div");
|
||||
divider.className = CSS_CLASSES.divider;
|
||||
container.appendChild(divider);
|
||||
|
||||
const updateSwitch = (checked) => {
|
||||
switchControl.classList.toggle(CSS_CLASSES.switchControlChecked, checked);
|
||||
switchControl.style.backgroundColor = checked ? CHECKED_COLOR : UNCHECKED_COLOR;
|
||||
sliderSVG.style.left = checked ? "12px" : "-3px";
|
||||
updateSVGIcon(sliderSVG, checked);
|
||||
};
|
||||
|
||||
const handleToggle = async () => {
|
||||
const prev = !checkbox.checked;
|
||||
if (typeof onToggle === "function") {
|
||||
const ret = await onToggle(checkbox.checked);
|
||||
if (ret === false) checkbox.checked = prev;
|
||||
}
|
||||
updateSwitch(checkbox.checked);
|
||||
};
|
||||
|
||||
switchControl.addEventListener("click", (e) => {
|
||||
if (e.target === checkbox) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
checkbox.checked = !checkbox.checked;
|
||||
checkbox.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
});
|
||||
|
||||
checkbox.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
checkbox.addEventListener("change", handleToggle);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
function createSliderSVG(isChecked) {
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svg.setAttribute("class", CSS_CLASSES.slider);
|
||||
svg.setAttribute("viewBox", "0 0 28 20");
|
||||
svg.setAttribute("preserveAspectRatio", "xMinYMid meet");
|
||||
svg.style.left = isChecked ? "12px" : "-3px";
|
||||
svg.style.position = "absolute";
|
||||
svg.style.transition = "left 0.2s";
|
||||
|
||||
const circle = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
||||
circle.setAttribute("fill", "white");
|
||||
circle.setAttribute("x", "4");
|
||||
circle.setAttribute("y", "0");
|
||||
circle.setAttribute("height", "20");
|
||||
circle.setAttribute("width", "20");
|
||||
circle.setAttribute("rx", "10");
|
||||
svg.appendChild(circle);
|
||||
|
||||
const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
icon.setAttribute("viewBox", "0 0 20 20");
|
||||
icon.setAttribute("fill", "none");
|
||||
|
||||
const color = isChecked ? CHECKED_COLOR : UNCHECKED_COLOR;
|
||||
|
||||
if (isChecked) {
|
||||
const checkPaths = [
|
||||
"M7.89561 14.8538L6.30462 13.2629L14.3099 5.25755L15.9009 6.84854L7.89561 14.8538Z",
|
||||
"M4.08643 11.0903L5.67742 9.49929L9.4485 13.2704L7.85751 14.8614L4.08643 11.0903Z"
|
||||
];
|
||||
checkPaths.forEach(d => {
|
||||
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
path.setAttribute("fill", color);
|
||||
path.setAttribute("d", d);
|
||||
icon.appendChild(path);
|
||||
});
|
||||
} else {
|
||||
const xPaths = [
|
||||
"M5.13231 6.72963L6.7233 5.13864L14.855 13.2704L13.264 14.8614L5.13231 6.72963Z",
|
||||
"M13.2704 5.13864L14.8614 6.72963L6.72963 14.8614L5.13864 13.2704L13.2704 5.13864Z"
|
||||
];
|
||||
xPaths.forEach(d => {
|
||||
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
path.setAttribute("fill", color);
|
||||
path.setAttribute("d", d);
|
||||
icon.appendChild(path);
|
||||
});
|
||||
}
|
||||
|
||||
svg.appendChild(icon);
|
||||
return svg;
|
||||
}
|
||||
|
||||
function createSVGIcon(parentSVG, isChecked) {
|
||||
const color = isChecked ? CHECKED_COLOR : UNCHECKED_COLOR;
|
||||
const paths = ICON_PATHS[isChecked ? "checked" : "unchecked"];
|
||||
|
||||
const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
icon.setAttribute("viewBox", "0 0 20 20");
|
||||
icon.setAttribute("fill", "none");
|
||||
|
||||
paths.forEach(d => {
|
||||
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
path.setAttribute("fill", color);
|
||||
path.setAttribute("d", d);
|
||||
icon.appendChild(path);
|
||||
});
|
||||
|
||||
parentSVG.appendChild(icon);
|
||||
}
|
||||
|
||||
function updateSVGIcon(sliderSVG, isChecked) {
|
||||
const oldIcon = sliderSVG.querySelector("svg svg");
|
||||
if (oldIcon) {
|
||||
oldIcon.remove();
|
||||
}
|
||||
createSVGIcon(sliderSVG, isChecked);
|
||||
}
|
||||
@ -29,8 +29,8 @@
|
||||
window.__BILLING_STANDALONE__ = /^\/billing/.test(location.pathname)
|
||||
|
||||
function protocolRelative(url) {
|
||||
if (!url) return '';
|
||||
return '//' + url.replace(/^https?:\/\//, '').replace(/\/$/, '');
|
||||
if (!url) return "";
|
||||
return "//" + url.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
||||
}
|
||||
|
||||
async function loadGlobalEnv() {
|
||||
@ -45,137 +45,70 @@
|
||||
CDN_HOST: protocolRelative(data.cdn.baseUrl),
|
||||
ASSET_ENDPOINT: protocolRelative(data.cdn.baseUrl),
|
||||
MEDIA_PROXY_ENDPOINT: protocolRelative(data.cdn.baseUrl),
|
||||
WIDGET_ENDPOINT: '//discord.com/widget',
|
||||
INVITE_HOST: 'discord.gg',
|
||||
GUILD_TEMPLATE_HOST: 'discord.new',
|
||||
GIFT_CODE_HOST: 'discord.gift',
|
||||
RELEASE_CHANNEL: 'stable',
|
||||
WIDGET_ENDPOINT: "//discord.com/widget",
|
||||
INVITE_HOST: "discord.gg",
|
||||
GUILD_TEMPLATE_HOST: "discord.new",
|
||||
GIFT_CODE_HOST: "discord.gift",
|
||||
RELEASE_CHANNEL: "stable",
|
||||
MARKETING_ENDPOINT: protocolRelative(data.api.baseUrl),
|
||||
BRAINTREE_KEY: 'production_5st77rrc_49pp2rp4phym7387',
|
||||
STRIPE_KEY: 'pk_live_CUQtlpQUF0vufWpnpUmQvcdi',
|
||||
BRAINTREE_KEY: "production_5st77rrc_49pp2rp4phym7387",
|
||||
STRIPE_KEY: "pk_live_CUQtlpQUF0vufWpnpUmQvcdi",
|
||||
NETWORKING_ENDPOINT: protocolRelative(data.api.baseUrl),
|
||||
RTC_LATENCY_ENDPOINT: protocolRelative(data.api.baseUrl),
|
||||
ACTIVITY_APPLICATION_HOST: protocolRelative(data.api.baseUrl),
|
||||
PROJECT_ENV: 'production',
|
||||
REMOTE_AUTH_ENDPOINT: '//remote-auth-gateway.discord.gg',
|
||||
SENTRY_TAGS: { buildId: '9af39da', buildType: 'normal' },
|
||||
PROJECT_ENV: "production",
|
||||
REMOTE_AUTH_ENDPOINT: "//remote-auth-gateway.discord.gg",
|
||||
SENTRY_TAGS: { buildId: "9af39da", buildType: "normal" },
|
||||
MIGRATION_SOURCE_ORIGIN: protocolRelative(data.api.baseUrl),
|
||||
MIGRATION_DESTINATION_ORIGIN: protocolRelative(data.api.baseUrl),
|
||||
HTML_TIMESTAMP: Date.now(),
|
||||
ALGOLIA_KEY: 'aca0d7082e4e63af5ba5917d5e96bed0',
|
||||
GATEWAY_URL: data.gateway.baseUrl
|
||||
ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0",
|
||||
GATEWAY_URL: data.gateway.baseUrl,
|
||||
VAPID_KEY: data.vapid.publicKey
|
||||
};
|
||||
|
||||
const customScripts = [
|
||||
"/assets/custom/pushMessages.js",
|
||||
"/assets/custom/rtcPatcher.js",
|
||||
"/assets/custom/web/switch.js",
|
||||
"/assets/custom/web/main.js",
|
||||
]
|
||||
|
||||
const normalScripts = [
|
||||
"/assets/053574d0f6edc579c71e.js",
|
||||
"/assets/771dcd97bfd5f9b77ef6.js",
|
||||
"/assets/0ffb9e3c90aa88720fc7.js",
|
||||
"/assets/cbc98c97c3635bf34da3.js",
|
||||
];
|
||||
|
||||
const scripts = [
|
||||
'/assets/053574d0f6edc579c71e.js',
|
||||
'/assets/771dcd97bfd5f9b77ef6.js',
|
||||
'/assets/0ffb9e3c90aa88720fc7.js',
|
||||
'/assets/cbc98c97c3635bf34da3.js'
|
||||
...normalScripts,
|
||||
...customScripts
|
||||
];
|
||||
|
||||
scripts.forEach(src => {
|
||||
const s = document.createElement('script');
|
||||
const s = document.createElement("script");
|
||||
s.src = src;
|
||||
s.defer = true;
|
||||
document.body.appendChild(s);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Failed to load GLOBAL_ENV', e);
|
||||
console.error("Failed to load GLOBAL_ENV", e);
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem("exerimentOverrides", JSON.stringify({
|
||||
"2022-03_text_in_voice": {
|
||||
type: "guild",
|
||||
revision: 1,
|
||||
bucket: 1,
|
||||
override: true
|
||||
}
|
||||
}));
|
||||
|
||||
loadGlobalEnv();
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
let lastCredentials = null;
|
||||
|
||||
const OriginalRTC = window.RTCPeerConnection;
|
||||
window.RTCPeerConnection = function(...args) {
|
||||
const pc = new OriginalRTC(...args);
|
||||
const originalCreateOffer = pc.createOffer.bind(pc);
|
||||
|
||||
pc.createOffer = async function(options) {
|
||||
const offer = await originalCreateOffer(options);
|
||||
|
||||
if (offer.sdp) {
|
||||
const lines = offer.sdp.split('\n');
|
||||
for (const line of lines) {
|
||||
if (line.includes('a=fingerprint')) {
|
||||
const fp = line.split('a=fingerprint:')[1]?.trim();
|
||||
if (fp) {
|
||||
lastCredentials = lastCredentials || {};
|
||||
lastCredentials.fingerprint = fp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return offer;
|
||||
};
|
||||
|
||||
return pc;
|
||||
};
|
||||
|
||||
Object.setPrototypeOf(window.RTCPeerConnection, OriginalRTC);
|
||||
Object.setPrototypeOf(window.RTCPeerConnection.prototype, OriginalRTC.prototype);
|
||||
|
||||
const OriginalSend = WebSocket.prototype.send;
|
||||
WebSocket.prototype.send = function(data) {
|
||||
try {
|
||||
let parsed = JSON.parse(data);
|
||||
|
||||
if (parsed.d && typeof parsed.d === 'object') {
|
||||
for (const [key, value] of Object.entries(parsed.d)) {
|
||||
if (typeof value === 'string' && (value.includes('a=extmap') || value.includes('a=ice-') || value.includes('a=rtpmap'))) {
|
||||
if (!value.includes('a=fingerprint')) {
|
||||
parsed.d[key] = addFingerprintToSdp(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return OriginalSend.call(this, JSON.stringify(parsed));
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
return OriginalSend.call(this, data);
|
||||
};
|
||||
|
||||
function addFingerprintToSdp(sdp) {
|
||||
const fingerprint = lastCredentials?.fingerprint;
|
||||
if (!fingerprint) return sdp;
|
||||
|
||||
const lines = sdp.split('\n');
|
||||
const result = [];
|
||||
let added = false;
|
||||
|
||||
for (const line of lines) {
|
||||
result.push(line);
|
||||
if (!added && line.includes('a=ice-pwd:')) {
|
||||
result.push(`a=fingerprint:${fingerprint}`);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result.join('\n');
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
localStorage.setItem("exerimentOverrides", JSON.stringify({
|
||||
"2022-03_text_in_voice": {
|
||||
type: "guild",
|
||||
revision: 1,
|
||||
bucket: 1,
|
||||
override: true
|
||||
}
|
||||
}));
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app-mount"></div>
|
||||
|
||||
|
Before Width: | Height: | Size: 374 B After Width: | Height: | Size: 374 B |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 707 B After Width: | Height: | Size: 707 B |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 271 B After Width: | Height: | Size: 271 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 769 B After Width: | Height: | Size: 769 B |
|
Before Width: | Height: | Size: 501 B After Width: | Height: | Size: 501 B |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 568 B After Width: | Height: | Size: 568 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 563 B After Width: | Height: | Size: 563 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 453 B After Width: | Height: | Size: 453 B |
|
Before Width: | Height: | Size: 814 B After Width: | Height: | Size: 814 B |
|
Before Width: | Height: | Size: 368 B After Width: | Height: | Size: 368 B |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 503 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 750 B After Width: | Height: | Size: 750 B |
|
Before Width: | Height: | Size: 500 B After Width: | Height: | Size: 500 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 575 B After Width: | Height: | Size: 575 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 489 B After Width: | Height: | Size: 489 B |
|
Before Width: | Height: | Size: 1004 B After Width: | Height: | Size: 1004 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 594 B After Width: | Height: | Size: 594 B |
|
Before Width: | Height: | Size: 239 B After Width: | Height: | Size: 239 B |
|
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 584 B |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 815 B After Width: | Height: | Size: 815 B |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 857 B After Width: | Height: | Size: 857 B |
|
Before Width: | Height: | Size: 942 B After Width: | Height: | Size: 942 B |
|
Before Width: | Height: | Size: 555 B After Width: | Height: | Size: 555 B |
|
Before Width: | Height: | Size: 1010 B After Width: | Height: | Size: 1010 B |
|
Before Width: | Height: | Size: 614 B After Width: | Height: | Size: 614 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 633 B After Width: | Height: | Size: 633 B |
|
Before Width: | Height: | Size: 439 B After Width: | Height: | Size: 439 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 640 B After Width: | Height: | Size: 640 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 490 B After Width: | Height: | Size: 490 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 937 B After Width: | Height: | Size: 937 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 565 B After Width: | Height: | Size: 565 B |