mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-13 05:37:48 +02:00
fix: hide story sections
This commit is contained in:
@ -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"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -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(
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
Reference in New Issue
Block a user