diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json index 97435152d..35edec1d5 100644 --- a/common/src/main/assets/lang/en_US.json +++ b/common/src/main/assets/lang/en_US.json @@ -881,7 +881,6 @@ "spotlight": "Spotlight" }, "add_friend_source_spoof": { - "added_by_quick_add": "By Quick Add", "added_by_username": "By Username", "added_by_mention": "By Mention", "added_by_group_chat": "By Group Chat", diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Experimental.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Experimental.kt index 953e9e26a..a403a0c58 100644 --- a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Experimental.kt +++ b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Experimental.kt @@ -43,7 +43,6 @@ class Experimental : ConfigContainer() { requireRestart() } val addFriendSourceSpoof = unique("add_friend_source_spoof", - "added_by_quick_add", "added_by_username", "added_by_mention", "added_by_group_chat", 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 index f8bf93d0e..1d401d82d 100644 --- a/common/src/main/kotlin/me/rhunk/snapenhance/common/data/SnapEnums.kt +++ b/common/src/main/kotlin/me/rhunk/snapenhance/common/data/SnapEnums.kt @@ -173,4 +173,48 @@ enum class QuotedMessageContentStatus { JOINEDAFTERORIGINALMESSAGESENT, UNAVAILABLE, STORYMEDIADELETEDBYPOSTER +} + +enum class FriendAddSource( + val id: Int +) { + UNKNOWN(0), + PHONE(1), + USERNAME(2), + QR_CODE(3), + ADDED_ME_BACK(4), + NEARBY(5), + SUGGESTED(6), + OFFICIAL_STORY_SEARCH(7), + DEEP_LINK(8), + INVITE(9), + STORY_CHROME(10), + SHARED_USERNAME(11), + SHARED_STORY(12), + GROUP_CHAT(13), + SHAZAM(14), + MOB(15), + FEATURED_OFFICIAL_STORY(16), + OUR_STORY(17), + INFLUENCER_RECOMMENDATION(18), + DISPLAY_NAME(198), + TEST(20), + MENTION(21), + SUBSCRIPTION(22), + MENTION_STICKER(23), + SNAPCODE_STICKER(24), + SPOTLIGHT(25), + PUBLIC_PROFILE(26), + LENS(27), + CHAT(28), + SNAP_ANYONE(29), + COMMUNITY(30), + NEARBY_FRIENDS(31), + SEARCH(32); + + companion object { + fun fromId(id: Int): FriendAddSource { + return entries.firstOrNull { it.id == id } ?: UNKNOWN + } + } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/action/impl/BulkMessagingAction.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/action/impl/BulkMessagingAction.kt index 475b4c797..ea4e2f54c 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/action/impl/BulkMessagingAction.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/action/impl/BulkMessagingAction.kt @@ -445,7 +445,7 @@ class BulkMessagingAction : AbstractAction() { private fun removeFriend(userId: String) { context.mappings.useMapper(FriendRelationshipChangerMapper::class) { val friendRelationshipChangerInstance = context.feature(AddFriendSourceSpoof::class).friendRelationshipChangerInstance!! - val removeMethod = removeFriendClass.getAsClass()?.methods?.first { + val removeMethod = friendshipRelationshipChangerKtx.getAsClass()?.methods?.first { it.name == removeFriendMethod.getAsString() } ?: throw Exception("Failed to find removeFriend method") diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/AddFriendSourceSpoof.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/AddFriendSourceSpoof.kt index 32873b37c..e9d905df9 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/AddFriendSourceSpoof.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/AddFriendSourceSpoof.kt @@ -1,9 +1,11 @@ package me.rhunk.snapenhance.core.features.impl.experiments +import me.rhunk.snapenhance.common.data.FriendAddSource +import me.rhunk.snapenhance.common.util.protobuf.ProtoEditor +import me.rhunk.snapenhance.core.event.events.impl.UnaryCallEvent import me.rhunk.snapenhance.core.features.Feature import me.rhunk.snapenhance.core.features.FeatureLoadParams import me.rhunk.snapenhance.core.util.hook.HookStage -import me.rhunk.snapenhance.core.util.hook.hook import me.rhunk.snapenhance.core.util.hook.hookConstructor import me.rhunk.snapenhance.mapper.impl.FriendRelationshipChangerMapper @@ -16,51 +18,50 @@ class AddFriendSourceSpoof : Feature("AddFriendSourceSpoof", loadParams = Featur classReference.get()?.hookConstructor(HookStage.AFTER) { param -> friendRelationshipChangerInstance = param.thisObject() } + } - classReference.get()?.hook(addFriendMethod.get()!!, HookStage.BEFORE) { param -> - val spoofedSource = context.config.experimental.addFriendSourceSpoof.getNullable() ?: return@hook - - fun setEnum(index: Int, value: String) { - val enumData = param.arg(index) - enumData::class.java.enumConstants.first { it.toString() == value }.let { - param.setArg(index, it) + context.event.subscribe(UnaryCallEvent::class) { event -> + if (event.uri != "/snapchat.friending.server.FriendAction/AddFriends") return@subscribe + val spoofedSource = context.config.experimental.addFriendSourceSpoof.getNullable() ?: return@subscribe + event.buffer = ProtoEditor(event.buffer).apply { + edit { + fun setPage(value: String) { + remove(1) + addString(1, value) } - } - when (spoofedSource) { - "added_by_quick_add" -> { - setEnum(1, "PROFILE") - setEnum(2, "ADD_FRIENDS_BUTTON_ON_TOP_BAR_ON_FRIENDS_FEED") - setEnum(3, "ADDED_BY_SUGGESTED") - } - "added_by_group_chat" -> { - setEnum(1, "PROFILE") - setEnum(2, "GROUP_PROFILE") - setEnum(3, "ADDED_BY_GROUP_CHAT") - } - "added_by_username" -> { - setEnum(1, "SEARCH") - setEnum(2, "SEARCH") - setEnum(3, "ADDED_BY_USERNAME") - } - "added_by_qr_code" -> { - setEnum(1, "PROFILE") - setEnum(2, "PROFILE") - setEnum(3, "ADDED_BY_QR_CODE") - } - "added_by_mention" -> { - setEnum(1, "CONTEXT_CARDS") - setEnum(2, "CONTEXT_CARD") - setEnum(3, "ADDED_BY_MENTION") - } - "added_by_community" -> { - setEnum(1, "PROFILE") - setEnum(2, "PROFILE") - setEnum(3, "ADDED_BY_COMMUNITY") + editEach(2) { + remove(3) // remove suggestion token + fun setSource(source: FriendAddSource) { + remove(2) + addVarInt(2, source.id) + } + + when (spoofedSource) { + "added_by_group_chat" -> { + setPage("group_profile") + setSource(FriendAddSource.GROUP_CHAT) + } + "added_by_username" -> { + setPage("search") + setSource(FriendAddSource.USERNAME) + } + "added_by_qr_code" -> { + setPage("scan_snapcode") + setSource(FriendAddSource.QR_CODE) + } + "added_by_mention" -> { + setPage("context_card") + setSource(FriendAddSource.MENTION) + } + "added_by_community" -> { + setPage("profile") + setSource(FriendAddSource.COMMUNITY) + } + } } - else -> return@hook } - } + }.toByteArray() } } } \ No newline at end of file diff --git a/mapper/src/main/kotlin/me/rhunk/snapenhance/mapper/impl/FriendRelationshipChangerMapper.kt b/mapper/src/main/kotlin/me/rhunk/snapenhance/mapper/impl/FriendRelationshipChangerMapper.kt index 051405859..397af4c43 100644 --- a/mapper/src/main/kotlin/me/rhunk/snapenhance/mapper/impl/FriendRelationshipChangerMapper.kt +++ b/mapper/src/main/kotlin/me/rhunk/snapenhance/mapper/impl/FriendRelationshipChangerMapper.kt @@ -9,9 +9,9 @@ import java.lang.reflect.Modifier class FriendRelationshipChangerMapper : AbstractClassMapper("FriendRelationshipChanger") { val classReference = classReference("class") - val addFriendMethod = string("addFriendMethod") - val removeFriendClass = classReference("removeFriendClass") + val friendshipRelationshipChangerKtx = classReference("removeFriendClass") + val addFriendMethod = string("addFriendMethod") val removeFriendMethod = string("removeFriendMethod") init { @@ -28,7 +28,6 @@ class FriendRelationshipChangerMapper : AbstractClassMapper("FriendRelationshipC this@FriendRelationshipChangerMapper.apply { classReference.set(classDef.getClassName()) - addFriendMethod.set(addFriendDexMethod.name) } return@mapper @@ -45,8 +44,19 @@ class FriendRelationshipChangerMapper : AbstractClassMapper("FriendRelationshipC getClass(it.parameterTypes[3])?.getClassName()?.endsWith("InteractionPlacementInfo") == true } ?: continue - removeFriendClass.set(classDef.getClassName()) + friendshipRelationshipChangerKtx.set(classDef.getClassName()) removeFriendMethod.set(removeFriendDexMethod.name) + + val addFriendDexMethod = classDef.methods.firstOrNull { + Modifier.isStatic(it.accessFlags) && + it.parameterTypes.size == 5 && + it.parameterTypes[1] == "Ljava/lang/String;" && + getClass(it.parameterTypes[2])?.isEnum() == true && + getClass(it.parameterTypes[4])?.isEnum() == true && + it.parameterTypes[5] == "I" + } ?: return@mapper + + addFriendMethod.set(addFriendDexMethod.name) return@mapper } }