Fixes For CSP

This commit is contained in:
thororen1234 2025-04-08 09:47:34 -04:00
parent df947ae7b3
commit 98896de20b
No known key found for this signature in database
12 changed files with 21 additions and 548 deletions

View file

@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
### Extra included plugins
<details>
<summary>153 additional plugins</summary>
<summary>152 additional plugins</summary>
### All Platforms
@ -23,7 +23,6 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
- AtSomeone by Joona
- BannersEverywhere by ImLvna & AutumnVN
- BetterActivities by D3SOX, Arjix, AutumnVN
- BetterAudioPlayer by Creations
- BetterBanReasons by Inbestigator
- BetterBlockedUsers by TheArmagan & Elvyra
- BetterInvites by iamme

View file

@ -318,6 +318,12 @@ function ThemesTab() {
<Forms.FormText>If using the BD site, click on "Download" and place the downloaded .theme.css file into your themes folder.</Forms.FormText>
</Card>
<Card className="vc-settings-card">
<Forms.FormTitle tag="h5">External Resources</Forms.FormTitle>
<Forms.FormText>For security reasons, loading resources (styles, fonts, images, ...) from most sites is blocked.</Forms.FormText>
<Forms.FormText>Make sure all your assets are hosted on GitHub, GitLab, Codeberg, Imgur, Discord or Google Fonts.</Forms.FormText>
</Card>
<Forms.FormSection title="Local Themes">
<QuickActionCard>
<>

View file

@ -1,312 +0,0 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./style.css";
import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { showToast, Toasts } from "@webpack/common";
const fileSizeLimit = 12e6;
function parseFileSize(size: string) {
const [value, unit] = size.split(" ");
const multiplier = {
B: 1,
KB: 1024,
MB: 1024 ** 2,
GB: 1024 ** 3,
TB: 1024 ** 4,
}[unit];
if (!multiplier) return;
return parseFloat(value) * multiplier;
}
function getMetadata(audioElement: HTMLElement) {
const metadataElement = audioElement.querySelector("[class^='metadataContent_']");
const nameElement = metadataElement?.querySelector("a");
const sizeElement = audioElement.querySelector("[class^='metadataContent_'] [class^='metadataSize_']");
const url = nameElement?.getAttribute("href");
const audioElementLink = audioElement.querySelector("audio");
if (!sizeElement?.textContent || !nameElement?.textContent || !url || !audioElementLink) return false;
const name = nameElement.textContent;
const size = parseFileSize(sizeElement.textContent);
if (size && size > fileSizeLimit) {
return false;
}
const elements = [metadataElement?.parentElement, audioElement.querySelector("[class^='audioControls_']")];
const computedStyle = getComputedStyle(audioElement);
const parentBorderRadius = computedStyle.borderRadius;
if (settings.store.forceMoveBelow) {
elements.forEach(element => {
if (element) (element as HTMLElement).style.zIndex = "2";
});
}
return {
name,
size,
url,
audio: audioElementLink,
parentBorderRadius: parentBorderRadius,
};
}
async function addListeners(audioElement: HTMLAudioElement, url: string, parentBorderRadius: string) {
const madeURL = new URL(url);
madeURL.searchParams.set("t", Date.now().toString());
const { href } = madeURL;
const response = await fetch(href);
const blob = await response.blob();
const blobUrl = URL.createObjectURL(blob);
const audioContext = new AudioContext();
const analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
const frequencyData = new Uint8Array(bufferLength);
const source = audioContext.createMediaElementSource(audioElement);
source.connect(analyser);
analyser.connect(audioContext.destination);
const canvas = document.createElement("canvas");
const canvasContext = canvas.getContext("2d");
if (!canvasContext) return;
canvas.classList.add("better-audio-visualizer");
audioElement.parentElement?.appendChild(canvas);
if (parentBorderRadius) canvas.style.borderRadius = parentBorderRadius;
function drawVisualizer() {
if (!audioElement.paused) {
requestAnimationFrame(drawVisualizer);
}
analyser.getByteTimeDomainData(dataArray);
analyser.getByteFrequencyData(frequencyData);
if (!canvasContext) return;
canvasContext.clearRect(0, 0, canvas.width, canvas.height);
if (settings.store.oscilloscope) drawOscilloscope(canvasContext, canvas, dataArray, bufferLength);
if (settings.store.spectrograph) drawSpectrograph(canvasContext, canvas, frequencyData, bufferLength);
}
audioElement.src = blobUrl;
audioElement.addEventListener("play", () => {
if (audioContext.state === "suspended") {
audioContext.resume();
}
drawVisualizer();
});
audioElement.addEventListener("pause", () => {
audioContext.suspend();
});
}
function drawOscilloscope(canvasContext, canvas, dataArray, bufferLength) {
const sliceWidth = canvas.width / bufferLength;
let x = 0;
const { oscilloscopeSolidColor, oscilloscopeColor } = settings.store;
const [r, g, b] = oscilloscopeColor.split(",").map(Number);
canvasContext.lineWidth = 2;
canvasContext.clearRect(0, 0, canvas.width, canvas.height);
canvasContext.beginPath();
for (let i = 0; i < bufferLength; i++) {
const v = dataArray[i] / 128.0;
const y = (v * canvas.height) / 2;
if (oscilloscopeSolidColor) {
canvasContext.strokeStyle = `rgb(${r}, ${g}, ${b})`;
} else {
const red = Math.min(r + (v * 100) + (i / bufferLength) * 155, 255);
const green = Math.min(g + (v * 50) + (i / bufferLength) * 155, 255);
const blue = Math.min(b + (v * 150) + (i / bufferLength) * 155, 255);
canvasContext.strokeStyle = `rgb(${red}, ${green}, ${blue})`;
}
if (i === 0) {
canvasContext.moveTo(x, y);
} else {
canvasContext.lineTo(x, y);
}
x += sliceWidth;
}
canvasContext.stroke();
}
function drawSpectrograph(canvasContext, canvas, frequencyData, bufferLength) {
const { spectrographSolidColor, spectrographColor } = settings.store;
const maxHeight = canvas.height;
const barWidth = canvas.width / bufferLength;
let x = 0;
const maxFrequencyValue = Math.max(...frequencyData);
if (maxFrequencyValue === 0 || !isFinite(maxFrequencyValue)) {
return;
}
for (let i = 0; i < bufferLength; i++) {
const normalizedHeight = (frequencyData[i] / maxFrequencyValue) * maxHeight;
if (spectrographSolidColor) {
canvasContext.fillStyle = `rgb(${spectrographColor})`;
} else {
const [r, g, b] = spectrographColor.split(",").map(Number);
const red = Math.min(r + (i / bufferLength) * 155, 255);
const green = Math.min(g + (i / bufferLength) * 155, 255);
const blue = Math.min(b + (i / bufferLength) * 155, 255);
const gradient = canvasContext.createLinearGradient(x, canvas.height - normalizedHeight, x, canvas.height);
gradient.addColorStop(0, `rgb(${red}, ${green}, ${blue})`);
const darkerColor = `rgb(${Math.max(red - 50, 0)},${Math.max(green - 50, 0)},${Math.max(blue - 50, 0)})`;
gradient.addColorStop(1, darkerColor);
canvasContext.fillStyle = gradient;
}
canvasContext.fillRect(x, canvas.height - normalizedHeight, barWidth, normalizedHeight);
x += barWidth + 0.5;
}
}
function scanForAudioElements(element: HTMLElement) {
element.querySelectorAll("[class^='wrapperAudio_']:not([data-better-audio-processed])").forEach(audioElement => {
(audioElement as HTMLElement).dataset.betterAudioProcessed = "true";
const metadata = getMetadata(audioElement as HTMLElement);
if (!metadata) return;
addListeners(metadata.audio, metadata.url, metadata.parentBorderRadius);
});
}
function createObserver(targetNode: HTMLElement) {
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
if (mutation.type === "childList") {
mutation.addedNodes.forEach(addedNode => {
if (addedNode instanceof HTMLElement) {
scanForAudioElements(addedNode);
}
});
}
});
});
observer.observe(targetNode, {
childList: true,
subtree: true,
});
}
function tryHexToRgb(hex) {
if (hex.startsWith("#")) {
const hexMatch = hex.match(/\w\w/g);
if (hexMatch) {
const [r, g, b] = hexMatch.map(x => parseInt(x, 16));
return `${r}, ${g}, ${b}`;
}
}
return hex;
}
function handleColorChange(value, settingKey, defaultValue) {
const rgbPattern = /^(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})$/;
if (!value.match(rgbPattern)) {
const rgb = tryHexToRgb(value);
if (rgb.match(rgbPattern)) {
settings.store[settingKey] = rgb;
} else {
showToast(`Invalid color format for ${settingKey}, make sure it's in the format 'R, G, B' or '#RRGGBB'`, Toasts.Type.FAILURE);
settings.store[settingKey] = defaultValue;
}
} else {
settings.store[settingKey] = value;
}
}
const settings = definePluginSettings({
oscilloscope: {
type: OptionType.BOOLEAN,
description: "Enable oscilloscope visualizer",
default: true,
},
spectrograph: {
type: OptionType.BOOLEAN,
description: "Enable spectrograph visualizer",
default: true,
},
oscilloscopeSolidColor: {
type: OptionType.BOOLEAN,
description: "Use solid color for oscilloscope",
default: false,
},
oscilloscopeColor: {
type: OptionType.STRING,
description: "Color for oscilloscope",
default: "255, 255, 255",
onChange: value => handleColorChange(value, "oscilloscopeColor", "255, 255, 255"),
},
spectrographSolidColor: {
type: OptionType.BOOLEAN,
description: "Use solid color for spectrograph",
default: false,
},
spectrographColor: {
type: OptionType.STRING,
description: "Color for spectrograph",
default: "33, 150, 243",
onChange: value => handleColorChange(value, "spectrographColor", "33, 150, 243"),
},
forceMoveBelow: {
type: OptionType.BOOLEAN,
description: "Force the visualizer below the audio player",
default: true,
},
});
export default definePlugin({
name: "BetterAudioPlayer",
description: "Adds a spectrograph and oscilloscope visualizer to audio attachment players",
authors: [EquicordDevs.creations],
settings,
start() {
const waitForContent = () => {
const targetNode = document.querySelector("[class^='content_']");
if (targetNode) {
scanForAudioElements(targetNode as HTMLElement);
createObserver(targetNode as HTMLElement);
} else {
requestAnimationFrame(waitForContent);
}
};
waitForContent();
},
});

