mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-06 09:24:30 +02:00
feat(social): auto sync scope
This commit is contained in:
parent
8bdd7a16b4
commit
f2e49e93fb
@ -13,6 +13,7 @@ import me.rhunk.snapenhance.core.database.objects.FriendInfo
|
||||
import me.rhunk.snapenhance.core.logger.LogLevel
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingFriendInfo
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo
|
||||
import me.rhunk.snapenhance.core.messaging.SocialScope
|
||||
import me.rhunk.snapenhance.core.util.SerializableDataObject
|
||||
import me.rhunk.snapenhance.download.DownloadProcessor
|
||||
import kotlin.system.measureTimeMillis
|
||||
@ -33,25 +34,36 @@ class BridgeService : Service() {
|
||||
return BridgeBinder()
|
||||
}
|
||||
|
||||
fun triggerFriendSync(friendId: String) {
|
||||
val syncedFriend = syncCallback.syncFriend(friendId)
|
||||
if (syncedFriend == null) {
|
||||
remoteSideContext.log.error("Failed to sync friend $friendId")
|
||||
return
|
||||
fun triggerScopeSync(scope: SocialScope, id: String, updateOnly: Boolean = false) {
|
||||
val modDatabase = remoteSideContext.modDatabase
|
||||
val syncedObject = when (scope) {
|
||||
SocialScope.FRIEND -> {
|
||||
if (updateOnly && modDatabase.getFriendInfo(id) == null) return
|
||||
syncCallback.syncFriend(id)
|
||||
}
|
||||
SerializableDataObject.fromJson<FriendInfo>(syncedFriend).let {
|
||||
remoteSideContext.modDatabase.syncFriend(it)
|
||||
SocialScope.GROUP -> {
|
||||
if (updateOnly && modDatabase.getGroupInfo(id) == null) return
|
||||
syncCallback.syncGroup(id)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun triggerGroupSync(groupId: String) {
|
||||
val syncedGroup = syncCallback.syncGroup(groupId)
|
||||
if (syncedGroup == null) {
|
||||
remoteSideContext.log.error("Failed to sync group $groupId")
|
||||
if (syncedObject == null) {
|
||||
remoteSideContext.log.error("Failed to sync $scope $id")
|
||||
return
|
||||
}
|
||||
SerializableDataObject.fromJson<MessagingGroupInfo>(syncedGroup).let {
|
||||
remoteSideContext.modDatabase.syncGroupInfo(it)
|
||||
|
||||
when (scope) {
|
||||
SocialScope.FRIEND -> {
|
||||
SerializableDataObject.fromJson<FriendInfo>(syncedObject).let {
|
||||
modDatabase.syncFriend(it)
|
||||
}
|
||||
}
|
||||
SocialScope.GROUP -> {
|
||||
SerializableDataObject.fromJson<MessagingGroupInfo>(syncedObject).let {
|
||||
modDatabase.syncGroupInfo(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,14 +153,14 @@ class BridgeService : Service() {
|
||||
measureTimeMillis {
|
||||
remoteSideContext.modDatabase.getFriends().map { it.userId } .forEach { friendId ->
|
||||
runCatching {
|
||||
triggerFriendSync(friendId)
|
||||
triggerScopeSync(SocialScope.FRIEND, friendId, true)
|
||||
}.onFailure {
|
||||
remoteSideContext.log.error("Failed to sync friend $friendId", it)
|
||||
}
|
||||
}
|
||||
remoteSideContext.modDatabase.getGroups().map { it.conversationId }.forEach { groupId ->
|
||||
runCatching {
|
||||
triggerGroupSync(groupId)
|
||||
triggerScopeSync(SocialScope.GROUP, groupId, true)
|
||||
}.onFailure {
|
||||
remoteSideContext.log.error("Failed to sync group $groupId", it)
|
||||
}
|
||||
@ -158,6 +170,11 @@ class BridgeService : Service() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun triggerSync(scope: String, id: String) {
|
||||
remoteSideContext.log.verbose("trigger sync for $scope $id")
|
||||
triggerScopeSync(SocialScope.getByName(scope), id, true)
|
||||
}
|
||||
|
||||
override fun passGroupsAndFriends(
|
||||
groups: List<String>,
|
||||
friends: List<String>
|
||||
|
@ -47,6 +47,7 @@ import me.rhunk.snapenhance.RemoteSideContext
|
||||
import me.rhunk.snapenhance.core.bridge.BridgeClient
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingFriendInfo
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo
|
||||
import me.rhunk.snapenhance.core.messaging.SocialScope
|
||||
import me.rhunk.snapenhance.core.util.snap.SnapWidgetBroadcastReceiverHelper
|
||||
|
||||
class AddFriendDialog(
|
||||
@ -239,7 +240,7 @@ class AddFriendDialog(
|
||||
getCurrentState = { context.modDatabase.getGroupInfo(group.conversationId) != null }
|
||||
) { state ->
|
||||
if (state) {
|
||||
context.bridgeService.triggerGroupSync(group.conversationId)
|
||||
context.bridgeService.triggerScopeSync(SocialScope.GROUP, group.conversationId)
|
||||
} else {
|
||||
context.modDatabase.deleteGroup(group.conversationId)
|
||||
}
|
||||
@ -266,7 +267,7 @@ class AddFriendDialog(
|
||||
getCurrentState = { context.modDatabase.getFriendInfo(friend.userId) != null }
|
||||
) { state ->
|
||||
if (state) {
|
||||
context.bridgeService.triggerFriendSync(friend.userId)
|
||||
context.bridgeService.triggerScopeSync(SocialScope.FRIEND, friend.userId)
|
||||
} else {
|
||||
context.modDatabase.deleteFriend(friend.userId)
|
||||
}
|
||||
|
@ -80,6 +80,11 @@ interface BridgeInterface {
|
||||
*/
|
||||
oneway void sync(SyncCallback callback);
|
||||
|
||||
/**
|
||||
* Trigger sync for an id
|
||||
*/
|
||||
void triggerSync(String scope, String id);
|
||||
|
||||
/**
|
||||
* Pass all groups and friends to be able to add them to the database
|
||||
* @param groups list of groups (MessagingGroupInfo as json string)
|
||||
|
@ -26,7 +26,8 @@ class EventDispatcher(
|
||||
context.classCache.conversationManager.hook("sendMessageWithContent", HookStage.BEFORE) { param ->
|
||||
context.event.post(SendMessageWithContentEvent(
|
||||
destinations = MessageDestinations(param.arg(0)),
|
||||
messageContent = MessageContent(param.arg(1))
|
||||
messageContent = MessageContent(param.arg(1)),
|
||||
callback = param.arg(2)
|
||||
).apply { adapter = param })?.also {
|
||||
if (it.canceled) {
|
||||
param.setResult(null)
|
||||
|
@ -14,10 +14,12 @@ import me.rhunk.snapenhance.ModContext
|
||||
import me.rhunk.snapenhance.bridge.BridgeInterface
|
||||
import me.rhunk.snapenhance.bridge.DownloadCallback
|
||||
import me.rhunk.snapenhance.bridge.SyncCallback
|
||||
import me.rhunk.snapenhance.bridge.scripting.IScripting
|
||||
import me.rhunk.snapenhance.core.BuildConfig
|
||||
import me.rhunk.snapenhance.core.bridge.types.BridgeFileType
|
||||
import me.rhunk.snapenhance.core.bridge.types.FileActionType
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingRuleType
|
||||
import me.rhunk.snapenhance.core.messaging.SocialScope
|
||||
import me.rhunk.snapenhance.data.LocalePair
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.Executors
|
||||
@ -118,24 +120,12 @@ class BridgeClient(
|
||||
|
||||
fun getApplicationApkPath() = service.getApplicationApkPath()
|
||||
|
||||
fun getAutoUpdaterTime(): Long {
|
||||
createAndReadFile(BridgeFileType.AUTO_UPDATER_TIMESTAMP, "0".toByteArray()).run {
|
||||
return if (isEmpty()) {
|
||||
0
|
||||
} else {
|
||||
String(this).toLong()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setAutoUpdaterTime(time: Long) {
|
||||
writeFile(BridgeFileType.AUTO_UPDATER_TIMESTAMP, time.toString().toByteArray())
|
||||
}
|
||||
|
||||
fun enqueueDownload(intent: Intent, callback: DownloadCallback) = service.enqueueDownload(intent, callback)
|
||||
|
||||
fun sync(callback: SyncCallback) = service.sync(callback)
|
||||
|
||||
fun triggerSync(scope: SocialScope, id: String) = service.triggerSync(scope.key, id)
|
||||
|
||||
fun passGroupsAndFriends(groups: List<String>, friends: List<String>) = service.passGroupsAndFriends(groups, friends)
|
||||
|
||||
fun getRules(targetUuid: String): List<MessagingRuleType> {
|
||||
@ -149,5 +139,5 @@ class BridgeClient(
|
||||
fun setRule(targetUuid: String, type: MessagingRuleType, state: Boolean)
|
||||
= service.setRule(targetUuid, type.key, state)
|
||||
|
||||
fun getScriptingInterface() = service.getScriptingInterface()
|
||||
fun getScriptingInterface(): IScripting = service.getScriptingInterface()
|
||||
}
|
||||
|
@ -8,8 +8,7 @@ enum class BridgeFileType(val value: Int, val fileName: String, val displayName:
|
||||
CONFIG(0, "config.json", "Config"),
|
||||
MAPPINGS(1, "mappings.json", "Mappings"),
|
||||
MESSAGE_LOGGER_DATABASE(2, "message_logger.db", "Message Logger",true),
|
||||
AUTO_UPDATER_TIMESTAMP(3, "auto_updater_timestamp.txt", "Auto Updater Timestamp"),
|
||||
PINNED_CONVERSATIONS(4, "pinned_conversations.txt", "Pinned Conversations");
|
||||
PINNED_CONVERSATIONS(3, "pinned_conversations.txt", "Pinned Conversations");
|
||||
|
||||
fun resolve(context: Context): File = if (isDatabase) {
|
||||
context.getDatabasePath(fileName)
|
||||
|
@ -3,8 +3,21 @@ package me.rhunk.snapenhance.core.eventbus.events.impl
|
||||
import me.rhunk.snapenhance.core.eventbus.events.AbstractHookEvent
|
||||
import me.rhunk.snapenhance.data.wrapper.impl.MessageContent
|
||||
import me.rhunk.snapenhance.data.wrapper.impl.MessageDestinations
|
||||
import me.rhunk.snapenhance.hook.HookStage
|
||||
import me.rhunk.snapenhance.hook.Hooker
|
||||
|
||||
class SendMessageWithContentEvent(
|
||||
val destinations: MessageDestinations,
|
||||
val messageContent: MessageContent
|
||||
) : AbstractHookEvent()
|
||||
val messageContent: MessageContent,
|
||||
private val callback: Any
|
||||
) : AbstractHookEvent() {
|
||||
|
||||
fun addCallbackResult(methodName: String, block: (args: Array<Any?>) -> Unit) {
|
||||
Hooker.ephemeralHookObjectMethod(
|
||||
callback::class.java,
|
||||
callback,
|
||||
methodName,
|
||||
HookStage.BEFORE
|
||||
) { block(it.args()) }
|
||||
}
|
||||
}
|
@ -19,7 +19,11 @@ enum class SocialScope(
|
||||
val tabRoute: String,
|
||||
) {
|
||||
FRIEND("friend", "friend_info/{id}"),
|
||||
GROUP("group", "group_info/{id}"),
|
||||
GROUP("group", "group_info/{id}");
|
||||
|
||||
companion object {
|
||||
fun getByName(name: String) = values().first { it.key == name }
|
||||
}
|
||||
}
|
||||
|
||||
enum class MessagingRuleType(
|
||||
|
@ -0,0 +1,42 @@
|
||||
package me.rhunk.snapenhance.features.impl
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import me.rhunk.snapenhance.core.eventbus.events.impl.SendMessageWithContentEvent
|
||||
import me.rhunk.snapenhance.core.messaging.SocialScope
|
||||
import me.rhunk.snapenhance.data.ContentType
|
||||
import me.rhunk.snapenhance.features.Feature
|
||||
import me.rhunk.snapenhance.features.FeatureLoadParams
|
||||
|
||||
class ScopeSync : Feature("Scope Sync", loadParams = FeatureLoadParams.INIT_SYNC) {
|
||||
companion object {
|
||||
private const val DELAY_BEFORE_SYNC = 2000L
|
||||
}
|
||||
|
||||
private val updateJobs = mutableMapOf<String, Job>()
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
private fun sync(conversationId: String) {
|
||||
context.database.getDMOtherParticipant(conversationId)?.also { participant ->
|
||||
context.bridgeClient.triggerSync(SocialScope.FRIEND, participant)
|
||||
} ?: run {
|
||||
context.bridgeClient.triggerSync(SocialScope.GROUP, conversationId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
context.event.subscribe(SendMessageWithContentEvent::class) { event ->
|
||||
if (event.messageContent.contentType != ContentType.SNAP) return@subscribe
|
||||
|
||||
event.addCallbackResult("onSuccess") {
|
||||
event.destinations.conversations.map { it.toString() }.forEach { conversationId ->
|
||||
updateJobs[conversationId]?.also { it.cancel() }
|
||||
|
||||
updateJobs[conversationId] = (coroutineScope.launch {
|
||||
delay(DELAY_BEFORE_SYNC)
|
||||
sync(conversationId)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ class SnapchatPlus: Feature("SnapchatPlus", loadParams = FeatureLoadParams.INIT_
|
||||
it.parameterTypes[0].name != "java.lang.Boolean"
|
||||
}.hook(HookStage.BEFORE) { param ->
|
||||
val instance = param.thisObject<Any>()
|
||||
val firstArg = param.args()[0]
|
||||
val firstArg = param.arg<Any>(0)
|
||||
|
||||
instance::class.java.declaredFields.filter { it.type == firstArg::class.java }.forEach {
|
||||
it.isAccessible = true
|
||||
|
@ -26,7 +26,7 @@ class HookAdapter(
|
||||
}
|
||||
|
||||
fun <T : Any> argNullable(index: Int): T? {
|
||||
return methodHookParam.args[index] as T?
|
||||
return methodHookParam.args.getOrNull(index) as T?
|
||||
}
|
||||
|
||||
fun setArg(index: Int, value: Any?) {
|
||||
@ -34,7 +34,7 @@ class HookAdapter(
|
||||
methodHookParam.args[index] = value
|
||||
}
|
||||
|
||||
fun args(): Array<Any> {
|
||||
fun args(): Array<Any?> {
|
||||
return methodHookParam.args
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ class HookAdapter(
|
||||
invokeOriginalSafe(args(), errorCallback)
|
||||
}
|
||||
|
||||
fun invokeOriginalSafe(args: Array<Any>, errorCallback: Consumer<Throwable>) {
|
||||
fun invokeOriginalSafe(args: Array<Any?>, errorCallback: Consumer<Throwable>) {
|
||||
runCatching {
|
||||
setResult(XposedBridge.invokeOriginalMethod(method(), thisObject(), args))
|
||||
}.onFailure {
|
||||
|
@ -6,34 +6,17 @@ import me.rhunk.snapenhance.features.Feature
|
||||
import me.rhunk.snapenhance.features.FeatureLoadParams
|
||||
import me.rhunk.snapenhance.features.impl.ConfigurationOverride
|
||||
import me.rhunk.snapenhance.features.impl.Messaging
|
||||
import me.rhunk.snapenhance.features.impl.ScopeSync
|
||||
import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader
|
||||
import me.rhunk.snapenhance.features.impl.downloader.ProfilePictureDownloader
|
||||
import me.rhunk.snapenhance.features.impl.experiments.AddFriendSourceSpoof
|
||||
import me.rhunk.snapenhance.features.impl.experiments.AmoledDarkMode
|
||||
import me.rhunk.snapenhance.features.impl.experiments.AppPasscode
|
||||
import me.rhunk.snapenhance.features.impl.experiments.DeviceSpooferHook
|
||||
import me.rhunk.snapenhance.features.impl.experiments.InfiniteStoryBoost
|
||||
import me.rhunk.snapenhance.features.impl.experiments.MeoPasscodeBypass
|
||||
import me.rhunk.snapenhance.features.impl.experiments.NoFriendScoreDelay
|
||||
import me.rhunk.snapenhance.features.impl.experiments.UnlimitedMultiSnap
|
||||
import me.rhunk.snapenhance.features.impl.experiments.*
|
||||
import me.rhunk.snapenhance.features.impl.privacy.DisableMetrics
|
||||
import me.rhunk.snapenhance.features.impl.privacy.PreventMessageSending
|
||||
import me.rhunk.snapenhance.features.impl.spying.AnonymousStoryViewing
|
||||
import me.rhunk.snapenhance.features.impl.spying.MessageLogger
|
||||
import me.rhunk.snapenhance.features.impl.spying.PreventReadReceipts
|
||||
import me.rhunk.snapenhance.features.impl.spying.StealthMode
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.AutoSave
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.CameraTweaks
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.DisableReplayInFF
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.DisableVideoLengthRestriction
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.GooglePlayServicesDialogs
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.LocationSpoofer
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.MediaQualityLevelOverride
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.Notifications
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.OldBitmojiSelfie
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.SendOverride
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.SnapchatPlus
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.UnlimitedSnapViewTime
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.*
|
||||
import me.rhunk.snapenhance.features.impl.ui.ClientBootstrapOverride
|
||||
import me.rhunk.snapenhance.features.impl.ui.PinConversations
|
||||
import me.rhunk.snapenhance.features.impl.ui.UITweaks
|
||||
@ -46,14 +29,16 @@ class FeatureManager(private val context: ModContext) : Manager {
|
||||
private val asyncLoadExecutorService = Executors.newFixedThreadPool(5)
|
||||
private val features = mutableListOf<Feature>()
|
||||
|
||||
private fun register(featureClass: KClass<out Feature>) {
|
||||
private fun register(vararg featureClasses: KClass<out Feature>) {
|
||||
featureClasses.forEach { clazz ->
|
||||
runCatching {
|
||||
with(featureClass.java.newInstance()) {
|
||||
context = this@FeatureManager.context
|
||||
features.add(this)
|
||||
clazz.constructors.first().call().also { feature ->
|
||||
feature.context = context
|
||||
features.add(feature)
|
||||
}
|
||||
}.onFailure {
|
||||
Logger.xposedLog("Failed to register feature ${featureClass.simpleName}", it)
|
||||
Logger.xposedLog("Failed to register feature ${clazz.simpleName}", it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,40 +48,43 @@ class FeatureManager(private val context: ModContext) : Manager {
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
register(Messaging::class)
|
||||
register(MediaDownloader::class)
|
||||
register(StealthMode::class)
|
||||
register(MenuViewInjector::class)
|
||||
register(PreventReadReceipts::class)
|
||||
register(AnonymousStoryViewing::class)
|
||||
register(MessageLogger::class)
|
||||
register(SnapchatPlus::class)
|
||||
register(DisableMetrics::class)
|
||||
register(PreventMessageSending::class)
|
||||
register(Notifications::class)
|
||||
register(AutoSave::class)
|
||||
register(UITweaks::class)
|
||||
register(ConfigurationOverride::class)
|
||||
register(SendOverride::class)
|
||||
register(UnlimitedSnapViewTime::class)
|
||||
register(DisableVideoLengthRestriction::class)
|
||||
register(MediaQualityLevelOverride::class)
|
||||
register(MeoPasscodeBypass::class)
|
||||
register(AppPasscode::class)
|
||||
register(LocationSpoofer::class)
|
||||
register(CameraTweaks::class)
|
||||
register(InfiniteStoryBoost::class)
|
||||
register(AmoledDarkMode::class)
|
||||
register(PinConversations::class)
|
||||
register(UnlimitedMultiSnap::class)
|
||||
register(DeviceSpooferHook::class)
|
||||
register(ClientBootstrapOverride::class)
|
||||
register(GooglePlayServicesDialogs::class)
|
||||
register(NoFriendScoreDelay::class)
|
||||
register(ProfilePictureDownloader::class)
|
||||
register(AddFriendSourceSpoof::class)
|
||||
register(DisableReplayInFF::class)
|
||||
register(OldBitmojiSelfie::class)
|
||||
register(
|
||||
ScopeSync::class,
|
||||
Messaging::class,
|
||||
MediaDownloader::class,
|
||||
StealthMode::class,
|
||||
MenuViewInjector::class,
|
||||
PreventReadReceipts::class,
|
||||
AnonymousStoryViewing::class,
|
||||
MessageLogger::class,
|
||||
SnapchatPlus::class,
|
||||
DisableMetrics::class,
|
||||
PreventMessageSending::class,
|
||||
Notifications::class,
|
||||
AutoSave::class,
|
||||
UITweaks::class,
|
||||
ConfigurationOverride::class,
|
||||
SendOverride::class,
|
||||
UnlimitedSnapViewTime::class,
|
||||
DisableVideoLengthRestriction::class,
|
||||
MediaQualityLevelOverride::class,
|
||||
MeoPasscodeBypass::class,
|
||||
AppPasscode::class,
|
||||
LocationSpoofer::class,
|
||||
CameraTweaks::class,
|
||||
InfiniteStoryBoost::class,
|
||||
AmoledDarkMode::class,
|
||||
PinConversations::class,
|
||||
UnlimitedMultiSnap::class,
|
||||
DeviceSpooferHook::class,
|
||||
ClientBootstrapOverride::class,
|
||||
GooglePlayServicesDialogs::class,
|
||||
NoFriendScoreDelay::class,
|
||||
ProfilePictureDownloader::class,
|
||||
AddFriendSourceSpoof::class,
|
||||
DisableReplayInFF::class,
|
||||
OldBitmojiSelfie::class,
|
||||
)
|
||||
|
||||
initializeFeatures()
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user