mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-10 07:03:06 -04:00
ReplaceActivityTypes -> RPCEditor
This commit is contained in:
parent
7161495c9b
commit
0ff495828f
7 changed files with 394 additions and 295 deletions
|
@ -111,8 +111,8 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
|
|||
- Quoter by Samwich
|
||||
- RemixMe by kvba
|
||||
- RepeatMessage by Tolgchu
|
||||
- ReplaceActivityTypes by Nyako
|
||||
- ReplyPingControl by ant0n & MrDiamond
|
||||
- RPCEditor by Nyako & nin0dev
|
||||
- RPCStats by Samwich
|
||||
- SearchFix by Jaxx
|
||||
- SekaiStickers by MaiKokain
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { CheckedTextInput } from "@components/CheckedTextInput";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { identity } from "@utils/misc";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { Card, Forms, React, Select, SnowflakeUtils, Switch } from "@webpack/common";
|
||||
|
||||
import { makeEmptyAppId } from ".";
|
||||
import { ActivityType, RpcApp, SettingsProps } from "./types";
|
||||
|
||||
|
||||
const fetchApplicationsRPC = findByCodeLazy("APPLICATION_RPC(", "Client ID");
|
||||
|
||||
const cachedApps: any = {};
|
||||
async function lookupApp(appId: string): Promise<RpcApp | null> {
|
||||
if (cachedApps[appId]) return cachedApps[appId];
|
||||
const socket: any = {};
|
||||
try {
|
||||
await fetchApplicationsRPC(socket, appId);
|
||||
console.log(`Lookup finished for ${socket.application.name}`);
|
||||
cachedApps[appId] = socket.application;
|
||||
return socket.application;
|
||||
} catch {
|
||||
console.log(`Lookup failed for ${appId}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function isValidSnowflake(v: string) {
|
||||
const regex = /^\d{17,20}$/;
|
||||
return regex.test(v) && !Number.isNaN(SnowflakeUtils.extractTimestamp(v));
|
||||
}
|
||||
|
||||
export function ReplaceTutorial() {
|
||||
return (
|
||||
<>
|
||||
<Forms.FormTitle tag="h3">How to get an Application ID</Forms.FormTitle>
|
||||
<Forms.FormText>
|
||||
The method of getting an app's id will differ depending on what app it is. If the source code is available you can most likely find it inside the app's repository.
|
||||
</Forms.FormText>
|
||||
<Forms.FormText>
|
||||
Another method is to start the app in question, then open Discord's console and look for a log from RPCServer saying something like
|
||||
<code>"cmd: 'SET_ACTIVITY'"</code> with your app's name somewhere inside
|
||||
</Forms.FormText>
|
||||
|
||||
<Forms.FormTitle tag="h3" style={{ color: "var(--text-danger)", textAlign: "center" }}>
|
||||
Note: ActivityTypes other than Playing will only show timestamps on Mobile. It's a Discord issue.
|
||||
</Forms.FormTitle>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function ReplaceSettings({ appIds, update, save }: SettingsProps) {
|
||||
async function onChange(val: string | boolean, index: number, key: string) {
|
||||
if (index === appIds.length - 1)
|
||||
appIds.push(makeEmptyAppId());
|
||||
|
||||
appIds[index][key] = val;
|
||||
|
||||
if (val && key === "appId") {
|
||||
const tempApp = await lookupApp(val.toString());
|
||||
appIds[index].appName = tempApp?.name || "Unknown";
|
||||
}
|
||||
|
||||
if (appIds[index].appId === "" && index !== appIds.length - 1)
|
||||
appIds.splice(index, 1);
|
||||
|
||||
save();
|
||||
update();
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
appIds.map((setting, i) =>
|
||||
<Card style={{ padding: "1em 1em 0" }} key={i}>
|
||||
<Switch
|
||||
value={setting.enabled}
|
||||
onChange={value => {
|
||||
onChange(value, i, "enabled");
|
||||
}}
|
||||
className={Margins.bottom16}
|
||||
hideBorder={true}
|
||||
>
|
||||
{setting.appName}
|
||||
</Switch>
|
||||
<Forms.FormTitle>Application ID</Forms.FormTitle>
|
||||
<CheckedTextInput
|
||||
value={setting.appId}
|
||||
onChange={async v => {
|
||||
onChange(v, i, "appId");
|
||||
}}
|
||||
validate={v =>
|
||||
!v || isValidSnowflake(v) || "Invalid appId, must be a snowflake"
|
||||
}
|
||||
/>
|
||||
{setting.activityType === ActivityType.STREAMING &&
|
||||
<>
|
||||
<Forms.FormTitle>Stream URL</Forms.FormTitle>
|
||||
<CheckedTextInput
|
||||
value={setting.streamUrl}
|
||||
onChange={async v => {
|
||||
onChange(v, i, "streamUrl");
|
||||
}}
|
||||
validate={st => !/https?:\/\/(www\.)?(twitch\.tv|youtube\.com)\/\w+/.test(st) && "Only Twitch and Youtube urls will work." || true}
|
||||
/>
|
||||
</>}
|
||||
<Forms.FormTitle>New activity type</Forms.FormTitle>
|
||||
<Select
|
||||
options={[
|
||||
{ label: "Playing", value: ActivityType.PLAYING },
|
||||
{ label: "Watching", value: ActivityType.WATCHING },
|
||||
{ label: "Listening", value: ActivityType.LISTENING },
|
||||
{ label: "Competing", value: ActivityType.COMPETING },
|
||||
{ label: "Streaming", value: ActivityType.STREAMING }
|
||||
]}
|
||||
select={value => {
|
||||
onChange(value, i, "activityType");
|
||||
}}
|
||||
className={Margins.bottom16}
|
||||
isSelected={value => setting.activityType === value}
|
||||
serialize={identity}
|
||||
/>
|
||||
<Switch
|
||||
value={setting.swapNameAndDetails}
|
||||
onChange={value => {
|
||||
onChange(value, i, "swapNameAndDetails");
|
||||
}}
|
||||
className={Margins.bottom16}
|
||||
hideBorder={true}
|
||||
>
|
||||
Swap presence name and details
|
||||
</Switch>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 your mom lol
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { DataStore } from "@api/index";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { useForceUpdater } from "@utils/react";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { React } from "@webpack/common";
|
||||
|
||||
import { ReplaceSettings, ReplaceTutorial } from "./ReplaceSettings";
|
||||
import { Activity, ActivityType, AppIdSetting } from "./types";
|
||||
|
||||
const APP_IDS_KEY = "ReplaceActivityType_appids";
|
||||
|
||||
export const makeEmptyAppId: () => AppIdSetting = () => ({
|
||||
appId: "",
|
||||
appName: "Unknown",
|
||||
streamUrl: "",
|
||||
swapNameAndDetails: false,
|
||||
activityType: ActivityType.PLAYING,
|
||||
enabled: true
|
||||
});
|
||||
|
||||
let appIds = [makeEmptyAppId()];
|
||||
|
||||
const settings = definePluginSettings({
|
||||
replacedAppIds: {
|
||||
type: OptionType.COMPONENT,
|
||||
description: "",
|
||||
component: () => {
|
||||
const update = useForceUpdater();
|
||||
return (
|
||||
<>
|
||||
<ReplaceSettings
|
||||
appIds={appIds}
|
||||
update={update}
|
||||
save={async () => DataStore.set(APP_IDS_KEY, appIds)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "ReplaceActivityTypes",
|
||||
description: "Replace the activity type (Playing) of any rich presence app (rats in my vencord?)",
|
||||
authors: [Devs.Nyako],
|
||||
patches: [
|
||||
// how has this patch not broken yet lol (i do not like fixing patches tho)
|
||||
{
|
||||
find: '"LocalActivityStore"',
|
||||
replacement: {
|
||||
match: /\i\((\i)\)\{.{0,50}activity.{0,10}=\i;/,
|
||||
replace: "$&$self.patchActivity($1.activity);",
|
||||
}
|
||||
}
|
||||
],
|
||||
settings,
|
||||
settingsAboutComponent: () => <ReplaceTutorial />,
|
||||
|
||||
async start() {
|
||||
appIds = await DataStore.get(APP_IDS_KEY) ?? [makeEmptyAppId()];
|
||||
},
|
||||
|
||||
patchActivity(activity: Activity) {
|
||||
if (!activity) return;
|
||||
console.log(activity);
|
||||
appIds.forEach(app => {
|
||||
if (app.enabled && app.appId === activity.application_id) {
|
||||
activity.type = app.activityType;
|
||||
|
||||
if (app.activityType === ActivityType.STREAMING && app.streamUrl) {
|
||||
activity.url = app.streamUrl;
|
||||
}
|
||||
|
||||
if (app.swapNameAndDetails) {
|
||||
const media = activity.details;
|
||||
activity.details = activity.name;
|
||||
activity.name = media;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export type AppIdSetting = {
|
||||
appName: string;
|
||||
appId: string;
|
||||
swapNameAndDetails: boolean;
|
||||
activityType: ActivityType;
|
||||
streamUrl: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
export interface Activity {
|
||||
state: string;
|
||||
details: string;
|
||||
timestamps?: {
|
||||
start?: number;
|
||||
end?: number;
|
||||
};
|
||||
url?: string;
|
||||
assets: ActivityAssets;
|
||||
buttons?: Array<string>;
|
||||
name: string;
|
||||
application_id: string;
|
||||
metadata?: {
|
||||
button_urls?: Array<string>;
|
||||
};
|
||||
type: number;
|
||||
}
|
||||
|
||||
export interface ActivityAssets {
|
||||
large_image: string;
|
||||
large_text: string;
|
||||
small_image: string;
|
||||
small_text: string;
|
||||
}
|
||||
|
||||
export const enum ActivityType {
|
||||
PLAYING = 0,
|
||||
STREAMING = 1,
|
||||
LISTENING = 2,
|
||||
WATCHING = 3,
|
||||
COMPETING = 5
|
||||
}
|
||||
|
||||
export interface SettingsProps {
|
||||
appIds: AppIdSetting[];
|
||||
update: () => void;
|
||||
save: () => void;
|
||||
}
|
||||
|
||||
export interface RpcApp {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
flags: number;
|
||||
}
|
224
src/equicordplugins/rpcEditor/ReplaceSettings.tsx
Normal file
224
src/equicordplugins/rpcEditor/ReplaceSettings.tsx
Normal file
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { CheckedTextInput } from "@components/CheckedTextInput";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { identity } from "@utils/misc";
|
||||
import { Card, Forms, PresenceStore, React, Select, SnowflakeUtils, Switch, TextInput, UserStore } from "@webpack/common";
|
||||
|
||||
import { Activity, ActivityType, AppIdSetting, makeEmptyAppId } from ".";
|
||||
|
||||
interface SettingsProps {
|
||||
appIds: AppIdSetting[];
|
||||
update: () => void;
|
||||
save: () => void;
|
||||
}
|
||||
|
||||
function isValidSnowflake(v: string) {
|
||||
const regex = /^\d{17,20}$/;
|
||||
return regex.test(v) && !Number.isNaN(SnowflakeUtils.extractTimestamp(v));
|
||||
}
|
||||
|
||||
export function ReplaceTutorial() {
|
||||
const activities: Activity[] = PresenceStore.getActivities(UserStore.getCurrentUser().id);
|
||||
return (
|
||||
<>
|
||||
<Forms.FormTitle tag="h3">IDs of currently running activities</Forms.FormTitle>
|
||||
{
|
||||
activities.length === 0 ? <Forms.FormText>No running activities</Forms.FormText> : activities.map(activity => { return activity.flags !== 48 ? <Forms.FormText>{activity.name}: {activity.application_id}</Forms.FormText> : null; /* hide spotify */ })
|
||||
}
|
||||
<Forms.FormTitle tag="h3" className={Margins.top8}>Available variables</Forms.FormTitle>
|
||||
<Forms.FormText>
|
||||
In all fields (except stream URL), you can put in variables that'll automatically be replaced by their original content:
|
||||
<pre style={{ fontFamily: "monospace" }}>
|
||||
:name:, :details:, :state:
|
||||
<br />
|
||||
:large_image:, :large_text:, :small_image:, :small_text:
|
||||
</pre>
|
||||
</Forms.FormText>
|
||||
<Forms.FormTitle tag="h3" className={Margins.top8}>More details</Forms.FormTitle>
|
||||
<Forms.FormText>
|
||||
Leave a field empty to leave it as is.
|
||||
<br />
|
||||
Set a field to "null" to hide it on the presence.
|
||||
<br />
|
||||
You may need to reload Discord for changes to apply.
|
||||
</Forms.FormText>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function ReplaceSettings({ appIds, update, save }: SettingsProps) {
|
||||
async function onChange(val: string | boolean, index: number, key: string) {
|
||||
if (index === appIds.length - 1)
|
||||
appIds.push(makeEmptyAppId());
|
||||
|
||||
appIds[index][key] = val;
|
||||
|
||||
save();
|
||||
update();
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
appIds.map((setting, i) =>
|
||||
<Card style={{ padding: "1em", opacity: !setting.enabled ? "60%" : "" }} key={i}>
|
||||
{
|
||||
isValidSnowflake(setting.appId) ?
|
||||
<Switch
|
||||
value={setting.enabled}
|
||||
onChange={value => {
|
||||
onChange(value, i, "enabled");
|
||||
}}
|
||||
className={Margins.bottom8}
|
||||
hideBorder={true}
|
||||
>
|
||||
Apply edits to app
|
||||
</Switch> : <Forms.FormTitle tag="h3">Add new application</Forms.FormTitle>
|
||||
}
|
||||
<Forms.FormTitle className={`${Margins.top8} ${Margins.bottom8}`}>Application ID</Forms.FormTitle>
|
||||
<CheckedTextInput
|
||||
value={setting.appId}
|
||||
onChange={async v => {
|
||||
onChange(v, i, "appId");
|
||||
}}
|
||||
validate={v =>
|
||||
!v || isValidSnowflake(v) || "Invalid application ID"
|
||||
}
|
||||
/>
|
||||
{
|
||||
isValidSnowflake(setting.appId) && <>
|
||||
<Forms.FormTitle className={Margins.top8}>New activity type</Forms.FormTitle>
|
||||
<Select
|
||||
options={[
|
||||
{ label: "Playing", value: ActivityType.PLAYING },
|
||||
{ label: "Watching", value: ActivityType.WATCHING },
|
||||
{ label: "Listening", value: ActivityType.LISTENING },
|
||||
{ label: "Competing", value: ActivityType.COMPETING },
|
||||
{ label: "Streaming", value: ActivityType.STREAMING }
|
||||
]}
|
||||
select={value => {
|
||||
onChange(value, i, "newActivityType");
|
||||
}}
|
||||
className={Margins.top8}
|
||||
isSelected={value => setting.newActivityType === value}
|
||||
serialize={identity}
|
||||
/>
|
||||
{
|
||||
setting.newActivityType === ActivityType.STREAMING &&
|
||||
<>
|
||||
<Forms.FormTitle className={`${Margins.top8} ${Margins.bottom8}`}>Stream URL (must be YouTube or Twitch)</Forms.FormTitle>
|
||||
<CheckedTextInput
|
||||
value={setting.newStreamUrl}
|
||||
onChange={async v => {
|
||||
onChange(v, i, "newStreamUrl");
|
||||
}}
|
||||
validate={v => {
|
||||
return /https?:\/\/(www\.)?(twitch\.tv|youtube\.com)\/\w+/.test(v) || "Invalid stream URL";
|
||||
}}
|
||||
/>
|
||||
|
||||
</>
|
||||
}
|
||||
{
|
||||
setting.newActivityType !== ActivityType.STREAMING &&
|
||||
<>
|
||||
<Forms.FormTitle className={Margins.top8}>New name {setting.newActivityType === ActivityType.PLAYING && "(first line)"}</Forms.FormTitle>
|
||||
<TextInput
|
||||
className={Margins.top8}
|
||||
value={setting.newName}
|
||||
onChange={async v => {
|
||||
onChange(v, i, "newName");
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
<Forms.FormTitle className={Margins.top8}>New details {setting.newActivityType === ActivityType.PLAYING ? "(second line)" : "(first line)"}</Forms.FormTitle>
|
||||
<TextInput
|
||||
className={Margins.top8}
|
||||
value={setting.newDetails}
|
||||
onChange={async v => {
|
||||
onChange(v, i, "newDetails");
|
||||
}}
|
||||
/>
|
||||
<Forms.FormTitle className={Margins.top8}>New state {setting.newActivityType === ActivityType.PLAYING ? "(third line)" : "(second line)"}</Forms.FormTitle>
|
||||
<TextInput
|
||||
className={Margins.top8}
|
||||
value={setting.newState}
|
||||
onChange={async v => {
|
||||
onChange(v, i, "newState");
|
||||
}}
|
||||
/>
|
||||
{
|
||||
!setting.disableAssets &&
|
||||
<>
|
||||
<Forms.FormText style={{ fontSize: "1.05rem", fontWeight: "500" }} className={Margins.top8}>Large image</Forms.FormText>
|
||||
<Forms.FormTitle className={Margins.top8}>Text {setting.newActivityType !== ActivityType.PLAYING && "(also third line)"}</Forms.FormTitle>
|
||||
<TextInput
|
||||
className={Margins.top8}
|
||||
value={setting.newLargeImageText}
|
||||
onChange={async v => {
|
||||
onChange(v, i, "newLargeImageText");
|
||||
}}
|
||||
/>
|
||||
<Forms.FormTitle className={Margins.top8}>URL</Forms.FormTitle>
|
||||
<TextInput
|
||||
className={Margins.top8}
|
||||
value={setting.newLargeImageUrl}
|
||||
onChange={async v => {
|
||||
onChange(v, i, "newLargeImageUrl");
|
||||
}}
|
||||
/>
|
||||
<Forms.FormText style={{ fontSize: "1.05rem", fontWeight: "500" }} className={Margins.top8}>Small image</Forms.FormText>
|
||||
<Forms.FormTitle className={Margins.top8}>Text</Forms.FormTitle>
|
||||
<TextInput
|
||||
className={Margins.top8}
|
||||
value={setting.newSmallImageText}
|
||||
onChange={async v => {
|
||||
onChange(v, i, "newSmallImageText");
|
||||
}}
|
||||
/>
|
||||
<Forms.FormTitle className={Margins.top8}>URL</Forms.FormTitle>
|
||||
<TextInput
|
||||
className={Margins.top8}
|
||||
value={setting.newSmallImageUrl}
|
||||
onChange={async v => {
|
||||
onChange(v, i, "newSmallImageUrl");
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
<Switch
|
||||
value={setting.disableAssets}
|
||||
onChange={value => {
|
||||
onChange(value, i, "disableAssets");
|
||||
}}
|
||||
className={Margins.top8}
|
||||
hideBorder={true}
|
||||
style={{ marginBottom: "0" }}
|
||||
>
|
||||
Hide assets (large & small images)
|
||||
</Switch>
|
||||
<Switch
|
||||
value={setting.disableTimestamps}
|
||||
onChange={value => {
|
||||
onChange(value, i, "disableTimestamps");
|
||||
}}
|
||||
className={Margins.top8}
|
||||
hideBorder={true}
|
||||
style={{ marginBottom: "0" }}
|
||||
>
|
||||
Hide timestamps
|
||||
</Switch>
|
||||
</>
|
||||
}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
155
src/equicordplugins/rpcEditor/index.tsx
Normal file
155
src/equicordplugins/rpcEditor/index.tsx
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { DataStore } from "@api/index";
|
||||
import { definePluginSettings, migratePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { useForceUpdater } from "@utils/react";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { React } from "@webpack/common";
|
||||
|
||||
import { ReplaceSettings, ReplaceTutorial } from "./ReplaceSettings";
|
||||
|
||||
const APP_IDS_KEY = "ReplaceActivityType_appids";
|
||||
export type AppIdSetting = {
|
||||
disableAssets: boolean;
|
||||
disableTimestamps: boolean;
|
||||
appId: string;
|
||||
enabled: boolean;
|
||||
newActivityType: ActivityType;
|
||||
newName: string,
|
||||
newDetails: string,
|
||||
newState: string,
|
||||
newLargeImageUrl: string,
|
||||
newLargeImageText: string,
|
||||
newSmallImageUrl: string,
|
||||
newSmallImageText: string;
|
||||
newStreamUrl: string;
|
||||
};
|
||||
|
||||
export interface Activity {
|
||||
state: string;
|
||||
details: string;
|
||||
timestamps?: {
|
||||
start?: number;
|
||||
end?: number;
|
||||
};
|
||||
url?: string;
|
||||
assets: ActivityAssets;
|
||||
buttons?: Array<string>;
|
||||
name: string;
|
||||
application_id: string;
|
||||
metadata?: {
|
||||
button_urls?: Array<string>;
|
||||
};
|
||||
type: number;
|
||||
flags: number;
|
||||
}
|
||||
|
||||
interface ActivityAssets {
|
||||
large_image: string;
|
||||
large_text: string;
|
||||
small_image: string;
|
||||
small_text: string;
|
||||
}
|
||||
|
||||
export const enum ActivityType {
|
||||
PLAYING = 0,
|
||||
STREAMING = 1,
|
||||
LISTENING = 2,
|
||||
WATCHING = 3,
|
||||
COMPETING = 5
|
||||
}
|
||||
|
||||
export const makeEmptyAppId: () => AppIdSetting = () => ({
|
||||
appId: "",
|
||||
enabled: true,
|
||||
newActivityType: ActivityType.PLAYING,
|
||||
newName: "",
|
||||
newDetails: "",
|
||||
newState: "",
|
||||
newLargeImageUrl: "",
|
||||
newLargeImageText: "",
|
||||
newSmallImageUrl: "",
|
||||
newSmallImageText: "",
|
||||
newStreamUrl: "",
|
||||
disableTimestamps: false,
|
||||
disableAssets: false
|
||||
});
|
||||
|
||||
let appIds = [makeEmptyAppId()];
|
||||
|
||||
const settings = definePluginSettings({
|
||||
replacedAppIds: {
|
||||
type: OptionType.COMPONENT,
|
||||
description: "",
|
||||
component: () => {
|
||||
const update = useForceUpdater();
|
||||
return (
|
||||
<>
|
||||
<ReplaceSettings
|
||||
appIds={appIds}
|
||||
update={update}
|
||||
save={async () => DataStore.set(APP_IDS_KEY, appIds)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
migratePluginSettings("RPCEditor", "ReplaceActivityTypes");
|
||||
export default definePlugin({
|
||||
name: "RPCEditor",
|
||||
description: "Edit the type and content of any Rich Presence",
|
||||
authors: [Devs.Nyako, Devs.nin0dev],
|
||||
patches: [
|
||||
{
|
||||
find: '"LocalActivityStore"',
|
||||
replacement: {
|
||||
match: /\i\(\i\)\{.{0,25}activity:(\i)\}=\i;/,
|
||||
replace: "$&$self.patchActivity($1);",
|
||||
}
|
||||
}
|
||||
],
|
||||
settings,
|
||||
settingsAboutComponent: () => <ReplaceTutorial />,
|
||||
|
||||
async start() {
|
||||
appIds = await DataStore.get(APP_IDS_KEY) ?? [makeEmptyAppId()];
|
||||
},
|
||||
parseField(text: string, originalActivity: Activity): string {
|
||||
if (text === "null") return "";
|
||||
return text
|
||||
.replaceAll(":name:", originalActivity.name)
|
||||
.replaceAll(":details:", originalActivity.details)
|
||||
.replaceAll(":state:", originalActivity.state)
|
||||
.replaceAll(":large_image:", originalActivity.assets.large_image)
|
||||
.replaceAll(":large_text:", originalActivity.assets.large_text)
|
||||
.replaceAll(":small_image:", originalActivity.assets.small_image)
|
||||
.replaceAll(":small_text:", originalActivity.assets.small_text);
|
||||
},
|
||||
patchActivity(activity: Activity) {
|
||||
if (!activity) return;
|
||||
appIds.forEach(app => {
|
||||
if (app.enabled && app.appId === activity.application_id) {
|
||||
const oldActivity = { ...activity };
|
||||
activity.type = app.newActivityType;
|
||||
if (app.newName) activity.name = this.parseField(app.newName, oldActivity);
|
||||
if (app.newActivityType === ActivityType.STREAMING && app.newStreamUrl) activity.url = app.newStreamUrl;
|
||||
if (app.newDetails) activity.details = this.parseField(app.newDetails, oldActivity);
|
||||
if (app.newState) activity.state = this.parseField(app.newState, oldActivity);
|
||||
if (app.newLargeImageText) activity.assets.large_text = this.parseField(app.newLargeImageText, oldActivity);
|
||||
if (app.newLargeImageUrl) activity.assets.large_image = this.parseField(app.newLargeImageUrl, oldActivity);
|
||||
if (app.newSmallImageText) activity.assets.small_text = this.parseField(app.newSmallImageText, oldActivity);
|
||||
if (app.newSmallImageUrl) activity.assets.small_image = this.parseField(app.newSmallImageUrl, oldActivity);
|
||||
// @ts-ignore here we are intentionally nulling assets
|
||||
if (app.disableAssets) activity.assets = {};
|
||||
if (app.disableTimestamps) activity.timestamps = {};
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
14
src/equicordplugins/rpcEditor/style.css
Normal file
14
src/equicordplugins/rpcEditor/style.css
Normal file
|
@ -0,0 +1,14 @@
|
|||
.vc-rpceditor-horizontal {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
|
||||
}
|
||||
|
||||
.vc-rpceditor-horizontal div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
||||
.vc-rpceditor-horizontal div:first-child {
|
||||
padding-right: 5px;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue