refactor: simplify NostrClient API for beginner-friendliness

Comprehensive rename of the NostrClient relay infrastructure to use
intuitive, self-documenting names instead of protocol-level jargon.

Interface/class renames:
- INostrClient → NostrClient (interface)
- NostrClient → DefaultNostrClient (implementation)
- IRequestListener → SubscriptionListener
- IRelayClientListener → RelayConnectionListener
- IOpenNostrRequest → SubscriptionHandle
- NostrClientStaticReq → StaticSubscription
- NostrClientDynamicReq → DynamicSubscription

Method renames:
- openReqSubscription() → subscribe()
- close(subId) → unsubscribe(subId)
- send() → publish()
- queryCount() → count()
- subscribe/unsubscribe(listener) → addConnectionListener/removeConnectionListener
- renewFilters() → syncFilters()

Callback renames:
- onEose → onCaughtUp
- isLive → isRealTime
- onStartReq → onSubscriptionStarted
- onCloseReq → onSubscriptionClosed

Extension function renames:
- query() → fetchAll()
- downloadFirstEvent() → fetchFirst()
- req() → subscribe()
- reqAsFlow() → subscribeAsFlow()
- reqUntilEoseAsFlow() → fetchAsFlow()
- sendAndWaitForResponse() → publishAndConfirm()
- queryCountSuspend() → count()
- queryCountMergedHll() → countMerged()
- reqBypassingRelayLimits() → fetchAllPages()
- updateFilter() → refresh() on SubscriptionHandle

