mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-13 13:47:47 +02:00
Merge branch 'dev' into dev_gh
This commit is contained in:
@ -143,6 +143,7 @@ class FeatureManager(
|
||||
FriendNotes(),
|
||||
DoubleTapChatAction(),
|
||||
SnapScoreChanges(),
|
||||
DisableSnapModeRestrictions(),
|
||||
)
|
||||
|
||||
features.values.toList().forEach { feature ->
|
||||
|
@ -104,6 +104,8 @@ class ComposerHooks: Feature("ComposerHooks") {
|
||||
override fun init() {
|
||||
if (config.globalState != true) return
|
||||
|
||||
val nativeBridgeClass = runCatching { findClass("com.snapchat.client.valdi.NativeBridge") }.getOrNull() ?: findClass("com.snapchat.client.composer.NativeBridge")
|
||||
|
||||
val importedFunctions = mutableMapOf<String, Any?>()
|
||||
|
||||
fun composerFunction(name: String, block: ComposerMarshaller.() -> Unit) {
|
||||
@ -118,7 +120,8 @@ class ComposerHooks: Feature("ComposerHooks") {
|
||||
"operaDownloadButton" to context.config.downloader.operaDownloadButton.get(),
|
||||
"bypassCameraRollLimit" to config.bypassCameraRollLimit.get(),
|
||||
"showFirstCreatedUsername" to config.showFirstCreatedUsername.get(),
|
||||
"composerLogs" to config.composerLogs.get()
|
||||
"composerLogs" to config.composerLogs.get(),
|
||||
"customSelfDestructSnapDelay" to config.customSelfDestructSnapDelay.get(),
|
||||
))
|
||||
}
|
||||
|
||||
@ -172,7 +175,8 @@ class ComposerHooks: Feature("ComposerHooks") {
|
||||
context.native.setComposerLoader("""
|
||||
const i = setInterval(() => {
|
||||
try {
|
||||
require('composer_core/src/DeviceBridge').getDisplayWidth();
|
||||
const _runtimeName = "${if (nativeBridgeClass.name == "com.snapchat.client.valdi.NativeBridge") "valdi" else "composer"}";
|
||||
require(_runtimeName + '_core/src/DeviceBridge').getDisplayWidth();
|
||||
clearInterval(i);
|
||||
(() => { const _getImportsFunctionName = "$getImportsFunctionName"; $loaderScript })();
|
||||
} catch (e) {}
|
||||
@ -195,16 +199,14 @@ class ComposerHooks: Feature("ComposerHooks") {
|
||||
}
|
||||
}
|
||||
|
||||
findClass("com.snapchat.client.composer.NativeBridge").apply {
|
||||
hook("registerNativeModuleFactory", HookStage.BEFORE) { param ->
|
||||
val moduleFactory = param.argNullable<Any>(1) ?: return@hook
|
||||
if (moduleFactory.javaClass.getMethod("getModulePath").invoke(moduleFactory)?.toString()?.contains("DeviceBridge") != true) return@hook
|
||||
Hooker.ephemeralHookObjectMethod(moduleFactory.javaClass, moduleFactory, "loadModule", HookStage.AFTER) { methodParam ->
|
||||
val result = methodParam.getResult() as? MutableMap<String, Any?> ?: return@ephemeralHookObjectMethod
|
||||
result[getImportsFunctionName] = newComposerFunction {
|
||||
pushUntyped(importedFunctions)
|
||||
true
|
||||
}
|
||||
nativeBridgeClass.hook("registerNativeModuleFactory", HookStage.BEFORE) { param ->
|
||||
val moduleFactory = param.argNullable<Any>(1) ?: return@hook
|
||||
if (moduleFactory.javaClass.getMethod("getModulePath").invoke(moduleFactory)?.toString()?.contains("DeviceBridge") != true) return@hook
|
||||
Hooker.ephemeralHookObjectMethod(moduleFactory.javaClass, moduleFactory, "loadModule", HookStage.AFTER) { methodParam ->
|
||||
val result = methodParam.getResult() as? MutableMap<String, Any?> ?: return@ephemeralHookObjectMethod
|
||||
result[getImportsFunctionName] = newComposerFunction {
|
||||
pushUntyped(importedFunctions)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
package me.rhunk.snapenhance.core.features.impl.tweaks
|
||||
|
||||
import me.rhunk.snapenhance.core.features.Feature
|
||||
import me.rhunk.snapenhance.core.util.dataBuilder
|
||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
||||
import me.rhunk.snapenhance.core.util.hook.hookConstructor
|
||||
|
||||
class DisableSnapModeRestrictions: Feature("Disable Snap Mode Restrictions") {
|
||||
override fun init() {
|
||||
if (!context.config.messaging.disableSnapModeRestrictions.get()) return
|
||||
|
||||
findClass("com.snapchat.client.messaging.SnapModeInfo").hookConstructor(HookStage.AFTER) { param ->
|
||||
param.thisObject<Any>().dataBuilder {
|
||||
set("mSelfDestructSnapDurationMs", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,8 +3,12 @@ package me.rhunk.snapenhance.core.features.impl.ui
|
||||
import me.rhunk.snapenhance.core.features.Feature
|
||||
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.core.util.ktx.getObjectField
|
||||
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
|
||||
import me.rhunk.snapenhance.mapper.impl.StreaksExpirationMapper
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
class CustomStreaksExpirationFormat: Feature("CustomStreaksExpirationFormat") {
|
||||
@ -12,44 +16,68 @@ class CustomStreaksExpirationFormat: Feature("CustomStreaksExpirationFormat") {
|
||||
return this.toString().padStart(2, '0')
|
||||
}
|
||||
|
||||
private val streakCache = ConcurrentHashMap<String, Pair<Int, Long>>()
|
||||
|
||||
override fun init() {
|
||||
context.classCache.feedEntry.hookConstructor(HookStage.AFTER) { param ->
|
||||
val conversationId = SnapUUID(param.thisObject<Any>().getObjectField("mConversationId")).toString()
|
||||
|
||||
val streakMetadata = param.thisObject<Any>().getObjectField("mStreakMetadata") ?: apply {
|
||||
if (streakCache.containsKey(conversationId)) streakCache.remove(conversationId)
|
||||
return@hookConstructor
|
||||
}
|
||||
|
||||
streakCache.put(conversationId, streakMetadata.getObjectField("mCount") as Int to
|
||||
streakMetadata.getObjectField("mExpirationTimestampMs") as Long)
|
||||
}
|
||||
|
||||
onNextActivityCreate {
|
||||
val expirationFormat by context.config.experimental.customStreaksExpirationFormat
|
||||
if (expirationFormat.isNotEmpty() || context.config.userInterface.streakExpirationInfo.get()) {
|
||||
context.mappings.useMapper(StreaksExpirationMapper::class) {
|
||||
runCatching {
|
||||
simpleStreaksFormatterClass.getAsClass()?.hook(formatSimpleStreaksTextMethod.get() ?: return@useMapper, HookStage.BEFORE) { param ->
|
||||
param.setResult(null)
|
||||
simpleStreaksFormatterClass.getAsClass()?.hook(
|
||||
formatSimpleStreaksTextMethod.get() ?: return@useMapper,
|
||||
HookStage.AFTER
|
||||
) { param ->
|
||||
val result = param.getResult() as? String ?: return@hook
|
||||
|
||||
streakCache[param.arg<String>(1)]?.also { (streakCount, expirationTime) ->
|
||||
if (expirationTime <= 0L) return@also
|
||||
|
||||
if (expirationFormat.isEmpty()) {
|
||||
val remainingTime = (expirationTime - System.currentTimeMillis()).milliseconds.inWholeHours
|
||||
var emojiIndex = result.indexOfFirst { it.code > 127 }.takeIf { it != -1 }
|
||||
?.let { it + 2 }
|
||||
|
||||
if (emojiIndex == null) {
|
||||
emojiIndex = result.length
|
||||
}
|
||||
|
||||
param.setResult(
|
||||
result.substring(0, emojiIndex) + remainingTime + result.substring(emojiIndex)
|
||||
)
|
||||
return@hook
|
||||
}
|
||||
|
||||
val delta = (expirationTime - System.currentTimeMillis()).milliseconds
|
||||
|
||||
val hourGlassEmoji =
|
||||
if (delta.inWholeMilliseconds in 1..(15.hours.inWholeMilliseconds)) if (expirationTime % 2 == 0L) "\u23F3" else "\u231B" else ""
|
||||
|
||||
param.setResult(
|
||||
expirationFormat
|
||||
.replace("%c", streakCount.toString())
|
||||
.replace("%e", hourGlassEmoji)
|
||||
.replace("%d", delta.inWholeDays.toString())
|
||||
.replace("%h", (delta.inWholeHours % 24).padZero())
|
||||
.replace("%m", (delta.inWholeMinutes % 60).padZero())
|
||||
.replace("%s", (delta.inWholeSeconds % 60).padZero())
|
||||
.replace("%w", delta.toString())
|
||||
)
|
||||
}
|
||||
}.onFailure {
|
||||
context.log.warn("Failed to hook simpleStreaksFormatterClass : " + it.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (expirationFormat.isEmpty()) return@onNextActivityCreate
|
||||
|
||||
context.mappings.useMapper(StreaksExpirationMapper::class) {
|
||||
streaksFormatterClass.getAsClass()?.hook(formatStreaksTextMethod.get() ?: return@useMapper, HookStage.AFTER) { param ->
|
||||
val streaksCount = param.argNullable(2) ?: 0
|
||||
val streaksExpiration = param.argNullable<Any>(3) ?: return@hook
|
||||
|
||||
val hourGlassTimeRemaining = streaksExpiration.getObjectField(hourGlassTimeRemainingField.get() ?: return@hook) as? Long ?: return@hook
|
||||
val expirationTime = streaksExpiration.getObjectField(expirationTimeField.get() ?: return@hook) as? Long ?: return@hook
|
||||
val delta = (expirationTime - System.currentTimeMillis()).milliseconds
|
||||
|
||||
val hourGlassEmoji = if (delta.inWholeMilliseconds in 1..hourGlassTimeRemaining) if (expirationTime % 2 == 0L) "\u23F3" else "\u231B" else ""
|
||||
|
||||
param.setResult(expirationFormat
|
||||
.replace("%c", streaksCount.toString())
|
||||
.replace("%e", hourGlassEmoji)
|
||||
.replace("%d", delta.inWholeDays.toString())
|
||||
.replace("%h", (delta.inWholeHours % 24).padZero())
|
||||
.replace("%m", (delta.inWholeMinutes % 60).padZero())
|
||||
.replace("%s", (delta.inWholeSeconds % 60).padZero())
|
||||
.replace("%w", delta.toString())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -38,8 +38,7 @@ class MenuViewInjector : Feature("MenuViewInjector") {
|
||||
onNextActivityCreate(defer = true) {
|
||||
menuMap.forEach { it.value.init() }
|
||||
|
||||
val chatActionMenu = context.resources.getIdentifier("chat_action_menu", "id")
|
||||
val hasV2ActionMenu = { context.feature(COFOverride::class).hasActionMenuV2 }
|
||||
val hasV2ActionMenu = { true }
|
||||
|
||||
context.event.subscribe(AddViewEvent::class) { event ->
|
||||
menuMap.forEach { it.value.onViewAdded(event) }
|
||||
|
Reference in New Issue
Block a user