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:
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,