https://claude.ai/code/session_01JPcYCcRx5eZN4GvgGxGwbf
This commit is contained in:
Claude
2026-03-28 03:23:20 +00:00
parent 0054c01921
commit 4e2717c5c5
169 changed files with 888 additions and 892 deletions
@@ -22,12 +22,12 @@ package com.vitorpamplona.quartz.nip01Core.relay.client
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.PoolCounts
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.PoolEventOutbox
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.PoolRequests
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayPool
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.AuthCmd
@@ -76,11 +76,11 @@ import kotlinx.coroutines.launch
* are connected to all necessary endpoints. It also listens to relay state changes to update
* subscriptions and message retries when connections are re-established.
*/
class NostrClient(
class DefaultNostrClient(
private val websocketBuilder: WebsocketBuilder,
private val parentScope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
) : INostrClient,
IRelayClientListener,
) : NostrClient,
RelayConnectionListener,
AutoCloseable {
private val relayPool: RelayPool = RelayPool(websocketBuilder, this)
@@ -91,7 +91,7 @@ class NostrClient(
private val activeCounts: PoolCounts = PoolCounts()
private val eventOutbox: PoolEventOutbox = PoolEventOutbox()
private var listeners = setOf<IRelayClientListener>()
private var listeners = setOf<RelayConnectionListener>()
// controls the state of the client in such a way that if it is active
// new filters will be sent to the relays and a potential reconnect can
@@ -164,10 +164,10 @@ class NostrClient(
refreshConnection.tryEmit(Reconnect(onlyIfChanged, ignoreRetryDelays))
}
override fun openReqSubscription(
override fun subscribe(
subId: String,
filters: Map<NormalizedRelayUrl, List<Filter>>,
listener: IRequestListener?,
listener: SubscriptionListener?,
) {
val relaysToUpdate = activeRequests.addOrUpdate(subId, filters, listener)
@@ -188,7 +188,7 @@ class NostrClient(
}
}
override fun queryCount(
override fun count(
subId: String,
filters: Map<NormalizedRelayUrl, List<Filter>>,
) {
@@ -208,7 +208,7 @@ class NostrClient(
}
}
override fun send(
override fun publish(
event: Event,
relayList: Set<NormalizedRelayUrl>,
) {
@@ -222,7 +222,7 @@ class NostrClient(
}
}
override fun close(subId: String) {
override fun unsubscribe(subId: String) {
val relaysToUpdateReqs = activeRequests.remove(subId)
val relaysToUpdateCounts = activeCounts.remove(subId)
@@ -232,7 +232,7 @@ class NostrClient(
}
}
override fun renewFilters(relay: IRelayClient) {
override fun syncFilters(relay: IRelayClient) {
if (isActive) {
scope.launch {
activeRequests.syncState(relay.url, relay::sendOrConnectAndSync)
@@ -259,7 +259,7 @@ class NostrClient(
pingMillis: Int,
compressed: Boolean,
) {
renewFilters(relay)
syncFilters(relay)
listeners.forEach { it.onConnected(relay, pingMillis, compressed) }
}
@@ -308,13 +308,13 @@ class NostrClient(
listeners.forEach { it.onCannotConnect(relay, errorMessage) }
}
override fun subscribe(listener: IRelayClientListener) {
override fun addConnectionListener(listener: RelayConnectionListener) {
listeners = listeners.plus(listener)
}
fun isSubscribed(listener: IRelayClientListener): Boolean = listeners.contains(listener)
fun hasConnectionListener(listener: RelayConnectionListener): Boolean = listeners.contains(listener)
override fun unsubscribe(listener: IRelayClientListener) {
override fun removeConnectionListener(listener: RelayConnectionListener) {
listeners = listeners.minus(listener)
}
@@ -22,8 +22,8 @@ package com.vitorpamplona.quartz.nip01Core.relay.client
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
@@ -31,7 +31,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
interface INostrClient : AutoCloseable {
interface NostrClient : AutoCloseable {
fun connectedRelaysFlow(): StateFlow<Set<NormalizedRelayUrl>>
fun availableRelaysFlow(): StateFlow<Set<NormalizedRelayUrl>>
@@ -52,29 +52,29 @@ interface INostrClient : AutoCloseable {
* This is called every time the relay connects
* and when auth is successful
*/
fun renewFilters(relay: IRelayClient)
fun syncFilters(relay: IRelayClient)
fun openReqSubscription(
fun subscribe(
subId: String = newSubId(),
filters: Map<NormalizedRelayUrl, List<Filter>>,
listener: IRequestListener? = null,
listener: SubscriptionListener? = null,
)
fun queryCount(
fun count(
subId: String = newSubId(),
filters: Map<NormalizedRelayUrl, List<Filter>>,
)
fun close(subId: String)
fun unsubscribe(subId: String)
fun send(
fun publish(
event: Event,
relayList: Set<NormalizedRelayUrl>,
)
fun subscribe(listener: IRelayClientListener)
fun addConnectionListener(listener: RelayConnectionListener)
fun unsubscribe(listener: IRelayClientListener)
fun removeConnectionListener(listener: RelayConnectionListener)
fun getReqFiltersOrNull(subId: String): Map<NormalizedRelayUrl, List<Filter>>?
@@ -87,7 +87,7 @@ interface INostrClient : AutoCloseable {
fun activeOutboxCache(url: NormalizedRelayUrl): Set<HexKey>
}
class EmptyNostrClient : INostrClient {
class EmptyNostrClient : NostrClient {
override fun connectedRelaysFlow() = MutableStateFlow(emptySet<NormalizedRelayUrl>())
override fun availableRelaysFlow() = MutableStateFlow(emptySet<NormalizedRelayUrl>())
@@ -103,29 +103,29 @@ class EmptyNostrClient : INostrClient {
override fun isActive() = false
override fun renewFilters(relay: IRelayClient) { }
override fun syncFilters(relay: IRelayClient) { }
override fun openReqSubscription(
override fun subscribe(
subId: String,
filters: Map<NormalizedRelayUrl, List<Filter>>,
listener: IRequestListener?,
listener: SubscriptionListener?,
) { }
override fun queryCount(
override fun count(
subId: String,
filters: Map<NormalizedRelayUrl, List<Filter>>,
) { }
override fun close(subId: String) { }
override fun unsubscribe(subId: String) { }
override fun send(
override fun publish(
event: Event,
relayList: Set<NormalizedRelayUrl>,
) { }
override fun subscribe(listener: IRelayClientListener) {}
override fun addConnectionListener(listener: RelayConnectionListener) {}
override fun unsubscribe(listener: IRelayClientListener) {}
override fun removeConnectionListener(listener: RelayConnectionListener) {}
override fun getReqFiltersOrNull(subId: String): Map<NormalizedRelayUrl, List<Filter>>? = null
@@ -21,8 +21,8 @@
package com.vitorpamplona.quartz.nip01Core.relay.client.accessories
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.EventMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
@@ -32,11 +32,11 @@ import com.vitorpamplona.quartz.utils.Log
* Listens to NostrClient's onEvent messages for caching purposes.
*/
class EventCollector(
val client: INostrClient,
val client: NostrClient,
val onEvent: (event: Event, relay: IRelayClient) -> Unit,
) {
private val clientListener =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onIncomingMessage(
relay: IRelayClient,
msgStr: String,
@@ -50,12 +50,12 @@ class EventCollector(
init {
Log.d("EventCollector", "Init, Subscribe")
client.subscribe(clientListener)
client.addConnectionListener(clientListener)
}
fun destroy() {
// makes sure to run
Log.d("EventCollector", "Destroy, Unsubscribe")
client.unsubscribe(clientListener)
client.removeConnectionListener(clientListener)
}
}
@@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip01Core.relay.client.accessories
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.CountMessage
@@ -43,7 +43,7 @@ import kotlinx.coroutines.withTimeoutOrNull
* @param timeoutMs How long to wait for a response (default 15 s).
* @return The [CountResult], or `null` on timeout.
*/
suspend fun INostrClient.queryCountSuspend(
suspend fun NostrClient.count(
relay: NormalizedRelayUrl,
filter: Filter,
timeoutMs: Long = 15_000,
@@ -52,7 +52,7 @@ suspend fun INostrClient.queryCountSuspend(
val resultChannel = Channel<CountResult>(UNLIMITED)
val listener =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onIncomingMessage(
relay: IRelayClient,
msgStr: String,
@@ -64,18 +64,18 @@ suspend fun INostrClient.queryCountSuspend(
}
}
subscribe(listener)
addConnectionListener(listener)
val result =
try {
queryCount(subId = subId, filters = mapOf(relay to listOf(filter)))
count(subId = subId, filters = mapOf(relay to listOf(filter)))
withTimeoutOrNull(timeoutMs) {
resultChannel.receive()
}
} finally {
close(subId)
unsubscribe(listener)
unsubscribe(subId)
removeConnectionListener(listener)
}
resultChannel.close()
@@ -92,7 +92,7 @@ suspend fun INostrClient.queryCountSuspend(
* @param timeoutMs How long to wait for all responses (default 15 s).
* @return Map of relay -> [CountResult] for every relay that responded in time.
*/
suspend fun INostrClient.queryCountSuspend(
suspend fun NostrClient.count(
filters: Map<NormalizedRelayUrl, List<Filter>>,
timeoutMs: Long = 15_000,
): Map<NormalizedRelayUrl, CountResult> {
@@ -102,7 +102,7 @@ suspend fun INostrClient.queryCountSuspend(
val resultChannel = Channel<Pair<NormalizedRelayUrl, CountResult>>(UNLIMITED)
val listener =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onIncomingMessage(
relay: IRelayClient,
msgStr: String,
@@ -115,12 +115,12 @@ suspend fun INostrClient.queryCountSuspend(
}
}
subscribe(listener)
addConnectionListener(listener)
filters.forEach { (relay, filterList) ->
val subId = newSubId()
subIdToRelay[subId] = relay
queryCount(subId = subId, filters = mapOf(relay to filterList))
count(subId = subId, filters = mapOf(relay to filterList))
}
val results = mutableMapOf<NormalizedRelayUrl, CountResult>()
@@ -132,8 +132,8 @@ suspend fun INostrClient.queryCountSuspend(
}
}
subIdToRelay.keys.forEach { close(it) }
unsubscribe(listener)
subIdToRelay.keys.forEach { unsubscribe(it) }
removeConnectionListener(listener)
resultChannel.close()
return results
@@ -155,7 +155,7 @@ suspend fun INostrClient.queryCountSuspend(
* @param timeoutMs How long to wait for all responses (default 15 s).
* @return A merged [CountResult], or `null` if no relay responded.
*/
suspend fun INostrClient.queryCountMergedHll(
suspend fun NostrClient.countMerged(
relays: List<NormalizedRelayUrl>,
filter: Filter,
timeoutMs: Long = 15_000,
@@ -163,7 +163,7 @@ suspend fun INostrClient.queryCountMergedHll(
if (relays.isEmpty()) return null
val results =
queryCountSuspend(
count(
filters = relays.associateWith { listOf(filter) },
timeoutMs = timeoutMs,
)
@@ -22,8 +22,8 @@ package com.vitorpamplona.quartz.nip01Core.relay.client.accessories
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
@@ -31,45 +31,45 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.withTimeoutOrNull
suspend fun INostrClient.query(
suspend fun NostrClient.fetchAll(
relay: String,
filter: Filter,
timeoutMs: Long = 30_000L,
) = query(newSubId(), mapOf(RelayUrlNormalizer.normalize(relay) to listOf(filter)), timeoutMs)
) = fetchAll(newSubId(), mapOf(RelayUrlNormalizer.normalize(relay) to listOf(filter)), timeoutMs)
suspend fun INostrClient.query(
suspend fun NostrClient.fetchAll(
relay: String,
filters: List<Filter>,
timeoutMs: Long = 30_000L,
) = query(newSubId(), mapOf(RelayUrlNormalizer.normalize(relay) to filters), timeoutMs)
) = fetchAll(newSubId(), mapOf(RelayUrlNormalizer.normalize(relay) to filters), timeoutMs)
suspend fun INostrClient.query(
suspend fun NostrClient.fetchAll(
subscriptionId: String = newSubId(),
relay: String,
filters: List<Filter>,
timeoutMs: Long = 30_000L,
) = query(subscriptionId, mapOf(RelayUrlNormalizer.normalize(relay) to filters), timeoutMs)
) = fetchAll(subscriptionId, mapOf(RelayUrlNormalizer.normalize(relay) to filters), timeoutMs)
suspend fun INostrClient.query(
suspend fun NostrClient.fetchAll(
relay: NormalizedRelayUrl,
filter: Filter,
timeoutMs: Long = 30_000L,
) = query(newSubId(), mapOf(relay to listOf(filter)), timeoutMs)
) = fetchAll(newSubId(), mapOf(relay to listOf(filter)), timeoutMs)
suspend fun INostrClient.query(
suspend fun NostrClient.fetchAll(
relay: NormalizedRelayUrl,
filters: List<Filter>,
timeoutMs: Long = 30_000L,
) = query(newSubId(), mapOf(relay to filters), timeoutMs)
) = fetchAll(newSubId(), mapOf(relay to filters), timeoutMs)
suspend fun INostrClient.query(
suspend fun NostrClient.fetchAll(
subscriptionId: String = newSubId(),
relay: NormalizedRelayUrl,
filters: List<Filter>,
timeoutMs: Long = 30_000L,
) = query(subscriptionId, mapOf(relay to filters), timeoutMs)
) = fetchAll(subscriptionId, mapOf(relay to filters), timeoutMs)
suspend fun INostrClient.query(
suspend fun NostrClient.fetchAll(
subscriptionId: String = newSubId(),
filters: Map<NormalizedRelayUrl, List<Filter>>,
timeoutMs: Long = 30_000L,
@@ -82,10 +82,10 @@ suspend fun INostrClient.query(
val remaining = filters.keys.toMutableSet()
val listener =
object : IRequestListener {
object : SubscriptionListener {
override fun onEvent(
event: Event,
isLive: Boolean,
isRealTime: Boolean,
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
@@ -110,7 +110,7 @@ suspend fun INostrClient.query(
doneChannel.trySend(relay)
}
override fun onEose(
override fun onCaughtUp(
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
@@ -119,7 +119,7 @@ suspend fun INostrClient.query(
}
try {
openReqSubscription(subscriptionId, filters, listener)
subscribe(subscriptionId, filters, listener)
withTimeoutOrNull(timeoutMs) {
while (remaining.isNotEmpty()) {
@@ -128,7 +128,7 @@ suspend fun INostrClient.query(
}
}
} finally {
close(subscriptionId)
unsubscribe(subscriptionId)
doneChannel.close()
}
@@ -21,8 +21,8 @@
package com.vitorpamplona.quartz.nip01Core.relay.client.accessories
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
@@ -51,7 +51,7 @@ import kotlin.math.min
* @param onEvent Called for every event received (in page order, after each EOSE).
* @return Total number of events received across all pages.
*/
suspend fun INostrClient.reqBypassingRelayLimits(
suspend fun NostrClient.fetchAllPages(
relay: NormalizedRelayUrl,
filters: List<Filter>,
timeoutMs: Long = 30_000L,
@@ -95,10 +95,10 @@ suspend fun INostrClient.reqBypassingRelayLimits(
try {
val listener =
object : IRequestListener {
object : SubscriptionListener {
override fun onEvent(
event: Event,
isLive: Boolean,
isRealTime: Boolean,
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
@@ -120,7 +120,7 @@ suspend fun INostrClient.reqBypassingRelayLimits(
}
}
override fun onEose(
override fun onCaughtUp(
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
@@ -144,16 +144,16 @@ suspend fun INostrClient.reqBypassingRelayLimits(
}
}
openReqSubscription(subId, mapOf(relay to remainingFilters), listener)
subscribe(subId, mapOf(relay to remainingFilters), listener)
withTimeoutOrNull(timeoutMs) {
doneChannel.receive()
}
close(subId)
unsubscribe(subId)
doneChannel.close()
} finally {
close(subId)
unsubscribe(subId)
doneChannel.close()
}
@@ -168,14 +168,14 @@ suspend fun INostrClient.reqBypassingRelayLimits(
return totalEvents
}
suspend fun INostrClient.reqBypassingRelayLimits(
suspend fun NostrClient.fetchAllPages(
relay: String,
filters: List<Filter>,
timeoutMs: Long = 30_000L,
onNewPage: ((Long) -> Unit)? = null,
onEvent: (Event) -> Unit,
): Int =
reqBypassingRelayLimits(
fetchAllPages(
relay = RelayUrlNormalizer.normalize(relay),
filters = filters,
timeoutMs = timeoutMs,
@@ -21,8 +21,8 @@
package com.vitorpamplona.quartz.nip01Core.relay.client.accessories
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
@@ -31,49 +31,49 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
import kotlinx.coroutines.withTimeoutOrNull
suspend fun INostrClient.downloadFirstEvent(
suspend fun NostrClient.fetchFirst(
relay: String,
filter: Filter,
) = downloadFirstEvent(newSubId(), mapOf(RelayUrlNormalizer.normalize(relay) to listOf(filter)))
) = fetchFirst(newSubId(), mapOf(RelayUrlNormalizer.normalize(relay) to listOf(filter)))
suspend fun INostrClient.downloadFirstEvent(
suspend fun NostrClient.fetchFirst(
relay: String,
filters: List<Filter>,
) = downloadFirstEvent(newSubId(), mapOf(RelayUrlNormalizer.normalize(relay) to filters))
) = fetchFirst(newSubId(), mapOf(RelayUrlNormalizer.normalize(relay) to filters))
suspend fun INostrClient.downloadFirstEvent(
suspend fun NostrClient.fetchFirst(
subscriptionId: String = newSubId(),
relay: String,
filters: List<Filter>,
) = downloadFirstEvent(subscriptionId, mapOf(RelayUrlNormalizer.normalize(relay) to filters))
) = fetchFirst(subscriptionId, mapOf(RelayUrlNormalizer.normalize(relay) to filters))
suspend fun INostrClient.downloadFirstEvent(
suspend fun NostrClient.fetchFirst(
relay: NormalizedRelayUrl,
filter: Filter,
) = downloadFirstEvent(newSubId(), mapOf(relay to listOf(filter)))
) = fetchFirst(newSubId(), mapOf(relay to listOf(filter)))
suspend fun INostrClient.downloadFirstEvent(
suspend fun NostrClient.fetchFirst(
relay: NormalizedRelayUrl,
filters: List<Filter>,
) = downloadFirstEvent(newSubId(), mapOf(relay to filters))
) = fetchFirst(newSubId(), mapOf(relay to filters))
suspend fun INostrClient.downloadFirstEvent(
suspend fun NostrClient.fetchFirst(
subscriptionId: String = newSubId(),
relay: NormalizedRelayUrl,
filters: List<Filter>,
) = downloadFirstEvent(subscriptionId, mapOf(relay to filters))
) = fetchFirst(subscriptionId, mapOf(relay to filters))
suspend fun INostrClient.downloadFirstEvent(
suspend fun NostrClient.fetchFirst(
subscriptionId: String = newSubId(),
filters: Map<NormalizedRelayUrl, List<Filter>>,
): Event? {
val resultChannel = Channel<Event?>(UNLIMITED)
val listener =
object : IRequestListener {
object : SubscriptionListener {
override fun onEvent(
event: Event,
isLive: Boolean,
isRealTime: Boolean,
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
@@ -96,7 +96,7 @@ suspend fun INostrClient.downloadFirstEvent(
resultChannel.trySend(null)
}
override fun onEose(
override fun onCaughtUp(
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
@@ -106,13 +106,13 @@ suspend fun INostrClient.downloadFirstEvent(
val result =
try {
openReqSubscription(subscriptionId, filters, listener)
subscribe(subscriptionId, filters, listener)
withTimeoutOrNull(30000) {
resultChannel.receive()
}
} finally {
close(subscriptionId)
unsubscribe(subscriptionId)
}
resultChannel.close()
@@ -21,8 +21,8 @@
package com.vitorpamplona.quartz.nip01Core.relay.client.accessories
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.OkMessage
@@ -41,42 +41,42 @@ class Result(
)
@OptIn(DelicateCoroutinesApi::class)
suspend fun INostrClient.sendAndWaitForResponse(
suspend fun NostrClient.publishAndConfirm(
event: Event,
relayList: Set<NormalizedRelayUrl>,
timeoutInSeconds: Long = 15,
): Boolean = sendAndWaitForResponseDetailed(event, relayList, timeoutInSeconds).any { it.value }
): Boolean = publishAndConfirmDetailed(event, relayList, timeoutInSeconds).any { it.value }
/**
* Sends an event to the given relays and waits for OK responses.
* Returns per-relay results: relay URL -> accepted (true/false).
*/
@OptIn(DelicateCoroutinesApi::class)
suspend fun INostrClient.sendAndWaitForResponseDetailed(
suspend fun NostrClient.publishAndConfirmDetailed(
event: Event,
relayList: Set<NormalizedRelayUrl>,
timeoutInSeconds: Long = 15,
): Map<NormalizedRelayUrl, Boolean> {
val resultChannel = Channel<Result>(UNLIMITED)
Log.d("sendAndWaitForResponse", "Waiting for ${relayList.size} responses")
Log.d("publishAndConfirm", "Waiting for ${relayList.size} responses")
val subscription =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onCannotConnect(
relay: IRelayClient,
errorMessage: String,
) {
if (relay.url in relayList) {
resultChannel.trySend(Result(relay.url, false))
Log.d("sendAndWaitForResponse", "Error from relay ${relay.url}: $errorMessage")
Log.d("publishAndConfirm", "Error from relay ${relay.url}: $errorMessage")
}
}
override fun onDisconnected(relay: IRelayClient) {
if (relay.url in relayList) {
resultChannel.trySend(Result(relay.url, false))
Log.d("sendAndWaitForResponse", "Disconnected from relay ${relay.url}")
Log.d("publishAndConfirm", "Disconnected from relay ${relay.url}")
}
}
@@ -91,7 +91,7 @@ suspend fun INostrClient.sendAndWaitForResponseDetailed(
is OkMessage -> {
if (msg.eventId == event.id) {
resultChannel.trySend(Result(relay.url, msg.success))
Log.d("sendAndWaitForResponse", "onSendResponse Received response for ${msg.eventId} from relay ${relay.url} message ${msg.message} success ${msg.success}")
Log.d("publishAndConfirm", "onSendResponse Received response for ${msg.eventId} from relay ${relay.url} message ${msg.message} success ${msg.success}")
}
}
}
@@ -100,7 +100,7 @@ suspend fun INostrClient.sendAndWaitForResponseDetailed(
val receivedResults =
try {
subscribe(subscription)
addConnectionListener(subscription)
// subscribe before sending the result.
val resultSubscription =
@@ -123,20 +123,20 @@ suspend fun INostrClient.sendAndWaitForResponseDetailed(
receivedResults
}
send(event, relayList)
publish(event, relayList)
result
}
resultSubscription.await()
} finally {
unsubscribe(subscription)
removeConnectionListener(subscription)
}
// Clean up the channel
resultChannel.close()
Log.d("sendAndWaitForResponse", "Finished with ${receivedResults.size} results")
Log.d("publishAndConfirm", "Finished with ${receivedResults.size} results")
return receivedResults
}
@@ -21,8 +21,8 @@
package com.vitorpamplona.quartz.nip01Core.relay.client.accessories
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.OkMessage
@@ -32,11 +32,11 @@ import com.vitorpamplona.quartz.utils.Log
* Listens to NostrClient's onEvent messages for caching purposes.
*/
class RelayInsertConfirmationCollector(
val client: INostrClient,
val client: NostrClient,
val onRelayReceived: (eventId: HexKey, relay: IRelayClient) -> Unit,
) {
private val clientListener =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onIncomingMessage(
relay: IRelayClient,
msgStr: String,
@@ -50,12 +50,12 @@ class RelayInsertConfirmationCollector(
init {
Log.d("RelayInsertConfirmationCollector", "Init, Subscribe")
client.subscribe(clientListener)
client.addConnectionListener(clientListener)
}
fun destroy() {
// makes sure to run
Log.d("RelayInsertConfirmationCollector", "Destroy, Unsubscribe")
client.unsubscribe(clientListener)
client.removeConnectionListener(clientListener)
}
}
@@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip01Core.relay.client.accessories
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.AuthMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.ClosedMessage
@@ -41,14 +41,14 @@ import com.vitorpamplona.quartz.utils.Log
* Listens to NostrClient's onNotify messages from the relay
*/
class RelayLogger(
val client: INostrClient,
val client: NostrClient,
val debugSending: Boolean = false,
val debugReceiving: Boolean = false,
) {
fun logTag(url: NormalizedRelayUrl) = "Relay ${url.displayUrl()}"
private val clientListener =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onIncomingMessage(
relay: IRelayClient,
msgStr: String,
@@ -110,12 +110,12 @@ class RelayLogger(
init {
Log.d("RelayLogger", "Init, Subscribe")
client.subscribe(clientListener)
client.addConnectionListener(clientListener)
}
fun destroy() {
// makes sure to run
Log.d("RelayLogger", "Destroy, Unsubscribe")
client.unsubscribe(clientListener)
client.removeConnectionListener(clientListener)
}
}
@@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip01Core.relay.client.accessories
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.NotifyMessage
@@ -31,7 +31,7 @@ import com.vitorpamplona.quartz.utils.Log
* Listens to NostrClient's onNotify messages from the relay
*/
class RelayNotifier(
val client: INostrClient,
val client: NostrClient,
val notify: (message: String, relay: IRelayClient) -> Unit,
) {
companion object {
@@ -39,7 +39,7 @@ class RelayNotifier(
}
private val clientListener =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onIncomingMessage(
relay: IRelayClient,
msgStr: String,
@@ -54,12 +54,12 @@ class RelayNotifier(
init {
Log.d(TAG, "Init, Subscribe")
client.subscribe(clientListener)
client.addConnectionListener(clientListener)
}
fun destroy() {
// makes sure to run
Log.d(TAG, "Destroy, Unsubscribe")
client.unsubscribe(clientListener)
client.removeConnectionListener(clientListener)
}
}
@@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip01Core.relay.client.accessories
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.utils.Log
@@ -30,7 +30,7 @@ import com.vitorpamplona.quartz.utils.Log
* Listens to NostrClient's onNotify messages from the relay
*/
class RelayOfflineTracker(
val client: INostrClient,
val client: NostrClient,
) {
companion object {
const val TAG = "RelayOfflineTracker"
@@ -39,7 +39,7 @@ class RelayOfflineTracker(
var cannotConnectRelays = setOf<NormalizedRelayUrl>()
private val clientListener =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onConnected(
relay: IRelayClient,
pingMillis: Int,
@@ -58,12 +58,12 @@ class RelayOfflineTracker(
init {
Log.d(TAG, "Init, Subscribe")
client.subscribe(clientListener)
client.addConnectionListener(clientListener)
}
fun destroy() {
// makes sure to run
Log.d(TAG, "Destroy, Unsubscribe")
client.unsubscribe(clientListener)
client.removeConnectionListener(clientListener)
}
}
@@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip01Core.relay.client.auth
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.AuthMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
@@ -46,14 +46,14 @@ object EmptyIAuthStatus : IAuthStatus {
}
class RelayAuthenticator(
val client: INostrClient,
val client: NostrClient,
val scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
val signWithAllLoggedInUsers: suspend (EventTemplate<RelayAuthEvent>) -> List<RelayAuthEvent>,
) : IAuthStatus {
private val authStatus = mutableMapOf<NormalizedRelayUrl, RelayAuthStatus>()
private val clientListener =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onIncomingMessage(
relay: IRelayClient,
msgStr: String,
@@ -95,7 +95,7 @@ class RelayAuthenticator(
) {
// if this is the OK of an auth event, renew all subscriptions and resend all outgoing events.
if (authStatus[relay.url]?.checkAuthResults(msg.eventId, msg.success) == true) {
client.renewFilters(relay)
client.syncFilters(relay)
}
}
@@ -103,12 +103,12 @@ class RelayAuthenticator(
init {
Log.d("RelayAuthenticator", "Init, Subscribe")
client.subscribe(clientListener)
client.addConnectionListener(clientListener)
}
fun destroy() {
// makes sure to run
Log.d("RelayAuthenticator", "Destroy, Unsubscribe")
client.unsubscribe(clientListener)
client.removeConnectionListener(clientListener)
}
}
@@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip01Core.relay.client.counts
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.ClosedMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.CountMessage
@@ -33,14 +33,14 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.utils.Log
class RelayActiveCountStates(
val client: INostrClient,
val client: NostrClient,
) {
private var queryStates = mutableMapOf<NormalizedRelayUrl, CountQueryState<String>>()
fun subGetOrCreate(relay: NormalizedRelayUrl): CountQueryState<String> = queryStates[relay] ?: CountQueryState<String>().also { queryStates.put(relay, it) }
private val clientListener =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onConnecting(relay: IRelayClient) {
queryStates.put(relay.url, CountQueryState())
}
@@ -75,12 +75,12 @@ class RelayActiveCountStates(
init {
Log.d("RelaySubStateMachine", "Init, Subscribe")
client.subscribe(clientListener)
client.addConnectionListener(clientListener)
}
fun destroy() {
// makes sure to run
Log.d("RelaySubStateMachine", "Destroy, Unsubscribe")
client.unsubscribe(clientListener)
client.removeConnectionListener(clientListener)
}
}
@@ -24,9 +24,9 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.Command
open class RedirectRelayClientListener(
val listener: IRelayClientListener,
) : IRelayClientListener {
open class RedirectConnectionListener(
val listener: RelayConnectionListener,
) : RelayConnectionListener {
override fun onConnecting(relay: IRelayClient) {
listener.onConnecting(relay)
}
@@ -24,7 +24,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.Command
interface IRelayClientListener {
interface RelayConnectionListener {
fun onConnecting(relay: IRelayClient) {}
/**
@@ -72,4 +72,4 @@ interface IRelayClientListener {
) {}
}
object EmptyClientListener : IRelayClientListener
object EmptyConnectionListener : RelayConnectionListener
@@ -20,9 +20,9 @@
*/
package com.vitorpamplona.quartz.nip01Core.relay.client.pool
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.ReqSubStatus
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.RequestSubscriptionState
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.ClosedMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.EoseMessage
@@ -51,7 +51,7 @@ class PoolRequests {
* to what the app whants to do.
*/
private val desiredSubs = LargeCache<String, Map<NormalizedRelayUrl, List<Filter>>>()
private val desiredSubListeners = LargeCache<String, IRequestListener>()
private val desiredSubListeners = LargeCache<String, SubscriptionListener>()
val desiredRelays = MutableStateFlow(setOf<NormalizedRelayUrl>())
/**
@@ -101,7 +101,7 @@ class PoolRequests {
fun addOrUpdate(
subId: String,
filters: Map<NormalizedRelayUrl, List<Filter>>,
listener: IRequestListener?,
listener: SubscriptionListener?,
): Set<NormalizedRelayUrl> {
// saves old relays
val oldRelays = desiredSubs.get(subId)?.keys ?: emptySet()
@@ -165,15 +165,15 @@ class PoolRequests {
when (cmd) {
is ReqCmd -> {
subState(cmd.subId).onOpenReq(relay, cmd.filters)
desiredSubListeners.get(cmd.subId)?.onStartReq(
desiredSubListeners.get(cmd.subId)?.onSubscriptionStarted(
relay = relay.url,
forFilters = cmd.filters,
)
}
is CloseCmd -> {
subState(cmd.subId).onCloseReq(relay)
desiredSubListeners.get(cmd.subId)?.onCloseReq(
subState(cmd.subId).onSubscriptionClosed(relay)
desiredSubListeners.get(cmd.subId)?.onSubscriptionClosed(
relay = relay.url,
)
}
@@ -193,7 +193,7 @@ class PoolRequests {
state?.onNewEvent(relay.url)
desiredSubListeners.get(msg.subId)?.onEvent(
event = msg.event,
isLive = state?.currentState(relay.url) == ReqSubStatus.LIVE,
isRealTime = state?.currentState(relay.url) == ReqSubStatus.LIVE,
relay = relay.url,
forFilters = state?.lastKnownFilterStates(relay.url),
)
@@ -201,8 +201,8 @@ class PoolRequests {
is EoseMessage -> {
val state = relayState.get(msg.subId)
state?.onEose(relay.url)
desiredSubListeners.get(msg.subId)?.onEose(
state?.onCaughtUp(relay.url)
desiredSubListeners.get(msg.subId)?.onCaughtUp(
relay = relay.url,
forFilters = state?.lastKnownFilterStates(relay.url),
)
@@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip01Core.relay.client.pool
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.EmptyClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.EmptyConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.client.single.basic.BasicRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
@@ -56,8 +56,8 @@ import kotlinx.coroutines.flow.update
*/
class RelayPool(
val websocketBuilder: WebsocketBuilder,
val listener: IRelayClientListener = EmptyClientListener,
) : IRelayClientListener {
val listener: RelayConnectionListener = EmptyConnectionListener,
) : RelayConnectionListener {
private val relays = LargeCache<NormalizedRelayUrl, IRelayClient>()
private val _connectedRelays = MutableStateFlow<Set<NormalizedRelayUrl>>(emptySet())
@@ -21,22 +21,22 @@
package com.vitorpamplona.quartz.nip01Core.relay.client.reqs
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.utils.RandomInstance
class NostrClientDynamicReq(
val client: INostrClient,
class DynamicSubscription(
val client: NostrClient,
val filter: () -> Map<NormalizedRelayUrl, List<Filter>>,
val onEvent: (event: Event) -> Unit = {},
) : IRequestListener,
IOpenNostrRequest {
) : SubscriptionListener,
SubscriptionHandle {
val subId = RandomInstance.randomChars(10)
override fun onEvent(
event: Event,
isLive: Boolean,
isRealTime: Boolean,
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
@@ -47,16 +47,16 @@ class NostrClientDynamicReq(
* Creates or Updates the filter with relays. This method should be called
* everytime the filter changes.
*/
override fun updateFilter() = client.openReqSubscription(subId, filter(), this)
override fun refresh() = client.subscribe(subId, filter(), this)
override fun close() = client.close(subId)
override fun close() = client.unsubscribe(subId)
init {
updateFilter()
refresh()
}
}
fun INostrClient.req(
fun NostrClient.subscribe(
filters: () -> Map<NormalizedRelayUrl, List<Filter>>,
onEvent: (event: Event) -> Unit = {},
): IOpenNostrRequest = NostrClientDynamicReq(this, filters, onEvent)
): SubscriptionHandle = DynamicSubscription(this, filters, onEvent)
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip01Core.relay.client.reqs
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
@@ -37,22 +37,22 @@ import kotlinx.coroutines.flow.callbackFlow
* 3. Closes the flow (completes) when the relay sends EOSE,
* cancelling the subscription automatically via [awaitClose].
*/
fun INostrClient.reqUntilEoseAsFlow(
fun NostrClient.fetchAsFlow(
relay: String,
filters: List<Filter>,
) = reqUntilEoseAsFlow(RelayUrlNormalizer.normalize(relay), filters)
) = fetchAsFlow(RelayUrlNormalizer.normalize(relay), filters)
fun INostrClient.reqUntilEoseAsFlow(
fun NostrClient.fetchAsFlow(
relay: String,
filter: Filter,
) = reqUntilEoseAsFlow(RelayUrlNormalizer.normalize(relay), listOf(filter))
) = fetchAsFlow(RelayUrlNormalizer.normalize(relay), listOf(filter))
fun INostrClient.reqUntilEoseAsFlow(
fun NostrClient.fetchAsFlow(
relay: NormalizedRelayUrl,
filter: Filter,
) = reqUntilEoseAsFlow(relay, listOf(filter))
) = fetchAsFlow(relay, listOf(filter))
fun INostrClient.reqUntilEoseAsFlow(
fun NostrClient.fetchAsFlow(
relay: NormalizedRelayUrl,
filters: List<Filter>,
): Flow<List<Event>> =
@@ -62,10 +62,10 @@ fun INostrClient.reqUntilEoseAsFlow(
var currentEvents = listOf<Event>()
val listener =
object : IRequestListener {
object : SubscriptionListener {
override fun onEvent(
event: Event,
isLive: Boolean,
isRealTime: Boolean,
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
@@ -76,7 +76,7 @@ fun INostrClient.reqUntilEoseAsFlow(
}
}
override fun onEose(
override fun onCaughtUp(
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
@@ -84,9 +84,9 @@ fun INostrClient.reqUntilEoseAsFlow(
}
}
openReqSubscription(subId, mapOf(relay to filters), listener)
subscribe(subId, mapOf(relay to filters), listener)
awaitClose {
close(subId)
unsubscribe(subId)
}
}
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip01Core.relay.client.reqs
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
@@ -42,22 +42,22 @@ import kotlinx.coroutines.flow.callbackFlow
* - They will be ignored if they are already in the list.
* - They will be added to the beginning of the list if they are new.
*/
fun INostrClient.reqAsFlow(
fun NostrClient.subscribeAsFlow(
relay: String,
filters: List<Filter>,
) = reqAsFlow(RelayUrlNormalizer.normalize(relay), filters)
) = subscribeAsFlow(RelayUrlNormalizer.normalize(relay), filters)
fun INostrClient.reqAsFlow(
fun NostrClient.subscribeAsFlow(
relay: String,
filter: Filter,
) = reqAsFlow(RelayUrlNormalizer.normalize(relay), listOf(filter))
) = subscribeAsFlow(RelayUrlNormalizer.normalize(relay), listOf(filter))
fun INostrClient.reqAsFlow(
fun NostrClient.subscribeAsFlow(
relay: NormalizedRelayUrl,
filter: Filter,
) = reqAsFlow(relay, listOf(filter))
) = subscribeAsFlow(relay, listOf(filter))
fun INostrClient.reqAsFlow(
fun NostrClient.subscribeAsFlow(
relay: NormalizedRelayUrl,
filters: List<Filter>,
): Flow<List<Event>> =
@@ -68,10 +68,10 @@ fun INostrClient.reqAsFlow(
var currentEvents = listOf<Event>()
val listener =
object : IRequestListener {
object : SubscriptionListener {
override fun onEvent(
event: Event,
isLive: Boolean,
isRealTime: Boolean,
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
@@ -90,7 +90,7 @@ fun INostrClient.reqAsFlow(
}
}
override fun onEose(
override fun onCaughtUp(
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
@@ -98,9 +98,9 @@ fun INostrClient.reqAsFlow(
}
}
openReqSubscription(subId, mapOf(relay to filters), listener)
subscribe(subId, mapOf(relay to filters), listener)
awaitClose {
close(subId)
unsubscribe(subId)
}
}
@@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip01Core.relay.client.reqs
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.ClosedMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.EoseMessage
@@ -34,14 +34,14 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.utils.Log
class RelayActiveRequestStates(
val client: INostrClient,
val client: NostrClient,
) {
private var subStates = mutableMapOf<NormalizedRelayUrl, RequestSubscriptionState<String>>()
fun subGetOrCreate(relay: NormalizedRelayUrl): RequestSubscriptionState<String> = subStates[relay] ?: RequestSubscriptionState<String>().also { subStates.put(relay, it) }
private val clientListener =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onConnecting(relay: IRelayClient) {
subStates[relay.url] = RequestSubscriptionState()
}
@@ -53,7 +53,7 @@ class RelayActiveRequestStates(
) {
when (msg) {
is EventMessage -> subGetOrCreate(relay.url).onNewEvent(msg.subId)
is EoseMessage -> subGetOrCreate(relay.url).onEose(msg.subId)
is EoseMessage -> subGetOrCreate(relay.url).onCaughtUp(msg.subId)
is ClosedMessage -> subGetOrCreate(relay.url).onClosed(msg.subId)
}
}
@@ -66,7 +66,7 @@ class RelayActiveRequestStates(
) {
when (cmd) {
is ReqCmd -> subGetOrCreate(relay.url).onOpenReq(cmd.subId, cmd.filters)
is CloseCmd -> subGetOrCreate(relay.url).onCloseReq(cmd.subId)
is CloseCmd -> subGetOrCreate(relay.url).onSubscriptionClosed(cmd.subId)
}
}
@@ -77,12 +77,12 @@ class RelayActiveRequestStates(
init {
Log.d("RelaySubStateMachine", "Init, Subscribe")
client.subscribe(clientListener)
client.addConnectionListener(clientListener)
}
fun destroy() {
// makes sure to run
Log.d("RelaySubStateMachine", "Destroy, Unsubscribe")
client.unsubscribe(clientListener)
client.removeConnectionListener(clientListener)
}
}
@@ -57,7 +57,7 @@ class RequestSubscriptionState<T> {
}
}
fun onEose(reference: T) {
fun onCaughtUp(reference: T) {
subStates[reference] = ReqSubStatus.LIVE
}
@@ -80,7 +80,7 @@ class RequestSubscriptionState<T> {
lastKnownFilterStates[reference] = filters
}
fun onCloseReq(reference: T) {
fun onSubscriptionClosed(reference: T) {
subStates[reference] = ReqSubStatus.CLOSED
filterStates.remove(reference)
}
@@ -21,23 +21,23 @@
package com.vitorpamplona.quartz.nip01Core.relay.client.reqs
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
import com.vitorpamplona.quartz.utils.RandomInstance
class NostrClientStaticReq(
val client: INostrClient,
class StaticSubscription(
val client: NostrClient,
val filter: Map<NormalizedRelayUrl, List<Filter>>,
val onEvent: (event: Event) -> Unit = {},
) : IRequestListener,
IOpenNostrRequest {
) : SubscriptionListener,
SubscriptionHandle {
val subId = RandomInstance.randomChars(10)
override fun onEvent(
event: Event,
isLive: Boolean,
isRealTime: Boolean,
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
@@ -48,50 +48,50 @@ class NostrClientStaticReq(
* Creates or Updates the filter with relays. This method should be called
* everytime the filter changes.
*/
override fun updateFilter() = client.openReqSubscription(subId, filter, this)
override fun refresh() = client.subscribe(subId, filter, this)
override fun close() = client.close(subId)
override fun close() = client.unsubscribe(subId)
init {
updateFilter()
refresh()
}
}
fun INostrClient.req(
fun NostrClient.subscribe(
relay: NormalizedRelayUrl,
filters: List<Filter>,
onEvent: (event: Event) -> Unit = {},
): IOpenNostrRequest = NostrClientStaticReq(this, mapOf(relay to filters), onEvent)
): SubscriptionHandle = StaticSubscription(this, mapOf(relay to filters), onEvent)
fun INostrClient.req(
fun NostrClient.subscribe(
relay: NormalizedRelayUrl,
filter: Filter,
onEvent: (event: Event) -> Unit = {},
): IOpenNostrRequest = NostrClientStaticReq(this, mapOf(relay to listOf(filter)), onEvent)
): SubscriptionHandle = StaticSubscription(this, mapOf(relay to listOf(filter)), onEvent)
fun INostrClient.req(
fun NostrClient.subscribe(
relays: List<NormalizedRelayUrl>,
filters: List<Filter>,
onEvent: (event: Event) -> Unit = {},
): IOpenNostrRequest = NostrClientStaticReq(this, relays.associateWith { filters }, onEvent)
): SubscriptionHandle = StaticSubscription(this, relays.associateWith { filters }, onEvent)
fun INostrClient.req(
fun NostrClient.subscribe(
relays: List<NormalizedRelayUrl>,
filter: Filter,
onEvent: (event: Event) -> Unit = {},
): IOpenNostrRequest = NostrClientStaticReq(this, relays.associateWith { listOf(filter) }, onEvent)
): SubscriptionHandle = StaticSubscription(this, relays.associateWith { listOf(filter) }, onEvent)
// -----------------------------------
// Helper methods with relay as string
// -----------------------------------
fun INostrClient.req(
fun NostrClient.subscribe(
relay: String,
filters: List<Filter>,
onEvent: (event: Event) -> Unit = {},
): IOpenNostrRequest = NostrClientStaticReq(this, mapOf(RelayUrlNormalizer.normalize(relay) to filters), onEvent)
): SubscriptionHandle = StaticSubscription(this, mapOf(RelayUrlNormalizer.normalize(relay) to filters), onEvent)
fun INostrClient.req(
fun NostrClient.subscribe(
relay: String,
filter: Filter,
onEvent: (event: Event) -> Unit = {},
): IOpenNostrRequest = NostrClientStaticReq(this, mapOf(RelayUrlNormalizer.normalize(relay) to listOf(filter)), onEvent)
): SubscriptionHandle = StaticSubscription(this, mapOf(RelayUrlNormalizer.normalize(relay) to listOf(filter)), onEvent)
@@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip01Core.relay.client.reqs
interface IOpenNostrRequest {
fun updateFilter()
interface SubscriptionHandle {
fun refresh()
fun close()
}
@@ -24,15 +24,15 @@ import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
interface IRequestListener {
fun onEose(
interface SubscriptionListener {
fun onCaughtUp(
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {}
fun onEvent(
event: Event,
isLive: Boolean,
isRealTime: Boolean,
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {}
@@ -49,10 +49,10 @@ interface IRequestListener {
forFilters: List<Filter>?,
) {}
fun onStartReq(
fun onSubscriptionStarted(
relay: String,
forFilters: List<Filter>,
) {}
fun onCloseReq(relay: String) {}
fun onSubscriptionClosed(relay: String) {}
}
@@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip01Core.relay.client.reqs.stats
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.EventMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
@@ -31,12 +31,12 @@ import com.vitorpamplona.quartz.utils.Log
* Listens to NostrClient's onNotify messages from the relay
*/
class RelayReqStats(
val client: INostrClient,
val client: NostrClient,
) {
private val stats = ReqStatsRepository()
private val clientListener =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onIncomingMessage(
relay: IRelayClient,
msgStr: String,
@@ -56,12 +56,12 @@ class RelayReqStats(
init {
Log.d("RelaySubStats", "Init, Subscribe")
client.subscribe(clientListener)
client.addConnectionListener(clientListener)
}
fun destroy() {
// makes sure to run
Log.d("RelaySubStats", "Destroy, Unsubscribe")
client.unsubscribe(clientListener)
client.removeConnectionListener(clientListener)
}
}
@@ -21,7 +21,7 @@
package com.vitorpamplona.quartz.nip01Core.relay.client.single.basic
import com.vitorpamplona.quartz.nip01Core.core.OptimizedJsonMapper
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.client.single.basic.BasicRelayClient.Companion.DELAY_TO_RECONNECT_IN_SECS
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.Command
@@ -55,7 +55,7 @@ import kotlin.coroutines.cancellation.CancellationException
open class BasicRelayClient(
override val url: NormalizedRelayUrl,
val socketBuilder: WebsocketBuilder,
val listener: IRelayClientListener,
val listener: RelayConnectionListener,
) : IRelayClient {
companion object {
// minimum wait time to reconnect: 1 second
@@ -22,9 +22,9 @@ package com.vitorpamplona.quartz.nip01Core.relay.client.single.standalone
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.EmptyClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RedirectRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.EmptyConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RedirectConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.client.single.basic.BasicRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
@@ -46,7 +46,7 @@ import com.vitorpamplona.quartz.utils.cache.LargeCache
class StandaloneRelayClient(
url: NormalizedRelayUrl,
socketBuilder: WebsocketBuilder,
listener: IRelayClientListener = EmptyClientListener,
listener: RelayConnectionListener = EmptyConnectionListener,
) {
private val outbox = LargeCache<HexKey, Event>()
private val reqs = LargeCache<String, List<Filter>>()
@@ -56,14 +56,14 @@ class StandaloneRelayClient(
BasicRelayClient(
url,
socketBuilder,
object : RedirectRelayClientListener(listener) {
object : RedirectConnectionListener(listener) {
override fun onConnected(
relay: IRelayClient,
pingMillis: Int,
compressed: Boolean,
) {
super.onConnected(relay, pingMillis, compressed)
renewFilters()
syncFilters()
}
override fun onIncomingMessage(
@@ -83,7 +83,7 @@ class StandaloneRelayClient(
},
)
fun renewFilters() {
fun syncFilters() {
outbox.forEach { id, event ->
client.sendOrConnectAndSync(EventCmd(event))
}
@@ -21,8 +21,8 @@
package com.vitorpamplona.quartz.nip01Core.relay.client.stats
import androidx.collection.LruCache
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.ClosedMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
@@ -35,7 +35,7 @@ import com.vitorpamplona.quartz.utils.Log
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
class RelayStats(
val client: INostrClient,
val client: NostrClient,
) {
private val innerCache =
object : LruCache<NormalizedRelayUrl, RelayStat>(1000) {
@@ -47,7 +47,7 @@ class RelayStats(
fun get(url: NormalizedRelayUrl): RelayStat = innerCache[url] ?: throw IllegalArgumentException("Should never happen")
private val clientListener =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onConnecting(relay: IRelayClient) {
super.onConnecting(relay)
with(get(relay.url)) {
@@ -116,12 +116,12 @@ class RelayStats(
init {
Log.d("RelayStats", "Init, Subscribe")
client.subscribe(clientListener)
client.addConnectionListener(clientListener)
}
fun destroy() {
// makes sure to run
Log.d("RelayStats", "Destroy, Unsubscribe")
client.unsubscribe(clientListener)
client.removeConnectionListener(clientListener)
}
}
@@ -20,14 +20,14 @@
*/
package com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
data class Subscription(
val id: String = newSubId(),
val listener: IRequestListener,
val listener: SubscriptionListener,
) {
private var currentVersion: Map<NormalizedRelayUrl, List<Filter>>? = null // Inactive when null
@@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.utils.cache.LargeCache
@@ -45,7 +45,7 @@ import com.vitorpamplona.quartz.utils.cache.LargeCache
* - Dismiss subscriptions with [dismissSubscription].
*/
class SubscriptionController(
val client: INostrClient,
val client: NostrClient,
) {
private val subscriptions = LargeCache<String, Subscription>()
@@ -53,7 +53,7 @@ class SubscriptionController(
fun requestNewSubscription(
subId: String,
listener: IRequestListener,
listener: SubscriptionListener,
): Subscription = Subscription(subId, listener).also { subscriptions.put(it.id, it) }
fun dismissSubscription(subId: String) = getSub(subId)?.let { dismissSubscription(it) }
@@ -61,7 +61,7 @@ class SubscriptionController(
fun dismissSubscription(subscription: Subscription) {
subscription.reset()
subscriptions.remove(subscription.id)
client.close(subscription.id)
client.unsubscribe(subscription.id)
}
fun updateRelays() {
@@ -77,23 +77,23 @@ class SubscriptionController(
fun updateRelaysIfNeeded(
subId: String,
listener: IRequestListener,
listener: SubscriptionListener,
newFilters: Map<NormalizedRelayUrl, List<Filter>>?,
oldFilters: Map<NormalizedRelayUrl, List<Filter>>?,
) {
if (oldFilters != null) {
if (newFilters == null) {
// was active and is not active anymore, just close.
client.close(subId)
client.unsubscribe(subId)
} else {
client.openReqSubscription(subId, newFilters, listener)
client.subscribe(subId, newFilters, listener)
}
} else {
if (newFilters == null) {
// was not active and is still not active, does nothing
} else {
// was not active and becomes active, sends the entire filter.
client.openReqSubscription(subId, newFilters, listener)
client.subscribe(subId, newFilters, listener)
}
}
}
@@ -54,13 +54,13 @@ class LiveEventStore(
suspend fun query(
filters: List<Filter>,
onEach: (Event) -> Unit,
onEose: () -> Unit,
onCaughtUp: () -> Unit,
) {
// 1. Replay stored events matching filters.
store.query(filters, onEach)
// 2. Signal end of stored events.
onEose()
onCaughtUp()
// 3. Stream live events until cancelled.
newEventStream.collect { newEvent ->
@@ -146,7 +146,7 @@ class RelaySession(
send(EventMessage(cmd.subId, event))
}
},
onEose = { send(EoseMessage(cmd.subId)) },
onCaughtUp = { send(EoseMessage(cmd.subId)) },
)
} catch (_: kotlinx.coroutines.CancellationException) {
// Subscription was closed this is expected.
@@ -22,8 +22,8 @@ package com.vitorpamplona.quartz.nip46RemoteSigner.signer
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.req
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.StaticSubscription
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
@@ -52,7 +52,7 @@ class NostrSignerRemote(
val signer: NostrSignerInternal,
val remotePubkey: HexKey,
val relays: Set<NormalizedRelayUrl>,
val client: INostrClient,
val client: NostrClient,
val permissions: String? = null,
val secret: String? = null,
) : NostrSigner(signer.pubKey) {
@@ -67,13 +67,16 @@ class NostrSignerRemote(
)
val subscription =
client.req(
relays = relays.toList(),
filter =
Filter(
kinds = listOf(NostrConnectEvent.KIND),
tags = mapOf("p" to listOf(signer.pubKey)),
),
StaticSubscription(
client,
relays.associateWith {
listOf(
Filter(
kinds = listOf(NostrConnectEvent.KIND),
tags = mapOf("p" to listOf(signer.pubKey)),
),
)
},
) { event ->
if (event is NostrConnectEvent) {
scope.launch {
@@ -83,7 +86,7 @@ class NostrSignerRemote(
}
fun openSubscription() {
subscription.updateFilter()
subscription.refresh()
}
fun closeSubscription() {
@@ -293,7 +296,7 @@ class NostrSignerRemote(
fun fromBunkerUri(
bunkerUri: String,
signer: NostrSignerInternal,
client: INostrClient,
client: NostrClient,
permissions: String? = null,
): NostrSignerRemote {
if (!bunkerUri.startsWith("bunker://")) throw Exception("Invalid bunker uri")
@@ -21,7 +21,7 @@
package com.vitorpamplona.quartz.nip46RemoteSigner.signer
import com.vitorpamplona.quartz.nip01Core.core.OptimizedJsonMapper
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequest
@@ -34,7 +34,7 @@ import kotlin.coroutines.resume
class RemoteSignerManager(
val timeout: Long = 30000,
val client: INostrClient,
val client: NostrClient,
val signer: NostrSignerInternal,
val remoteKey: String,
val relayList: Set<NormalizedRelayUrl>,
@@ -77,7 +77,7 @@ class RemoteSignerManager(
awaitingRequests.put(request.id, continuation)
client.send(event, relayList = relayList)
client.publish(event, relayList = relayList)
}
return when (result) {
@@ -21,7 +21,7 @@
package com.vitorpamplona.quartz.nip77Negentropy
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
@@ -58,7 +58,7 @@ interface INegentropyListener {
/**
* Manages NIP-77 negentropy sync sessions across multiple relays.
*
* This class acts as a [IRelayClientListener] that intercepts NEG-MSG and NEG-ERR
* This class acts as a [RelayConnectionListener] that intercepts NEG-MSG and NEG-ERR
* messages and drives the reconciliation protocol. It maintains active sessions
* per relay and subscription ID.
*
@@ -69,7 +69,7 @@ interface INegentropyListener {
*/
class NegentropyManager(
private val listener: INegentropyListener,
) : IRelayClientListener {
) : RelayConnectionListener {
private val activeSessions = mutableMapOf<String, Pair<NormalizedRelayUrl, NegentropySession>>()
fun startSync(
@@ -22,9 +22,9 @@ package com.vitorpamplona.quartz.nip46RemoteSigner.signer
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
@@ -37,10 +37,10 @@ import kotlin.test.assertNotNull
import kotlin.test.assertTrue
/**
* INostrClient that records openReqSubscription calls for verification.
* NostrClient that records subscribe calls for verification.
* Used instead of mockk since commonTest doesn't have mockk.
*/
private class TrackingNostrClient : INostrClient {
private class TrackingNostrClient : NostrClient {
data class SubscriptionRecord(
val subId: String,
val filters: Map<NormalizedRelayUrl, List<Filter>>,
@@ -64,33 +64,33 @@ private class TrackingNostrClient : INostrClient {
override fun isActive() = false
override fun renewFilters(relay: IRelayClient) {}
override fun syncFilters(relay: IRelayClient) {}
override fun openReqSubscription(
override fun subscribe(
subId: String,
filters: Map<NormalizedRelayUrl, List<Filter>>,
listener: IRequestListener?,
listener: SubscriptionListener?,
) {
subscriptions.add(SubscriptionRecord(subId, filters))
}
override fun queryCount(
override fun count(
subId: String,
filters: Map<NormalizedRelayUrl, List<Filter>>,
) {}
override fun close(subId: String) {}
override fun unsubscribe(subId: String) {}
override fun send(
override fun publish(
event: Event,
relayList: Set<NormalizedRelayUrl>,
) {
sentEvents.add(event to relayList)
}
override fun subscribe(listener: IRelayClientListener) {}
override fun addConnectionListener(listener: RelayConnectionListener) {}
override fun unsubscribe(listener: IRelayClientListener) {}
override fun removeConnectionListener(listener: RelayConnectionListener) {}
override fun getReqFiltersOrNull(subId: String): Map<NormalizedRelayUrl, List<Filter>>? = null
@@ -120,7 +120,7 @@ class NostrSignerRemoteIsolationTest {
val trackingClient = TrackingNostrClient()
val ephemeralSigner = NostrSignerInternal(KeyPair())
// Construction triggers client.req() which calls openReqSubscription
// Construction triggers client.subscribe() which calls subscribe
NostrSignerRemote(
signer = ephemeralSigner,
remotePubkey = validHex,
@@ -19,10 +19,9 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.relay
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.downloadFirstEvent
import com.vitorpamplona.quartz.nip01Core.relay.client.DefaultNostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.fetchFirst
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -37,10 +36,10 @@ class NostrClientFirstEventTest : BaseNostrClientTest() {
fun testDownloadFirstEvent() =
runBlocking {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val event =
client.downloadFirstEvent(
client.fetchFirst(
relay = "wss://nos.lol",
filter =
Filter(
@@ -19,11 +19,10 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.relay
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
import com.vitorpamplona.quartz.nip01Core.relay.client.DefaultNostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
@@ -43,24 +42,24 @@ class NostrClientManualSubTest : BaseNostrClientTest() {
fun testEoseAfter100Events() =
runBlocking {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val resultChannel = Channel<String>(UNLIMITED)
val events = mutableListOf<String>()
val mySubId = "test-sub-id-1"
val listener =
object : IRequestListener {
object : SubscriptionListener {
override fun onEvent(
event: Event,
isLive: Boolean,
isRealTime: Boolean,
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
resultChannel.trySend(event.id)
}
override fun onEose(
override fun onCaughtUp(
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
@@ -79,7 +78,7 @@ class NostrClientManualSubTest : BaseNostrClientTest() {
),
)
client.openReqSubscription(mySubId, filters, listener)
client.subscribe(mySubId, filters, listener)
withTimeoutOrNull(10000) {
while (events.size < 101) {
@@ -90,7 +89,7 @@ class NostrClientManualSubTest : BaseNostrClientTest() {
resultChannel.close()
client.close(mySubId)
client.unsubscribe(mySubId)
client.disconnect()
appScope.cancel()
@@ -19,9 +19,8 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.relay
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.queryCountSuspend
import com.vitorpamplona.quartz.nip01Core.relay.client.DefaultNostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.count
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.normalizeRelayUrl
import junit.framework.TestCase.assertTrue
@@ -43,9 +42,9 @@ class NostrClientQueryCountTest : BaseNostrClientTest() {
fun testQueryCountSuspend() =
runBlocking {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val result = client.queryCountSuspend(relay = fiatjaf, filter = metadata)
val result = client.count(fiatjaf, metadata)
assertTrue((result?.count ?: 0) > 1)
@@ -57,9 +56,9 @@ class NostrClientQueryCountTest : BaseNostrClientTest() {
fun testQueryCountSuspendAllEvents() =
runBlocking {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val result = client.queryCountSuspend(relay = fiatjaf, filter = Filter())
val result = client.count(fiatjaf, Filter())
assertTrue((result?.count ?: 0) > 1)
@@ -71,20 +70,19 @@ class NostrClientQueryCountTest : BaseNostrClientTest() {
fun testQueryCountSuspendMultipleRelays() =
runBlocking {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val result =
client.queryCountSuspend(
filters =
mapOf(
fiatjaf to listOf(metadata, outboxRelays),
utxo to listOf(metadata, outboxRelays),
),
val results =
client.count(
mapOf(
fiatjaf to listOf(metadata, outboxRelays),
utxo to listOf(metadata, outboxRelays),
),
)
result.forEach { url, result ->
println("${url.url}: ${result.count}")
assertTrue(result.count > 1)
results.forEach { (url, countResult) ->
println("${url.url}: ${countResult.count}")
assertTrue(countResult.count > 1)
}
client.disconnect()
@@ -19,10 +19,9 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.relay
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.DefaultNostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.EoseMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.EventMessage
@@ -49,14 +48,14 @@ class NostrClientRepeatSubTest : BaseNostrClientTest() {
fun testRepeatSubEvents() =
runBlocking {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val resultChannel = Channel<String>(UNLIMITED)
val events = mutableListOf<String>()
val mySubId = "test-sub-id-2"
val listener =
object : IRelayClientListener {
object : RelayConnectionListener {
override fun onIncomingMessage(
relay: IRelayClient,
msgStr: String,
@@ -79,7 +78,7 @@ class NostrClientRepeatSubTest : BaseNostrClientTest() {
}
}
client.subscribe(listener)
client.addConnectionListener(listener)
val filters =
mapOf(
@@ -121,10 +120,10 @@ class NostrClientRepeatSubTest : BaseNostrClientTest() {
Log.d("Test", "Processing message ${events.size}")
// simulates an update in the middle of the sub
if (events.size == 1) {
client.openReqSubscription(mySubId, filtersShouldIgnore)
client.subscribe(mySubId, filtersShouldIgnore)
}
if (events.size == 5) {
client.openReqSubscription(mySubId, filtersShouldSendAfterEOSE)
client.subscribe(mySubId, filtersShouldSendAfterEOSE)
}
events.add(resultChannel.receive())
}
@@ -132,12 +131,12 @@ class NostrClientRepeatSubTest : BaseNostrClientTest() {
}
launch {
client.openReqSubscription(mySubId, filters)
client.subscribe(mySubId, filters)
}
}
client.close(mySubId)
client.unsubscribe(listener)
client.unsubscribe(mySubId)
client.removeConnectionListener(listener)
client.disconnect()
appScope.cancel()
@@ -19,11 +19,10 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.relay
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.reqBypassingRelayLimits
import com.vitorpamplona.quartz.nip01Core.relay.client.DefaultNostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.fetchAllPages
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import kotlinx.coroutines.CoroutineScope
@@ -40,13 +39,13 @@ class NostrClientReqBypassingRelayLimitsTest : BaseNostrClientTest() {
fun testDownloadFromRelayReturnsMetadataEvents() =
runBlocking {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val events = mutableListOf<Event>()
// nos.lol returns only 500 events per req
val totalFound =
client.reqBypassingRelayLimits(
client.fetchAllPages(
relay = "wss://nos.lol",
filters =
listOf(
@@ -74,14 +73,14 @@ class NostrClientReqBypassingRelayLimitsTest : BaseNostrClientTest() {
fun testDownloadFromRelayReturnsMetadataAndContactListEvents() =
runBlocking {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val metadataEvents = mutableListOf<Event>()
val contactListEvents = mutableListOf<Event>()
// nos.lol returns only 500 events per req
val totalFound =
client.reqBypassingRelayLimits(
client.fetchAllPages(
relay = "wss://nos.lol",
filters =
listOf(
@@ -19,10 +19,9 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.relay
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.sendAndWaitForResponse
import com.vitorpamplona.quartz.nip01Core.relay.client.DefaultNostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.publishAndConfirm
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.normalizeRelayUrl
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
@@ -39,20 +38,20 @@ class NostrClientSendAndWaitTest : BaseNostrClientTest() {
fun testSendAndWaitForResponse() =
runBlocking {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val randomSigner = NostrSignerInternal(KeyPair())
val event = randomSigner.sign(TextNoteEvent.build("Hello World"))
val resultDamus =
client.sendAndWaitForResponse(
client.publishAndConfirm(
event = event,
relayList = setOf("wss://nostr.bitcoiner.social".normalizeRelayUrl()),
)
val resultNos =
client.sendAndWaitForResponse(
client.publishAndConfirm(
event = event,
relayList = setOf("wss://nos.lol".normalizeRelayUrl()),
)
@@ -19,11 +19,10 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.relay
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.reqAsFlow
import com.vitorpamplona.quartz.nip01Core.relay.client.DefaultNostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.subscribeAsFlow
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.CoroutineScope
@@ -50,10 +49,10 @@ class NostrClientSubscriptionAsFlowTest : BaseNostrClientTest() {
fun testNostrClientSubscriptionAsFlow() =
runTest {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val flow =
client.reqAsFlow(
client.subscribeAsFlow(
relay = "wss://nos.lol",
filter =
Filter(
@@ -89,10 +88,10 @@ class NostrClientSubscriptionAsFlowTest : BaseNostrClientTest() {
fun testNostrClientSubscriptionAsFlowDebouncing() =
runTest {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val flow =
client.reqAsFlow(
client.subscribeAsFlow(
relay = "wss://nos.lol",
filter =
Filter(
@@ -19,12 +19,12 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.relay
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.req
import com.vitorpamplona.quartz.nip01Core.relay.client.DefaultNostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.StaticSubscription
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@@ -41,19 +41,23 @@ class NostrClientSubscriptionTest : BaseNostrClientTest() {
fun testNostrClientSubscription() =
runBlocking {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val resultChannel = Channel<Event>(UNLIMITED)
val events = mutableSetOf<Event>()
val sub =
client.req(
relay = "wss://nos.lol",
filter =
Filter(
kinds = listOf(MetadataEvent.KIND),
limit = 100,
),
StaticSubscription(
client,
mapOf(
RelayUrlNormalizer.normalize("wss://nos.lol") to
listOf(
Filter(
kinds = listOf(MetadataEvent.KIND),
limit = 100,
),
),
),
) { event ->
assertEquals(MetadataEvent.KIND, event.kind)
resultChannel.trySend(event)
@@ -19,11 +19,10 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.relay
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.reqUntilEoseAsFlow
import com.vitorpamplona.quartz.nip01Core.relay.client.DefaultNostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.fetchAsFlow
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.CoroutineScope
@@ -50,10 +49,10 @@ class NostrClientSubscriptionUntilEoseAsFlowTest : BaseNostrClientTest() {
fun testNostrClientSubscriptionUntilEoseAsFlow() =
runTest {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val flow =
client.reqUntilEoseAsFlow(
client.fetchAsFlow(
relay = "wss://nos.lol",
filter =
Filter(
@@ -89,10 +88,10 @@ class NostrClientSubscriptionUntilEoseAsFlowTest : BaseNostrClientTest() {
fun testNostrClientSubscriptionUntilEoseAsFlowDebouncing() =
runTest {
val appScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
val client = NostrClient(socketBuilder, appScope)
val client = DefaultNostrClient(socketBuilder, appScope)
val flow =
client.reqUntilEoseAsFlow(
client.fetchAsFlow(
relay = "wss://nos.lol",
filter =
Filter(