View file

@ -1,10 +0,0 @@
.better-audio-visualizer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
border: none;
}

View file

@ -5,13 +5,12 @@
*/
import * as DataStore from "@api/DataStore";
import { CheckedTextInput } from "@components/CheckedTextInput";
import { Flex } from "@components/Flex";
import { Button, Forms, React, TabBar, Text, TextArea, Toasts } from "@webpack/common";
import { JSX } from "react";
import { convert as convertLineEP, getIdFromUrl as getLineEmojiPackIdFromUrl, getStickerPackById as getLineEmojiPackById, isLineEmojiPackHtml, parseHtml as getLineEPFromHtml } from "../lineEmojis";
import { convert as convertLineSP, getIdFromUrl as getLineStickerPackIdFromUrl, getStickerPackById as getLineStickerPackById, isLineStickerPackHtml, parseHtml as getLineSPFromHtml } from "../lineStickers";
import { convert as convertLineEP, isLineEmojiPackHtml, parseHtml as getLineEPFromHtml } from "../lineEmojis";
import { convert as convertLineSP, isLineStickerPackHtml, parseHtml as getLineSPFromHtml } from "../lineStickers";
import { isV1, migrate } from "../migrate-v1";
import { deleteStickerPack, getStickerPack, getStickerPackMetas, saveStickerPack } from "../stickers";
import { SettingsTabsKey, Sticker, StickerPack, StickerPackMeta } from "../types";
@ -89,9 +88,8 @@ const StickerPackMetadata = ({ meta, hoveredStickerPackId, setHoveredStickerPack
export const Settings = () => {
const [stickerPackMetas, setstickerPackMetas] = React.useState<StickerPackMeta[]>([]);
const [addStickerUrl, setAddStickerUrl] = React.useState<string>("");
const [addStickerHtml, setAddStickerHtml] = React.useState<string>("");
const [tab, setTab] = React.useState<SettingsTabsKey>(SettingsTabsKey.ADD_STICKER_PACK_URL);
const [tab, setTab] = React.useState<SettingsTabsKey>(SettingsTabsKey.ADD_STICKER_PACK_HTML);
const [hoveredStickerPackId, setHoveredStickerPackId] = React.useState<string | null>(null);
const [_isV1, setV1] = React.useState<boolean>(false);
@ -123,114 +121,6 @@ export const Settings = () => {
}
</TabBar>
{tab === SettingsTabsKey.ADD_STICKER_PACK_URL &&
<div className="section">
<Forms.FormTitle tag="h5">Add Sticker Pack from URL</Forms.FormTitle>
<Forms.FormText>
<p>
Currently LINE stickers/emojis supported only. <br />
Get Telegram stickers with <a href="#" onClick={() => VencordNative.native.openExternal("https://github.com/lekoOwO/MoreStickersConverter")}> MoreStickersConverter</a>.
</p>
</Forms.FormText>
<Flex flexDirection="row" style={{
alignItems: "center",
justifyContent: "center"
}} >
<span style={{
flexGrow: 1
}}>
<CheckedTextInput
value={addStickerUrl}
onChange={setAddStickerUrl}
validate={(v: string) => {
try {
getLineStickerPackIdFromUrl(v);
return true;
} catch (e: any) { }
try {
getLineEmojiPackIdFromUrl(v);
return true;
} catch (e: any) { }
return "Invalid URL";
}}
placeholder="Sticker Pack URL"
/>
</span>
<Button
size={Button.Sizes.SMALL}
onClick={async e => {
e.preventDefault();
let type: string = "";
try {
getLineStickerPackIdFromUrl(addStickerUrl);
type = "LineStickerPack";
} catch (e: any) { }
try {
getLineEmojiPackIdFromUrl(addStickerUrl);
type = "LineEmojiPack";
} catch (e: any) { }
let errorMessage = "";
switch (type) {
case "LineStickerPack": {
try {
const id = getLineStickerPackIdFromUrl(addStickerUrl);
const lineSP = await getLineStickerPackById(id);
const stickerPack = convertLineSP(lineSP);
await saveStickerPack(stickerPack);
} catch (e: any) {
console.error(e);
errorMessage = e.message;
}
break;
}
case "LineEmojiPack": {
try {
const id = getLineEmojiPackIdFromUrl(addStickerUrl);
const lineEP = await getLineEmojiPackById(id);
const stickerPack = convertLineEP(lineEP);
await saveStickerPack(stickerPack);
} catch (e: any) {
console.error(e);
errorMessage = e.message;
}
break;
}
}
setAddStickerUrl("");
refreshStickerPackMetas();
if (errorMessage) {
Toasts.show({
message: errorMessage,
type: Toasts.Type.FAILURE,
id: Toasts.genId(),
options: {
duration: 1000
}
});
} else {
Toasts.show({
message: "Sticker Pack added",
type: Toasts.Type.SUCCESS,
id: Toasts.genId(),
options: {
duration: 1000
}
});
}
}}
>Insert</Button>
</Flex>
</div>
}
{tab === SettingsTabsKey.ADD_STICKER_PACK_HTML &&
<div className="section">
<Forms.FormTitle tag="h5">Add Sticker Pack from HTML</Forms.FormTitle>

View file

@ -5,7 +5,6 @@
*/
import { LineEmoji, LineEmojiPack, Sticker, StickerPack } from "./types";
import { lineFetch } from "./utils";
export interface StickerCategory {
title: string;
@ -123,16 +122,3 @@ export function parseHtml(html: string): LineEmojiPack {
export function isLineEmojiPackHtml(html: string): boolean {
return html.includes("data-test=\"emoji-name-title\"");
}
/**
* Get stickers from LINE
*
* @param {string} id The id of the sticker pack.
* @return {Promise<LineEmojiPack>} The sticker pack.
*/
export async function getStickerPackById(id: string, region = "en"): Promise<LineEmojiPack> {
const res = await lineFetch(`https://store.line.me/emojishop/product/${id}/${region}`);
const html = await res.text();
return parseHtml(html);
}

View file

@ -5,7 +5,6 @@
*/
import { LineSticker, LineStickerPack, Sticker, StickerPack } from "./types";
import { lineFetch } from "./utils";
export interface StickerCategory {
title: string;
@ -123,16 +122,3 @@ export function parseHtml(html: string): LineStickerPack {
export function isLineStickerPackHtml(html: string): boolean {
return html.includes("data-test=\"sticker-name-title\"");
}
/**
* Get stickers from LINE
*
* @param {string} id The id of the sticker pack.
* @return {Promise<LineStickerPack>} The sticker pack.
*/
export async function getStickerPackById(id: string, region = "en"): Promise<LineStickerPack> {
const res = await lineFetch(`https://store.line.me/stickershop/product/${id}/${region}`);
const html = await res.text();
return parseHtml(html);
}

View file

@ -1,77 +0,0 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { setRecentStickers } from "./components";
import {
convert,
getStickerPackById
} from "./lineStickers";
import {
deleteStickerPack,
getStickerPackMetas,
saveStickerPack
} from "./stickers";
import { StickerPack } from "./types";
export async function initTest() {
console.log("initTest.");
console.log("Clearing recent stickers.");
setRecentStickers([]);
// Clear all sticker packs
console.log("Clearing all sticker packs.");
const stickerPackMetas = await getStickerPackMetas();
for (const meta of stickerPackMetas) {
await deleteStickerPack(meta.id);
}
// Add test sticker packs
console.log("Adding test sticker packs.");
const lineStickerPackIds = [
"22814489", // LV.47
"22567773", // LV.46
"22256215", // LV.45
"21936635", // LV.44
"21836565", // LV.43
];
const ps: Promise<StickerPack | null>[] = [];
for (const id of lineStickerPackIds) {
ps.push((async () => {
try {
const lsp = await getStickerPackById(id);
const sp = convert(lsp);
return sp;
} catch (e) {
console.error("Failed to fetch sticker pack: " + id);
console.error(e);
return null;
}
})());
}
const stickerPacks = (await Promise.all(ps)).filter(sp => sp !== null) as StickerPack[];
console.log("Saving test sticker packs.");
for (const sp of stickerPacks) {
await saveStickerPack(sp);
}
console.log(await getStickerPackMetas());
}
export async function clearTest() {
console.log("clearTest.");
console.log("Clearing recent stickers.");
setRecentStickers([]);
// Clear all sticker packs
console.log("Clearing all sticker packs.");
const stickerPackMetas = await getStickerPackMetas();
for (const meta of stickerPackMetas) {
await deleteStickerPack(meta.id);
}
}

View file

@ -97,7 +97,6 @@ export interface PickerContentRowGrid {
}
export enum SettingsTabsKey {
ADD_STICKER_PACK_URL = "Add from URL",
ADD_STICKER_PACK_HTML = "Add from HTML",
ADD_STICKER_PACK_FILE = "Add from File",
MISC = "Misc",

View file

@ -14,10 +14,6 @@ import { FFmpegState } from "./types";
export const cl = classNameFactory("vc-more-stickers-");
export const clPicker = (className: string, ...args: any[]) => cl("picker-" + className, ...args);
export function lineFetch(url: string | URL, init?: RequestInit | undefined) {
return fetch(url, init);
}
export class Mutex {
current = Promise.resolve();
lock() {

View file

@ -53,6 +53,16 @@ export const CspPolicies: PolicyMap = {
"dearrow-thumb.ajay.app": MediaSrc, // Dearrow Thumbnail CDN
"usrbg.is-hardly.online": MediaSrc, // USRBG API
"icons.duckduckgo.com": MediaSrc, // DuckDuckGo Favicon API (Reverse Image Search)
// Equicord
"cdn.nest.rip": MediaSrc, // Nest CDN
"*.equicord.org": MediaSrc, // Equicord CDN
"discord-themes.com": MediaAndCssSrc, // Discord Themes CDN
"fonts.google.com": ConnectSrc,
"store.line.me": MediaSrc, // Line Store
"lrclib.net": ConnectSrc, // Lrclib API
"spotify-lyrics-api-pi.vercel.app": ConnectSrc, // Spotify Lyrics API
"stats.fm": MediaSrc, // Stats.fm API
};
const findHeader = (headers: PolicyMap, headerName: Lowercase<string>) => {

View file

@ -28,7 +28,7 @@ export let socket: WebSocket | undefined;
export function initWs(isManual = false) {
let wasConnected = isManual;
let hasErrored = false;
const ws = socket = new WebSocket(`ws://localhost:${PORT}`);
const ws = socket = new WebSocket(`ws://127.0.0.1:${PORT}`);
function replyData(data: OutgoingMessage) {
ws.send(JSON.stringify(data));