mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-01-11 02:16:23 +00:00
replaced plugin by @nyakowint plugin
This commit is contained in:
parent
c8ed4b5a8e
commit
e175dbfa2c
2 changed files with 249 additions and 37 deletions
155
src/plugins/rpcEditor/ReplaceSettings.tsx
Normal file
155
src/plugins/rpcEditor/ReplaceSettings.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 { CheckedTextInput } from "@components/CheckedTextInput";
|
||||||
|
import { Margins } from "@utils/margins";
|
||||||
|
import { identity } from "@utils/misc";
|
||||||
|
import { findByPropsLazy } from "@webpack";
|
||||||
|
import { Card, Forms, React, Select, SnowflakeUtils, Switch } from "@webpack/common";
|
||||||
|
|
||||||
|
import { ActivityType, AppIdSetting, makeEmptyAppId } from ".";
|
||||||
|
|
||||||
|
interface SettingsProps {
|
||||||
|
appIds: AppIdSetting[];
|
||||||
|
update: () => void;
|
||||||
|
save: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RpcApp {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
icon: string;
|
||||||
|
flags: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RPCUtils = findByPropsLazy("fetchApplicationsRPC", "getRemoteIconURL");
|
||||||
|
|
||||||
|
const cachedApps: any = {};
|
||||||
|
async function lookupApp(appId: string): Promise<RpcApp | null> {
|
||||||
|
if (cachedApps[appId]) return cachedApps[appId];
|
||||||
|
const socket: any = {};
|
||||||
|
try {
|
||||||
|
await RPCUtils.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" }}>
|
||||||
|
<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,61 +1,98 @@
|
||||||
/*
|
/*
|
||||||
* Vencord, a Discord client mod
|
* Vencord, a Discord client mod
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
* Copyright (c) 2023 your mom lol
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { DataStore } from "@api/index";
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
|
import { useForceUpdater } from "@utils/react";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { Forms, React, TextInput, useState } from "@webpack/common";
|
import { React } from "@webpack/common";
|
||||||
import { DataStore } from "@api/index";
|
|
||||||
|
|
||||||
const DATASTORE_IDS_KEY = "RPCEditor_ActivityIds";
|
import { ReplaceSettings, ReplaceTutorial } from "./ReplaceSettings";
|
||||||
|
|
||||||
function Input({ initialValue, onChange, placeholder }: {
|
const APP_IDS_KEY = "ReplaceActivityType_appids";
|
||||||
placeholder: string;
|
export type AppIdSetting = {
|
||||||
initialValue: string;
|
appName: string;
|
||||||
onChange(value: string): void;
|
appId: string;
|
||||||
}) {
|
swapNameAndDetails: boolean;
|
||||||
const [value, setValue] = useState(initialValue);
|
activityType: ActivityType;
|
||||||
return (
|
streamUrl: string;
|
||||||
<TextInput
|
enabled: boolean;
|
||||||
placeholder={placeholder}
|
};
|
||||||
value={value}
|
|
||||||
onChange={setValue}
|
export interface Activity {
|
||||||
spellCheck={false}
|
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 const settings = definePluginSettings({
|
interface ActivityAssets {
|
||||||
idsToEdit: {
|
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: "",
|
||||||
|
appName: "Unknown",
|
||||||
|
streamUrl: "",
|
||||||
|
swapNameAndDetails: false,
|
||||||
|
activityType: ActivityType.PLAYING,
|
||||||
|
enabled: true
|
||||||
|
});
|
||||||
|
|
||||||
|
let appIds = [makeEmptyAppId()];
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
replacedAppIds: {
|
||||||
type: OptionType.COMPONENT,
|
type: OptionType.COMPONENT,
|
||||||
description: "",
|
description: "",
|
||||||
component: async () => {
|
component: () => {
|
||||||
|
const update = useForceUpdater();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Forms.FormTitle style={{ marginBottom: "0px" }}>Comma separated list of activity IDs/names to
|
<ReplaceSettings
|
||||||
edit</Forms.FormTitle>
|
appIds={appIds}
|
||||||
<Input placeholder={"886685857560539176, YouTube"}
|
update={update}
|
||||||
initialValue={DataStore.get(DATASTORE_IDS_KEY) ?? ""} onChange={e => explode()}/>
|
save={async () => DataStore.set(APP_IDS_KEY, appIds)}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function explode() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "RPCEditor",
|
name: "RPCEditor",
|
||||||
description: "Allows editing the type or content of any Rich Presence. (Configure in settings)",
|
description: "Edit the type and content of any Rich Presence",
|
||||||
authors: [Devs.nin0dev],
|
authors: [Devs.Nyako, Devs.nin0dev],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "LocalActivityStore",
|
find: '="LocalActivityStore",',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /LOCAL_ACTIVITY_UPDATE:function\((\i)\)\{/,
|
match: /LOCAL_ACTIVITY_UPDATE:function\((\i)\)\{/,
|
||||||
replace: "$&$self.patchActivity($1.activity);",
|
replace: "$&$self.patchActivity($1.activity);",
|
||||||
|
@ -63,10 +100,30 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
settings,
|
settings,
|
||||||
patchActivity(activity: any) {
|
settingsAboutComponent: () => <ReplaceTutorial />,
|
||||||
// not finished, this'll change all activities to listening :husk:
|
|
||||||
|
async start() {
|
||||||
|
appIds = await DataStore.get(APP_IDS_KEY) ?? [makeEmptyAppId()];
|
||||||
|
},
|
||||||
|
|
||||||
|
patchActivity(activity: Activity) {
|
||||||
|
if (!activity) return;
|
||||||
console.log(activity);
|
console.log(activity);
|
||||||
activity.type = 2;
|
appIds.forEach(app => {
|
||||||
activity.assets.large_text = null; // bomb premid image text
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue