Skip to content

Commit c7a2d02

Browse files
committed
feat(core): snap score changes
Signed-off-by: rhunk <[email protected]>
1 parent 7ded784 commit c7a2d02

File tree

8 files changed

+107
-0
lines changed

8 files changed

+107
-0
lines changed

app/src/main/kotlin/me/rhunk/snapenhance/RemoteTracker.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import me.rhunk.snapenhance.common.data.TrackerRuleEvent
88
import me.rhunk.snapenhance.common.util.toSerialized
99
import me.rhunk.snapenhance.storage.getRuleTrackerScopes
1010
import me.rhunk.snapenhance.storage.getTrackerEvents
11+
import me.rhunk.snapenhance.storage.updateFriendScore
1112

1213

1314
class RemoteTracker(
@@ -26,4 +27,8 @@ class RemoteTracker(
2627
ScopedTrackerRule(it.key, context.database.getRuleTrackerScopes(it.key.id))
2728
}).toSerialized()
2829
}
30+
31+
override fun updateFriendScore(userId: String, score: Long): Long {
32+
return context.database.updateFriendScore(userId, score)
33+
}
2934
}

app/src/main/kotlin/me/rhunk/snapenhance/storage/AppDatabase.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ class AppDatabase(
7878
"params TEXT",
7979
"actions TEXT"
8080
),
81+
"friend_scores" to listOf(
82+
"userId CHAR(36) PRIMARY KEY",
83+
"score BIGINT"
84+
),
8185
"quick_tiles" to listOf(
8286
"key VARCHAR PRIMARY KEY",
8387
"position INTEGER",

app/src/main/kotlin/me/rhunk/snapenhance/storage/Tracker.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import me.rhunk.snapenhance.common.data.TrackerRuleActionParams
99
import me.rhunk.snapenhance.common.data.TrackerRuleEvent
1010
import me.rhunk.snapenhance.common.data.TrackerScopeType
1111
import me.rhunk.snapenhance.common.util.ktx.getInteger
12+
import me.rhunk.snapenhance.common.util.ktx.getLongOrNull
1213
import me.rhunk.snapenhance.common.util.ktx.getStringOrNull
1314
import kotlin.coroutines.suspendCoroutine
1415

@@ -195,3 +196,24 @@ fun AppDatabase.getRuleTrackerScopes(ruleId: Int, limit: Int = Int.MAX_VALUE): M
195196
}
196197
return scopes
197198
}
199+
200+
fun AppDatabase.updateFriendScore(userId: String, score: Long): Long {
201+
return runBlocking {
202+
suspendCoroutine { continuation ->
203+
executeAsync {
204+
val currentScore = database.rawQuery("SELECT score FROM friend_scores WHERE userId = ?", arrayOf(userId)).use { cursor ->
205+
if (!cursor.moveToFirst()) return@use null
206+
cursor.getLongOrNull("score")
207+
}
208+
209+
if (currentScore != null) {
210+
database.execSQL("UPDATE friend_scores SET score = ? WHERE userId = ?", arrayOf(score, userId))
211+
} else {
212+
database.execSQL("INSERT INTO friend_scores (userId, score) VALUES (?, ?)", arrayOf(userId, score))
213+
}
214+
215+
continuation.resumeWith(Result.success(currentScore ?: -1))
216+
}
217+
}
218+
}
219+
}

common/src/main/aidl/me/rhunk/snapenhance/bridge/logger/TrackerInterface.aidl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ package me.rhunk.snapenhance.bridge.logger;
22

33
interface TrackerInterface {
44
String getTrackedEvents(String eventType); // returns serialized TrackerEventsResult
5+
6+
long updateFriendScore(String userId, long score); // returns old score (-1 if not found)
57
}

common/src/main/assets/lang/en_US.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,10 @@
11521152
"prevent_forced_logout": {
11531153
"name": "Prevent Forced Logout",
11541154
"description": "Prevents Snapchat from logging you out when you login on another device"
1155+
},
1156+
"snapscore_changes": {
1157+
"name": "Snapscore Changes",
1158+
"description": "Tracks changes in friends Snapscore\nUse this feature in newer versions of Snapchat only"
11551159
}
11561160
}
11571161
},

