mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-12 13:17:42 +02:00
feat: native hooks config
- fix GalleryMediaSendOverride false positive
This commit is contained in:
@ -75,26 +75,12 @@
|
||||
|
||||
"features": {
|
||||
"notices": {
|
||||
"unstable": "This feature is unstable and may not work as expected",
|
||||
"may_ban": "This feature may get you banned\nUse at your own risk",
|
||||
"may_break_internal_behavior": "This feature may break Snapchat internal behavior",
|
||||
"may_cause_crashes": "This feature may cause crashes"
|
||||
"unstable": "This is unstable and may not work as expected",
|
||||
"may_ban": "This may get you banned\nUse at your own risk",
|
||||
"may_break_internal_behavior": "This may break Snapchat internal behavior",
|
||||
"may_cause_crashes": "This may cause crashes"
|
||||
},
|
||||
"properties": {
|
||||
"spoof": {
|
||||
"name": "Spoof",
|
||||
"description": "Spoof your information",
|
||||
"properties": {
|
||||
"location": {
|
||||
"name": "Location",
|
||||
"description": "Spoof your location"
|
||||
},
|
||||
"device": {
|
||||
"name": "Device",
|
||||
"description": "Spoof your device"
|
||||
}
|
||||
}
|
||||
},
|
||||
"downloader": {
|
||||
"name": "Downloader",
|
||||
"description": "Download Snaps and Stories",
|
||||
@ -249,10 +235,6 @@
|
||||
"name": "Disable Metrics",
|
||||
"description": "Disables some analytics data sent to Snapchat"
|
||||
},
|
||||
"disable_bitmoji": {
|
||||
"name": "Disable Bitmoji",
|
||||
"description": "Disables friends profile bitmoji for the whole app"
|
||||
},
|
||||
"block_ads": {
|
||||
"name": "Block Ads",
|
||||
"description": "Prevent ads from being displayed"
|
||||
@ -331,6 +313,34 @@
|
||||
"name": "Experimental",
|
||||
"description": "Experimental features",
|
||||
"properties": {
|
||||
"native_hooks": {
|
||||
"name": "Native Hooks",
|
||||
"description": "Unsafe features that hook into Snapchat's native code",
|
||||
"properties": {
|
||||
"disable_bitmoji": {
|
||||
"name": "Disable Bitmoji",
|
||||
"description": "Disables friends profile bitmoji"
|
||||
},
|
||||
"fix_gallery_media_override": {
|
||||
"name": "Fix Gallery Media Override",
|
||||
"description": "Fixes various issues with the Gallery Media Send Override feature (e.g. save snaps in chat)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"spoof": {
|
||||
"name": "Spoof",
|
||||
"description": "Spoof your information",
|
||||
"properties": {
|
||||
"location": {
|
||||
"name": "Location",
|
||||
"description": "Spoof your location"
|
||||
},
|
||||
"device": {
|
||||
"name": "Device",
|
||||
"description": "Spoof your device"
|
||||
}
|
||||
}
|
||||
},
|
||||
"app_passcode": {
|
||||
"name": "App Passcode",
|
||||
"description": "Sets a passcode to lock the app"
|
||||
|
@ -124,16 +124,12 @@ class ModContext {
|
||||
|
||||
fun reloadConfig() {
|
||||
modConfig.loadFromBridge(bridgeClient)
|
||||
runCatching {
|
||||
native.loadConfig(
|
||||
NativeConfig(
|
||||
disableBitmoji = config.global.disableBitmoji.get(),
|
||||
disableMetrics = config.global.disableMetrics.get()
|
||||
)
|
||||
native.loadNativeConfig(
|
||||
NativeConfig(
|
||||
disableBitmoji = config.experimental.nativeHooks.disableBitmoji.get(),
|
||||
disableMetrics = config.global.disableMetrics.get()
|
||||
)
|
||||
}.onFailure {
|
||||
Logger.xposedLog("Failed to load native config", it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun getConfigLocale(): String {
|
||||
|
@ -99,6 +99,7 @@ class SnapEnhance {
|
||||
measureTime {
|
||||
with(appContext) {
|
||||
reloadConfig()
|
||||
initNative()
|
||||
withContext(appContext.coroutineDispatcher) {
|
||||
translation.userLocale = getConfigLocale()
|
||||
translation.loadFromBridge(bridgeClient)
|
||||
@ -121,18 +122,6 @@ class SnapEnhance {
|
||||
private fun onActivityCreate() {
|
||||
measureTime {
|
||||
with(appContext) {
|
||||
runCatching {
|
||||
native.initOnce(appContext.androidContext.classLoader)
|
||||
native.nativeUnaryCallCallback = { request ->
|
||||
event.post(UnaryCallEvent(request.uri, request.buffer))?.also {
|
||||
request.buffer = it.buffer
|
||||
request.canceled = it.canceled
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
Logger.xposedLog("Failed to init native", it)
|
||||
}
|
||||
|
||||
features.onActivityCreate()
|
||||
actionManager.init()
|
||||
}
|
||||
@ -141,6 +130,21 @@ class SnapEnhance {
|
||||
}
|
||||
}
|
||||
|
||||
private fun initNative() {
|
||||
// don't initialize native when not logged in
|
||||
if (!appContext.database.hasArroyo()) return
|
||||
appContext.native.apply {
|
||||
if (appContext.config.experimental.nativeHooks.globalState != true) return@apply
|
||||
initOnce(appContext.androidContext.classLoader)
|
||||
nativeUnaryCallCallback = { request ->
|
||||
appContext.event.post(UnaryCallEvent(request.uri, request.buffer))?.also {
|
||||
request.buffer = it.buffer
|
||||
request.canceled = it.canceled
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun syncRemote() {
|
||||
val database = appContext.database
|
||||
|
||||
|
@ -11,8 +11,8 @@ class OpenMap: AbstractAction("action.open_map") {
|
||||
val mapActivityIntent = Intent()
|
||||
mapActivityIntent.setClassName(BuildConfig.APPLICATION_ID, "me.rhunk.snapenhance.ui.MapActivity")
|
||||
mapActivityIntent.putExtra("location", Bundle().apply {
|
||||
putDouble("latitude", context.config.spoof.location.latitude.get().toDouble())
|
||||
putDouble("longitude", context.config.spoof.location.longitude.get().toDouble())
|
||||
putDouble("latitude", context.config.experimental.spoof.location.latitude.get().toDouble())
|
||||
putDouble("longitude", context.config.experimental.spoof.location.longitude.get().toDouble())
|
||||
})
|
||||
|
||||
context.mainActivity!!.startActivityForResult(mapActivityIntent, 0x1337)
|
||||
|
@ -3,6 +3,8 @@ package me.rhunk.snapenhance.core.config.impl
|
||||
import me.rhunk.snapenhance.core.config.ConfigContainer
|
||||
|
||||
class Experimental : ConfigContainer() {
|
||||
val nativeHooks = container("native_hooks", NativeHooks()) { icon = "Memory" }
|
||||
val spoof = container("spoof", Spoof()) { icon = "Fingerprint" }
|
||||
val appPasscode = string("app_passcode")
|
||||
val appLockOnResume = boolean("app_lock_on_resume")
|
||||
val infiniteStoryBoost = boolean("infinite_story_boost")
|
||||
|
@ -8,7 +8,6 @@ class Global : ConfigContainer() {
|
||||
val snapchatPlus = boolean("snapchat_plus") { addNotices(FeatureNotice.MAY_BAN) }
|
||||
val autoUpdater = unique("auto_updater", "EVERY_LAUNCH", "DAILY", "WEEKLY").apply { set("DAILY") }
|
||||
val disableMetrics = boolean("disable_metrics")
|
||||
val disableBitmoji = boolean("disable_bitmoji") { addNotices(FeatureNotice.UNSTABLE) }
|
||||
val blockAds = boolean("block_ads")
|
||||
val disableVideoLengthRestrictions = boolean("disable_video_length_restrictions") { addNotices(FeatureNotice.MAY_BAN) }
|
||||
val disableGooglePlayDialogs = boolean("disable_google_play_dialogs")
|
||||
|
@ -0,0 +1,8 @@
|
||||
package me.rhunk.snapenhance.core.config.impl
|
||||
|
||||
import me.rhunk.snapenhance.core.config.ConfigContainer
|
||||
|
||||
class NativeHooks: ConfigContainer(hasGlobalState = true) {
|
||||
val disableBitmoji = boolean("disable_bitmoji")
|
||||
val fixGalleryMediaOverride = boolean("fix_gallery_media_override")
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package me.rhunk.snapenhance.core.config.impl
|
||||
|
||||
import me.rhunk.snapenhance.core.config.ConfigContainer
|
||||
import me.rhunk.snapenhance.core.config.FeatureNotice
|
||||
|
||||
class RootConfig : ConfigContainer() {
|
||||
val downloader = container("downloader", DownloaderConfig()) { icon = "Download"}
|
||||
@ -10,6 +11,7 @@ class RootConfig : ConfigContainer() {
|
||||
val rules = container("rules", Rules()) { icon = "Rule" }
|
||||
val camera = container("camera", Camera()) { icon = "Camera"}
|
||||
val streaksReminder = container("streaks_reminder", StreaksReminderConfig()) { icon = "Alarm" }
|
||||
val experimental = container("experimental", Experimental()) { icon = "Science" }
|
||||
val spoof = container("spoof", Spoof()) { icon = "Fingerprint" }
|
||||
val experimental = container("experimental", Experimental()) { icon = "Science"; addNotices(
|
||||
FeatureNotice.UNSTABLE, FeatureNotice.MAY_CAUSE_CRASHES
|
||||
) }
|
||||
}
|
@ -12,7 +12,7 @@ class MessageSender(
|
||||
private val context: ModContext,
|
||||
) {
|
||||
companion object {
|
||||
val redSnapProto: (Boolean) -> ByteArray = {hasAudio ->
|
||||
val redSnapProto: () -> ByteArray = {
|
||||
ProtoWriter().apply {
|
||||
from(11, 5) {
|
||||
from(1) {
|
||||
@ -24,7 +24,7 @@ class MessageSender(
|
||||
addVarInt(6, 0)
|
||||
}
|
||||
from(2) {
|
||||
addVarInt(5, if (hasAudio) 1 else 0)
|
||||
addVarInt(5, 1) // audio by default
|
||||
addBuffer(6, byteArrayOf())
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,10 @@ import me.rhunk.snapenhance.hook.Hooker
|
||||
|
||||
class DeviceSpooferHook: Feature("device_spoofer", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) {
|
||||
override fun asyncOnActivityCreate() {
|
||||
if (context.config.spoof.globalState != true) return
|
||||
if (context.config.experimental.spoof.globalState != true) return
|
||||
|
||||
val fingerprint by context.config.spoof.device.fingerprint
|
||||
val androidId by context.config.spoof.device.androidId
|
||||
val fingerprint by context.config.experimental.spoof.device.fingerprint
|
||||
val androidId by context.config.experimental.spoof.device.androidId
|
||||
|
||||
if (fingerprint.isNotEmpty()) {
|
||||
val fingerprintClass = android.os.Build::class.java
|
||||
|
@ -1,5 +1,6 @@
|
||||
package me.rhunk.snapenhance.features.impl.tweaks
|
||||
|
||||
import me.rhunk.snapenhance.Constants
|
||||
import me.rhunk.snapenhance.core.eventbus.events.impl.SendMessageWithContentEvent
|
||||
import me.rhunk.snapenhance.core.eventbus.events.impl.UnaryCallEvent
|
||||
import me.rhunk.snapenhance.data.ContentType
|
||||
@ -11,21 +12,36 @@ import me.rhunk.snapenhance.util.protobuf.ProtoEditor
|
||||
import me.rhunk.snapenhance.util.protobuf.ProtoReader
|
||||
|
||||
class GalleryMediaSendOverride : Feature("Gallery Media Send Override", loadParams = FeatureLoadParams.INIT_SYNC) {
|
||||
private var isLastSnapSavable = false
|
||||
|
||||
override fun init() {
|
||||
val typeNames = listOf(
|
||||
val fixGalleryMediaSendOverride = context.config.experimental.nativeHooks.let {
|
||||
it.globalState == true && it.fixGalleryMediaOverride.get()
|
||||
}
|
||||
val typeNames = mutableListOf(
|
||||
"ORIGINAL",
|
||||
"SNAP",
|
||||
"LIVE_SNAP",
|
||||
"NOTE"
|
||||
).associateWith {
|
||||
).also {
|
||||
if (fixGalleryMediaSendOverride) {
|
||||
it.add("SAVABLE_SNAP")
|
||||
}
|
||||
}.associateWith {
|
||||
it
|
||||
}
|
||||
|
||||
context.event.subscribe(UnaryCallEvent::class) { event ->
|
||||
context.event.subscribe(UnaryCallEvent::class, { fixGalleryMediaSendOverride }) { event ->
|
||||
if (event.uri != "/messagingcoreservice.MessagingCoreService/CreateContentMessage") return@subscribe
|
||||
if (!isLastSnapSavable) return@subscribe
|
||||
ProtoReader(event.buffer).also {
|
||||
// only affect snaps
|
||||
if (!it.containsPath(*Constants.ARROYO_MEDIA_CONTAINER_PROTO_PATH, 11)) return@subscribe
|
||||
}
|
||||
isLastSnapSavable = false
|
||||
|
||||
event.buffer = ProtoEditor(event.buffer).apply {
|
||||
//remove the mas view time
|
||||
edit(4, 4, 11, 5, 2) {
|
||||
//remove the max view time
|
||||
edit(*Constants.ARROYO_MEDIA_CONTAINER_PROTO_PATH, 11, 5, 2) {
|
||||
remove(8)
|
||||
addBuffer(6, byteArrayOf())
|
||||
}
|
||||
@ -43,7 +59,6 @@ class GalleryMediaSendOverride : Feature("Gallery Media Send Override", loadPara
|
||||
context.event.subscribe(SendMessageWithContentEvent::class, {
|
||||
context.config.messaging.galleryMediaSendOverride.get()
|
||||
}) { event ->
|
||||
|
||||
val localMessageContent = event.messageContent
|
||||
if (localMessageContent.contentType != ContentType.EXTERNAL_MEDIA) return@subscribe
|
||||
|
||||
@ -70,9 +85,12 @@ class GalleryMediaSendOverride : Feature("Gallery Media Send Override", loadPara
|
||||
}
|
||||
|
||||
when (overrideType) {
|
||||
"SNAP", "LIVE_SNAP" -> {
|
||||
"SNAP", "SAVABLE_SNAP" -> {
|
||||
localMessageContent.contentType = ContentType.SNAP
|
||||
localMessageContent.content = MessageSender.redSnapProto(overrideType == "LIVE_SNAP")
|
||||
localMessageContent.content = MessageSender.redSnapProto()
|
||||
if (overrideType == "SAVABLE_SNAP") {
|
||||
isLastSnapSavable = true
|
||||
}
|
||||
}
|
||||
|
||||
"NOTE" -> {
|
||||
|
@ -14,7 +14,7 @@ class LocationSpoofer: Feature("LocationSpoof", loadParams = FeatureLoadParams.A
|
||||
val bundle = intent.getBundleExtra("location") ?: return@hook
|
||||
param.setResult(null)
|
||||
|
||||
with(context.config.spoof.location) {
|
||||
with(context.config.experimental.spoof.location) {
|
||||
latitude.set(bundle.getFloat("latitude"))
|
||||
longitude.set(bundle.getFloat("longitude"))
|
||||
|
||||
@ -22,10 +22,10 @@ class LocationSpoofer: Feature("LocationSpoof", loadParams = FeatureLoadParams.A
|
||||
}
|
||||
}
|
||||
|
||||
if (context.config.spoof.location.globalState != true) return
|
||||
if (context.config.experimental.spoof.location.globalState != true) return
|
||||
|
||||
val latitude by context.config.spoof.location.latitude
|
||||
val longitude by context.config.spoof.location.longitude
|
||||
val latitude by context.config.experimental.spoof.location.latitude
|
||||
val longitude by context.config.experimental.spoof.location.longitude
|
||||
|
||||
val locationClass = android.location.Location::class.java
|
||||
val locationManagerClass = android.location.LocationManager::class.java
|
||||
|
Reference in New Issue
Block a user