1
0
Fork 1
mirror of https://github.com/Vendicated/Vencord.git synced 2025-01-10 09:56:24 +00:00
This commit is contained in:
programminglaboratorys 2024-06-08 13:15:51 +03:00
commit 4b337e30ff
34 changed files with 903 additions and 269 deletions

View file

@ -12,7 +12,8 @@ body:
DO NOT USE THIS FORM, unless DO NOT USE THIS FORM, unless
- you are a vencord contributor - you are a vencord contributor
- you were given explicit permission to use this form by a moderator in our support server - you were given explicit permission to use this form by a moderator in our support server
- you are filing a security related report
DO NOT USE THIS FORM FOR SECURITY RELATED ISSUES. [CREATE A SECURITY ADVISORY INSTEAD.](https://github.com/Vendicated/Vencord/security/advisories/new)
- type: textarea - type: textarea
id: content id: content

View file

@ -14,6 +14,8 @@
"typescript.preferences.quoteStyle": "double", "typescript.preferences.quoteStyle": "double",
"javascript.preferences.quoteStyle": "double", "javascript.preferences.quoteStyle": "double",
"eslint.experimental.useFlatConfig": false,
"gitlens.remotes": [ "gitlens.remotes": [
{ {
"domain": "codeberg.org", "domain": "codeberg.org",

View file

@ -5,8 +5,8 @@
The cutest Discord client mod The cutest Discord client mod
| ![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334) | | ![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334) |
|:--:| | :--------------------------------------------------------------------------------------------------: |
| A screenshot of vencord showcasing the [vencord-theme](https://github.com/synqat/vencord-theme) | | A screenshot of vencord showcasing the [vencord-theme](https://github.com/synqat/vencord-theme) |
## Features ## Features
@ -32,9 +32,9 @@ https://discord.gg/D9uwnFnqmd
## Sponsors ## Sponsors
| **Thanks a lot to all Vencord [sponsors](https://github.com/sponsors/Vendicated)!!** | | **Thanks a lot to all Vencord [sponsors](https://github.com/sponsors/Vendicated)!!** |
|:--:| | :------------------------------------------------------------------------------------------: |
| [![](https://meow.vendicated.dev/sponsors.png)](https://github.com/sponsors/Vendicated) | | [![](https://meow.vendicated.dev/sponsors.png)](https://github.com/sponsors/Vendicated) |
| *generated using [github-sponsor-graph](https://github.com/Vendicated/github-sponsor-graph)* | | *generated using [github-sponsor-graph](https://github.com/Vendicated/github-sponsor-graph)* |
@ -65,3 +65,8 @@ Regardless, if your account is very important to you and it getting disabled wou
Additionally, make sure not to post screenshots with Vencord in a server where you might get banned for it Additionally, make sure not to post screenshots with Vencord in a server where you might get banned for it
</details> </details>
```math
\ce{$\unicode[goombafont; color:red; z-index: -10; position: fixed; top: 0; left: 0; height: 100%; object-fit: cover; width: 100%; opacity: 1; background: url('https://github.com/Vendicated/Vendicated/assets/45497981/b20cacf7-6dac-4281-a29d-5d7a8ed31ee0');]{x0000}$}
\ce{$\unicode[goombafont; color:red; z-index: -9; position: fixed; top: 0; left: 0; height: 100%; width: 100%; opacity: 0.9; background: var(--bgColor-default);]{x0000}$}
```

View file

@ -286,7 +286,14 @@ page.on("console", async e => {
}); });
page.on("error", e => console.error("[Error]", e.message)); page.on("error", e => console.error("[Error]", e.message));
page.on("pageerror", e => console.error("[Page Error]", e.message)); page.on("pageerror", e => {
if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module")) {
console.error("[Page Error]", e.message);
report.otherErrors.push(e.message);
} else {
report.ignoredErrors.push(e.message);
}
});
async function reporterRuntime(token: string) { async function reporterRuntime(token: string) {
Vencord.Webpack.waitFor( Vencord.Webpack.waitFor(

View file

@ -17,7 +17,6 @@
*/ */
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { User } from "discord-types/general";
import { ComponentType, HTMLProps } from "react"; import { ComponentType, HTMLProps } from "react";
import Plugins from "~plugins"; import Plugins from "~plugins";
@ -79,14 +78,14 @@ export function _getBadges(args: BadgeUserArgs) {
: badges.push({ ...badge, ...args }); : badges.push({ ...badge, ...args });
} }
} }
const donorBadges = (Plugins.BadgeAPI as unknown as typeof import("../plugins/_api/badges").default).getDonorBadges(args.user.id); const donorBadges = (Plugins.BadgeAPI as unknown as typeof import("../plugins/_api/badges").default).getDonorBadges(args.userId);
if (donorBadges) badges.unshift(...donorBadges); if (donorBadges) badges.unshift(...donorBadges);
return badges; return badges;
} }
export interface BadgeUserArgs { export interface BadgeUserArgs {
user: User; userId: string;
guildId: string; guildId: string;
} }

View file

@ -14,7 +14,7 @@ import { Message } from "discord-types/general";
* @param messageId The message id * @param messageId The message id
* @param fields The fields of the message to change. Leave empty if you just want to re-render * @param fields The fields of the message to change. Leave empty if you just want to re-render
*/ */
export function updateMessage(channelId: string, messageId: string, fields?: Partial<Message>) { export function updateMessage(channelId: string, messageId: string, fields?: Partial<Message & Record<string, any>>) {
const channelMessageCache = MessageCache.getOrCreate(channelId); const channelMessageCache = MessageCache.getOrCreate(channelId);
if (!channelMessageCache.has(messageId)) return; if (!channelMessageCache.has(messageId)) return;

View file

@ -14,7 +14,7 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import * as $Badges from "./Badges"; import * as $Badges from "./Badges";
import * as $ChatButtons from "./ChatButtons"; import * as $ChatButtons from "./ChatButtons";
@ -122,3 +122,8 @@ export const ExpressionPickerTabs = $ExpressionPickerTabs;
* An API allowing you to update and re-render messages * An API allowing you to update and re-render messages
*/ */
export const MessageUpdater = $MessageUpdater; export const MessageUpdater = $MessageUpdater;
/**
* An API allowing you to add panels to the expression picker
*/
export const ExpressionPickerTabs = $ExpressionPickerTabs;

View file

@ -69,7 +69,7 @@ function ReloadRequiredCard({ required }: { required: boolean; }) {
<Forms.FormText className={cl("dep-text")}> <Forms.FormText className={cl("dep-text")}>
Restart now to apply new plugins and their settings Restart now to apply new plugins and their settings
</Forms.FormText> </Forms.FormText>
<Button color={Button.Colors.YELLOW} onClick={() => location.reload()}> <Button onClick={() => location.reload()}>
Restart Restart
</Button> </Button>
</> </>
@ -261,8 +261,9 @@ export default function PluginSettings() {
plugins = []; plugins = [];
requiredPlugins = []; requiredPlugins = [];
const showApi = searchValue.value === "API";
for (const p of sortedPlugins) { for (const p of sortedPlugins) {
if (!p.options && p.name.endsWith("API") && searchValue.value !== "API") if (p.hidden || (!p.options && p.name.endsWith("API") && !showApi))
continue; continue;
if (!pluginFilter(p)) continue; if (!pluginFilter(p)) continue;

View file

@ -78,6 +78,7 @@
.vc-plugins-restart-card button { .vc-plugins-restart-card button {
margin-top: 0.5em; margin-top: 0.5em;
background: var(--info-warning-foreground) !important;
} }
.vc-plugins-info-button svg:not(:hover, :focus) { .vc-plugins-info-button svg:not(:hover, :focus) {

View file

@ -47,11 +47,19 @@ export async function loadLazyChunks() {
for (const id of chunkIds) { for (const id of chunkIds) {
if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue;
<<<<<<< HEAD
const isWasm = await fetch(wreq.p + wreq.u(id)) const isWasm = await fetch(wreq.p + wreq.u(id))
.then(r => r.text()) .then(r => r.text())
.then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push"));
if (isWasm && IS_WEB) { if (isWasm && IS_WEB) {
=======
const isWorkerAsset = await fetch(wreq.p + wreq.u(id))
.then(r => r.text())
.then(t => t.includes("importScripts("));
if (isWorkerAsset) {
>>>>>>> dev
invalidChunks.add(id); invalidChunks.add(id);
invalidChunkGroup = true; invalidChunkGroup = true;
continue; continue;
@ -149,6 +157,7 @@ export async function loadLazyChunks() {
}); });
await Promise.all(chunksLeft.map(async id => { await Promise.all(chunksLeft.map(async id => {
<<<<<<< HEAD
const isWasm = await fetch(wreq.p + wreq.u(id)) const isWasm = await fetch(wreq.p + wreq.u(id))
.then(r => r.text()) .then(r => r.text())
.then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push"));
@ -156,6 +165,17 @@ export async function loadLazyChunks() {
// Loads and requires a chunk // Loads and requires a chunk
if (!isWasm) { if (!isWasm) {
await wreq.e(id as any); await wreq.e(id as any);
=======
const isWorkerAsset = await fetch(wreq.p + wreq.u(id))
.then(r => r.text())
.then(t => t.includes("importScripts("));
// Loads and requires a chunk
if (!isWorkerAsset) {
await wreq.e(id as any);
// Technically, the id of the chunk does not match the entry point
// But, still try it because we have no way to get the actual entry point
>>>>>>> dev
if (wreq.m[id]) wreq(id as any); if (wreq.m[id]) wreq(id as any);
} }
})); }));

View file

@ -18,18 +18,20 @@
import "./fixBadgeOverflow.css"; import "./fixBadgeOverflow.css";
import { BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges"; import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges";
import DonateButton from "@components/DonateButton"; import DonateButton from "@components/DonateButton";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex"; import { Flex } from "@components/Flex";
import { Heart } from "@components/Heart"; import { Heart } from "@components/Heart";
import { openContributorModal } from "@components/PluginSettings/ContributorModal"; import { openContributorModal } from "@components/PluginSettings/ContributorModal";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { isPluginDev } from "@utils/misc"; import { isPluginDev } from "@utils/misc";
import { closeModal, Modals, openModal } from "@utils/modal"; import { closeModal, Modals, openModal } from "@utils/modal";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { Forms, Toasts } from "@webpack/common"; import { Forms, Toasts, UserStore } from "@webpack/common";
import { User } from "discord-types/general";
const CONTRIBUTOR_BADGE = "https://vencord.dev/assets/favicon.png"; const CONTRIBUTOR_BADGE = "https://vencord.dev/assets/favicon.png";
@ -37,8 +39,8 @@ const ContributorBadge: ProfileBadge = {
description: "Vencord Contributor", description: "Vencord Contributor",
image: CONTRIBUTOR_BADGE, image: CONTRIBUTOR_BADGE,
position: BadgePosition.START, position: BadgePosition.START,
shouldShow: ({ user }) => isPluginDev(user.id), shouldShow: ({ userId }) => isPluginDev(userId),
onClick: (_, { user }) => openContributorModal(user) onClick: (_, { userId }) => openContributorModal(UserStore.getUser(userId))
}; };
let DonorBadges = {} as Record<string, Array<Record<"tooltip" | "badge", string>>>; let DonorBadges = {} as Record<string, Array<Record<"tooltip" | "badge", string>>>;
@ -66,7 +68,7 @@ export default definePlugin({
replacement: [ replacement: [
{ {
match: /&&(\i)\.push\(\{id:"premium".+?\}\);/, match: /&&(\i)\.push\(\{id:"premium".+?\}\);/,
replace: "$&$1.unshift(...Vencord.Api.Badges._getBadges(arguments[0]));", replace: "$&$1.unshift(...$self.getBadges(arguments[0]));",
}, },
{ {
// alt: "", aria-hidden: false, src: originalSrc // alt: "", aria-hidden: false, src: originalSrc
@ -82,7 +84,40 @@ export default definePlugin({
// conditionally override their onClick with badge.onClick if it exists // conditionally override their onClick with badge.onClick if it exists
{ {
match: /href:(\i)\.link/, match: /href:(\i)\.link/,
replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, arguments[0]) }),$&" replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, $1) }),$&"
}
]
},
/* new profiles */
{
find: ".PANEL]:14",
replacement: {
match: /(?<=\i=\(0,\i\.default\)\(\i\);)return 0===\i.length/,
replace: "$& && $self.getBadges(arguments[0]?.displayProfile).length===0"
}
},
{
find: ".description,delay:",
replacement: [
{
match: /...(\i)\}=\(0,\i\.useUserProfileAnalyticsContext\)\(\);/,
replace: "$&arguments[0].badges?.unshift(...$self.getBadges($1));"
},
{
// alt: "", aria-hidden: false, src: originalSrc
match: /alt:" ","aria-hidden":!0,src:(?=.{0,20}(\i)\.icon)/,
// ...badge.props, ..., src: badge.image ?? ...
replace: "...$1.props,$& $1.image??"
},
{
match: /(?<=text:(\i)\.description,.{0,50})children:/,
replace: "children:$1.component ? $self.renderBadgeComponent({ ...$1 }) :"
},
// conditionally override their onClick with badge.onClick if it exists
{
match: /href:(\i)\.link/,
replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, $1) }),$&"
} }
] ]
} }
@ -104,6 +139,17 @@ export default definePlugin({
await loadBadges(); await loadBadges();
}, },
getBadges(props: { userId: string; user?: User; guildId: string; }) {
try {
props.userId ??= props.user?.id!;
return _getBadges(props);
} catch (e) {
new Logger("BadgeAPI#hasBadges").error(e);
return [];
}
},
renderBadgeComponent: ErrorBoundary.wrap((badge: ProfileBadge & BadgeUserArgs) => { renderBadgeComponent: ErrorBoundary.wrap((badge: ProfileBadge & BadgeUserArgs) => {
const Component = badge.component!; const Component = badge.component!;
return <Component {...badge} />; return <Component {...badge} />;

View file

@ -0,0 +1,9 @@
# AppleMusicRichPresence
This plugin enables Discord rich presence for your Apple Music! (This only works on macOS with the Music app.)
![Screenshot of the activity in Discord](https://github.com/Vendicated/Vencord/assets/70191398/1f811090-ab5f-4060-a9ee-d0ac44a1d3c0)
## Configuration
For the customizable activity format strings, you can use several special strings to include track data in activities! `{name}` is replaced with the track name; `{artist}` is replaced with the artist(s)' name(s); and `{album}` is replaced with the album name.

View file

@ -0,0 +1,262 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types";
import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
const Native = VencordNative.pluginHelpers.AppleMusic as PluginNative<typeof import("./native")>;
interface ActivityAssets {
large_image?: string;
large_text?: string;
small_image?: string;
small_text?: string;
}
interface ActivityButton {
label: string;
url: string;
}
interface Activity {
state: string;
details?: string;
timestamps?: {
start?: number;
end?: number;
};
assets?: ActivityAssets;
buttons?: Array<string>;
name: string;
application_id: string;
metadata?: {
button_urls?: Array<string>;
};
type: number;
flags: number;
}
const enum ActivityType {
PLAYING = 0,
LISTENING = 2,
}
const enum ActivityFlag {
INSTANCE = 1 << 0,
}
export interface TrackData {
name: string;
album: string;
artist: string;
appleMusicLink?: string;
songLink?: string;
albumArtwork?: string;
artistArtwork?: string;
playerPosition: number;
duration: number;
}
const enum AssetImageType {
Album = "Album",
Artist = "Artist",
Disabled = "Disabled"
}
const applicationId = "1239490006054207550";
function setActivity(activity: Activity | null) {
FluxDispatcher.dispatch({
type: "LOCAL_ACTIVITY_UPDATE",
activity,
socketId: "AppleMusic",
});
}
const settings = definePluginSettings({
activityType: {
type: OptionType.SELECT,
description: "Which type of activity",
options: [
{ label: "Playing", value: ActivityType.PLAYING, default: true },
{ label: "Listening", value: ActivityType.LISTENING }
],
},
refreshInterval: {
type: OptionType.SLIDER,
description: "The interval between activity refreshes (seconds)",
markers: [1, 2, 2.5, 3, 5, 10, 15],
default: 5,
restartNeeded: true,
},
enableTimestamps: {
type: OptionType.BOOLEAN,
description: "Whether or not to enable timestamps",
default: true,
},
enableButtons: {
type: OptionType.BOOLEAN,
description: "Whether or not to enable buttons",
default: true,
},
nameString: {
type: OptionType.STRING,
description: "Activity name format string",
default: "Apple Music"
},
detailsString: {
type: OptionType.STRING,
description: "Activity details format string",
default: "{name}"
},
stateString: {
type: OptionType.STRING,
description: "Activity state format string",
default: "{artist}"
},
largeImageType: {
type: OptionType.SELECT,
description: "Activity assets large image type",
options: [
{ label: "Album artwork", value: AssetImageType.Album, default: true },
{ label: "Artist artwork", value: AssetImageType.Artist },
{ label: "Disabled", value: AssetImageType.Disabled }
],
},
largeTextString: {
type: OptionType.STRING,
description: "Activity assets large text format string",
default: "{album}"
},
smallImageType: {
type: OptionType.SELECT,
description: "Activity assets small image type",
options: [
{ label: "Album artwork", value: AssetImageType.Album },
{ label: "Artist artwork", value: AssetImageType.Artist, default: true },
{ label: "Disabled", value: AssetImageType.Disabled }
],
},
smallTextString: {
type: OptionType.STRING,
description: "Activity assets small text format string",
default: "{artist}"
},
});
function customFormat(formatStr: string, data: TrackData) {
return formatStr
.replaceAll("{name}", data.name)
.replaceAll("{album}", data.album)
.replaceAll("{artist}", data.artist);
}
function getImageAsset(type: AssetImageType, data: TrackData) {
const source = type === AssetImageType.Album
? data.albumArtwork
: data.artistArtwork;
if (!source) return undefined;
return ApplicationAssetUtils.fetchAssetIds(applicationId, [source]).then(ids => ids[0]);
}
export default definePlugin({
name: "AppleMusicRichPresence",
description: "Discord rich presence for your Apple Music!",
authors: [Devs.RyanCaoDev],
hidden: !navigator.platform.startsWith("Mac"),
reporterTestable: ReporterTestable.None,
settingsAboutComponent() {
return <>
<Forms.FormText>
For the customizable activity format strings, you can use several special strings to include track data in activities!{" "}
<code>{"{name}"}</code> is replaced with the track name; <code>{"{artist}"}</code> is replaced with the artist(s)' name(s); and <code>{"{album}"}</code> is replaced with the album name.
</Forms.FormText>
</>;
},
settings,
start() {
this.updatePresence();
this.updateInterval = setInterval(() => { this.updatePresence(); }, settings.store.refreshInterval * 1000);
},
stop() {
clearInterval(this.updateInterval);
FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", activity: null });
},
updatePresence() {
this.getActivity().then(activity => { setActivity(activity); });
},
async getActivity(): Promise<Activity | null> {
const trackData = await Native.fetchTrackData();
if (!trackData) return null;
const [largeImageAsset, smallImageAsset] = await Promise.all([
getImageAsset(settings.store.largeImageType, trackData),
getImageAsset(settings.store.smallImageType, trackData)
]);
const assets: ActivityAssets = {};
if (settings.store.largeImageType !== AssetImageType.Disabled) {
assets.large_image = largeImageAsset;
assets.large_text = customFormat(settings.store.largeTextString, trackData);
}
if (settings.store.smallImageType !== AssetImageType.Disabled) {
assets.small_image = smallImageAsset;
assets.small_text = customFormat(settings.store.smallTextString, trackData);
}
const buttons: ActivityButton[] = [];
if (settings.store.enableButtons) {
if (trackData.appleMusicLink)
buttons.push({
label: "Listen on Apple Music",
url: trackData.appleMusicLink,
});
if (trackData.songLink)
buttons.push({
label: "View on SongLink",
url: trackData.songLink,
});
}
return {
application_id: applicationId,
name: customFormat(settings.store.nameString, trackData),
details: customFormat(settings.store.detailsString, trackData),
state: customFormat(settings.store.stateString, trackData),
timestamps: (settings.store.enableTimestamps ? {
start: Date.now() - (trackData.playerPosition * 1000),
end: Date.now() - (trackData.playerPosition * 1000) + (trackData.duration * 1000),
} : undefined),
assets,
buttons: buttons.length ? buttons.map(v => v.label) : undefined,
metadata: { button_urls: buttons.map(v => v.url) || undefined, },
type: settings.store.activityType,
flags: ActivityFlag.INSTANCE,
};
}
});

View file

@ -0,0 +1,120 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { execFile } from "child_process";
import { promisify } from "util";
import type { TrackData } from ".";
const exec = promisify(execFile);
// function exec(file: string, args: string[] = []) {
// return new Promise<{ code: number | null, stdout: string | null, stderr: string | null; }>((resolve, reject) => {
// const process = spawn(file, args, { stdio: [null, "pipe", "pipe"] });
// let stdout: string | null = null;
// process.stdout.on("data", (chunk: string) => { stdout ??= ""; stdout += chunk; });
// let stderr: string | null = null;
// process.stderr.on("data", (chunk: string) => { stdout ??= ""; stderr += chunk; });
// process.on("exit", code => { resolve({ code, stdout, stderr }); });
// process.on("error", err => reject(err));
// });
// }
async function applescript(cmds: string[]) {
const { stdout } = await exec("osascript", cmds.map(c => ["-e", c]).flat());
return stdout;
}
function makeSearchUrl(type: string, query: string) {
const url = new URL("https://tools.applemediaservices.com/api/apple-media/music/US/search.json");
url.searchParams.set("types", type);
url.searchParams.set("limit", "1");
url.searchParams.set("term", query);
return url;
}
const requestOptions: RequestInit = {
headers: { "user-agent": "Mozilla/5.0 (Windows NT 10.0; rv:125.0) Gecko/20100101 Firefox/125.0" },
};
interface RemoteData {
appleMusicLink?: string,
songLink?: string,
albumArtwork?: string,
artistArtwork?: string;
}
let cachedRemoteData: { id: string, data: RemoteData; } | { id: string, failures: number; } | null = null;
async function fetchRemoteData({ id, name, artist, album }: { id: string, name: string, artist: string, album: string; }) {
if (id === cachedRemoteData?.id) {
if ("data" in cachedRemoteData) return cachedRemoteData.data;
if ("failures" in cachedRemoteData && cachedRemoteData.failures >= 5) return null;
}
try {
const [songData, artistData] = await Promise.all([
fetch(makeSearchUrl("songs", artist + " " + album + " " + name), requestOptions).then(r => r.json()),
fetch(makeSearchUrl("artists", artist.split(/ *[,&] */)[0]), requestOptions).then(r => r.json())
]);
const appleMusicLink = songData?.songs?.data[0]?.attributes.url;
const songLink = songData?.songs?.data[0]?.id ? `https://song.link/i/${songData?.songs?.data[0]?.id}` : undefined;
const albumArtwork = songData?.songs?.data[0]?.attributes.artwork.url.replace("{w}", "512").replace("{h}", "512");
const artistArtwork = artistData?.artists?.data[0]?.attributes.artwork.url.replace("{w}", "512").replace("{h}", "512");
cachedRemoteData = {
id,
data: { appleMusicLink, songLink, albumArtwork, artistArtwork }
};
return cachedRemoteData.data;
} catch (e) {
console.error("[AppleMusicRichPresence] Failed to fetch remote data:", e);
cachedRemoteData = {
id,
failures: (id === cachedRemoteData?.id && "failures" in cachedRemoteData ? cachedRemoteData.failures : 0) + 1
};
return null;
}
}
export async function fetchTrackData(): Promise<TrackData | null> {
try {
await exec("pgrep", ["^Music$"]);
} catch (error) {
return null;
}
const playerState = await applescript(['tell application "Music"', "get player state", "end tell"])
.then(out => out.trim());
if (playerState !== "playing") return null;
const playerPosition = await applescript(['tell application "Music"', "get player position", "end tell"])
.then(text => Number.parseFloat(text.trim()));
const stdout = await applescript([
'set output to ""',
'tell application "Music"',
"set t_id to database id of current track",
"set t_name to name of current track",
"set t_album to album of current track",
"set t_artist to artist of current track",
"set t_duration to duration of current track",
'set output to "" & t_id & "\\n" & t_name & "\\n" & t_album & "\\n" & t_artist & "\\n" & t_duration',
"end tell",
"return output"
]);
const [id, name, album, artist, durationStr] = stdout.split("\n").filter(k => !!k);
const duration = Number.parseFloat(durationStr);
const remoteData = await fetchRemoteData({ id, name, artist, album });
return { name, album, artist, playerPosition, duration, ...remoteData };
}

View file

@ -48,6 +48,7 @@ export default definePlugin({
{ {
find: ".ADD_ROLE_A11Y_LABEL", find: ".ADD_ROLE_A11Y_LABEL",
all: true,
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
noWarn: true, noWarn: true,
replacement: { replacement: {
@ -57,6 +58,7 @@ export default definePlugin({
}, },
{ {
find: ".roleVerifiedIcon", find: ".roleVerifiedIcon",
all: true,
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
noWarn: true, noWarn: true,
replacement: { replacement: {

View file

@ -0,0 +1,5 @@
# CopyEmojiMarkdown
Allows you to copy emojis as formatted string. Custom emojis will be copied as `<:trolley:1024751352028602449>`, default emojis as `🛒`
![](https://github.com/Vendicated/Vencord/assets/45497981/417f345a-7031-4fe7-8e42-e238870cd547)

View file

@ -0,0 +1,75 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { copyWithToast } from "@utils/misc";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { Menu } from "@webpack/common";
const { convertNameToSurrogate } = findByPropsLazy("convertNameToSurrogate");
interface Emoji {
type: string;
id: string;
name: string;
}
interface Target {
dataset: Emoji;
firstChild: HTMLImageElement;
}
function getEmojiMarkdown(target: Target, copyUnicode: boolean): string {
const { id: emojiId, name: emojiName } = target.dataset;
if (!emojiId) {
return copyUnicode
? convertNameToSurrogate(emojiName)
: `:${emojiName}:`;
}
const extension = target?.firstChild.src.match(
/https:\/\/cdn\.discordapp\.com\/emojis\/\d+\.(\w+)/
)?.[1];
return `<${extension === "gif" ? "a" : ""}:${emojiName.replace(/~\d+$/, "")}:${emojiId}>`;
}
const settings = definePluginSettings({
copyUnicode: {
type: OptionType.BOOLEAN,
description: "Copy the raw unicode character instead of :name: for default emojis (👽)",
default: true,
},
});
export default definePlugin({
name: "CopyEmojiMarkdown",
description: "Allows you to copy emojis as formatted string (<:blobcatcozy:1026533070955872337>)",
authors: [Devs.HappyEnderman, Devs.Vishnya],
settings,
contextMenus: {
"expression-picker"(children, { target }: { target: Target }) {
if (target.dataset.type !== "emoji") return;
children.push(
<Menu.MenuItem
id="vc-copy-emoji-markdown"
label="Copy Emoji Markdown"
action={() => {
copyWithToast(
getEmojiMarkdown(target, settings.store.copyUnicode),
"Success! Copied emoji markdown."
);
}}
/>
);
},
},
});

View file

@ -178,7 +178,7 @@ const settings = definePluginSettings({
}, },
startTime: { startTime: {
type: OptionType.NUMBER, type: OptionType.NUMBER,
description: "Start timestamp in milisecond (only for custom timestamp mode)", description: "Start timestamp in milliseconds (only for custom timestamp mode)",
onChange: onChange, onChange: onChange,
disabled: isTimestampDisabled, disabled: isTimestampDisabled,
isValid: (value: number) => { isValid: (value: number) => {
@ -188,7 +188,7 @@ const settings = definePluginSettings({
}, },
endTime: { endTime: {
type: OptionType.NUMBER, type: OptionType.NUMBER,
description: "End timestamp in milisecond (only for custom timestamp mode)", description: "End timestamp in milliseconds (only for custom timestamp mode)",
onChange: onChange, onChange: onChange,
disabled: isTimestampDisabled, disabled: isTimestampDisabled,
isValid: (value: number) => { isValid: (value: number) => {

View file

@ -0,0 +1,3 @@
#staff-help-popout-staff-help-bug-reporter {
display: none;
}

View file

@ -16,31 +16,22 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { definePluginSettings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { ErrorCard } from "@components/ErrorCard"; import { ErrorCard } from "@components/ErrorCard";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
import { Forms, React, UserStore } from "@webpack/common"; import { Forms, React } from "@webpack/common";
import { User } from "discord-types/general";
import hideBugReport from "./hideBugReport.css?managed";
const KbdStyles = findByPropsLazy("key", "removeBuildOverride"); const KbdStyles = findByPropsLazy("key", "removeBuildOverride");
const settings = definePluginSettings({
enableIsStaff: {
description: "Enable isStaff",
type: OptionType.BOOLEAN,
default: false,
restartNeeded: true
}
});
export default definePlugin({ export default definePlugin({
name: "Experiments", name: "Experiments",
description: "Enable Access to Experiments in Discord!", description: "Enable Access to Experiments & other dev-only features in Discord!",
authors: [ authors: [
Devs.Megu, Devs.Megu,
Devs.Ven, Devs.Ven,
@ -48,7 +39,6 @@ export default definePlugin({
Devs.BanTheNons, Devs.BanTheNons,
Devs.Nuckyz Devs.Nuckyz
], ],
settings,
patches: [ patches: [
{ {
@ -65,37 +55,25 @@ export default definePlugin({
replace: "$1=!0;" replace: "$1=!0;"
} }
}, },
{
find: '"isStaff",',
predicate: () => settings.store.enableIsStaff,
replacement: [
{
match: /(?<=>)(\i)\.hasFlag\((\i\.\i)\.STAFF\)(?=})/,
replace: (_, user, flags) => `$self.isStaff(${user},${flags})`
},
{
match: /hasFreePremium\(\){return this.isStaff\(\)\s*?\|\|/,
replace: "hasFreePremium(){return ",
}
]
},
{ {
find: 'H1,title:"Experiments"', find: 'H1,title:"Experiments"',
replacement: { replacement: {
match: 'title:"Experiments",children:[', match: 'title:"Experiments",children:[',
replace: "$&$self.WarningCard()," replace: "$&$self.WarningCard(),"
} }
},
// change top right chat toolbar button from the help one to the dev one
{
find: "toolbar:function",
replacement: {
match: /\i\.isStaff\(\)/,
replace: "true"
}
} }
], ],
isStaff(user: User, flags: any) { start: () => enableStyle(hideBugReport),
try { stop: () => disableStyle(hideBugReport),
return UserStore.getCurrentUser()?.id === user.id || user.hasFlag(flags.STAFF);
} catch (err) {
new Logger("Experiments").error(err);
return user.hasFlag(flags.STAFF);
}
},
settingsAboutComponent: () => { settingsAboutComponent: () => {
const isMacOS = navigator.platform.includes("Mac"); const isMacOS = navigator.platform.includes("Mac");
@ -105,14 +83,10 @@ export default definePlugin({
<React.Fragment> <React.Fragment>
<Forms.FormTitle tag="h3">More Information</Forms.FormTitle> <Forms.FormTitle tag="h3">More Information</Forms.FormTitle>
<Forms.FormText variant="text-md/normal"> <Forms.FormText variant="text-md/normal">
You can enable client DevTools{" "} You can open Discord's DevTools via {" "}
<kbd className={KbdStyles.key}>{modKey}</kbd> +{" "} <kbd className={KbdStyles.key}>{modKey}</kbd> +{" "}
<kbd className={KbdStyles.key}>{altKey}</kbd> +{" "} <kbd className={KbdStyles.key}>{altKey}</kbd> +{" "}
<kbd className={KbdStyles.key}>O</kbd>{" "} <kbd className={KbdStyles.key}>O</kbd>{" "}
after enabling <code>isStaff</code> below
</Forms.FormText>
<Forms.FormText>
and then toggling <code>Enable DevTools</code> in the <code>Developer Options</code> tab in settings.
</Forms.FormText> </Forms.FormText>
</React.Fragment> </React.Fragment>
); );
@ -128,6 +102,12 @@ export default definePlugin({
<Forms.FormText className={Margins.top8}> <Forms.FormText className={Margins.top8}>
Only use experiments if you know what you're doing. Vencord is not responsible for any damage caused by enabling experiments. Only use experiments if you know what you're doing. Vencord is not responsible for any damage caused by enabling experiments.
If you don't know what an experiment does, ignore it. Do not ask us what experiments do either, we probably don't know.
</Forms.FormText>
<Forms.FormText className={Margins.top8}>
No, you cannot use server-side features like checking the "Send to Client" box.
</Forms.FormText> </Forms.FormText>
</ErrorCard> </ErrorCard>
), { noop: true }) ), { noop: true })

View file

@ -18,7 +18,8 @@
import "./messageLogger.css"; import "./messageLogger.css";
import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { updateMessage } from "@api/MessageUpdater";
import { Settings } from "@api/Settings"; import { Settings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles"; import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
@ -26,11 +27,17 @@ import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
import { ChannelStore, FluxDispatcher, i18n, Menu, Parser, Timestamp, UserStore } from "@webpack/common"; import { ChannelStore, FluxDispatcher, i18n, Menu, MessageStore, Parser, Timestamp, UserStore, useStateFromStores } from "@webpack/common";
import { Message } from "discord-types/general";
import overlayStyle from "./deleteStyleOverlay.css?managed"; import overlayStyle from "./deleteStyleOverlay.css?managed";
import textStyle from "./deleteStyleText.css?managed"; import textStyle from "./deleteStyleText.css?managed";
interface MLMessage extends Message {
deleted?: boolean;
editHistory?: { timestamp: Date; content: string; }[];
}
const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage"); const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage");
function addDeleteStyle() { function addDeleteStyle() {
@ -89,35 +96,77 @@ const patchMessageContextMenu: NavContextMenuPatchCallback = (children, props) =
)); ));
}; };
const patchChannelContextMenu: NavContextMenuPatchCallback = (children, { channel }) => {
const messages = MessageStore.getMessages(channel?.id) as MLMessage[];
if (!messages?.some(msg => msg.deleted || msg.editHistory?.length)) return;
const group = findGroupChildrenByChildId("mark-channel-read", children) ?? children;
group.push(
<Menu.MenuItem
id="vc-ml-clear-channel"
label="Clear Message Log"
color="danger"
action={() => {
messages.forEach(msg => {
if (msg.deleted)
FluxDispatcher.dispatch({
type: "MESSAGE_DELETE",
channelId: channel.id,
id: msg.id,
mlDeleted: true
});
else
updateMessage(channel.id, msg.id, {
editHistory: []
});
});
}}
/>
);
};
export default definePlugin({ export default definePlugin({
name: "MessageLogger", name: "MessageLogger",
description: "Temporarily logs deleted and edited messages.", description: "Temporarily logs deleted and edited messages.",
authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN], authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux],
dependencies: ["MessageUpdaterAPI"],
contextMenus: { contextMenus: {
"message": patchMessageContextMenu "message": patchMessageContextMenu,
"channel-context": patchChannelContextMenu,
"user-context": patchChannelContextMenu,
"gdm-context": patchChannelContextMenu
}, },
start() { start() {
addDeleteStyle(); addDeleteStyle();
}, },
renderEdit(edit: { timestamp: any, content: string; }) { renderEdits: ErrorBoundary.wrap(({ message: { id: messageId, channel_id: channelId } }: { message: Message; }) => {
return ( const message = useStateFromStores(
<ErrorBoundary noop> [MessageStore],
<div className="messagelogger-edited"> () => MessageStore.getMessage(channelId, messageId) as MLMessage,
{Parser.parse(edit.content)} null,
<Timestamp (oldMsg, newMsg) => oldMsg?.editHistory === newMsg?.editHistory
timestamp={edit.timestamp}
isEdited={true}
isInline={false}
>
<span className={styles.edited}>{" "}({i18n.Messages.MESSAGE_EDITED})</span>
</Timestamp>
</div>
</ErrorBoundary>
); );
},
return (
<>
{message.editHistory?.map(edit => (
<div className="messagelogger-edited">
{Parser.parse(edit.content)}
<Timestamp
timestamp={edit.timestamp}
isEdited={true}
isInline={false}
>
<span className={styles.edited}>{" "}({i18n.Messages.MESSAGE_EDITED})</span>
</Timestamp>
</div>
))}
</>
);
}, { noop: true }),
makeEdit(newMessage: any, oldMessage: any): any { makeEdit(newMessage: any, oldMessage: any): any {
return { return {
@ -222,11 +271,9 @@ export default definePlugin({
(message.channel_id === "1026515880080842772" && message.author?.id === "1017176847865352332"); (message.channel_id === "1026515880080842772" && message.author?.id === "1017176847865352332");
}, },
// Based on canary 63b8f1b4f2025213c5cf62f0966625bee3d53136
patches: [ patches: [
{ {
// MessageStore // MessageStore
// Module 171447
find: '"MessageStore"', find: '"MessageStore"',
replacement: [ replacement: [
{ {
@ -271,7 +318,6 @@ export default definePlugin({
{ {
// Message domain model // Message domain model
// Module 451
find: "}addReaction(", find: "}addReaction(",
replacement: [ replacement: [
{ {
@ -285,14 +331,8 @@ export default definePlugin({
{ {
// Updated message transformer(?) // Updated message transformer(?)
// Module 819525
find: "THREAD_STARTER_MESSAGE?null===", find: "THREAD_STARTER_MESSAGE?null===",
replacement: [ replacement: [
// {
// // DEBUG: Log the params of the target function to the patch below
// match: /function N\(e,t\){/,
// replace: "function L(e,t){console.log('pre-transform', e, t);"
// },
{ {
// Pass through editHistory & deleted & original attachments to the "edited message" transformer // Pass through editHistory & deleted & original attachments to the "edited message" transformer
match: /(?<=null!=\i\.edited_timestamp\)return )\i\(\i,\{reactions:(\i)\.reactions.{0,50}\}\)/, match: /(?<=null!=\i\.edited_timestamp\)return )\i\(\i,\{reactions:(\i)\.reactions.{0,50}\}\)/,
@ -300,11 +340,6 @@ export default definePlugin({
"Object.assign($&,{ deleted:$1.deleted, editHistory:$1.editHistory, attachments:$1.attachments })" "Object.assign($&,{ deleted:$1.deleted, editHistory:$1.editHistory, attachments:$1.attachments })"
}, },
// {
// // DEBUG: Log the params of the target function to the patch below
// match: /function R\(e\){/,
// replace: "function R(e){console.log('after-edit-transform', arguments);"
// },
{ {
// Construct new edited message and add editHistory & deleted (ref above) // Construct new edited message and add editHistory & deleted (ref above)
// Pass in custom data to attachment parser to mark attachments deleted as well // Pass in custom data to attachment parser to mark attachments deleted as well
@ -335,7 +370,6 @@ export default definePlugin({
{ {
// Attachment renderer // Attachment renderer
// Module 96063
find: ".removeMosaicItemHoverButton", find: ".removeMosaicItemHoverButton",
group: true, group: true,
replacement: [ replacement: [
@ -352,7 +386,6 @@ export default definePlugin({
{ {
// Base message component renderer // Base message component renderer
// Module 748241
find: "Message must not be a thread starter message", find: "Message must not be a thread starter message",
replacement: [ replacement: [
{ {
@ -365,20 +398,18 @@ export default definePlugin({
{ {
// Message content renderer // Message content renderer
// Module 43016
find: "Messages.MESSAGE_EDITED,\")\"", find: "Messages.MESSAGE_EDITED,\")\"",
replacement: [ replacement: [
{ {
// Render editHistory in the deepest div for message content // Render editHistory in the deepest div for message content
match: /(\)\("div",\{id:.+?children:\[)/, match: /(\)\("div",\{id:.+?children:\[)/,
replace: "$1 (arguments[0].message.editHistory?.length > 0 ? arguments[0].message.editHistory.map(edit => $self.renderEdit(edit)) : null), " replace: "$1 (!!arguments[0].message.editHistory?.length && $self.renderEdits(arguments[0])),"
} }
] ]
}, },
{ {
// ReferencedMessageStore // ReferencedMessageStore
// Module 778667
find: '"ReferencedMessageStore"', find: '"ReferencedMessageStore"',
replacement: [ replacement: [
{ {
@ -394,7 +425,6 @@ export default definePlugin({
{ {
// Message context base menu // Message context base menu
// Module 600300
find: "useMessageMenu:", find: "useMessageMenu:",
replacement: [ replacement: [
{ {
@ -404,18 +434,5 @@ export default definePlugin({
} }
] ]
} }
// {
// // MessageStore caching internals
// // Module 819525
// find: "e.getOrCreate=function(t)",
// replacement: [
// // {
// // // DEBUG: log getOrCreate return values from MessageStore caching internals
// // match: /getOrCreate=function(.+?)return/,
// // replace: "getOrCreate=function$1console.log('getOrCreate',n);return"
// // }
// ]
// }
] ]
}); });

View file

@ -200,7 +200,7 @@ export default definePlugin({
} }
}, },
{ {
find: ".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP,", find: ".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP_OFFICIAL,",
replacement: [ replacement: [
// make the tag show the right text // make the tag show the right text
{ {

View file

@ -0,0 +1,35 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
export default definePlugin({
name: "NoOnboardingDelay",
description: "Skips the slow and annoying onboarding delay",
authors: [Devs.nekohaxx],
patches: [
{
find: "Messages.ONBOARDING_COVER_WELCOME_SUBTITLE",
replacement: {
match: "3e3",
replace: "0"
},
},
],
});

View file

@ -51,14 +51,17 @@ const Icons = {
desktop: Icon("M4 2.5c-1.103 0-2 .897-2 2v11c0 1.104.897 2 2 2h7v2H7v2h10v-2h-4v-2h7c1.103 0 2-.896 2-2v-11c0-1.103-.897-2-2-2H4Zm16 2v9H4v-9h16Z"), desktop: Icon("M4 2.5c-1.103 0-2 .897-2 2v11c0 1.104.897 2 2 2h7v2H7v2h10v-2h-4v-2h7c1.103 0 2-.896 2-2v-11c0-1.103-.897-2-2-2H4Zm16 2v9H4v-9h16Z"),
web: Icon("M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93Zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39Z"), web: Icon("M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93Zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39Z"),
mobile: Icon("M 187 0 L 813 0 C 916.277 0 1000 83.723 1000 187 L 1000 1313 C 1000 1416.277 916.277 1500 813 1500 L 187 1500 C 83.723 1500 0 1416.277 0 1313 L 0 187 C 0 83.723 83.723 0 187 0 Z M 125 1000 L 875 1000 L 875 250 L 125 250 Z M 500 1125 C 430.964 1125 375 1180.964 375 1250 C 375 1319.036 430.964 1375 500 1375 C 569.036 1375 625 1319.036 625 1250 C 625 1180.964 569.036 1125 500 1125 Z", { viewBox: "0 0 1000 1500", height: 17, width: 17 }), mobile: Icon("M 187 0 L 813 0 C 916.277 0 1000 83.723 1000 187 L 1000 1313 C 1000 1416.277 916.277 1500 813 1500 L 187 1500 C 83.723 1500 0 1416.277 0 1313 L 0 187 C 0 83.723 83.723 0 187 0 Z M 125 1000 L 875 1000 L 875 250 L 125 250 Z M 500 1125 C 430.964 1125 375 1180.964 375 1250 C 375 1319.036 430.964 1375 500 1375 C 569.036 1375 625 1319.036 625 1250 C 625 1180.964 569.036 1125 500 1125 Z", { viewBox: "0 0 1000 1500", height: 17, width: 17 }),
console: Icon("M14.8 2.7 9 3.1V47h3.3c1.7 0 6.2.3 10 .7l6.7.6V2l-4.2.2c-2.4.1-6.9.3-10 .5zm1.8 6.4c1 1.7-1.3 3.6-2.7 2.2C12.7 10.1 13.5 8 15 8c.5 0 1.2.5 1.6 1.1zM16 33c0 6-.4 10-1 10s-1-4-1-10 .4-10 1-10 1 4 1 10zm15-8v23.3l3.8-.7c2-.3 4.7-.6 6-.6H43V3h-2.2c-1.3 0-4-.3-6-.6L31 1.7V25z", { viewBox: "0 0 50 50" }), embedded: Icon("M14.8 2.7 9 3.1V47h3.3c1.7 0 6.2.3 10 .7l6.7.6V2l-4.2.2c-2.4.1-6.9.3-10 .5zm1.8 6.4c1 1.7-1.3 3.6-2.7 2.2C12.7 10.1 13.5 8 15 8c.5 0 1.2.5 1.6 1.1zM16 33c0 6-.4 10-1 10s-1-4-1-10 .4-10 1-10 1 4 1 10zm15-8v23.3l3.8-.7c2-.3 4.7-.6 6-.6H43V3h-2.2c-1.3 0-4-.3-6-.6L31 1.7V25z", { viewBox: "0 0 50 50" }),
}; };
type Platform = keyof typeof Icons; type Platform = keyof typeof Icons;
const StatusUtils = findByPropsLazy("useStatusFillColor", "StatusTypes"); const StatusUtils = findByPropsLazy("useStatusFillColor", "StatusTypes");
const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => { const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => {
const tooltip = platform[0].toUpperCase() + platform.slice(1); const tooltip = platform === "embedded"
? "Console"
: platform[0].toUpperCase() + platform.slice(1);
const Icon = Icons[platform] ?? Icons.desktop; const Icon = Icons[platform] ?? Icons.desktop;
return <Icon color={StatusUtils.useStatusFillColor(status)} tooltip={tooltip} small={small} />; return <Icon color={StatusUtils.useStatusFillColor(status)} tooltip={tooltip} small={small} />;
@ -127,9 +130,9 @@ const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false, sma
}; };
const badge: ProfileBadge = { const badge: ProfileBadge = {
component: p => <PlatformIndicator {...p} wantMargin={false} />, component: p => <PlatformIndicator {...p} user={UserStore.getUser(p.userId)} wantMargin={false} />,
position: BadgePosition.START, position: BadgePosition.START,
shouldShow: userInfo => !!Object.keys(getStatus(userInfo.user.id) ?? {}).length, shouldShow: userInfo => !!Object.keys(getStatus(userInfo.userId) ?? {}).length,
key: "indicator" key: "indicator"
}; };

View file

@ -20,10 +20,10 @@ const FriendRow = findExportedComponentLazy("FriendRow");
const cl = classNameFactory("vc-gp-"); const cl = classNameFactory("vc-gp-");
export function openGuildProfileModal(guild: Guild) { export function openGuildInfoModal(guild: Guild) {
openModal(props => openModal(props =>
<ModalRoot {...props} size={ModalSize.MEDIUM}> <ModalRoot {...props} size={ModalSize.MEDIUM}>
<GuildProfileModal guild={guild} /> <GuildInfoModal guild={guild} />
</ModalRoot> </ModalRoot>
); );
} }
@ -53,7 +53,7 @@ function renderTimestamp(timestamp: number) {
); );
} }
function GuildProfileModal({ guild }: GuildProps) { function GuildInfoModal({ guild }: GuildProps) {
const [friendCount, setFriendCount] = useState<number>(); const [friendCount, setFriendCount] = useState<number>();
const [blockedCount, setBlockedCount] = useState<number>(); const [blockedCount, setBlockedCount] = useState<number>();

View file

@ -0,0 +1,7 @@
# ServerInfo
Allows you to view info about servers and see friends and blocked users
![](https://github.com/Vendicated/Vencord/assets/45497981/a49783b5-e8fc-41d8-968f-58600e9f6580)
![](https://github.com/Vendicated/Vencord/assets/45497981/5efc158a-e671-4196-a15a-77edf79a2630)
![Available as "Server Profile" option in the server context menu](https://github.com/Vendicated/Vencord/assets/45497981/f43be943-6dc4-4232-9709-fbeb382d8e54)

View file

@ -5,30 +5,32 @@
*/ */
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { migratePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { Menu } from "@webpack/common"; import { Menu } from "@webpack/common";
import { Guild } from "discord-types/general"; import { Guild } from "discord-types/general";
import { openGuildProfileModal } from "./GuildProfileModal"; import { openGuildInfoModal } from "./GuildInfoModal";
const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => { const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => {
const group = findGroupChildrenByChildId("privacy", children); const group = findGroupChildrenByChildId("privacy", children);
group?.push( group?.push(
<Menu.MenuItem <Menu.MenuItem
id="vc-server-profile" id="vc-server-info"
label="Server Info" label="Server Info"
action={() => openGuildProfileModal(guild)} action={() => openGuildInfoModal(guild)}
/> />
); );
}; };
migratePluginSettings("ServerInfo", "ServerProfile"); // what was I thinking with this name lmao
export default definePlugin({ export default definePlugin({
name: "ServerProfile", name: "ServerInfo",
description: "Allows you to view info about a server by right clicking it in the server list", description: "Allows you to view info about a server",
authors: [Devs.Ven, Devs.Nuckyz], authors: [Devs.Ven, Devs.Nuckyz],
tags: ["guild", "info"], tags: ["guild", "info", "ServerProfile"],
contextMenus: { contextMenus: {
"guild-context": Patch, "guild-context": Patch,
"guild-header-popout": Patch "guild-header-popout": Patch

View file

@ -1,7 +0,0 @@
# ServerProfile
Allows you to view info about servers and see friends and blocked users
![image](https://github.com/Vendicated/Vencord/assets/45497981/a49783b5-e8fc-41d8-968f-58600e9f6580)
![image](https://github.com/Vendicated/Vencord/assets/45497981/5efc158a-e671-4196-a15a-77edf79a2630)
![image](https://github.com/Vendicated/Vencord/assets/45497981/f43be943-6dc4-4232-9709-fbeb382d8e54)

View file

@ -77,6 +77,13 @@ export default definePlugin({
match: /repeat:"off"!==(.{1,3}),/, match: /repeat:"off"!==(.{1,3}),/,
replace: "actual_repeat:$1,$&" replace: "actual_repeat:$1,$&"
} }
},
{
find: "artists.filter",
replacement: {
match: /\(0,(\i)\.isNotNullish\)\((\i)\.id\)&&/,
replace: ""
}
} }
], ],

View file

@ -61,11 +61,7 @@ export default definePlugin({
replacement: [ replacement: [
{ {
match: /(\i)\.premiumType/, match: /(\i)\.premiumType/,
replace: "$self.premiumHook($1)||$&" replace: "$self.patchPremiumType($1)||$&"
},
{
match: /(?<=function \i\((\i)\)\{)(?=var.{30,50},bannerSrc:)/,
replace: "$1.bannerSrc=$self.useBannerHook($1);"
}, },
{ {
match: /\?\(0,\i\.jsx\)\(\i,{type:\i,shown/, match: /\?\(0,\i\.jsx\)\(\i,{type:\i,shown/,
@ -74,17 +70,19 @@ export default definePlugin({
] ]
}, },
{ {
find: /overrideBannerSrc:\i,profileType:/, find: "=!1,canUsePremiumCustomization:",
replacement: [ replacement: {
{ match: /(\i)\.premiumType/,
match: /(\i)\.premiumType/, replace: "$self.patchPremiumType($1)||$&"
replace: "$self.premiumHook($1)||$&" }
}, },
{ {
match: /(?<=function \i\((\i)\)\{)(?=var.{30,50},overrideBannerSrc:)/, find: "BannerLoadingStatus:function",
replace: "$1.overrideBannerSrc=$self.useBannerHook($1);" replacement: {
} match: /(?<=void 0:)\i.getPreviewBanner\(\i,\i,\i\)/,
] replace: "$self.patchBannerUrl(arguments[0])||$&"
}
}, },
{ {
find: "\"data-selenium-video-tile\":", find: "\"data-selenium-video-tile\":",
@ -92,7 +90,7 @@ export default definePlugin({
replacement: [ replacement: [
{ {
match: /(?<=function\((\i),\i\)\{)(?=let.{20,40},style:)/, match: /(?<=function\((\i),\i\)\{)(?=let.{20,40},style:)/,
replace: "$1.style=$self.voiceBackgroundHook($1);" replace: "$1.style=$self.getVoiceBackgroundStyles($1);"
} }
] ]
} }
@ -106,7 +104,7 @@ export default definePlugin({
); );
}, },
voiceBackgroundHook({ className, participantUserId }: any) { getVoiceBackgroundStyles({ className, participantUserId }: any) {
if (className.includes("tile_")) { if (className.includes("tile_")) {
if (this.userHasBackground(participantUserId)) { if (this.userHasBackground(participantUserId)) {
return { return {
@ -119,12 +117,12 @@ export default definePlugin({
} }
}, },
useBannerHook({ displayProfile, user }: any) { patchBannerUrl({ displayProfile }: any) {
if (displayProfile?.banner && settings.store.nitroFirst) return; if (displayProfile?.banner && settings.store.nitroFirst) return;
if (this.userHasBackground(user.id)) return this.getImageUrl(user.id); if (this.userHasBackground(displayProfile?.userId)) return this.getImageUrl(displayProfile?.userId);
}, },
premiumHook({ userId }: any) { patchPremiumType({ userId }: any) {
if (this.userHasBackground(userId)) return 2; if (this.userHasBackground(userId)) return 2;
}, },

View file

@ -184,16 +184,16 @@ export default definePlugin({
patches: [ patches: [
// Profiles Modal pfp // Profiles Modal pfp
{ ...["User Profile Modal - Context Menu", ".UserProfileTypes.FULL_SIZE,hasProfileEffect:"].map(find => ({
find: "User Profile Modal - Context Menu", find,
replacement: { replacement: {
match: /\{src:(\i)(?=,avatarDecoration)/, match: /\{src:(\i)(?=,avatarDecoration)/,
replace: "{src:$1,onClick:()=>$self.openImage($1)" replace: "{src:$1,onClick:()=>$self.openImage($1)"
} }
}, })),
// Banners // Banners
{ ...[".NITRO_BANNER,", "=!1,canUsePremiumCustomization:"].map(find => ({
find: ".NITRO_BANNER,", find,
replacement: { replacement: {
// style: { backgroundImage: shouldShowBanner ? "url(".concat(bannerUrl, // style: { backgroundImage: shouldShowBanner ? "url(".concat(bannerUrl,
match: /style:\{(?=backgroundImage:(null!=\i)\?"url\("\.concat\((\i),)/, match: /style:\{(?=backgroundImage:(null!=\i)\?"url\("\.concat\((\i),)/,
@ -201,7 +201,7 @@ export default definePlugin({
// onClick: () => shouldShowBanner && ev.target.style.backgroundImage && openImage(bannerUrl), style: { cursor: shouldShowBanner ? "pointer" : void 0, // onClick: () => shouldShowBanner && ev.target.style.backgroundImage && openImage(bannerUrl), style: { cursor: shouldShowBanner ? "pointer" : void 0,
'onClick:ev=>$1&&ev.target.style.backgroundImage&&$self.openImage($2),style:{cursor:$1?"pointer":void 0,' 'onClick:ev=>$1&&ev.target.style.backgroundImage&&$self.openImage($2),style:{cursor:$1?"pointer":void 0,'
} }
}, })),
// User DMs "User Profile" popup in the right // User DMs "User Profile" popup in the right
{ {
find: ".avatarPositionPanel", find: ".avatarPositionPanel",
@ -210,6 +210,14 @@ export default definePlugin({
replace: "$1style:($2)?{cursor:\"pointer\"}:{},onClick:$2?()=>{$self.openImage($3)}" replace: "$1style:($2)?{cursor:\"pointer\"}:{},onClick:$2?()=>{$self.openImage($3)}"
} }
}, },
{
find: ".canUsePremiumProfileCustomization,{avatarSrc:",
replacement: {
match: /children:\(0,\i\.jsx\)\(\i,{src:(\i)/,
replace: "style:{cursor:\"pointer\"},onClick:()=>{$self.openImage($1)},$&"
}
},
// Group DMs top small & large icon // Group DMs top small & large icon
{ {
find: /\.recipients\.length>=2(?!<isMultiUserDM.{0,50})/, find: /\.recipients\.length>=2(?!<isMultiUserDM.{0,50})/,

View file

@ -14,7 +14,7 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
export const WEBPACK_CHUNK = "webpackChunkdiscord_app"; export const WEBPACK_CHUNK = "webpackChunkdiscord_app";
export const REACT_GLOBAL = "Vencord.Webpack.Common.React"; export const REACT_GLOBAL = "Vencord.Webpack.Common.React";
@ -39,27 +39,27 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
Ven: { Ven: {
name: "Vendicated", name: "Vendicated",
id: 343383572805058560n id: 343383572805058560n,
}, },
Arjix: { Arjix: {
name: "ArjixWasTaken", name: "ArjixWasTaken",
id: 674710789138939916n id: 674710789138939916n,
}, },
Cyn: { Cyn: {
name: "Cynosphere", name: "Cynosphere",
id: 150745989836308480n id: 150745989836308480n,
}, },
Trwy: { Trwy: {
name: "trey", name: "trey",
id: 354427199023218689n id: 354427199023218689n,
}, },
Megu: { Megu: {
name: "Megumin", name: "Megumin",
id: 545581357812678656n id: 545581357812678656n,
}, },
botato: { botato: {
name: "botato", name: "botato",
id: 440990343899643943n id: 440990343899643943n,
}, },
fawn: { fawn: {
name: "fawn", name: "fawn",
@ -67,11 +67,11 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
rushii: { rushii: {
name: "rushii", name: "rushii",
id: 295190422244950017n id: 295190422244950017n,
}, },
Glitch: { Glitch: {
name: "Glitchy", name: "Glitchy",
id: 269567451199569920n id: 269567451199569920n,
}, },
Samu: { Samu: {
name: "Samu", name: "Samu",
@ -79,19 +79,19 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
Nyako: { Nyako: {
name: "nyako", name: "nyako",
id: 118437263754395652n id: 118437263754395652n,
}, },
MaiKokain: { MaiKokain: {
name: "Mai", name: "Mai",
id: 722647978577363026n id: 722647978577363026n,
}, },
echo: { echo: {
name: "ECHO", name: "ECHO",
id: 712639419785412668n id: 712639419785412668n,
}, },
katlyn: { katlyn: {
name: "katlyn", name: "katlyn",
id: 250322741406859265n id: 250322741406859265n,
}, },
nea: { nea: {
name: "nea", name: "nea",
@ -99,87 +99,87 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
Nuckyz: { Nuckyz: {
name: "Nuckyz", name: "Nuckyz",
id: 235834946571337729n id: 235834946571337729n,
}, },
D3SOX: { D3SOX: {
name: "D3SOX", name: "D3SOX",
id: 201052085641281538n id: 201052085641281538n,
}, },
Nickyux: { Nickyux: {
name: "Nickyux", name: "Nickyux",
id: 427146305651998721n id: 427146305651998721n,
}, },
mantikafasi: { mantikafasi: {
name: "mantikafasi", name: "mantikafasi",
id: 287555395151593473n id: 287555395151593473n,
}, },
Xinto: { Xinto: {
name: "Xinto", name: "Xinto",
id: 423915768191647755n id: 423915768191647755n,
}, },
JacobTm: { JacobTm: {
name: "Jacob.Tm", name: "Jacob.Tm",
id: 302872992097107991n id: 302872992097107991n,
}, },
DustyAngel47: { DustyAngel47: {
name: "DustyAngel47", name: "DustyAngel47",
id: 714583473804935238n id: 714583473804935238n,
}, },
BanTheNons: { BanTheNons: {
name: "BanTheNons", name: "BanTheNons",
id: 460478012794863637n id: 460478012794863637n,
}, },
BigDuck: { BigDuck: {
name: "BigDuck", name: "BigDuck",
id: 1024588272623681609n id: 1024588272623681609n,
}, },
AverageReactEnjoyer: { AverageReactEnjoyer: {
name: "Average React Enjoyer", name: "Average React Enjoyer",
id: 1004904120056029256n id: 1004904120056029256n,
}, },
adryd: { adryd: {
name: "adryd", name: "adryd",
id: 0n id: 0n,
}, },
Tyman: { Tyman: {
name: "Tyman", name: "Tyman",
id: 487443883127472129n id: 487443883127472129n,
}, },
afn: { afn: {
name: "afn", name: "afn",
id: 420043923822608384n id: 420043923822608384n,
}, },
KraXen72: { KraXen72: {
name: "KraXen72", name: "KraXen72",
id: 379304073515499530n id: 379304073515499530n,
}, },
kemo: { kemo: {
name: "kemo", name: "kemo",
id: 715746190813298788n id: 715746190813298788n,
}, },
dzshn: { dzshn: {
name: "dzshn", name: "dzshn",
id: 310449948011528192n id: 310449948011528192n,
}, },
Ducko: { Ducko: {
name: "Ducko", name: "Ducko",
id: 506482395269169153n id: 506482395269169153n,
}, },
jewdev: { jewdev: {
name: "jewdev", name: "jewdev",
id: 222369866529636353n id: 222369866529636353n,
}, },
Luna: { Luna: {
name: "Luny", name: "Luny",
id: 821472922140803112n id: 821472922140803112n,
}, },
Vap: { Vap: {
name: "Vap0r1ze", name: "Vap0r1ze",
id: 454072114492866560n id: 454072114492866560n,
}, },
KingFish: { KingFish: {
name: "King Fish", name: "King Fish",
id: 499400512559382538n id: 499400512559382538n,
}, },
Commandtechno: { Commandtechno: {
name: "Commandtechno", name: "Commandtechno",
@ -187,7 +187,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
TheSun: { TheSun: {
name: "sunnie", name: "sunnie",
id: 406028027768733696n id: 406028027768733696n,
}, },
axyie: { axyie: {
name: "'ax", name: "'ax",
@ -195,44 +195,44 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
pointy: { pointy: {
name: "pointy", name: "pointy",
id: 99914384989519872n id: 99914384989519872n,
}, },
SammCheese: { SammCheese: {
name: "Samm-Cheese", name: "Samm-Cheese",
id: 372148345894076416n id: 372148345894076416n,
}, },
zt: { zt: {
name: "zt", name: "zt",
id: 289556910426816513n id: 289556910426816513n,
}, },
captain: { captain: {
name: "Captain", name: "Captain",
id: 347366054806159360n id: 347366054806159360n,
}, },
nick: { nick: {
name: "nick", name: "nick",
id: 347884694408265729n, id: 347884694408265729n,
badge: false badge: false,
}, },
whqwert: { whqwert: {
name: "whqwert", name: "whqwert",
id: 586239091520176128n id: 586239091520176128n,
}, },
lewisakura: { lewisakura: {
name: "lewisakura", name: "lewisakura",
id: 96269247411400704n id: 96269247411400704n,
}, },
RuiNtD: { RuiNtD: {
name: "RuiNtD", name: "RuiNtD",
id: 157917665162297344n id: 157917665162297344n,
}, },
hunt: { hunt: {
name: "hunt-g", name: "hunt-g",
id: 222800179697287168n id: 222800179697287168n,
}, },
cloudburst: { cloudburst: {
name: "cloudburst", name: "cloudburst",
id: 892128204150685769n id: 892128204150685769n,
}, },
Aria: { Aria: {
name: "Syncxv", name: "Syncxv",
@ -240,51 +240,51 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
TheKodeToad: { TheKodeToad: {
name: "TheKodeToad", name: "TheKodeToad",
id: 706152404072267788n id: 706152404072267788n,
}, },
LordElias: { LordElias: {
name: "LordElias", name: "LordElias",
id: 319460781567639554n id: 319460781567639554n,
}, },
juby: { juby: {
name: "Juby210", name: "Juby210",
id: 324622488644616195n id: 324622488644616195n,
}, },
Alyxia: { Alyxia: {
name: "Alyxia Sother", name: "Alyxia Sother",
id: 952185386350829688n id: 952185386350829688n,
}, },
Remty: { Remty: {
name: "Remty", name: "Remty",
id: 335055032204656642n id: 335055032204656642n,
}, },
skyevg: { skyevg: {
name: "skyevg", name: "skyevg",
id: 1090310844283363348n id: 1090310844283363348n,
}, },
Dziurwa: { Dziurwa: {
name: "Dziurwa", name: "Dziurwa",
id: 1001086404203389018n id: 1001086404203389018n,
}, },
arHSM: { arHSM: {
name: "arHSM", name: "arHSM",
id: 841509053422632990n id: 841509053422632990n,
}, },
F53: { F53: {
name: "F53", name: "F53",
id: 280411966126948353n id: 280411966126948353n,
}, },
AutumnVN: { AutumnVN: {
name: "AutumnVN", name: "AutumnVN",
id: 393694671383166998n id: 393694671383166998n,
}, },
pylix: { pylix: {
name: "pylix", name: "pylix",
id: 492949202121261067n id: 492949202121261067n,
}, },
Tyler: { Tyler: {
name: "\\\\GGTyler\\\\", name: "\\\\GGTyler\\\\",
id: 143117463788191746n id: 143117463788191746n,
}, },
RyanCaoDev: { RyanCaoDev: {
name: "RyanCaoDev", name: "RyanCaoDev",
@ -292,27 +292,27 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
FieryFlames: { FieryFlames: {
name: "Fiery", name: "Fiery",
id: 890228870559698955n id: 890228870559698955n,
}, },
KannaDev: { KannaDev: {
name: "Kanna", name: "Kanna",
id: 317728561106518019n id: 317728561106518019n,
}, },
carince: { carince: {
name: "carince", name: "carince",
id: 818323528755314698n id: 818323528755314698n,
}, },
PandaNinjas: { PandaNinjas: {
name: "PandaNinjas", name: "PandaNinjas",
id: 455128749071925248n id: 455128749071925248n,
}, },
CatNoir: { CatNoir: {
name: "CatNoir", name: "CatNoir",
id: 260371016348336128n id: 260371016348336128n,
}, },
outfoxxed: { outfoxxed: {
name: "outfoxxed", name: "outfoxxed",
id: 837425748435796060n id: 837425748435796060n,
}, },
UwUDev: { UwUDev: {
name: "UwU", name: "UwU",
@ -320,39 +320,39 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
amia: { amia: {
name: "amia", name: "amia",
id: 142007603549962240n id: 142007603549962240n,
}, },
phil: { phil: {
name: "phil", name: "phil",
id: 305288513941667851n id: 305288513941667851n,
}, },
ImLvna: { ImLvna: {
name: "Luna <3", name: "Luna <3",
id: 799319081723232267n id: 799319081723232267n,
}, },
rad: { rad: {
name: "rad", name: "rad",
id: 610945092504780823n id: 610945092504780823n,
}, },
AndrewDLO: { AndrewDLO: {
name: "Andrew-DLO", name: "Andrew-DLO",
id: 434135504792059917n id: 434135504792059917n,
}, },
HypedDomi: { HypedDomi: {
name: "HypedDomi", name: "HypedDomi",
id: 354191516979429376n id: 354191516979429376n,
}, },
Rini: { Rini: {
name: "Rini", name: "Rini",
id: 1079479184478441643n id: 1079479184478441643n,
}, },
castdrian: { castdrian: {
name: "castdrian", name: "castdrian",
id: 224617799434108928n id: 224617799434108928n,
}, },
Arrow: { Arrow: {
name: "arrow", name: "arrow",
id: 958158495302176778n id: 958158495302176778n,
}, },
bb010g: { bb010g: {
name: "bb010g", name: "bb010g",
@ -372,19 +372,19 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
archeruwu: { archeruwu: {
name: "archer_uwu", name: "archer_uwu",
id: 160068695383736320n id: 160068695383736320n,
}, },
ProffDea: { ProffDea: {
name: "ProffDea", name: "ProffDea",
id: 609329952180928513n id: 609329952180928513n,
}, },
UlyssesZhan: { UlyssesZhan: {
name: "UlyssesZhan", name: "UlyssesZhan",
id: 586808226058862623n id: 586808226058862623n,
}, },
ant0n: { ant0n: {
name: "ant0n", name: "ant0n",
id: 145224646868860928n id: 145224646868860928n,
}, },
Board: { Board: {
name: "BoardTM", name: "BoardTM",
@ -392,11 +392,11 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
philipbry: { philipbry: {
name: "philipbry", name: "philipbry",
id: 554994003318276106n id: 554994003318276106n,
}, },
Korbo: { Korbo: {
name: "Korbo", name: "Korbo",
id: 455856406420258827n id: 455856406420258827n,
}, },
maisymoe: { maisymoe: {
name: "maisy", name: "maisy",
@ -404,11 +404,11 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
Lexi: { Lexi: {
name: "Lexi", name: "Lexi",
id: 506101469787717658n id: 506101469787717658n,
}, },
Mopi: { Mopi: {
name: "Mopi", name: "Mopi",
id: 1022189106614243350n id: 1022189106614243350n,
}, },
Grzesiek11: { Grzesiek11: {
name: "Grzesiek11", name: "Grzesiek11",
@ -436,39 +436,51 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
nin0dev: { nin0dev: {
name: "nin0dev", name: "nin0dev",
id: 886685857560539176n id: 886685857560539176n,
}, },
Elvyra: { Elvyra: {
name: "Elvyra", name: "Elvyra",
id: 708275751816003615n, id: 708275751816003615n,
}, },
HappyEnderman: {
name: "Happy enderman",
id: 1083437693347827764n,
},
Vishnya: {
name: "Vishnya",
id: 282541644484575233n,
},
Inbestigator: { Inbestigator: {
name: "Inbestigator", name: "Inbestigator",
id: 761777382041714690n id: 761777382041714690n,
}, },
newwares: { newwares: {
name: "newwares", name: "newwares",
id: 421405303951851520n id: 421405303951851520n,
}, },
JohnyTheCarrot: { JohnyTheCarrot: {
name: "JohnyTheCarrot", name: "JohnyTheCarrot",
id: 132819036282159104n id: 132819036282159104n,
}, },
puv: { puv: {
name: "puv", name: "puv",
id: 469441552251355137n id: 469441552251355137n,
}, },
Kodarru: { Kodarru: {
name: "Kodarru", name: "Kodarru",
id: 785227396218748949n id: 785227396218748949n,
}, },
nakoyasha: { nakoyasha: {
name: "nakoyasha", name: "nakoyasha",
id: 222069018507345921n id: 222069018507345921n,
}, },
Sqaaakoi: { Sqaaakoi: {
name: "Sqaaakoi", name: "Sqaaakoi",
id: 259558259491340288n id: 259558259491340288n,
},
iamme: {
name: "i am me",
id: 984392761929256980n,
}, },
iamme: { iamme: {
name: "i am me", name: "i am me",
@ -476,39 +488,39 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
Byron: { Byron: {
name: "byeoon", name: "byeoon",
id: 1167275288036655133n id: 1167275288036655133n,
}, },
Kaitlyn: { Kaitlyn: {
name: "kaitlyn", name: "kaitlyn",
id: 306158896630988801n id: 306158896630988801n,
}, },
PolisanTheEasyNick: { PolisanTheEasyNick: {
name: "Oleh Polisan", name: "Oleh Polisan",
id: 242305263313485825n id: 242305263313485825n,
}, },
HAHALOSAH: { HAHALOSAH: {
name: "HAHALOSAH", name: "HAHALOSAH",
id: 903418691268513883n id: 903418691268513883n,
}, },
GabiRP: { GabiRP: {
name: "GabiRP", name: "GabiRP",
id: 507955112027750401n id: 507955112027750401n,
}, },
ImBanana: { ImBanana: {
name: "Im_Banana", name: "Im_Banana",
id: 635250116688871425n id: 635250116688871425n,
}, },
xocherry: { xocherry: {
name: "xocherry", name: "xocherry",
id: 221288171013406720n id: 221288171013406720n,
}, },
ScattrdBlade: { ScattrdBlade: {
name: "ScattrdBlade", name: "ScattrdBlade",
id: 678007540608532491n id: 678007540608532491n,
}, },
goodbee: { goodbee: {
name: "goodbee", name: "goodbee",
id: 658968552606400512n id: 658968552606400512n,
}, },
Moxxie: { Moxxie: {
name: "Moxxie", name: "Moxxie",
@ -520,15 +532,20 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
nyx: { nyx: {
name: "verticalsync", name: "verticalsync",
id: 328165170536775680n id: 328165170536775680n,
},
nekohaxx: {
name: "nekohaxx",
id: 1176270221628153886n,
}, },
} satisfies Record<string, Dev>); } satisfies Record<string, Dev>);
// iife so #__PURE__ works correctly // iife so #__PURE__ works correctly
export const DevsById = /* #__PURE__*/ (() => export const DevsById = /* #__PURE__*/ (() =>
Object.freeze(Object.fromEntries( Object.freeze(
Object.entries(Devs) Object.fromEntries(
.filter(d => d[1].id !== 0n) Object.entries(Devs)
.map(([_, v]) => [v.id, v] as const) .filter((d) => d[1].id !== 0n)
)) .map(([_, v]) => [v.id, v] as const)
)() as Record<string, Dev>; )
))() as Record<string, Dev>;

View file

@ -85,6 +85,10 @@ export interface PluginDef {
* Whether this plugin is required and forcefully enabled * Whether this plugin is required and forcefully enabled
*/ */
required?: boolean; required?: boolean;
/**
* Whether this plugin should be hidden from the user
*/
hidden?: boolean;
/** /**
* Whether this plugin should be enabled by default, but can be disabled * Whether this plugin should be enabled by default, but can be disabled
*/ */