Compare commits

...

25 commits

Author SHA1 Message Date
sadan4
95fb0e114b
Merge a88db90860 into 6cce8a8bc4 2024-09-17 22:49:29 +02:00
Nuckyz
6cce8a8bc4
Experiments: Allow clips to be recorded without streaming
Some checks failed
Sync to Codeberg / codeberg (push) Has been cancelled
test / test (push) Has been cancelled
2024-09-17 14:30:23 -03:00
Nuckyz
1848b16536
ReviewDB: Fix in panel profile 2024-09-17 14:30:23 -03:00
Kyuuhachi
c572116b97
BetterSettings: Add submenu for plugins (#2858)
Co-authored-by: Vendicated <vendicated@riseup.net>
2024-09-17 15:40:11 +00:00
Lumap
e26986f66a
AppleMusicRichPresence: fix formatting when listening to radio (#2869)
Co-authored-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com>
Co-authored-by: v <vendicated@riseup.net>
2024-09-17 17:29:46 +02:00
sadan4
a88db90860
Merge branch 'dev' into emoteCloner 2024-09-15 21:44:54 -04:00
sadan4
74b03c0962
Discard changes to src/utils/constants.ts 2024-09-15 21:43:14 -04:00
sadan
7d70d53083
remove hang status and add channel statuses 2024-09-15 21:42:52 -04:00
sadan4
b3bf32abd7
Merge branch 'dev' into emoteCloner 2024-09-04 20:20:42 -04:00
sadan
f8bf23db1f
fix contsatns 2024-09-02 01:16:32 -04:00
sadan4
6ac7edccec
Merge branch 'dev' into emoteCloner 2024-09-02 01:11:50 -04:00
sadan4
6366cc2e12
Merge branch 'main' into emoteCloner 2024-09-02 01:11:25 -04:00
sadan4
8bba7b7ab7
Merge branch 'main' into emoteCloner 2024-07-30 23:13:11 -04:00
sadan4
efc38a1d2e
Merge branch 'main' into emoteCloner 2024-07-20 18:20:05 -04:00
sadan
b4e4085382
fix patch modifying the user object 2024-07-15 13:18:26 -04:00
sadan4
85e3fa4cc5
Merge branch 'main' into emoteCloner 2024-07-14 23:18:50 -04:00
sadan
359d7564a5
fix wacky logic 2024-07-14 23:16:58 -04:00
sadan4
04bfbe4553
Merge branch 'main' into emoteCloner 2024-07-14 22:13:21 -04:00
sadan
bdae37ff23
fix var names, regex, and types 2024-07-11 02:20:07 -04:00
sadan4
8e649cfcc4
Update src/plugins/emoteCloner/index.tsx
Co-authored-by: Drew <84212701+MrDiamondDog@users.noreply.github.com>
2024-07-11 02:09:21 -04:00
sadan
9c819e83b3
fix regex remove fake nitro name part 2024-07-10 23:35:19 -04:00
sadan
850b940306
fix regex and remove log 2024-07-10 22:35:27 -04:00
sadan
add443e67b
inline patch 2024-07-10 22:33:36 -04:00
sadan
eed1c2b400
Merge branch 'main' into emoteCloner 2024-07-10 13:57:54 -04:00
sadan
d7366ae171
read desc
add emote cloner to
- emojis in user statuses
- hang statuses (vc statuses)
add name finding for fake nitro emojis that have &name=emoji_name
2024-07-10 02:28:18 -04:00
8 changed files with 174 additions and 31 deletions

View file

@ -24,7 +24,7 @@ interface ActivityButton {
}
interface Activity {
state: string;
state?: string;
details?: string;
timestamps?: {
start?: number;
@ -52,8 +52,8 @@ const enum ActivityFlag {
export interface TrackData {
name: string;
album: string;
artist: string;
album?: string;
artist?: string;
appleMusicLink?: string;
songLink?: string;
@ -61,8 +61,8 @@ export interface TrackData {
albumArtwork?: string;
artistArtwork?: string;
playerPosition: number;
duration: number;
playerPosition?: number;
duration?: number;
}
const enum AssetImageType {
@ -155,8 +155,8 @@ const settings = definePluginSettings({
function customFormat(formatStr: string, data: TrackData) {
return formatStr
.replaceAll("{name}", data.name)
.replaceAll("{album}", data.album)
.replaceAll("{artist}", data.artist);
.replaceAll("{album}", data.album ?? "")
.replaceAll("{artist}", data.artist ?? "");
}
function getImageAsset(type: AssetImageType, data: TrackData) {
@ -212,14 +212,16 @@ export default definePlugin({
const assets: ActivityAssets = {};
const isRadio = Number.isNaN(trackData.duration) && (trackData.playerPosition === 0);
if (settings.store.largeImageType !== AssetImageType.Disabled) {
assets.large_image = largeImageAsset;
assets.large_text = customFormat(settings.store.largeTextString, trackData);
if (!isRadio) 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);
if (!isRadio) assets.small_text = customFormat(settings.store.smallTextString, trackData);
}
const buttons: ActivityButton[] = [];
@ -243,17 +245,17 @@ export default definePlugin({
name: customFormat(settings.store.nameString, trackData),
details: customFormat(settings.store.detailsString, trackData),
state: customFormat(settings.store.stateString, trackData),
state: isRadio ? undefined : customFormat(settings.store.stateString, trackData),
timestamps: (settings.store.enableTimestamps ? {
timestamps: (trackData.playerPosition && trackData.duration && settings.store.enableTimestamps) ? {
start: Date.now() - (trackData.playerPosition * 1000),
end: Date.now() - (trackData.playerPosition * 1000) + (trackData.duration * 1000),
} : undefined),
} : undefined,
assets,
buttons: buttons.length ? buttons.map(v => v.label) : undefined,
metadata: { button_urls: buttons.map(v => v.url) || undefined, },
buttons: !isRadio && buttons.length ? buttons.map(v => v.label) : undefined,
metadata: !isRadio && buttons.length ? { button_urls: buttons.map(v => v.url) } : undefined,
type: settings.store.activityType,
flags: ActivityFlag.INSTANCE,

View file

@ -0,0 +1,68 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { openPluginModal } from "@components/PluginSettings/PluginModal";
import { isObjectEmpty } from "@utils/misc";
import { Alerts, i18n, Menu, useMemo, useState } from "@webpack/common";
import Plugins from "~plugins";
function onRestartNeeded() {
Alerts.show({
title: "Restart required",
body: <p>You have changed settings that require a restart.</p>,
confirmText: "Restart now",
cancelText: "Later!",
onConfirm: () => location.reload()
});
}
export default function PluginsSubmenu() {
const sortedPlugins = useMemo(() => Object.values(Plugins)
.sort((a, b) => a.name.localeCompare(b.name)), []);
const [query, setQuery] = useState("");
const search = query.toLowerCase();
const include = (p: typeof Plugins[keyof typeof Plugins]) => (
Vencord.Plugins.isPluginEnabled(p.name)
&& p.options && !isObjectEmpty(p.options)
&& (
p.name.toLowerCase().includes(search)
|| p.description.toLowerCase().includes(search)
|| p.tags?.some(t => t.toLowerCase().includes(search))
)
);
const plugins = sortedPlugins.filter(include);
return (
<>
<Menu.MenuControlItem
id="vc-plugins-search"
control={(props, ref) => (
<Menu.MenuSearchControl
{...props}
query={query}
onChange={setQuery}
ref={ref}
placeholder={i18n.Messages.SEARCH}
/>
)}
/>
{!!plugins.length && <Menu.MenuSeparator />}
{plugins.map(p => (
<Menu.MenuItem
key={p.name}
id={p.name}
label={p.name}
action={() => openPluginModal(p, onRestartNeeded)}
/>
))}
</>
);
}

View file

@ -13,6 +13,8 @@ import { waitFor } from "@webpack";
import { ComponentDispatch, FocusLock, i18n, Menu, useEffect, useRef } from "@webpack/common";
import type { HTMLAttributes, ReactElement } from "react";
import PluginsSubmenu from "./PluginsSubmenu";
type SettingsEntry = { section: string, label: string; };
const cl = classNameFactory("");
@ -118,13 +120,21 @@ export default definePlugin({
},
{ // Settings cog context menu
find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL",
replacement: {
match: /(EXPERIMENTS:.+?)(\(0,\i.\i\)\(\))(?=\.filter\(\i=>\{let\{section:\i\}=)/,
replace: "$1$self.wrapMenu($2)"
}
}
replacement: [
{
match: /(EXPERIMENTS:.+?)(\(0,\i.\i\)\(\))(?=\.filter\(\i=>\{let\{section:\i\}=)/,
replace: "$1$self.wrapMenu($2)"
},
{
match: /case \i\.\i\.DEVELOPER_OPTIONS:return \i;/,
replace: "$&case 'VencordPlugins':return $self.PluginsSubmenu();"
}
]
},
],
PluginsSubmenu,
// This is the very outer layer of the entire ui, so we can't wrap this in an ErrorBoundary
// without possibly also catching unrelated errors of children.
//

View file

@ -126,7 +126,7 @@ export default definePlugin({
}
},
{
find: '"Handling ping: "',
find: '"_handleLocalVideoDisabled: ',
predicate: () => settings.store.disableNoisyLoggers,
replacement: {
match: /new \i\.\i\("RTCConnection\("\.concat.+?\)\)(?=,)/,

View file

@ -25,11 +25,16 @@ import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/moda
import definePlugin from "@utils/types";
import { findByCodeLazy, findStoreLazy } from "@webpack";
import { Constants, EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
import { Channel } from "discord-types/general";
import { Promisable } from "type-fest";
const StickersStore = findStoreLazy("StickersStore");
const uploadEmoji = findByCodeLazy(".GUILD_EMOJIS(", "EMOJI_UPLOAD_START");
const ChannelStatusStore: {
getChannelStatus(c: Channel): string;
} = findStoreLazy("ChannelStatusStore");
interface Sticker {
t: "Sticker";
description: string;
@ -272,12 +277,12 @@ function CloneModal({ data }: { data: Sticker | Emoji; }) {
);
}
function buildMenuItem(type: "Emoji" | "Sticker", fetchData: () => Promisable<Omit<Sticker | Emoji, "t">>) {
function buildMenuItem(type: "Emoji" | "Sticker", fetchData: () => Promisable<Omit<Sticker | Emoji, "t">>, label?: string) {
return (
<Menu.MenuItem
id="emote-cloner"
id={`emote-cloner${label ?? ""}`}
key="emote-cloner"
label={`Clone ${type}`}
label={label ?? `Clone ${type}`}
action={() =>
openModalLazy(async () => {
const res = await fetchData();
@ -360,13 +365,57 @@ const expressionPickerPatch: NavContextMenuPatchCallback = (children, props: { t
}
};
const emojiRegex = /https:\/\/cdn\.discordapp\.com\/emojis\/(\d+)\.([a-zA-Z]{3,4}).*/;
const imageContextPatch: NavContextMenuPatchCallback = (children, props: {
src: string;
}) => {
// this context menu is called on normal images, as well as stock emojis.
const matches = [...props.src.match(emojiRegex) ?? []];
if (matches.length === 0) return;
children.push(buildMenuItem("Emoji", () => ({
id: matches[1],
isAnimated: (matches[2] === "gif"),
name: "ProfileEmoji"
})));
};
const emojiMatchRegex = /<(a?):(\w+):(\d{19})>/g;
const channelContextStatusPatch: NavContextMenuPatchCallback = (children, props: {
channel: Channel;
}) => {
const status = ChannelStatusStore.getChannelStatus(props.channel);
if (!status) return;
const emojis = [...status.matchAll(emojiMatchRegex)];
if (emojis.length === 0) return;
console.log(emojis);
children.push((
<Menu.MenuItem id="vc-emoteCloner-item" label="Clone Emoji">
{
emojis.map(([_, animated, name, id]) => (
buildMenuItem("Emoji", () => ({
id,
name,
isAnimated: !!animated,
}),
`Clone ${name}`
)
))
}
</Menu.MenuItem>
));
};
export default definePlugin({
name: "EmoteCloner",
description: "Allows you to clone Emotes & Stickers to your own server (right click them)",
tags: ["StickerCloner"],
authors: [Devs.Ven, Devs.Nuckyz],
authors: [Devs.Ven, Devs.Nuckyz, Devs.sadan],
contextMenus: {
"message": messageContextMenuPatch,
"expression-picker": expressionPickerPatch
}
"expression-picker": expressionPickerPatch,
"image-context": imageContextPatch,
"channel-context": channelContextStatusPatch
},
});

View file

@ -23,12 +23,13 @@ import { ErrorCard } from "@components/ErrorCard";
import { Devs } from "@utils/constants";
import { Margins } from "@utils/margins";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { findByPropsLazy, findLazy } from "@webpack";
import { Forms, React } from "@webpack/common";
import hideBugReport from "./hideBugReport.css?managed";
const KbdStyles = findByPropsLazy("key", "combo");
const BugReporterExperiment = findLazy(m => m?.definition?.id === "2024-09_bug_reporter");
const settings = definePluginSettings({
toolbarDevMenu: {
@ -78,8 +79,8 @@ export default definePlugin({
{
find: "toolbar:function",
replacement: {
match: /\i\.isStaff\(\)/,
replace: "true"
match: /hasBugReporterAccess:(\i)/,
replace: "_hasBugReporterAccess:$1=true"
},
predicate: () => settings.store.toolbarDevMenu
},
@ -91,10 +92,18 @@ export default definePlugin({
match: /\i\.isDM\(\)\|\|\i\.isThread\(\)/,
replace: "false",
}
},
// enable option to always record clips even if you are not streaming
{
find: "isDecoupledGameClippingEnabled(){",
replacement: {
match: /\i\.isStaff\(\)/,
replace: "true"
}
}
],
start: () => enableStyle(hideBugReport),
start: () => !BugReporterExperiment.getCurrentConfig().hasBugReporterAccess && enableStyle(hideBugReport),
stop: () => disableStyle(hideBugReport),
settingsAboutComponent: () => {

View file

@ -91,7 +91,7 @@ export default definePlugin({
}
},
{
find: ".PANEL,isInteractionSource:",
find: ".PANEL,interactionType:",
replacement: {
match: /{profileType:\i\.\i\.PANEL,children:\[/,
replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user}),"

View file

@ -72,6 +72,11 @@ export interface Menu {
onChange(value: number): void,
renderValue?(value: number): string,
}>;
MenuSearchControl: RC<{
query: string
onChange(query: string): void;
placeholder?: string;
}>;
}
export interface ContextMenuApi {