common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Experimental.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,5 @@ class Experimental : ConfigContainer() {
9494
"added_by_quick_add",
9595
) { addNotices(FeatureNotice.BAN_RISK) }
9696
val preventForcedLogout = boolean("prevent_forced_logout") { requireRestart(); addNotices(FeatureNotice.BAN_RISK, FeatureNotice.INTERNAL_BEHAVIOR); }
97+
val snapScoreChanges = boolean("snapscore_changes") { requireRestart() }
9798
}

core/src/main/kotlin/me/rhunk/snapenhance/core/features/FeatureManager.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ class FeatureManager(
142142
VoiceNoteOverride(),
143143
FriendNotes(),
144144
DoubleTapChatAction(),
145+
SnapScoreChanges(),
145146
)
146147

147148
features.values.toList().forEach { feature ->
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package me.rhunk.snapenhance.core.features.impl.experiments
2+
3+
import android.view.ViewGroup
4+
import me.rhunk.snapenhance.common.util.protobuf.ProtoReader
5+
import me.rhunk.snapenhance.core.event.events.impl.AddViewEvent
6+
import me.rhunk.snapenhance.core.event.events.impl.UnaryCallEvent
7+
import me.rhunk.snapenhance.core.features.Feature
8+
import me.rhunk.snapenhance.core.ui.getComposerContext
9+
import me.rhunk.snapenhance.core.ui.getComposerViewNode
10+
import me.rhunk.snapenhance.core.util.ktx.getObjectField
11+
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
12+
13+
class SnapScoreChanges: Feature("Snap Score Changes") {
14+
private val scores = mutableMapOf<String, Long>()
15+
private var lastViewedUserId: String? = null
16+
17+
override fun init() {
18+
if (!context.config.experimental.snapScoreChanges.get()) return
19+
20+
context.event.subscribe(UnaryCallEvent::class) { event ->
21+
if (event.uri != "/com.snapchat.atlas.gw.AtlasGw/GetFriendsUserScore") return@subscribe
22+
23+
event.addResponseCallback {
24+
synchronized(scores) {
25+
ProtoReader(buffer).eachBuffer(1) {
26+
val friendUUID = getByteArray(1) ?: return@eachBuffer
27+
val score = getVarInt(2) ?: return@eachBuffer
28+
29+
scores[SnapUUID(friendUUID).toString()] = score
30+
}
31+
}
32+
}
33+
}
34+
35+
context.event.subscribe(AddViewEvent::class) { event ->
36+
if (event.viewClassName.endsWith("UnifiedProfileFlatlandProfileViewTopViewFrameLayout")) {
37+
val composerView = (event.view as ViewGroup).getChildAt(0) ?: return@subscribe
38+
val composerContext = composerView.getComposerContext() ?: return@subscribe
39+
40+
lastViewedUserId = composerContext.viewModel?.getObjectField("_userId")?.toString()
41+
}
42+
43+
if (event.viewClassName.endsWith("ProfileFlatlandFriendSnapScoreIdentityPillDialogView")) {
44+
event.view.post {
45+
val composerViewNode = event.view.getComposerViewNode() ?: return@post
46+
val surface = composerViewNode.getChildren().getOrNull(1) ?: return@post
47+
48+
val snapTextView = surface.getChildren().lastOrNull {
49+
it.getClassName() == "com.snap.composer.views.ComposerSnapTextView"
50+
} ?: return@post
51+
52+
53+
val currentFriendScore = scores[lastViewedUserId] ?: (event.view.getComposerContext()?.viewModel?.getObjectField("_friendSnapScore") as? Double)?.toLong() ?: return@post
54+
55+
val oldSnapScore = context.bridgeClient.getTracker().updateFriendScore(
56+
lastViewedUserId ?: return@post,
57+
currentFriendScore
58+
)
59+
60+
val diff = currentFriendScore - oldSnapScore
61+
62+
snapTextView.setAttribute("value", "${if (oldSnapScore != -1L && diff > 0) "\uD83D\uDCC8 +$diff !\n\n" else ""}Last Checked Score: ${oldSnapScore.takeIf { it != -1L } ?: "N/A"}")
63+
event.view.invalidate()
64+
}
65+
}
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)