mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-28 04:20:20 +02:00
feat(experimental): best friend pinning
This commit is contained in:
parent
ddf1edb35d
commit
174dca6754
@ -936,6 +936,10 @@
|
||||
"name": "No Friend Score Delay",
|
||||
"description": "Removes the delay when viewing a Friends Score"
|
||||
},
|
||||
"best_friend_pinning": {
|
||||
"name": "Best Friend Pinning",
|
||||
"description": "Allows you to pin a friend as your number one best friend. Note: only you can see your pinned best friend"
|
||||
},
|
||||
"e2ee": {
|
||||
"name": "End-To-End Encryption",
|
||||
"description": "Encrypts your messages with AES using a shared secret key\nMake sure to save your key somewhere safe!",
|
||||
|
@ -9,7 +9,8 @@ enum class BridgeFileType(val value: Int, val fileName: String, val displayName:
|
||||
MAPPINGS(1, "mappings.json", "Mappings"),
|
||||
MESSAGE_LOGGER_DATABASE(2, "message_logger.db", "Message Logger",true),
|
||||
PINNED_CONVERSATIONS(3, "pinned_conversations.txt", "Pinned Conversations"),
|
||||
SUSPEND_LOCATION_STATE(4, "suspend_location_state.txt", "Suspend Location State");
|
||||
SUSPEND_LOCATION_STATE(4, "suspend_location_state.txt", "Suspend Location State"),
|
||||
PINNED_BEST_FRIEND(5, "pinned_best_friend.txt", "Pinned Best Friend");
|
||||
|
||||
fun resolve(context: Context): File = if (isDatabase) {
|
||||
context.getDatabasePath(fileName)
|
||||
|
@ -48,6 +48,7 @@ class Experimental : ConfigContainer() {
|
||||
val infiniteStoryBoost = boolean("infinite_story_boost")
|
||||
val meoPasscodeBypass = boolean("meo_passcode_bypass")
|
||||
val noFriendScoreDelay = boolean("no_friend_score_delay") { requireRestart()}
|
||||
val bestFriendPinning = boolean("best_friend_pinning") { requireRestart(); addNotices(FeatureNotice.UNSTABLE) }
|
||||
val e2eEncryption = container("e2ee", E2EEConfig()) { requireRestart(); nativeHooks() }
|
||||
val hiddenSnapchatPlusFeatures = boolean("hidden_snapchat_plus_features") {
|
||||
addNotices(FeatureNotice.BAN_RISK, FeatureNotice.UNSTABLE)
|
||||
|
@ -167,6 +167,7 @@ class ProtoReader(private val buffer: ByteArray) {
|
||||
}
|
||||
return value
|
||||
}
|
||||
fun getFixed64(vararg ids: Int) = followPath(*ids, excludeLast = true)?.getFixed64(ids.last())
|
||||
|
||||
|
||||
fun getFixed32(id: Int): Int {
|
||||
|
@ -429,4 +429,62 @@ class DatabaseAccess(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBestFriends(): List<FriendInfo> {
|
||||
return useDatabase(DatabaseType.MAIN)?.performOperation {
|
||||
safeRawQuery(
|
||||
"SELECT * FROM Friend WHERE friendmojiCategories != ''",
|
||||
null
|
||||
)?.use { query ->
|
||||
val list = mutableListOf<FriendInfo>()
|
||||
while (query.moveToNext()) {
|
||||
val friendInfo = FriendInfo()
|
||||
try {
|
||||
friendInfo.write(query)
|
||||
} catch (_: Throwable) {}
|
||||
list.add(friendInfo)
|
||||
}
|
||||
list
|
||||
}
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
fun updatePinnedBestFriendStatus(userId: String, friendmoji: String) {
|
||||
useDatabase(DatabaseType.MAIN, writeMode = true)?.apply {
|
||||
val numberOneBestFriends = getBestFriends().filter { friend ->
|
||||
friend.friendmojiCategories?.split(",")?.any { it.startsWith("number_one") } == true
|
||||
}
|
||||
|
||||
numberOneBestFriends.forEach { friendInfo ->
|
||||
performOperation {
|
||||
update(
|
||||
"Friend",
|
||||
ContentValues().apply {
|
||||
put("friendmojiCategories", friendInfo.friendmojiCategories?.split(",")?.filter {
|
||||
it == "on_fire" || it == "birthday"
|
||||
}?.joinToString(",") ?: "")
|
||||
put("isPinnedBestFriend", 0)
|
||||
},
|
||||
"userId = ?",
|
||||
arrayOf(friendInfo.userId)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val friend = getFriendInfo(userId) ?: return@apply
|
||||
performOperation {
|
||||
update(
|
||||
"Friend",
|
||||
ContentValues().apply {
|
||||
put("friendmojiCategories", (friend.friendmojiCategories?.split(",") ?: listOf()).toMutableList().apply {
|
||||
add(friendmoji)
|
||||
}.joinToString(","))
|
||||
put("isPinnedBestFriend", 1)
|
||||
},
|
||||
"userId = ?",
|
||||
arrayOf(userId)
|
||||
)
|
||||
}
|
||||
}?.close()
|
||||
}
|
||||
}
|
@ -51,4 +51,11 @@ abstract class BridgeFileFeature(name: String, private val bridgeFileType: Bridg
|
||||
fileLines.add(line)
|
||||
updateFile()
|
||||
}
|
||||
|
||||
protected fun clear() {
|
||||
fileLines.clear()
|
||||
updateFile()
|
||||
}
|
||||
|
||||
protected fun lines() = fileLines.toList()
|
||||
}
|
@ -127,6 +127,7 @@ class FeatureManager(
|
||||
CustomStreaksExpirationFormat(),
|
||||
ComposerHooks(),
|
||||
DisableCustomTabs(),
|
||||
BestFriendPinning(),
|
||||
)
|
||||
initializeFeatures()
|
||||
}
|
||||
|
@ -0,0 +1,94 @@
|
||||
package me.rhunk.snapenhance.core.features.impl.experiments
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.FavoriteBorder
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import me.rhunk.snapenhance.common.bridge.types.BridgeFileType
|
||||
import me.rhunk.snapenhance.common.util.protobuf.ProtoReader
|
||||
import me.rhunk.snapenhance.core.event.events.impl.NetworkApiRequestEvent
|
||||
import me.rhunk.snapenhance.core.event.events.impl.UnaryCallEvent
|
||||
import me.rhunk.snapenhance.core.features.BridgeFileFeature
|
||||
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
||||
import me.rhunk.snapenhance.core.ui.triggerRootCloseTouchEvent
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.UUID
|
||||
|
||||
class BestFriendPinning: BridgeFileFeature("Best Friend Pinning", BridgeFileType.PINNED_BEST_FRIEND, loadParams = FeatureLoadParams.INIT_SYNC) {
|
||||
private fun updatePinnedBestFriendStatus() {
|
||||
lines().firstOrNull()?.trim()?.let {
|
||||
context.database.updatePinnedBestFriendStatus(it.substring(0, 36), "number_one_bf_for_two_months")
|
||||
}
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
if (!context.config.experimental.bestFriendPinning.get()) return
|
||||
reload()
|
||||
|
||||
context.event.subscribe(UnaryCallEvent::class) { event ->
|
||||
if (!event.uri.endsWith("/PinBestFriend") && !event.uri.endsWith("/UnpinBestFriend")) return@subscribe
|
||||
event.canceled = true
|
||||
val userId = ProtoReader(event.buffer).let {
|
||||
UUID(it.getFixed64(1, 1) ?: return@subscribe, it.getFixed64(1, 2)?: return@subscribe).toString()
|
||||
}
|
||||
|
||||
clear()
|
||||
put(userId)
|
||||
|
||||
updatePinnedBestFriendStatus()
|
||||
|
||||
val username = context.database.getFriendInfo(userId)?.mutableUsername ?: "Unknown"
|
||||
|
||||
context.inAppOverlay.showStatusToast(
|
||||
icon = Icons.Default.FavoriteBorder,
|
||||
"Pinned $username as best friend! Please restart the app to apply changes.",
|
||||
durationMs = 5000
|
||||
)
|
||||
|
||||
context.coroutineScope.launch(Dispatchers.Main) {
|
||||
delay(500)
|
||||
@Suppress("DEPRECATION")
|
||||
context.mainActivity!!.onBackPressed()
|
||||
context.mainActivity!!.triggerRootCloseTouchEvent()
|
||||
}
|
||||
}
|
||||
|
||||
context.event.subscribe(NetworkApiRequestEvent::class) { event ->
|
||||
if (!event.url.contains("ami/friends")) return@subscribe
|
||||
val pinnedBFF = lines().firstOrNull()?.trim() ?: return@subscribe
|
||||
|
||||
event.onSuccess { buffer ->
|
||||
val jsonObject = context.gson.fromJson(
|
||||
InputStreamReader(buffer?.inputStream() ?: return@onSuccess, Charsets.UTF_8),
|
||||
JsonObject::class.java
|
||||
).apply {
|
||||
getAsJsonArray("friends").map { it.asJsonObject }.forEach { friend ->
|
||||
if (friend.get("user_id").asString != pinnedBFF) return@forEach
|
||||
friend.add("friendmojis", JsonArray().apply {
|
||||
friend.getAsJsonArray("friendmojis").map { it.asJsonObject }.forEach { friendmoji ->
|
||||
val category = friendmoji.get("category_name").asString
|
||||
if (category == "on_fire" || category == "birthday") {
|
||||
add(friendmoji)
|
||||
}
|
||||
}
|
||||
add(JsonObject().apply {
|
||||
addProperty("category_name", "number_one_bf_for_two_months")
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
jsonObject.toString().toByteArray(Charsets.UTF_8).let {
|
||||
setArg(2, ByteBuffer.allocateDirect(it.size).apply {
|
||||
put(it)
|
||||
flip()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user