Plugin
IgnoreActivities
Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings below
1
import { definePluginSettings } from "@api/Settings";2
import { getUserSettingLazy } from "@api/UserSettings";3
import ErrorBoundary from "@components/ErrorBoundary";4
import { Flex } from "@components/Flex";5
import CustomRpcPlugin from "@plugins/customRPC";6
import { Devs } from "@utils/constants";7
import { Margins } from "@utils/margins";8
import definePlugin, { OptionType } from "@utils/types";9
import { Button, Forms, RunningGameStore, showToast, TextArea, Toasts, Tooltip, useEffect, useState } from "@webpack/common";10
11
const enum ActivitiesTypes {12
Game,13
Embedded14
}15
16
interface IgnoredActivity {17
id: string;18
name: string;19
type: ActivitiesTypes;20
}21
22
const enum FilterMode {23
Whitelist,24
Blacklist25
}26
27
const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!;28
29
function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) {30
return (31
<Tooltip text={tooltipText}>32
{tooltipProps => (33
<button34
{...tooltipProps}35
onClick={e => handleActivityToggle(e, activity)}36
style={{ all: "unset", cursor: "pointer", display: "flex", justifyContent: "center", alignItems: "center" }}37
>38
<svg39
width="24"40
height="24"41
viewBox="0 -960 960 960"42
>43
<path fill={fill} d={path} />44
</svg>45
</button>46
)}47
</Tooltip>48
);49
}50
51
const ToggleIconOn = (activity: IgnoredActivity, fill: string) => ToggleIcon(activity, "Disable activity", "M480-320q75 0 127.5-52.5T660-500q0-75-52.5-127.5T480-680q-75 0-127.5 52.5T300-500q0 75 52.5 127.5T480-320Zm0-72q-45 0-76.5-31.5T372-500q0-45 31.5-76.5T480-608q45 0 76.5 31.5T588-500q0 45-31.5 76.5T480-392Zm0 192q-146 0-266-81.5T40-500q54-137 174-218.5T480-800q146 0 266 81.5T920-500q-54 137-174 218.5T480-200Zm0-300Zm0 220q113 0 207.5-59.5T832-500q-50-101-144.5-160.5T480-720q-113 0-207.5 59.5T128-500q50 101 144.5 160.5T480-280Z", fill);52
const ToggleIconOff = (activity: IgnoredActivity, fill: string) => ToggleIcon(activity, "Enable activity", "m644-428-58-58q9-47-27-88t-93-32l-58-58q17-8 34.5-12t37.5-4q75 0 127.5 52.5T660-500q0 20-4 37.5T644-428Zm128 126-58-56q38-29 67.5-63.5T832-500q-50-101-143.5-160.5T480-720q-29 0-57 4t-55 12l-62-62q41-17 84-25.5t90-8.5q151 0 269 83.5T920-500q-23 59-60.5 109.5T772-302Zm20 246L624-222q-35 11-70.5 16.5T480-200q-151 0-269-83.5T40-500q21-53 53-98.5t73-81.5L56-792l56-56 736 736-56 56ZM222-624q-29 26-53 57t-41 67q50 101 143.5 160.5T480-280q20 0 39-2.5t39-5.5l-36-38q-11 3-21 4.5t-21 1.5q-75 0-127.5-52.5T300-500q0-11 1.5-21t4.5-21l-84-82Zm319 93Zm-151 75Z", fill);53
54
function ToggleActivityComponent(activity: IgnoredActivity, isPlaying = false) {55
const s = settings.use(["ignoredActivities"]);56
const { ignoredActivities } = s;57
58
if (ignoredActivities.some(act => act.id === activity.id)) return ToggleIconOff(activity, "var(--status-danger)");59
return ToggleIconOn(activity, isPlaying ? "var(--green-300)" : "var(--interactive-icon-default)");60
}61
62
function handleActivityToggle(e: React.MouseEvent<HTMLButtonElement, MouseEvent>, activity: IgnoredActivity) {63
e.stopPropagation();64
65
const ignoredActivityIndex = settings.store.ignoredActivities.findIndex(act => act.id === activity.id);66
if (ignoredActivityIndex === -1) settings.store.ignoredActivities.push(activity);67
else settings.store.ignoredActivities.splice(ignoredActivityIndex, 1);68
}69
70
function recalculateActivities() {71
ShowCurrentGame.updateSetting(old => old);72
}73
74
function ImportCustomRPCComponent() {75
return (76
<Flex flexDirection="column">77
<Forms.FormText>Import the application id of the CustomRPC plugin to the filter list</Forms.FormText>78
<div>79
<Button80
onClick={() => {81
const id = CustomRpcPlugin.settings.store.appID;82
if (!id) {83
return showToast("CustomRPC application ID is not set.", Toasts.Type.FAILURE);84
}85
86
const isAlreadyAdded = idsListPushID?.(id);87
if (isAlreadyAdded) {88
showToast("CustomRPC application ID is already added.", Toasts.Type.FAILURE);89
}90
}}91
>92
Import CustomRPC ID93
</Button>94
</div>95
</Flex>96
);97
}98
99
let idsListPushID: ((id: string) => boolean) | null = null;100
101
function IdsListComponent(props: { setValue: (value: string) => void; }) {102
const [idsList, setIdsList] = useState<string>(settings.store.idsList ?? "");103
104
idsListPushID = (id: string) => {105
const currentIds = new Set(idsList.split(",").map(id => id.trim()).filter(Boolean));106
107
const isAlreadyAdded = currentIds.has(id) || (currentIds.add(id), false);108
109
const ids = Array.from(currentIds).join(", ");110
setIdsList(ids);111
props.setValue(ids);112
113
return isAlreadyAdded;114
};115
116
useEffect(() => () => {117
idsListPushID = null;118
}, []);119
120
function handleChange(newValue: string) {121
setIdsList(newValue);122
props.setValue(newValue);123
}124
125
return (126
<section>127
<Forms.FormTitle tag="h3">Filter List</Forms.FormTitle>128
<Forms.FormText className={Margins.bottom8}>Comma separated list of activity IDs to filter (Useful for filtering specific RPC activities and CustomRPC</Forms.FormText>129
<TextArea130
type="text"131
value={idsList}132
onChange={handleChange}133
placeholder="235834946571337729, 343383572805058560"134
/>135
</section>136
);137
}138
139
const settings = definePluginSettings({140
importCustomRPC: {141
type: OptionType.COMPONENT,142
component: ImportCustomRPCComponent143
},144
listMode: {145
type: OptionType.SELECT,146
description: "Change the mode of the filter list",147
options: [148
{149
label: "Whitelist",150
value: FilterMode.Whitelist,151
default: true152
},153
{154
label: "Blacklist",155
value: FilterMode.Blacklist,156
}157
],158
onChange: recalculateActivities159
},160
idsList: {161
type: OptionType.COMPONENT,162
default: "",163
onChange(newValue: string) {164
const ids = new Set(newValue.split(",").map(id => id.trim()).filter(Boolean));165
settings.store.idsList = Array.from(ids).join(", ");166
recalculateActivities();167
},168
component: props => <IdsListComponent setValue={props.setValue} />169
},170
ignorePlaying: {171
type: OptionType.BOOLEAN,172
description: "Ignore all playing activities (These are usually game and RPC activities)",173
default: false,174
onChange: recalculateActivities175
},176
ignoreStreaming: {177
type: OptionType.BOOLEAN,178
description: "Ignore all streaming activities",179
default: false,180
onChange: recalculateActivities181
},182
ignoreListening: {183
type: OptionType.BOOLEAN,184
description: "Ignore all listening activities (These are usually spotify activities)",185
default: false,186
onChange: recalculateActivities187
},188
ignoreWatching: {189
type: OptionType.BOOLEAN,190
description: "Ignore all watching activities",191
default: false,192
onChange: recalculateActivities193
},194
ignoreCompeting: {195
type: OptionType.BOOLEAN,196
description: "Ignore all competing activities (These are normally special game activities)",197
default: false,198
onChange: recalculateActivities199
},200
ignoredActivities: {201
type: OptionType.CUSTOM,202
default: [] as IgnoredActivity[],203
onChange: recalculateActivities204
}205
});206
207
function isActivityTypeIgnored(type: number, id?: string) {208
if (id && settings.store.idsList.includes(id)) {209
return settings.store.listMode === FilterMode.Blacklist;210
}211
212
switch (type) {213
case 0: return settings.store.ignorePlaying;214
case 1: return settings.store.ignoreStreaming;215
case 2: return settings.store.ignoreListening;216
case 3: return settings.store.ignoreWatching;217
case 5: return settings.store.ignoreCompeting;218
}219
220
return false;221
}222
223
export default definePlugin({224
name: "IgnoreActivities",225
authors: [Devs.Nuckyz, Devs.Kylie],226
description: "Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings below",227
tags: ["Activity", "Privacy", "Customisation"],228
dependencies: ["UserSettingsAPI"],229
230
settings,231
232
patches: [233
{234
find: 039;"LocalActivityStore"039;,235
replacement: [236
{237
match: /\.LISTENING.+?(?=!?\i\(\)\(\i,\i\))(?<=(\i)\.push.+?)/,238
replace: (m, activities) => `${m}${activities}=${activities}.filter($self.isActivityNotIgnored);`239
}240
]241
},242
{243
find: 039;"ActivityTrackingStore"039;,244
replacement: {245
match: /getVisibleRunningGames\(\).+?;(?=for)(?<=(\i)=\i\.\i\.getVisibleRunningGames.+?)/,246
replace: (m, runningGames) => `${m}${runningGames}=${runningGames}.filter(({id,name})=>$self.isActivityNotIgnored({type:0,application_id:id,name}));`247
}248
},249
{250
find: "#{intl::SETTINGS_GAMES_TOGGLE_OVERLAY}",251
replacement: {252
match: /(\i)&&!\i\|\|\i\?null(?<=(\i)\.verified&&.+?)/,253
replace: "$self.renderToggleGameActivityButton($2,$1),$&"254
}255
},256
257
// Activities from the apps launcher in the bottom right of the chat bar258
{259
find: "#{intl::EMBEDDED_ACTIVITIES_DEVELOPER_ACTIVITY}",260
replacement: {261
match: /lineClamp:1.{0,50}?(?=!\i&&\i\?.+?application:(\i))/,262
replace: "$&$self.renderToggleActivityButton($1),"263
}264
}265
],266
267
async start() {268
if (settings.store.ignoredActivities.length !== 0) {269
const gamesSeen = RunningGameStore.getGamesSeen() as { id?: string; exePath: string; }[];270
271
for (const [index, ignoredActivity] of settings.store.ignoredActivities.entries()) {272
if (ignoredActivity.type !== ActivitiesTypes.Game) continue;273
274
if (!gamesSeen.some(game => game.id === ignoredActivity.id || game.exePath === ignoredActivity.id)) {275
settings.store.ignoredActivities.splice(index, 1);276
}277
}278
}279
},280
281
isActivityNotIgnored(props: { type: number; application_id?: string; name?: string; }) {282
if (isActivityTypeIgnored(props.type, props.application_id)) return false;283
284
if (props.application_id != null) {285
return !settings.store.ignoredActivities.some(activity => activity.id === props.application_id) || (settings.store.listMode === FilterMode.Whitelist && settings.store.idsList.includes(props.application_id));286
} else {287
const exePath = RunningGameStore.getRunningGames().find(game => game.name === props.name)?.exePath;288
if (exePath) {289
return !settings.store.ignoredActivities.some(activity => activity.id === exePath);290
}291
}292
293
return true;294
},295
296
renderToggleGameActivityButton(props: { id?: string; name: string, exePath: string; }, nowPlaying: boolean) {297
return (298
<ErrorBoundary noop>299
<div style={{ marginLeft: 12, zIndex: 0 }}>300
{ToggleActivityComponent({ id: props.id ?? props.exePath, name: props.name, type: ActivitiesTypes.Game }, nowPlaying)}301
</div>302
</ErrorBoundary>303
);304
},305
306
renderToggleActivityButton(props: { id: string; name: string; }) {307
return (308
<ErrorBoundary noop>309
{ToggleActivityComponent({ id: props.id, name: props.name, type: ActivitiesTypes.Embedded })}310
</ErrorBoundary>311
);312
}313
});314