mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-10 08:27:04 +00:00
feat(nip29): wire relay groups into LocalCache
Populate RelayGroupChannel from incoming events, mirroring the ephemeral-chat consume path: - relayGroupChannels cache keyed by GroupId + getOrCreate/getIfExists. - consume(GroupMetadataEvent): store the kind 39000 addressable note and update the channel's name/picture/about/flags. Keyed by (serving relay + group id): NIP-29 events don't carry their host relay, so the channel is associated only when the serving relay (provenance) is known. - attachToRelayGroupIfScoped(): route kind-9 chat + kind-1068 poll events that carry an `h` tag into their group's channel timeline, on top of normal consume. - Dispatch GroupMetadataEvent and the group-aware ChatEvent/PollEvent branches. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B5MLY4hq5LXJ2D5WeLRyXj
This commit is contained in:
@@ -32,6 +32,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LargeSoftCache
|
||||
import com.vitorpamplona.amethyst.commons.model.emphChat.EphemeralChatChannel
|
||||
import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChannel
|
||||
import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel
|
||||
import com.vitorpamplona.amethyst.commons.model.nip53LiveActivities.LiveActivitiesChannel
|
||||
import com.vitorpamplona.amethyst.commons.model.observables.CreatedAtIdHexComparator
|
||||
import com.vitorpamplona.amethyst.commons.model.observables.EventListMatchingFilter
|
||||
@@ -152,6 +153,9 @@ import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMuteUserEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
||||
import com.vitorpamplona.quartz.nip29RelayGroups.GroupId
|
||||
import com.vitorpamplona.quartz.nip29RelayGroups.groupId
|
||||
import com.vitorpamplona.quartz.nip29RelayGroups.metadata.GroupMetadataEvent
|
||||
import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent
|
||||
import com.vitorpamplona.quartz.nip30CustomEmoji.selection.EmojiPackSelectionEvent
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||
@@ -334,6 +338,7 @@ object LocalCache : ILocalCache, ICacheProvider {
|
||||
val publicChatChannels = LargeCache<HexKey, PublicChatChannel>()
|
||||
val liveChatChannels = LargeCache<Address, LiveActivitiesChannel>()
|
||||
val ephemeralChannels = LargeCache<RoomId, EphemeralChatChannel>()
|
||||
val relayGroupChannels = LargeCache<GroupId, RelayGroupChannel>()
|
||||
|
||||
val paymentTracker = NwcPaymentTracker()
|
||||
|
||||
@@ -607,6 +612,8 @@ object LocalCache : ILocalCache, ICacheProvider {
|
||||
|
||||
fun getEphemeralChatChannelIfExists(key: RoomId): EphemeralChatChannel? = ephemeralChannels.get(key)
|
||||
|
||||
fun getRelayGroupChannelIfExists(key: GroupId): RelayGroupChannel? = relayGroupChannels.get(key)
|
||||
|
||||
fun getLiveActivityChannelIfExists(key: Address): LiveActivitiesChannel? = liveChatChannels.get(key)
|
||||
|
||||
fun getNoteIfExists(event: Event): Note? =
|
||||
@@ -695,6 +702,8 @@ object LocalCache : ILocalCache, ICacheProvider {
|
||||
|
||||
fun getOrCreateEphemeralChannel(key: RoomId): EphemeralChatChannel = ephemeralChannels.getOrCreate(key) { EphemeralChatChannel(key) }
|
||||
|
||||
fun getOrCreateRelayGroupChannel(key: GroupId): RelayGroupChannel = relayGroupChannels.getOrCreate(key) { RelayGroupChannel(key) }
|
||||
|
||||
fun checkGetOrCreatePublicChatChannel(key: String): PublicChatChannel? {
|
||||
if (isValidHex(key)) {
|
||||
return getOrCreatePublicChatChannel(key)
|
||||
@@ -1722,6 +1731,47 @@ object LocalCache : ILocalCache, ICacheProvider {
|
||||
return new
|
||||
}
|
||||
|
||||
/**
|
||||
* NIP-29 relay-signed group metadata (kind 39000). Stored as an addressable
|
||||
* note and used to populate the [RelayGroupChannel]'s name/picture/about/
|
||||
* flags. The group is keyed by (host relay + group id): unlike NIP-C7, a
|
||||
* NIP-29 event does not carry its host relay in a tag — the host is the relay
|
||||
* that served it — so the channel can only be associated when we know the
|
||||
* serving relay (provenance). With no relay we still store the metadata.
|
||||
*/
|
||||
fun consume(
|
||||
event: GroupMetadataEvent,
|
||||
relay: NormalizedRelayUrl?,
|
||||
wasVerified: Boolean,
|
||||
): Boolean {
|
||||
val new = consumeBaseReplaceable(event, relay, wasVerified)
|
||||
|
||||
if (relay != null) {
|
||||
val note = getOrCreateAddressableNote(event.address())
|
||||
val channel = getOrCreateRelayGroupChannel(GroupId(event.groupId(), relay))
|
||||
(note.event as? GroupMetadataEvent)?.let { channel.updateGroupInfo(it, note) }
|
||||
}
|
||||
|
||||
return new
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a group-scoped content event (a kind-9 chat, kind-1068 poll, …
|
||||
* carrying an `h` tag) to its [RelayGroupChannel]. NIP-29 reuses the generic
|
||||
* content kinds and scopes them with `h`, so the note is consumed normally
|
||||
* and then, when it belongs to a group and we know the serving relay, added
|
||||
* to that group's channel timeline.
|
||||
*/
|
||||
private fun attachToRelayGroupIfScoped(
|
||||
event: Event,
|
||||
relay: NormalizedRelayUrl?,
|
||||
) {
|
||||
val groupId = event.groupId() ?: return
|
||||
if (relay == null) return
|
||||
val channel = getOrCreateRelayGroupChannel(GroupId(groupId, relay))
|
||||
channel.addNote(getOrCreateNote(event.id), relay)
|
||||
}
|
||||
|
||||
fun consume(
|
||||
event: LiveActivitiesChatMessageEvent,
|
||||
relay: NormalizedRelayUrl?,
|
||||
@@ -3543,6 +3593,10 @@ object LocalCache : ILocalCache, ICacheProvider {
|
||||
consumeBaseReplaceable(event, relay, wasVerified)
|
||||
}
|
||||
|
||||
is GroupMetadataEvent -> {
|
||||
consume(event, relay, wasVerified)
|
||||
}
|
||||
|
||||
is ExternalIdentitiesEvent -> {
|
||||
consumeBaseReplaceable(event, relay, wasVerified)
|
||||
}
|
||||
@@ -3897,11 +3951,15 @@ object LocalCache : ILocalCache, ICacheProvider {
|
||||
}
|
||||
|
||||
is ChatEvent -> {
|
||||
consumeRegularEvent(event, relay, wasVerified)
|
||||
consumeRegularEvent(event, relay, wasVerified).also {
|
||||
if (it) attachToRelayGroupIfScoped(event, relay)
|
||||
}
|
||||
}
|
||||
|
||||
is PollEvent -> {
|
||||
consumeRegularEvent(event, relay, wasVerified)
|
||||
consumeRegularEvent(event, relay, wasVerified).also {
|
||||
if (it) attachToRelayGroupIfScoped(event, relay)
|
||||
}
|
||||
}
|
||||
|
||||
is PollResponseEvent -> {
|
||||
|
||||
Reference in New Issue
Block a user