Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Facebook): Add Hide sponsored stories patch #3627

Merged
merged 12 commits into from
Oct 17, 2024
6 changes: 6 additions & 0 deletions api/revanced-patches.api
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,12 @@ public final class app/revanced/patches/duolingo/debug/EnableDebugMenuPatch : ap
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class app/revanced/patches/facebook/ads/story/HideStoryAdsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/facebook/ads/story/HideStoryAdsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package app.revanced.patches.facebook.ads.mainfeed

import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.facebook.ads.mainfeed.fingerprints.GetStoryVisibilityFingerprint
import app.revanced.patches.facebook.ads.mainfeed.fingerprints.GraphQLStorySponsoredDataGetterFingerprint

@Patch(
name = "Hide Sponsored stories",
PantlessCoding marked this conversation as resolved.
Show resolved Hide resolved
description = "Hide Sponsored stories in main feed.",
PantlessCoding marked this conversation as resolved.
Show resolved Hide resolved
compatiblePackages = [CompatiblePackage("com.facebook.katana")]
)
@Suppress("unused")
object HideSponsoredStoriesPatch : BytecodePatch(setOf(GetStoryVisibilityFingerprint, GraphQLStorySponsoredDataGetterFingerprint)) {
override fun execute(context: BytecodeContext) {
GetStoryVisibilityFingerprint.result?.apply {

// Get the sponsored data model getter
PantlessCoding marked this conversation as resolved.
Show resolved Hide resolved
val sponsoredDataModelGetter = GraphQLStorySponsoredDataGetterFingerprint.result ?: throw GraphQLStorySponsoredDataGetterFingerprint.exception;
PantlessCoding marked this conversation as resolved.
Show resolved Hide resolved

// Hide stories with sponsored data defined
// Check if param type is GraphQLStory
// If so, calling the sponsoredDataModelGetter, only sponsored content has non-null data
// Sponsored Stories gets their visibility forced to StoryVisibility.GONE
PantlessCoding marked this conversation as resolved.
Show resolved Hide resolved
mutableMethod.addInstructionsWithLabels(scanResult.patternScanResult!!.startIndex, """
instance-of v0, p0, Lcom/facebook/graphql/model/GraphQLStory;
if-eqz v0, :resume_normal
invoke-virtual {p0}, Lcom/facebook/graphql/model/GraphQLStory;->${sponsoredDataModelGetter.method.name}()${sponsoredDataModelGetter.method.returnType}
move-result-object v0
if-eqz v0, :resume_normal
const-string v0, "GONE"
return-object v0
:resume_normal
nop
""")
} ?: throw GetStoryVisibilityFingerprint.exception
PantlessCoding marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package app.revanced.patches.facebook.ads.mainfeed.fingerprints

import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Annotation
import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue

internal object GetStoryVisibilityFingerprint : MethodFingerprint(
returnType = "Ljava/lang/String;",
accessFlags = (AccessFlags.PUBLIC or AccessFlags.STATIC),
opcodes = listOf(
Opcode.INSTANCE_OF,
Opcode.IF_NEZ,
Opcode.INSTANCE_OF,
Opcode.IF_NEZ,
Opcode.INSTANCE_OF,
Opcode.IF_NEZ,
Opcode.CONST
),
strings = listOf("This should not be called for base class object"),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package app.revanced.patches.facebook.ads.mainfeed.fingerprints

import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Annotation
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i
import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue

internal object GraphQLStorySponsoredDataGetterFingerprint : MethodFingerprint(
customFingerprint = { methodDef, classDef ->
// All methods within initialise a generic model with the same instructions apart from the value of those two constants
PantlessCoding marked this conversation as resolved.
Show resolved Hide resolved
classDef.type == "Lcom/facebook/graphql/model/GraphQLStory;" &&
(methodDef.implementation?.instructions?.elementAt(1) as? Instruction31i)?.narrowLiteral == -132939024 &&
PantlessCoding marked this conversation as resolved.
Show resolved Hide resolved
(methodDef.implementation?.instructions?.elementAt(2) as? Instruction31i)?.narrowLiteral == 341202575
PantlessCoding marked this conversation as resolved.
Show resolved Hide resolved
},
)
Loading