commit 5d8cdc3cfce8408ade7d2abbc885659383d19c2a
parent 5328dec6209bfb6596267acc736cd6ed6747bd45
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Sat, 13 Jan 2024 17:39:08 +0100

fix: hide story sections

Diffstat:
Mcommon/src/main/assets/lang/en_US.json | 24+++++++++++++-----------
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Global.kt | 2+-
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/UserInterfaceTweaks.kt | 3+--
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/data/SnapEnums.kt | 16++++++++++++++++
Acore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/MixerStories.kt | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/Stories.kt | 102-------------------------------------------------------------------------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/ui/UITweaks.kt | 26+++-----------------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/manager/impl/FeatureManager.kt | 4++--
8 files changed, 156 insertions(+), 141 deletions(-)

diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json @@ -296,9 +296,9 @@ "name": "Hide Quick Add in Friend Feed", "description": "Hides the Quick Add section in the friend feed" }, - "hide_story_sections": { - "name": "Hide Story Section", - "description": "Hide certain UI Elements shown in the story section" + "hide_story_suggestions": { + "name": "Hide Story Suggestions", + "description": "Removes suggestions from the Stories page" }, "hide_ui_components": { "name": "Hide UI Components", @@ -494,9 +494,9 @@ "name": "Disable Metrics", "description": "Blocks sending specific analytic data to Snapchat" }, - "disable_public_stories": { - "name": "Disable Public Stories", - "description": "Removes every public story from the Discover page\nMay require a clean cache to work properly" + "disable_story_sections": { + "name": "Disable Story Sections", + "description": "Removes sections from the Stories page\nMay require a refresh to work properly" }, "block_ads": { "name": "Block Ads", @@ -809,12 +809,9 @@ "hide_voice_record_button": "Remove Voice Record Button", "hide_unread_chat_hint": "Remove Unread Chat Hint" }, - "hide_story_sections": { + "hide_story_suggestions": { "hide_friend_suggestions": "Hide friend suggestions", - "hide_suggested_friend_stories": "Hide suggested friend stories", - "hide_friends": "Hide friends section", - "hide_suggested": "Hide suggested section", - "hide_for_you": "Hide For You section" + "hide_suggested_friend_stories": "Hide suggested friend stories" }, "home_tab": { "map": "Map", @@ -868,6 +865,11 @@ "1_month": "1 Month", "3_months": "3 Months", "6_months": "6 Months" + }, + "disable_story_sections": { + "friends": "Friends", + "following": "Following", + "discover": "Discover" } } }, diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Global.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Global.kt @@ -12,7 +12,7 @@ class Global : ConfigContainer() { val snapchatPlus = boolean("snapchat_plus") { requireRestart() } val disableConfirmationDialogs = multiple("disable_confirmation_dialogs", "remove_friend", "block_friend", "ignore_friend", "hide_friend", "hide_conversation", "clear_conversation") { requireRestart() } val disableMetrics = boolean("disable_metrics") { requireRestart() } - val disablePublicStories = boolean("disable_public_stories") { requireRestart(); requireCleanCache() } + val disableStorySections = multiple("disable_story_sections", "friends", "following", "discover") { requireRestart(); requireCleanCache() } val blockAds = boolean("block_ads") val spotlightCommentsUsername = boolean("spotlight_comments_username") { requireRestart() } val bypassVideoLengthRestriction = unique("bypass_video_length_restriction", "split", "single") { addNotices( diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/UserInterfaceTweaks.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/UserInterfaceTweaks.kt @@ -34,8 +34,7 @@ class UserInterfaceTweaks : ConfigContainer() { val hideFriendFeedEntry = boolean("hide_friend_feed_entry") { requireRestart() } val hideStreakRestore = boolean("hide_streak_restore") { requireRestart() } val hideQuickAddFriendFeed = boolean("hide_quick_add_friend_feed") { requireRestart() } - val hideStorySections = multiple("hide_story_sections", - "hide_friend_suggestions", "hide_suggested_friend_stories", "hide_friends", "hide_suggested", "hide_for_you") { requireRestart() } + val hideStorySuggestions = multiple("hide_story_suggestions", "hide_friend_suggestions", "hide_suggested_friend_stories") { requireRestart() } val hideUiComponents = multiple("hide_ui_components", "hide_voice_record_button", "hide_stickers_button", diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/data/SnapEnums.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/data/SnapEnums.kt @@ -142,4 +142,20 @@ enum class FriendLinkType(val value: Int, val shortName: String) { return entries.firstOrNull { it.value == value } ?: MUTUAL } } +} + +enum class MixerStoryType( + val index: Int, +) { + UNKNOWN(-1), + SUBSCRIPTIONS(2), + DISCOVER(3), + FRIENDS(5), + MY_STORIES(6); + + companion object { + fun fromIndex(index: Int): MixerStoryType { + return entries.firstOrNull { it.index == index } ?: UNKNOWN + } + } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/MixerStories.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/MixerStories.kt @@ -0,0 +1,119 @@ +package me.rhunk.snapenhance.core.features.impl + +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import me.rhunk.snapenhance.common.data.StoryData +import me.rhunk.snapenhance.common.data.MixerStoryType +import me.rhunk.snapenhance.common.util.protobuf.ProtoEditor +import me.rhunk.snapenhance.core.event.events.impl.NetworkApiRequestEvent +import me.rhunk.snapenhance.core.features.Feature +import me.rhunk.snapenhance.core.features.FeatureLoadParams +import java.nio.ByteBuffer +import kotlin.coroutines.suspendCoroutine +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi + +class MixerStories : Feature("MixerStories", loadParams = FeatureLoadParams.INIT_SYNC) { + @OptIn(ExperimentalEncodingApi::class) + override fun init() { + val disableDiscoverSections by context.config.global.disableStorySections + + fun canRemoveDiscoverSection(id: Int): Boolean { + val storyType = MixerStoryType.fromIndex(id) + return (storyType == MixerStoryType.SUBSCRIPTIONS && disableDiscoverSections.contains("following")) || + (storyType == MixerStoryType.DISCOVER && disableDiscoverSections.contains("discover")) || + (storyType == MixerStoryType.FRIENDS && disableDiscoverSections.contains("friends")) + } + + context.event.subscribe(NetworkApiRequestEvent::class) { event -> + fun cancelRequest() { + runBlocking { + suspendCoroutine { + context.httpServer.ensureServerStarted()?.let { server -> + event.url = "http://127.0.0.1:${server.port}" + it.resumeWith(Result.success(Unit)) + } ?: run { + event.canceled = true + it.resumeWith(Result.success(Unit)) + } + } + } + } + + if (event.url.endsWith("readreceipt-indexer/batchuploadreadreceipts")) { + if (context.config.messaging.anonymousStoryViewing.get()) { + cancelRequest() + return@subscribe + } + if (!context.config.messaging.preventStoryRewatchIndicator.get()) return@subscribe + event.hookRequestBuffer { buffer -> + ProtoEditor(buffer).apply { + edit { + get(2).removeIf { + it.toReader().getVarInt(7, 4) == 1L + } + } + }.toByteArray() + } + return@subscribe + } + + if (event.url.endsWith("df-mixer-prod/stories") || + event.url.endsWith("df-mixer-prod/batch_stories") || + event.url.endsWith("df-mixer-prod/soma/stories") || + event.url.endsWith("df-mixer-prod/soma/batch_stories") + ) { + event.onSuccess { buffer -> + val editor = ProtoEditor(buffer ?: return@onSuccess) + editor.edit { + editEach(3) { + val sectionType = firstOrNull(10)?.toReader()?.getVarInt(1)?.toInt() ?: return@editEach + + if (sectionType == MixerStoryType.FRIENDS.index && context.config.experimental.storyLogger.get()) { + val storyMap = mutableMapOf<String, MutableList<StoryData>>() + + firstOrNull(3)?.toReader()?.eachBuffer(3) { + followPath(36) { + eachBuffer(1) data@{ + val userId = getString(8, 1) ?: return@data + + storyMap.getOrPut(userId) { + mutableListOf() + }.add(StoryData( + url = getString(2, 2)?.substringBefore("?") ?: return@data, + postedAt = getVarInt(3) ?: -1L, + createdAt = getVarInt(27) ?: -1L, + key = Base64.decode(getString(2, 5) ?: return@data), + iv = Base64.decode(getString(2, 4) ?: return@data) + )) + } + } + } + + context.coroutineScope.launch { + storyMap.forEach { (userId, stories) -> + stories.forEach { story -> + runCatching { + context.bridgeClient.getMessageLogger().addStory(userId, story.url, story.postedAt, story.createdAt, story.key, story.iv) + }.onFailure { + context.log.error("Failed to log story", it) + } + } + } + } + } + + if (canRemoveDiscoverSection(sectionType)) { + remove(3) + addBuffer(3, byteArrayOf()) + } + } + } + + setArg(2, ByteBuffer.wrap(editor.toByteArray())) + } + return@subscribe + } + } + } +}+ \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/Stories.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/Stories.kt @@ -1,101 +0,0 @@ -package me.rhunk.snapenhance.core.features.impl - -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import me.rhunk.snapenhance.common.data.StoryData -import me.rhunk.snapenhance.common.util.protobuf.ProtoEditor -import me.rhunk.snapenhance.common.util.protobuf.ProtoReader -import me.rhunk.snapenhance.core.event.events.impl.NetworkApiRequestEvent -import me.rhunk.snapenhance.core.features.Feature -import me.rhunk.snapenhance.core.features.FeatureLoadParams -import java.nio.ByteBuffer -import kotlin.coroutines.suspendCoroutine -import kotlin.io.encoding.Base64 -import kotlin.io.encoding.ExperimentalEncodingApi - -class Stories : Feature("Stories", loadParams = FeatureLoadParams.INIT_SYNC) { - @OptIn(ExperimentalEncodingApi::class) - override fun init() { - val disablePublicStories by context.config.global.disablePublicStories - val storyLogger by context.config.experimental.storyLogger - - context.event.subscribe(NetworkApiRequestEvent::class) { event -> - fun cancelRequest() { - runBlocking { - suspendCoroutine { - context.httpServer.ensureServerStarted()?.let { server -> - event.url = "http://127.0.0.1:${server.port}" - it.resumeWith(Result.success(Unit)) - } ?: run { - event.canceled = true - it.resumeWith(Result.success(Unit)) - } - } - } - } - - if (event.url.endsWith("readreceipt-indexer/batchuploadreadreceipts")) { - if (context.config.messaging.anonymousStoryViewing.get()) { - cancelRequest() - return@subscribe - } - if (!context.config.messaging.preventStoryRewatchIndicator.get()) return@subscribe - event.hookRequestBuffer { buffer -> - ProtoEditor(buffer).apply { - edit { - get(2).removeIf { - it.toReader().getVarInt(7, 4) == 1L - } - } - }.toByteArray() - } - return@subscribe - } - if (disablePublicStories && (event.url.endsWith("df-mixer-prod/stories") || event.url.endsWith("df-mixer-prod/batch_stories"))) { - event.onSuccess { buffer -> - val payload = ProtoEditor(buffer ?: return@onSuccess).apply { - edit(3) { remove(3) } - }.toByteArray() - setArg(2, ByteBuffer.wrap(payload)) - } - return@subscribe - } - - if (storyLogger && event.url.endsWith("df-mixer-prod/soma/batch_stories")) { - event.onSuccess { buffer -> - val stories = mutableMapOf<String, MutableList<StoryData>>() - val reader = ProtoReader(buffer ?: return@onSuccess) - reader.followPath(3, 3) { - eachBuffer(3) { - followPath(36) { - eachBuffer(1) data@{ - val userId = getString(8, 1) ?: return@data - - stories.getOrPut(userId) { - mutableListOf() - }.add(StoryData( - url = getString(2, 2)?.substringBefore("?") ?: return@data, - postedAt = getVarInt(3) ?: -1L, - createdAt = getVarInt(27) ?: -1L, - key = Base64.decode(getString(2, 5) ?: return@data), - iv = Base64.decode(getString(2, 4) ?: return@data) - )) - } - } - } - } - - context.coroutineScope.launch { - stories.forEach { (userId, stories) -> - stories.forEach { story -> - context.bridgeClient.getMessageLogger().addStory(userId, story.url, story.postedAt, story.createdAt, story.key, story.iv) - } - } - } - } - - return@subscribe - } - } - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/ui/UITweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/ui/UITweaks.kt @@ -42,7 +42,7 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE override fun onActivityCreate() { val blockAds by context.config.global.blockAds val hiddenElements by context.config.userInterface.hideUiComponents - val hideStorySections by context.config.userInterface.hideStorySections + val hideStorySuggestions by context.config.userInterface.hideStorySuggestions val isImmersiveCamera by context.config.camera.immersiveCameraPreview val displayMetrics = context.resources.displayMetrics @@ -77,7 +77,7 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE var friendCardFrameSize: Size? = null - context.event.subscribe(BindViewEvent::class, { hideStorySections.contains("hide_suggested_friend_stories") }) { event -> + context.event.subscribe(BindViewEvent::class, { hideStorySuggestions.contains("hide_suggested_friend_stories") }) { event -> if (event.view.id != friendCardFrame) return@subscribe val friendStoryData = event.prevModel::class.java.findFieldsToString(event.prevModel, once = true) { _, value -> @@ -105,23 +105,8 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE val viewId = event.view.id val view = event.view - if (hideStorySections.contains("hide_for_you")) { - if (viewId == getId("df_large_story", "id") || - viewId == getId("df_promoted_story", "id")) { - hideStorySection(event) - return@subscribe - } - if (viewId == getId("stories_load_progress_layout", "id")) { - event.canceled = true - } - } - - if (hideStorySections.contains("hide_friends") && viewId == getId("friend_card_frame", "id")) { - hideStorySection(event) - } - //mappings? - if (hideStorySections.contains("hide_friend_suggestions") && view.javaClass.superclass?.name?.endsWith("StackDrawLayout") == true) { + if (hideStorySuggestions.contains("hide_friend_suggestions") && view.javaClass.superclass?.name?.endsWith("StackDrawLayout") == true) { val layoutParams = view.layoutParams as? FrameLayout.LayoutParams ?: return@subscribe if (layoutParams.width == -1 && layoutParams.height == -2 && @@ -134,11 +119,6 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE } } - if (hideStorySections.contains("hide_suggested") && (viewId == getId("df_small_story", "id")) - ) { - hideStorySection(event) - } - if (blockAds && viewId == getId("df_promoted_story", "id")) { hideStorySection(event) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/manager/impl/FeatureManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/manager/impl/FeatureManager.kt @@ -10,7 +10,7 @@ import me.rhunk.snapenhance.core.features.MessagingRuleFeature import me.rhunk.snapenhance.core.features.impl.ConfigurationOverride import me.rhunk.snapenhance.core.features.impl.OperaViewerParamsOverride import me.rhunk.snapenhance.core.features.impl.ScopeSync -import me.rhunk.snapenhance.core.features.impl.Stories +import me.rhunk.snapenhance.core.features.impl.MixerStories import me.rhunk.snapenhance.core.features.impl.downloader.MediaDownloader import me.rhunk.snapenhance.core.features.impl.downloader.ProfilePictureDownloader import me.rhunk.snapenhance.core.features.impl.experiments.* @@ -113,7 +113,7 @@ class FeatureManager( BypassScreenshotDetection::class, HalfSwipeNotifier::class, DisableConfirmationDialogs::class, - Stories::class, + MixerStories::class, DisableComposerModules::class, FideliusIndicator::class, EditTextOverride::class,