mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 16:57:39 +00:00
Separating Reports from Metadata watching
This commit is contained in:
+2
@@ -24,6 +24,7 @@ import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.loaders.UserLoaderSubAssembler
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.watchers.UserReportsSubAssembler
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.watchers.UserWatcherSubAssembler
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
|
||||
|
||||
@@ -40,6 +41,7 @@ class UserFinderFilterAssembler(
|
||||
listOf(
|
||||
UserLoaderSubAssembler(client, ::allKeys),
|
||||
UserWatcherSubAssembler(client, ::allKeys),
|
||||
UserReportsSubAssembler(client, ::allKeys),
|
||||
)
|
||||
|
||||
override fun invalidateFilters() = group.forEach { it.invalidateFilters() }
|
||||
|
||||
+16
-12
@@ -35,17 +35,21 @@ fun filterReportsToKeysFromTrusted(
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
if (targets.isEmpty() || trustedAccounts.isEmpty()) return emptyList()
|
||||
val sortedTargets = targets.sorted()
|
||||
return trustedAccounts.map {
|
||||
RelayBasedFilter(
|
||||
relay = it.key,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = ReportKindList,
|
||||
authors = it.value.sorted(),
|
||||
tags = mapOf("p" to sortedTargets),
|
||||
since = since?.get(it.key)?.time,
|
||||
),
|
||||
)
|
||||
val sortedTargets = mapOf("p" to targets.sorted())
|
||||
return trustedAccounts.mapNotNull {
|
||||
if (it.value.isNotEmpty()) {
|
||||
RelayBasedFilter(
|
||||
relay = it.key,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = ReportKindList,
|
||||
authors = it.value.sorted(),
|
||||
tags = sortedTargets,
|
||||
since = since?.get(it.key)?.time,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.watchers
|
||||
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.relayClient.eoseManagers.SingleSubEoseManager
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderQueryState
|
||||
import com.vitorpamplona.amethyst.service.relays.EOSEAccountFast
|
||||
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.ammolite.relays.filters.MutableTime
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.utils.mapOfSet
|
||||
import kotlin.collections.flatten
|
||||
|
||||
class UserReportsSubAssembler(
|
||||
client: NostrClient,
|
||||
allKeys: () -> Set<UserFinderQueryState>,
|
||||
) : SingleSubEoseManager<UserFinderQueryState>(client, allKeys) {
|
||||
var lastUsersOnFilter: Set<User> = emptySet()
|
||||
|
||||
/**
|
||||
* This assembler saves the EOSE per user key. That EOSE includes their metadata, etc
|
||||
* and reports, but only from trusted accounts (follows of all logged in users).
|
||||
*/
|
||||
var latestEOSEs: EOSEAccountFast<User> = EOSEAccountFast<User>(2000)
|
||||
|
||||
override fun newEose(
|
||||
relay: NormalizedRelayUrl,
|
||||
time: Long,
|
||||
) {
|
||||
lastUsersOnFilter.forEach {
|
||||
latestEOSEs.newEose(it, relay, time)
|
||||
}
|
||||
super.newEose(relay, time)
|
||||
}
|
||||
|
||||
override fun updateFilter(
|
||||
keys: List<UserFinderQueryState>,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? {
|
||||
if (keys.isEmpty()) return null
|
||||
|
||||
lastUsersOnFilter = keys.mapTo(mutableSetOf()) { it.user }
|
||||
|
||||
if (lastUsersOnFilter.isEmpty()) return null
|
||||
|
||||
val accounts = keys.mapTo(mutableSetOf()) { it.account }
|
||||
|
||||
val trustedAccounts =
|
||||
mapOfSet {
|
||||
accounts.map { it.followsPerRelay.value }.forEach {
|
||||
add(it)
|
||||
}
|
||||
}
|
||||
|
||||
return groupByRelayPresence(lastUsersOnFilter, latestEOSEs, trustedAccounts.keys)
|
||||
.map { group ->
|
||||
val groupIds = group.map { it.pubkeyHex }.toSet()
|
||||
if (groupIds.isNotEmpty()) {
|
||||
val minEOSEs = findMinimumEOSEsForUsers(group, latestEOSEs)
|
||||
filterReportsToKeysFromTrusted(groupIds, trustedAccounts, minEOSEs)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}.flatten()
|
||||
}
|
||||
|
||||
fun groupByRelayPresence(
|
||||
users: Iterable<User>,
|
||||
eoseCache: EOSEAccountFast<User>,
|
||||
inRelays: Set<NormalizedRelayUrl>,
|
||||
): Collection<List<User>> =
|
||||
users
|
||||
.groupBy {
|
||||
eoseCache
|
||||
.since(it)
|
||||
?.keys
|
||||
?.intersect(inRelays)
|
||||
?.hashCode()
|
||||
}.values
|
||||
.map {
|
||||
// important to keep in order otherwise the Relay thinks the filter has changed and we REQ again
|
||||
it.sortedBy { it.pubkeyHex }
|
||||
}
|
||||
|
||||
fun findMinimumEOSEsForUsers(
|
||||
users: List<User>,
|
||||
eoseCache: EOSEAccountFast<User>,
|
||||
): SincePerRelayMap {
|
||||
val minLatestEOSEs = mutableMapOf<NormalizedRelayUrl, MutableTime>()
|
||||
|
||||
users.forEach {
|
||||
eoseCache.since(it)?.forEach {
|
||||
val minEose = minLatestEOSEs[it.key]
|
||||
if (minEose == null) {
|
||||
minLatestEOSEs.put(it.key, it.value.copy())
|
||||
} else {
|
||||
minEose.updateIfOlder(it.value.time)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return minLatestEOSEs
|
||||
}
|
||||
|
||||
override fun distinct(key: UserFinderQueryState) = key.user
|
||||
}
|
||||
+1
-10
@@ -29,7 +29,6 @@ import com.vitorpamplona.ammolite.relays.filters.MutableTime
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.utils.mapOfSet
|
||||
import kotlin.collections.flatten
|
||||
|
||||
class UserWatcherSubAssembler(
|
||||
@@ -67,20 +66,12 @@ class UserWatcherSubAssembler(
|
||||
|
||||
if (lastUsersOnFilter.isEmpty()) return null
|
||||
|
||||
val trustedAccounts =
|
||||
mapOfSet {
|
||||
keys.mapTo(mutableSetOf()) { it.account }.map { it.followsPerRelay.value }.forEach {
|
||||
add(it)
|
||||
}
|
||||
}
|
||||
|
||||
return groupByRelayPresence(lastUsersOnFilter, latestEOSEs)
|
||||
.map { group ->
|
||||
val groupIds = group.map { it.pubkeyHex }.toSet()
|
||||
if (groupIds.isNotEmpty()) {
|
||||
val minEOSEs = findMinimumEOSEsForUsers(group, latestEOSEs)
|
||||
filterUserMetadataForKey(groupIds, minEOSEs) +
|
||||
filterReportsToKeysFromTrusted(groupIds, trustedAccounts, minEOSEs)
|
||||
filterUserMetadataForKey(groupIds, minEOSEs)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user