From c893bb2a5df2dd667ed5b29391e0413b7624a576 Mon Sep 17 00:00:00 2001 From: redphx <96280+redphx@users.noreply.github.com> Date: Wed, 11 Dec 2024 07:25:48 +0700 Subject: [PATCH] Block notifications --- dist/better-xcloud.lite.user.js | 39 +++++----- dist/better-xcloud.user.js | 74 ++++++++++++------- src/modules/patcher/patcher.ts | 55 ++++++++++++-- src/utils/network.ts | 29 +++++--- .../global-settings-storage.ts | 4 +- src/utils/translation.ts | 3 + src/utils/utils.ts | 17 +++++ 7 files changed, 158 insertions(+), 63 deletions(-) diff --git a/dist/better-xcloud.lite.user.js b/dist/better-xcloud.lite.user.js index bb872bc..53a8c5a 100755 --- a/dist/better-xcloud.lite.user.js +++ b/dist/better-xcloud.lite.user.js @@ -269,6 +269,9 @@ var SUPPORTED_LANGUAGES = { "zh-CN": "中文(简体)", "zh-TW": "中文(繁體)" }, Texts = { + notifications: "Notifications", + invites: "Invites", + achievements: "Achievements", chat: "Chat", "friends-followers": "Friends and followers", byog: "Stream your own game", @@ -1716,7 +1719,9 @@ class GlobalSettingsStorage extends BaseSettingsStore { multipleOptions: { chat: t("chat"), friends: t("friends-followers"), - byog: t("byog") + byog: t("byog"), + "notifications-invites": t("notifications") + ": " + t("invites"), + "notifications-achievements": t("notifications") + ": " + t("achievements") } }, "userAgent.profile": { @@ -2006,6 +2011,10 @@ function clearAllData() { } catch (e) {} alert(t("clear-data-success")); } +function blockAllNotifications() { + let blockFeatures = getPref("block.features"); + return ["friends", "notifications-achievements", "notifications-invites"].every((value) => blockFeatures.includes(value)); +} class SoundShortcut { static adjustGainNodeVolume(amount) { if (!getPref("audio.volume.booster.enabled")) return 0; @@ -5627,29 +5636,19 @@ async function patchIceCandidates(request, consoleAddrs) { } function interceptHttpRequests() { let BLOCKED_URLS = []; - if (getPref("block.tracking")) clearAllLogs(), BLOCKED_URLS = BLOCKED_URLS.concat([ - "https://arc.msn.com", - "https://browser.events.data.microsoft.com", - "https://dc.services.visualstudio.com", - "https://2c06dea3f26c40c69b8456d319791fd0@o427368.ingest.sentry.io", - "https://mscom.demdex.net" - ]); + if (getPref("block.tracking")) clearAllLogs(), BLOCKED_URLS.push("https://arc.msn.com", "https://browser.events.data.microsoft.com", "https://dc.services.visualstudio.com", "https://2c06dea3f26c40c69b8456d319791fd0@o427368.ingest.sentry.io", "https://mscom.demdex.net"); let blockFeatures2 = getPref("block.features"); - if (blockFeatures2.includes("chat")) BLOCKED_URLS = BLOCKED_URLS.concat([ - "https://xblmessaging.xboxlive.com/network/xbox/users/me/inbox" - ]); - if (blockFeatures2.includes("friends")) BLOCKED_URLS = BLOCKED_URLS.concat([ - "https://peoplehub.xboxlive.com/users/me/people/social", - "https://peoplehub.xboxlive.com/users/me/people/recommendations" - ]); + if (blockFeatures2.includes("chat")) BLOCKED_URLS.push("https://xblmessaging.xboxlive.com/network/xbox/users/me/inbox"); + if (blockFeatures2.includes("friends")) BLOCKED_URLS.push("https://peoplehub.xboxlive.com/users/me/people/social", "https://peoplehub.xboxlive.com/users/me/people/recommendations"); + if (blockAllNotifications()) BLOCKED_URLS.push("https://notificationinbox.xboxlive.com/"); let xhrPrototype = XMLHttpRequest.prototype, nativeXhrOpen = xhrPrototype.open, nativeXhrSend = xhrPrototype.send; xhrPrototype.open = function(method, url) { return this._url = url, nativeXhrOpen.apply(this, arguments); }, xhrPrototype.send = function(...arg) { - for (let blocked of BLOCKED_URLS) - if (this._url.startsWith(blocked)) { - if (blocked === "https://dc.services.visualstudio.com") window.setTimeout(clearAllLogs, 1000); - return !1; + for (let url of BLOCKED_URLS) + if (this._url.startsWith(url)) { + if (url === "https://dc.services.visualstudio.com") window.setTimeout(clearAllLogs, 1000); + return BxLogger.warning("Blocked URL", url), !1; } return nativeXhrSend.apply(this, arguments); }; @@ -5671,7 +5670,7 @@ function interceptHttpRequests() { window.BX_FETCH = window.fetch = async (request, init) => { let url = typeof request === "string" ? request : request.url; for (let blocked of BLOCKED_URLS) - if (url.startsWith(blocked)) return new Response('{"acc":1,"webResult":{}}', { + if (url.startsWith(blocked)) return BxLogger.warning("Blocked URL", url), new Response('{"acc":1,"webResult":{}}', { status: 200, statusText: "200 OK" }); diff --git a/dist/better-xcloud.user.js b/dist/better-xcloud.user.js index 91dc774..4ae02c6 100755 --- a/dist/better-xcloud.user.js +++ b/dist/better-xcloud.user.js @@ -298,6 +298,9 @@ var SUPPORTED_LANGUAGES = { "zh-CN": "中文(简体)", "zh-TW": "中文(繁體)" }, Texts = { + notifications: "Notifications", + invites: "Invites", + achievements: "Achievements", chat: "Chat", "friends-followers": "Friends and followers", byog: "Stream your own game", @@ -1792,7 +1795,9 @@ class GlobalSettingsStorage extends BaseSettingsStore { multipleOptions: { chat: t("chat"), friends: t("friends-followers"), - byog: t("byog") + byog: t("byog"), + "notifications-invites": t("notifications") + ": " + t("invites"), + "notifications-achievements": t("notifications") + ": " + t("achievements") } }, "userAgent.profile": { @@ -2111,6 +2116,15 @@ function clearAllData() { } catch (e) {} alert(t("clear-data-success")); } +function blockAllNotifications() { + let blockFeatures = getPref("block.features"); + return ["friends", "notifications-achievements", "notifications-invites"].every((value) => blockFeatures.includes(value)); +} +function blockSomeNotifications() { + let blockFeatures = getPref("block.features"); + if (blockAllNotifications()) return !1; + return ["friends", "notifications-achievements", "notifications-invites"].some((value) => blockFeatures.includes(value)); +} class SoundShortcut { static adjustGainNodeVolume(amount) { if (!getPref("audio.volume.booster.enabled")) return 0; @@ -4622,6 +4636,23 @@ if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) { let text = "sendAbsoluteMouseCapableMessage(e){"; if (!str.includes(text)) return !1; return str = str.replace(text, text + "return;"), str; + }, + changeNotificationsSubscription(str) { + let text = ";buildSubscriptionQueryParamsForNotifications(", index = str.indexOf(text); + if (index < 0) return !1; + index += text.length; + let subsVar = str[index]; + index = str.indexOf("{", index) + 1; + let blockFeatures = getPref("block.features"), filters = []; + if (blockFeatures.includes("notifications-invites")) filters.push("GameInvite", "PartyInvite"); + if (blockFeatures.includes("friends")) filters.push("Follower"); + if (blockFeatures.includes("notifications-achievements")) filters.push("AchievementUnlock"); + let newCode = ` +let subs = ${subsVar}; +subs = subs.filter(val => !${JSON.stringify(filters)}.includes(val)); +${subsVar} = subs; +`; + return str = PatcherUtils.insertAt(str, index, newCode), str; } }, PATCH_ORDERS = PatcherUtils.filterPatches([ ...AppInterface && getPref("nativeMkb.mode") === "on" ? [ @@ -4669,12 +4700,15 @@ if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) { ...BX_FLAGS.EnableXcloudLogging ? [ "enableConsoleLogging", "enableXcloudLogger" + ] : [], + ...blockSomeNotifications() ? [ + "changeNotificationsSubscription" ] : [] -]), HOME_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([ - getPref("ui.hideSections").includes("friends") && "ignorePlayWithFriendsSection", - getPref("ui.hideSections").includes("all-games") && "ignoreAllGamesSection", - STATES.browser.capabilities.touch && getPref("ui.hideSections").includes("touch") && "ignorePlayWithTouchSection", - (getPref("ui.hideSections").includes("native-mkb") || getPref("ui.hideSections").includes("most-popular")) && "ignoreSiglSections" +]), hideSections = getPref("ui.hideSections"), HOME_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([ + hideSections.includes("friends") && "ignorePlayWithFriendsSection", + hideSections.includes("all-games") && "ignoreAllGamesSection", + STATES.browser.capabilities.touch && hideSections.includes("touch") && "ignorePlayWithTouchSection", + hideSections.some((value) => ["native-mkb", "most-popular"].includes(value)) && "ignoreSiglSections" ]), STREAM_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([ "patchXcloudTitleInfo", "disableGamepadDisconnectedScreen", @@ -7984,29 +8018,19 @@ async function patchIceCandidates(request, consoleAddrs) { } function interceptHttpRequests() { let BLOCKED_URLS = []; - if (getPref("block.tracking")) clearAllLogs(), BLOCKED_URLS = BLOCKED_URLS.concat([ - "https://arc.msn.com", - "https://browser.events.data.microsoft.com", - "https://dc.services.visualstudio.com", - "https://2c06dea3f26c40c69b8456d319791fd0@o427368.ingest.sentry.io", - "https://mscom.demdex.net" - ]); + if (getPref("block.tracking")) clearAllLogs(), BLOCKED_URLS.push("https://arc.msn.com", "https://browser.events.data.microsoft.com", "https://dc.services.visualstudio.com", "https://2c06dea3f26c40c69b8456d319791fd0@o427368.ingest.sentry.io", "https://mscom.demdex.net"); let blockFeatures2 = getPref("block.features"); - if (blockFeatures2.includes("chat")) BLOCKED_URLS = BLOCKED_URLS.concat([ - "https://xblmessaging.xboxlive.com/network/xbox/users/me/inbox" - ]); - if (blockFeatures2.includes("friends")) BLOCKED_URLS = BLOCKED_URLS.concat([ - "https://peoplehub.xboxlive.com/users/me/people/social", - "https://peoplehub.xboxlive.com/users/me/people/recommendations" - ]); + if (blockFeatures2.includes("chat")) BLOCKED_URLS.push("https://xblmessaging.xboxlive.com/network/xbox/users/me/inbox"); + if (blockFeatures2.includes("friends")) BLOCKED_URLS.push("https://peoplehub.xboxlive.com/users/me/people/social", "https://peoplehub.xboxlive.com/users/me/people/recommendations"); + if (blockAllNotifications()) BLOCKED_URLS.push("https://notificationinbox.xboxlive.com/"); let xhrPrototype = XMLHttpRequest.prototype, nativeXhrOpen = xhrPrototype.open, nativeXhrSend = xhrPrototype.send; xhrPrototype.open = function(method, url) { return this._url = url, nativeXhrOpen.apply(this, arguments); }, xhrPrototype.send = function(...arg) { - for (let blocked of BLOCKED_URLS) - if (this._url.startsWith(blocked)) { - if (blocked === "https://dc.services.visualstudio.com") window.setTimeout(clearAllLogs, 1000); - return !1; + for (let url of BLOCKED_URLS) + if (this._url.startsWith(url)) { + if (url === "https://dc.services.visualstudio.com") window.setTimeout(clearAllLogs, 1000); + return BxLogger.warning("Blocked URL", url), !1; } return nativeXhrSend.apply(this, arguments); }; @@ -8028,7 +8052,7 @@ function interceptHttpRequests() { window.BX_FETCH = window.fetch = async (request, init) => { let url = typeof request === "string" ? request : request.url; for (let blocked of BLOCKED_URLS) - if (url.startsWith(blocked)) return new Response('{"acc":1,"webResult":{}}', { + if (url.startsWith(blocked)) return BxLogger.warning("Blocked URL", url), new Response('{"acc":1,"webResult":{}}', { status: 200, statusText: "200 OK" }); diff --git a/src/modules/patcher/patcher.ts b/src/modules/patcher/patcher.ts index df5879e..b1f37c9 100755 --- a/src/modules/patcher/patcher.ts +++ b/src/modules/patcher/patcher.ts @@ -1,7 +1,7 @@ import { AppInterface, SCRIPT_VERSION, STATES } from "@utils/global"; import { BX_FLAGS } from "@utils/bx-flags"; import { BxLogger } from "@utils/bx-logger"; -import { hashCode, renderString } from "@utils/utils"; +import { blockSomeNotifications, hashCode, renderString } from "@utils/utils"; import { BxEvent } from "@/utils/bx-event"; import codeControllerShortcuts from "./patches/controller-shortcuts.js" with { type: "text" }; @@ -14,7 +14,7 @@ import { PrefKey, StorageKey } from "@/enums/pref-keys.js"; import { getPref } from "@/utils/settings-storages/global-settings-storage"; import { GamePassCloudGallery } from "@/enums/game-pass-gallery"; import { t } from "@/utils/translation"; -import { NativeMkbMode, TouchControllerMode, UiLayout, UiSection } from "@/enums/pref-values"; +import { BlockFeature, NativeMkbMode, TouchControllerMode, UiLayout, UiSection } from "@/enums/pref-values"; import { PatcherUtils } from "./patcher-utils.js"; export type PatchName = keyof typeof PATCHES; @@ -935,7 +935,43 @@ if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) { str = str.replace(text, text + 'return;'); return str; - } + }, + + changeNotificationsSubscription(str: string) { + let text = ';buildSubscriptionQueryParamsForNotifications('; + let index = str.indexOf(text); + if (index < 0) { + return false; + } + + index += text.length; + // Get parameter name + const subsVar = str[index]; + + // Find index after { + index = str.indexOf('{', index) + 1; + const blockFeatures = getPref(PrefKey.BLOCK_FEATURES); + const filters = []; + if (blockFeatures.includes(BlockFeature.NOTIFICATIONS_INVITES)) { + filters.push('GameInvite', 'PartyInvite'); + } + + if (blockFeatures.includes(BlockFeature.FRIENDS)) { + filters.push('Follower'); + } + + if (blockFeatures.includes(BlockFeature.NOTIFICATIONS_ACHIEVEMENTS)) { + filters.push('AchievementUnlock'); + } + + const newCode = ` +let subs = ${subsVar}; +subs = subs.filter(val => !${JSON.stringify(filters)}.includes(val)); +${subsVar} = subs; +`; + str = PatcherUtils.insertAt(str, index, newCode); + return str; + }, }; let PATCH_ORDERS = PatcherUtils.filterPatches([ @@ -1001,13 +1037,18 @@ let PATCH_ORDERS = PatcherUtils.filterPatches([ 'enableConsoleLogging', 'enableXcloudLogger', ] : []), + + ...(blockSomeNotifications() ? [ + 'changeNotificationsSubscription', + ] : []), ]); +const hideSections = getPref(PrefKey.UI_HIDE_SECTIONS); let HOME_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([ - getPref(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.FRIENDS) && 'ignorePlayWithFriendsSection', - getPref(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.ALL_GAMES) && 'ignoreAllGamesSection', - STATES.browser.capabilities.touch && getPref(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.TOUCH) && 'ignorePlayWithTouchSection', - (getPref(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.NATIVE_MKB) || getPref(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.MOST_POPULAR)) && 'ignoreSiglSections', + hideSections.includes(UiSection.FRIENDS) && 'ignorePlayWithFriendsSection', + hideSections.includes(UiSection.ALL_GAMES) && 'ignoreAllGamesSection', + STATES.browser.capabilities.touch && hideSections.includes(UiSection.TOUCH) && 'ignorePlayWithTouchSection', + hideSections.some(value => [UiSection.NATIVE_MKB, UiSection.MOST_POPULAR].includes(value)) && 'ignoreSiglSections', ]); // Only when playing diff --git a/src/utils/network.ts b/src/utils/network.ts index cf2bebd..9aab359 100755 --- a/src/utils/network.ts +++ b/src/utils/network.ts @@ -12,6 +12,7 @@ import { PrefKey } from "@/enums/pref-keys"; import { getPref } from "./settings-storages/global-settings-storage"; import type { RemotePlayConsoleAddresses } from "@/types/network"; import { BlockFeature, StreamResolution } from "@/enums/pref-values"; +import { blockAllNotifications } from "./utils"; type RequestType = 'xcloud' | 'xhome'; @@ -128,13 +129,13 @@ export function interceptHttpRequests() { // Clear Applications Insight buffers clearAllLogs(); - BLOCKED_URLS = BLOCKED_URLS.concat([ + BLOCKED_URLS.push( 'https://arc.msn.com', 'https://browser.events.data.microsoft.com', 'https://dc.services.visualstudio.com', 'https://2c06dea3f26c40c69b8456d319791fd0@o427368.ingest.sentry.io', 'https://mscom.demdex.net', - ]); + ); } @@ -142,16 +143,23 @@ export function interceptHttpRequests() { // 'https://accounts.xboxlive.com/family/memberXuid', const blockFeatures = getPref(PrefKey.BLOCK_FEATURES); if (blockFeatures.includes(BlockFeature.CHAT)) { - BLOCKED_URLS = BLOCKED_URLS.concat([ + BLOCKED_URLS.push( 'https://xblmessaging.xboxlive.com/network/xbox/users/me/inbox', - ]); + ); } if (blockFeatures.includes(BlockFeature.FRIENDS)) { - BLOCKED_URLS = BLOCKED_URLS.concat([ + BLOCKED_URLS.push( 'https://peoplehub.xboxlive.com/users/me/people/social', 'https://peoplehub.xboxlive.com/users/me/people/recommendations', - ]); + ); + } + + // Block all notifications + if (blockAllNotifications()) { + BLOCKED_URLS.push( + 'https://notificationinbox.xboxlive.com/', + ); } const xhrPrototype = XMLHttpRequest.prototype; @@ -166,11 +174,13 @@ export function interceptHttpRequests() { }; xhrPrototype.send = function(...arg) { - for (const blocked of BLOCKED_URLS) { - if ((this as any)._url.startsWith(blocked)) { - if (blocked === 'https://dc.services.visualstudio.com') { + for (const url of BLOCKED_URLS) { + if ((this as any)._url.startsWith(url)) { + if (url === 'https://dc.services.visualstudio.com') { window.setTimeout(clearAllLogs, 1000); } + + BxLogger.warning('Blocked URL', url); return false; } } @@ -202,6 +212,7 @@ export function interceptHttpRequests() { // Check blocked URLs for (const blocked of BLOCKED_URLS) { if (url.startsWith(blocked)) { + BxLogger.warning('Blocked URL', url); return new Response('{"acc":1,"webResult":{}}', { status: 200, statusText: '200 OK', diff --git a/src/utils/settings-storages/global-settings-storage.ts b/src/utils/settings-storages/global-settings-storage.ts index 5e83d4c..5ae38af 100755 --- a/src/utils/settings-storages/global-settings-storage.ts +++ b/src/utils/settings-storages/global-settings-storage.ts @@ -603,8 +603,8 @@ export class GlobalSettingsStorage extends BaseSettingsStorage { [BlockFeature.CHAT]: t('chat'), [BlockFeature.FRIENDS]: t('friends-followers'), [BlockFeature.BYOG]: t('byog'), - // [BlockFeature.NOTIFICATIONS_INVITES]: ut('Notifications: Invites'), - // [BlockFeature.NOTIFICATIONS_ACHIEVEMENTS]: ut('Notifications: Achievements'), + [BlockFeature.NOTIFICATIONS_INVITES]: t('notifications') + ': ' + t('invites'), + [BlockFeature.NOTIFICATIONS_ACHIEVEMENTS]: t('notifications') + ': ' + t('achievements'), }, }, diff --git a/src/utils/translation.ts b/src/utils/translation.ts index 6c0c542..76b5d68 100755 --- a/src/utils/translation.ts +++ b/src/utils/translation.ts @@ -27,6 +27,9 @@ export const SUPPORTED_LANGUAGES = { }; const Texts = { + "notifications": "Notifications", + "invites": "Invites", + "achievements": "Achievements", "chat": "Chat", "friends-followers": "Friends and followers", "byog": "Stream your own game", diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 11584fa..437a160 100755 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -5,6 +5,7 @@ import { Toast } from "./toast"; import { PrefKey } from "@/enums/pref-keys"; import { getPref, setPref } from "./settings-storages/global-settings-storage"; import { LocalDb } from "./local-db/local-db"; +import { BlockFeature } from "@/enums/pref-values"; /** * Check for update @@ -155,3 +156,19 @@ export function clearAllData() { alert(t('clear-data-success')); } + +export function blockAllNotifications() { + const blockFeatures = getPref(PrefKey.BLOCK_FEATURES); + const blockAll = [BlockFeature.FRIENDS, BlockFeature.NOTIFICATIONS_ACHIEVEMENTS, BlockFeature.NOTIFICATIONS_INVITES].every(value => blockFeatures.includes(value)); + return blockAll; +} + +export function blockSomeNotifications() { + const blockFeatures = getPref(PrefKey.BLOCK_FEATURES); + if (blockAllNotifications()) { + return false; + } + + const blockSome = [BlockFeature.FRIENDS, BlockFeature.NOTIFICATIONS_ACHIEVEMENTS, BlockFeature.NOTIFICATIONS_INVITES].some(value => blockFeatures.includes(value)); + return blockSome; +}