kopia lustrzana https://github.com/vitorpamplona/amethyst
186 wiersze
6.7 KiB
Kotlin
186 wiersze
6.7 KiB
Kotlin
package com.vitorpamplona.amethyst.service
|
|
|
|
import com.vitorpamplona.amethyst.model.AddressableNote
|
|
import com.vitorpamplona.amethyst.model.Note
|
|
import com.vitorpamplona.amethyst.service.model.BadgeAwardEvent
|
|
import com.vitorpamplona.amethyst.service.model.BadgeDefinitionEvent
|
|
import com.vitorpamplona.amethyst.service.model.BadgeProfilesEvent
|
|
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
|
|
import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent
|
|
import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
|
|
import com.vitorpamplona.amethyst.service.model.LnZapEvent
|
|
import com.vitorpamplona.amethyst.service.model.LnZapRequestEvent
|
|
import com.vitorpamplona.amethyst.service.model.LongTextNoteEvent
|
|
import com.vitorpamplona.amethyst.service.model.ReactionEvent
|
|
import com.vitorpamplona.amethyst.service.model.ReportEvent
|
|
import com.vitorpamplona.amethyst.service.model.RepostEvent
|
|
import com.vitorpamplona.amethyst.service.model.TextNoteEvent
|
|
import com.vitorpamplona.amethyst.service.relays.FeedType
|
|
import com.vitorpamplona.amethyst.service.relays.JsonFilter
|
|
import com.vitorpamplona.amethyst.service.relays.TypedFilter
|
|
import java.util.Date
|
|
|
|
object NostrSingleEventDataSource : NostrDataSource("SingleEventFeed") {
|
|
private var eventsToWatch = setOf<Note>()
|
|
private var addressesToWatch = setOf<Note>()
|
|
|
|
private fun createTagToAddressFilter(): List<TypedFilter>? {
|
|
val addressesToWatch = eventsToWatch.filter { it.address() != null } + addressesToWatch
|
|
|
|
if (addressesToWatch.isEmpty()) {
|
|
return null
|
|
}
|
|
|
|
val now = Date().time / 1000
|
|
|
|
return addressesToWatch.filter {
|
|
val lastTime = it.lastReactionsDownloadTime
|
|
lastTime == null || lastTime < (now - 10)
|
|
}.mapNotNull {
|
|
it.address()?.let { aTag ->
|
|
TypedFilter(
|
|
types = FeedType.values().toSet(),
|
|
filter = JsonFilter(
|
|
kinds = listOf(
|
|
TextNoteEvent.kind, LongTextNoteEvent.kind,
|
|
ReactionEvent.kind, RepostEvent.kind, ReportEvent.kind,
|
|
LnZapEvent.kind, LnZapRequestEvent.kind,
|
|
BadgeAwardEvent.kind, BadgeDefinitionEvent.kind, BadgeProfilesEvent.kind
|
|
),
|
|
tags = mapOf("a" to listOf(aTag.toTag())),
|
|
since = it.lastReactionsDownloadTime
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun createAddressFilter(): List<TypedFilter>? {
|
|
val addressesToWatch = addressesToWatch.filter { it.event == null }
|
|
|
|
if (addressesToWatch.isEmpty()) {
|
|
return null
|
|
}
|
|
|
|
val now = Date().time / 1000
|
|
|
|
return addressesToWatch.filter {
|
|
val lastTime = it.lastReactionsDownloadTime
|
|
lastTime == null || lastTime < (now - 10)
|
|
}.mapNotNull {
|
|
it.address()?.let { aTag ->
|
|
TypedFilter(
|
|
types = FeedType.values().toSet(),
|
|
filter = JsonFilter(
|
|
kinds = listOf(aTag.kind),
|
|
tags = mapOf("d" to listOf(aTag.dTag)),
|
|
authors = listOf(aTag.pubKeyHex.substring(0, 8))
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun createRepliesAndReactionsFilter(): List<TypedFilter>? {
|
|
val reactionsToWatch = eventsToWatch
|
|
|
|
if (reactionsToWatch.isEmpty()) {
|
|
return null
|
|
}
|
|
|
|
val now = Date().time / 1000
|
|
|
|
return reactionsToWatch.filter {
|
|
val lastTime = it.lastReactionsDownloadTime
|
|
lastTime == null || lastTime < (now - 10)
|
|
}.map {
|
|
TypedFilter(
|
|
types = FeedType.values().toSet(),
|
|
filter = JsonFilter(
|
|
kinds = listOf(
|
|
TextNoteEvent.kind,
|
|
LongTextNoteEvent.kind,
|
|
ReactionEvent.kind,
|
|
RepostEvent.kind,
|
|
ReportEvent.kind,
|
|
LnZapEvent.kind,
|
|
LnZapRequestEvent.kind
|
|
),
|
|
tags = mapOf("e" to listOf(it.idHex)),
|
|
since = it.lastReactionsDownloadTime
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
fun createLoadEventsIfNotLoadedFilter(): List<TypedFilter>? {
|
|
val directEventsToLoad = eventsToWatch
|
|
.filter { it.event == null }
|
|
|
|
val threadingEventsToLoad = eventsToWatch
|
|
.mapNotNull { it.replyTo }
|
|
.flatten()
|
|
.filter { it !is AddressableNote && it.event == null }
|
|
|
|
val interestedEvents =
|
|
(directEventsToLoad + threadingEventsToLoad)
|
|
.map { it.idHex }.toSet()
|
|
|
|
if (interestedEvents.isEmpty()) {
|
|
return null
|
|
}
|
|
|
|
// downloads linked events to this event.
|
|
return listOf(
|
|
TypedFilter(
|
|
types = FeedType.values().toSet(),
|
|
filter = JsonFilter(
|
|
kinds = listOf(
|
|
TextNoteEvent.kind, LongTextNoteEvent.kind, ReactionEvent.kind, RepostEvent.kind, LnZapEvent.kind, LnZapRequestEvent.kind,
|
|
ChannelMessageEvent.kind, ChannelCreateEvent.kind, ChannelMetadataEvent.kind, BadgeDefinitionEvent.kind, BadgeAwardEvent.kind, BadgeProfilesEvent.kind
|
|
),
|
|
ids = interestedEvents.toList()
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
val singleEventChannel = requestNewChannel { time ->
|
|
eventsToWatch.forEach {
|
|
it.lastReactionsDownloadTime = time
|
|
}
|
|
// Many relays operate with limits in the amount of filters.
|
|
// As information comes, the filters will be rotated to get more data.
|
|
invalidateFilters()
|
|
}
|
|
|
|
override fun updateChannelFilters() {
|
|
val reactions = createRepliesAndReactionsFilter()
|
|
val missing = createLoadEventsIfNotLoadedFilter()
|
|
val addresses = createAddressFilter()
|
|
val addressReactions = createTagToAddressFilter()
|
|
|
|
singleEventChannel.typedFilters = listOfNotNull(reactions, missing, addresses, addressReactions).flatten().ifEmpty { null }
|
|
}
|
|
|
|
fun add(eventId: Note) {
|
|
eventsToWatch = eventsToWatch.plus(eventId)
|
|
invalidateFilters()
|
|
}
|
|
|
|
fun remove(eventId: Note) {
|
|
eventsToWatch = eventsToWatch.minus(eventId)
|
|
invalidateFilters()
|
|
}
|
|
|
|
fun addAddress(aTag: Note) {
|
|
addressesToWatch = addressesToWatch.plus(aTag)
|
|
invalidateFilters()
|
|
}
|
|
|
|
fun removeAddress(aTag: Note) {
|
|
addressesToWatch = addressesToWatch.minus(aTag)
|
|
invalidateFilters()
|
|
}
|
|
}
|