fix chatbuttonplus and fixFileExtensions issues (#421)
* fix chatbuttonplus issues * fix fixFileExtensions * fix guildPickerDumper * fix multiple slicing of proxy
This commit is contained in:
parent
43293e1873
commit
941ecdfd96
4 changed files with 121 additions and 26 deletions
|
|
@ -27,7 +27,7 @@ let buttonEntries: ButtonEntry[] = [];
|
||||||
const BUTTON_ENTRIES_KEY = "ChatButtonsPlus_buttonEntries";
|
const BUTTON_ENTRIES_KEY = "ChatButtonsPlus_buttonEntries";
|
||||||
|
|
||||||
function generateId(): string {
|
function generateId(): string {
|
||||||
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
return Date.now().toString(36) + Math.random().toString(36).substring(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleButtonClick(context: string) {
|
async function handleButtonClick(context: string) {
|
||||||
|
|
@ -67,9 +67,32 @@ async function removeButtonEntry(id: string, forceUpdate: () => void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function resetAllButtons(forceUpdate: () => void) {
|
||||||
|
try {
|
||||||
|
buttonEntries.length = 0;
|
||||||
|
await DataStore.set(BUTTON_ENTRIES_KEY, []);
|
||||||
|
forceUpdate();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("ChatButtonsPlus: Failed to reset buttons:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function ButtonEntries() {
|
function ButtonEntries() {
|
||||||
const update = useForceUpdater();
|
const update = useForceUpdater();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const loadEntries = async () => {
|
||||||
|
try {
|
||||||
|
const storedEntries = await DataStore.get(BUTTON_ENTRIES_KEY) ?? [];
|
||||||
|
buttonEntries = storedEntries;
|
||||||
|
update();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("ChatButtonsPlus: Failed to load entries:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadEntries();
|
||||||
|
}, []);
|
||||||
|
|
||||||
async function setLabel(id: string, value: string) {
|
async function setLabel(id: string, value: string) {
|
||||||
try {
|
try {
|
||||||
const index = buttonEntries.findIndex(entry => entry.id === id);
|
const index = buttonEntries.findIndex(entry => entry.id === id);
|
||||||
|
|
@ -87,7 +110,7 @@ function ButtonEntries() {
|
||||||
try {
|
try {
|
||||||
const index = buttonEntries.findIndex(entry => entry.id === id);
|
const index = buttonEntries.findIndex(entry => entry.id === id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
buttonEntries[index].message = value.trim() || "Hello!";
|
buttonEntries[index].message = value;
|
||||||
await DataStore.set(BUTTON_ENTRIES_KEY, buttonEntries);
|
await DataStore.set(BUTTON_ENTRIES_KEY, buttonEntries);
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
@ -99,10 +122,12 @@ function ButtonEntries() {
|
||||||
async function setSvg(id: string, value: string) {
|
async function setSvg(id: string, value: string) {
|
||||||
try {
|
try {
|
||||||
const index = buttonEntries.findIndex(entry => entry.id === id);
|
const index = buttonEntries.findIndex(entry => entry.id === id);
|
||||||
if (index !== -1 && isValidSvg(value.trim())) {
|
if (index !== -1) {
|
||||||
buttonEntries[index].svg = value.trim();
|
if (value.trim() === "" || isValidSvg(value.trim())) {
|
||||||
await DataStore.set(BUTTON_ENTRIES_KEY, buttonEntries);
|
buttonEntries[index].svg = value.trim();
|
||||||
update();
|
await DataStore.set(BUTTON_ENTRIES_KEY, buttonEntries);
|
||||||
|
update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("ChatButtonsPlus: Failed to update SVG:", error);
|
console.error("ChatButtonsPlus: Failed to update SVG:", error);
|
||||||
|
|
@ -185,19 +210,23 @@ function ButtonEntries() {
|
||||||
|
|
||||||
<div className="chatButtonsPlus-field">
|
<div className="chatButtonsPlus-field">
|
||||||
<Forms.FormText className="chatButtonsPlus-label">Message to Send</Forms.FormText>
|
<Forms.FormText className="chatButtonsPlus-label">Message to Send</Forms.FormText>
|
||||||
<TextInput
|
<textarea
|
||||||
|
className="chatButtonsPlus-textarea"
|
||||||
placeholder="Message to send when clicked"
|
placeholder="Message to send when clicked"
|
||||||
value={entry.message}
|
value={entry.message}
|
||||||
onChange={e => setMessage(entry.id, e)}
|
onChange={e => setMessage(entry.id, e.target.value)}
|
||||||
|
rows={2}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="chatButtonsPlus-field">
|
<div className="chatButtonsPlus-field">
|
||||||
<Forms.FormText className="chatButtonsPlus-label">Custom SVG Path (24x24 viewBox)</Forms.FormText>
|
<Forms.FormText className="chatButtonsPlus-label">Custom SVG Path (24x24 viewBox)</Forms.FormText>
|
||||||
<TextInput
|
<textarea
|
||||||
|
className="chatButtonsPlus-textarea"
|
||||||
placeholder='<path fill="currentColor" d="..."/>'
|
placeholder='<path fill="currentColor" d="..."/>'
|
||||||
value={entry.svg}
|
value={entry.svg}
|
||||||
onChange={e => setSvg(entry.id, e)}
|
onChange={e => setSvg(entry.id, e.target.value)}
|
||||||
|
rows={3}
|
||||||
/>
|
/>
|
||||||
<Forms.FormText className="chatButtonsPlus-description">
|
<Forms.FormText className="chatButtonsPlus-description">
|
||||||
Enter SVG path elements for a 24x24 viewBox. Use "currentColor" for the fill to match Discord's theme.
|
Enter SVG path elements for a 24x24 viewBox. Use "currentColor" for the fill to match Discord's theme.
|
||||||
|
|
@ -210,7 +239,16 @@ function ButtonEntries() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{elements}
|
{elements}
|
||||||
<Button onClick={() => addButtonEntry(update)}>Add Button</Button>
|
<div className="chatButtonsPlus-actions">
|
||||||
|
<Button onClick={() => addButtonEntry(update)}>Add Button</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => resetAllButtons(update)}
|
||||||
|
look={Button.Looks.OUTLINED}
|
||||||
|
color={Button.Colors.RED}
|
||||||
|
>
|
||||||
|
Reset All
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,4 +49,30 @@
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatButtonsPlus-textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 2px solid var(--text-muted);
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--background-secondary);
|
||||||
|
color: var(--header-primary);
|
||||||
|
font-family: var(--font-primary);
|
||||||
|
font-size: 14px;
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 60px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatButtonsPlus-textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--brand-experiment);
|
||||||
|
box-shadow: 0 0 0 1px var(--brand-experiment);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatButtonsPlus-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
@ -11,12 +11,13 @@ import definePlugin, { ReporterTestable } from "@utils/types";
|
||||||
import { CloudUpload } from "@vencord/discord-types";
|
import { CloudUpload } from "@vencord/discord-types";
|
||||||
|
|
||||||
const extensionMap = {
|
const extensionMap = {
|
||||||
"ogg": [".ogv", ".oga", ".ogx", ".ogm", ".spx", ".opus"],
|
"ogg": [".ogv", ".oga", ".ogx", ".ogm", ".spx", ".opus", ".flac", ".aac", ".wma"],
|
||||||
"jpg": [".jpg", ".jpeg", ".jfif", ".jpe", ".jif", ".jfi", ".pjpeg", ".pjp"],
|
"jpg": [".jpeg", ".jfif", ".jpe", ".jif", ".jfi", ".pjpeg", ".pjp", ".bmp", ".tiff", ".tif", ".webp"],
|
||||||
"svg": [".svgz"],
|
"svg": [".svgz", ".ai", ".eps"],
|
||||||
"mp4": [".m4v", ".m4r", ".m4p"],
|
"mp4": [".m4v", ".m4r", ".m4p", ".avi", ".mkv", ".wmv", ".flv", ".3gp", ".webm"],
|
||||||
"m4a": [".m4b"],
|
"m4a": [".m4b", ".aiff", ".wav"],
|
||||||
"mov": [".movie", ".qt"],
|
"mov": [".movie", ".qt", ".asf", ".rm", ".rmvb"],
|
||||||
|
"png": [".ico", ".cur"],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reverseExtensionMap = Object.entries(extensionMap).reduce((acc, [target, exts]) => {
|
export const reverseExtensionMap = Object.entries(extensionMap).reduce((acc, [target, exts]) => {
|
||||||
|
|
@ -50,6 +51,6 @@ export default definePlugin({
|
||||||
const ext = extIdx !== -1 ? file.slice(extIdx) : "";
|
const ext = extIdx !== -1 ? file.slice(extIdx) : "";
|
||||||
const newExt = reverseExtensionMap[ext] || ext;
|
const newExt = reverseExtensionMap[ext] || ext;
|
||||||
|
|
||||||
return fileName + newExt;
|
upload.filename = fileName + newExt;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ import type { Guild } from "@vencord/discord-types";
|
||||||
import { EmojiStore, Menu, StickersStore } from "@webpack/common";
|
import { EmojiStore, Menu, StickersStore } from "@webpack/common";
|
||||||
import { zipSync } from "fflate";
|
import { zipSync } from "fflate";
|
||||||
|
|
||||||
|
const StickerExt = [, "png", "apng", "json", "gif"] as const;
|
||||||
|
|
||||||
const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => {
|
const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => {
|
||||||
// Assuming "privacy" is the correct ID for the group you want to modify.
|
// Assuming "privacy" is the correct ID for the group you want to modify.
|
||||||
const group = findGroupChildrenByChildId("privacy", children);
|
const group = findGroupChildrenByChildId("privacy", children);
|
||||||
|
|
@ -36,16 +38,44 @@ async function zipGuildAssets(guild: Guild, type: "emojis" | "stickers") {
|
||||||
return console.log("Server not found!");
|
return console.log("Server not found!");
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchAsset = async e => {
|
const getProxyEndpoint = () => {
|
||||||
const ext = e.animated ? ".gif" : ".png";
|
const rawEndpoint = window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT;
|
||||||
const filename = e.id + ext;
|
return rawEndpoint.startsWith("//") ? rawEndpoint.slice(2) : rawEndpoint;
|
||||||
const url = isEmojis
|
};
|
||||||
? `https://${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/emojis/${filename}?size=512&quality=lossless`
|
|
||||||
: `https://${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${filename}?size=4096&lossless=true`;
|
const fetchAsset = async (e: any) => {
|
||||||
|
let ext: string;
|
||||||
|
let url: string;
|
||||||
|
const endpoint = getProxyEndpoint();
|
||||||
|
|
||||||
|
if (isEmojis) {
|
||||||
|
ext = e.animated ? ".gif" : ".png";
|
||||||
|
url = `https://${endpoint}/emojis/${e.id}${ext}?size=512&quality=lossless`;
|
||||||
|
} else {
|
||||||
|
ext = "." + StickerExt[e.format_type];
|
||||||
|
const urlExt = e.format_type === 2 ? ".png" : ext;
|
||||||
|
url = `https://${endpoint}/stickers/${e.id}${urlExt}?size=4096&lossless=true`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sanitizedName = e.name.replace(/[<>:"/\\|?*]/g, "_");
|
||||||
|
let filename = `${sanitizedName}_${e.id}${ext}`;
|
||||||
|
|
||||||
|
let response = await fetch(url);
|
||||||
|
|
||||||
|
if (!isEmojis && e.format_type === 2 && (!response.ok || response.headers.get("content-type")?.includes("text"))) {
|
||||||
|
const gifUrl = `https://${endpoint}/stickers/${e.id}.gif?size=4096&lossless=true`;
|
||||||
|
const gifResponse = await fetch(gifUrl);
|
||||||
|
|
||||||
|
if (gifResponse.ok && !gifResponse.headers.get("content-type")?.includes("text")) {
|
||||||
|
response = gifResponse;
|
||||||
|
ext = ".gif";
|
||||||
|
filename = `${sanitizedName}_${e.id}${ext}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch(url);
|
|
||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
return { file: new Uint8Array(await blob.arrayBuffer()), filename };
|
const arrayBuffer = await blob.arrayBuffer();
|
||||||
|
return { file: new Uint8Array(arrayBuffer), filename };
|
||||||
};
|
};
|
||||||
|
|
||||||
const assetPromises = items.map(e => fetchAsset(e));
|
const assetPromises = items.map(e => fetchAsset(e));
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue