mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-20 09:50:41 +02:00
Adds support for NIP-53 Meeting room to quartz
This commit is contained in:
@@ -93,6 +93,9 @@ import com.vitorpamplona.quartz.nip52Calendar.CalendarEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.CalendarRSVPEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.CalendarTimeSlotEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.presence.MeetingRoomPresenceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportEvent
|
||||
@@ -253,6 +256,9 @@ class EventFactory {
|
||||
LnZapPrivateEvent.KIND -> LnZapPrivateEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
LnZapRequestEvent.KIND -> LnZapRequestEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
LongTextNoteEvent.KIND -> LongTextNoteEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
MeetingRoomEvent.KIND -> MeetingRoomEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
MeetingRoomPresenceEvent.KIND -> MeetingRoomPresenceEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
MeetingSpaceEvent.KIND -> MeetingSpaceEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
MetadataEvent.KIND -> MetadataEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
MuteListEvent.KIND -> MuteListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
NNSEvent.KIND -> NNSEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
|
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.any
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.SummaryTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.MeetingSpaceTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.CurrentParticipantsTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.EndsTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.ParticipantTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.PinnedEventTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.RecordingTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.RelayListTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.StartsTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.StatusTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.StreamingTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.TotalParticipantsTag
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
class MeetingRoomEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||
EventHintProvider,
|
||||
AddressHintProvider,
|
||||
PubKeyHintProvider {
|
||||
override fun eventHints(): List<EventIdHint> {
|
||||
val pinnedEvents = pinned()
|
||||
if (pinnedEvents.isEmpty()) return emptyList()
|
||||
|
||||
val relays = allRelayUrls()
|
||||
|
||||
return if (relays.isNotEmpty()) {
|
||||
pinnedEvents
|
||||
.map { eventId ->
|
||||
relays.map { relay ->
|
||||
EventIdHint(eventId, relay)
|
||||
}
|
||||
}.flatten()
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun linkedEventIds() = tags.mapNotNull(PinnedEventTag::parse)
|
||||
|
||||
override fun addressHints(): List<AddressHint> = tags.mapNotNull(MeetingSpaceTag::parseAsHint)
|
||||
|
||||
override fun linkedAddressIds(): List<String> = tags.mapNotNull(MeetingSpaceTag::parseAddressId)
|
||||
|
||||
override fun pubKeyHints() = tags.mapNotNull(ParticipantTag::parseAsHint)
|
||||
|
||||
override fun linkedPubKeys() = tags.mapNotNull(ParticipantTag::parseKey)
|
||||
|
||||
fun interactiveRoom() = tags.firstNotNullOfOrNull(MeetingSpaceTag::parse)
|
||||
|
||||
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
|
||||
|
||||
fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse)
|
||||
|
||||
fun image() = tags.firstNotNullOfOrNull(ImageTag::parse)
|
||||
|
||||
fun streaming() = tags.firstNotNullOfOrNull(StreamingTag::parse)
|
||||
|
||||
fun recording() = tags.firstNotNullOfOrNull(RecordingTag::parse)
|
||||
|
||||
fun starts() = tags.firstNotNullOfOrNull(StartsTag::parse)
|
||||
|
||||
fun ends() = tags.firstNotNullOfOrNull(EndsTag::parse)
|
||||
|
||||
fun status() = checkStatus(tags.firstNotNullOfOrNull(StatusTag::parseEnum))
|
||||
|
||||
fun isLive() = status() == StatusTag.STATUS.LIVE
|
||||
|
||||
fun currentParticipants() = tags.firstNotNullOfOrNull(CurrentParticipantsTag::parse)
|
||||
|
||||
fun totalParticipants() = tags.firstNotNullOfOrNull(TotalParticipantsTag::parse)
|
||||
|
||||
fun participantKeys(): List<HexKey> = tags.mapNotNull(ParticipantTag::parseKey)
|
||||
|
||||
fun participants() = tags.mapNotNull(ParticipantTag::parse)
|
||||
|
||||
fun relays() = tags.mapNotNull(RelayListTag::parse).flatten()
|
||||
|
||||
fun allRelayUrls() = tags.mapNotNull(RelayListTag::parse).flatten()
|
||||
|
||||
fun hasHost() = tags.any(ParticipantTag::isHost)
|
||||
|
||||
fun host() = tags.firstNotNullOfOrNull(ParticipantTag::parseHost)
|
||||
|
||||
fun hosts() = tags.mapNotNull(ParticipantTag::parseHost)
|
||||
|
||||
fun pinned() = tags.mapNotNull(PinnedEventTag::parse)
|
||||
|
||||
fun checkStatus(eventStatus: StatusTag.STATUS?): StatusTag.STATUS? =
|
||||
if (eventStatus == StatusTag.STATUS.LIVE && createdAt < TimeUtils.eightHoursAgo()) {
|
||||
StatusTag.STATUS.ENDED
|
||||
} else if (eventStatus == StatusTag.STATUS.PLANNED) {
|
||||
val starts = starts()
|
||||
val ends = ends()
|
||||
if (starts != null && starts < TimeUtils.oneHourAgo()) {
|
||||
StatusTag.STATUS.ENDED
|
||||
} else if (ends != null && ends < TimeUtils.oneHourAgo()) {
|
||||
StatusTag.STATUS.ENDED
|
||||
} else {
|
||||
eventStatus
|
||||
}
|
||||
} else {
|
||||
eventStatus
|
||||
}
|
||||
|
||||
fun participantsIntersect(keySet: Set<String>): Boolean = keySet.contains(pubKey) || tags.any(ParticipantTag::isIn, keySet)
|
||||
|
||||
companion object {
|
||||
const val KIND = 30313
|
||||
const val ALT = "Meeting room event"
|
||||
|
||||
suspend fun create(
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
): MeetingRoomEvent {
|
||||
val tags = arrayOf(AltTag.assemble(ALT))
|
||||
return signer.sign(createdAt, KIND, tags, "")
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.any
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.SummaryTag
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.EndpointUrlTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.RelayListTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.RoomNameTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.ServiceUrlTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.StatusTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.ParticipantTag
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
class MeetingSpaceEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||
PubKeyHintProvider {
|
||||
override fun pubKeyHints() = tags.mapNotNull(ParticipantTag::parseAsHint)
|
||||
|
||||
override fun linkedPubKeys() = tags.mapNotNull(ParticipantTag::parseKey)
|
||||
|
||||
fun room() = tags.firstNotNullOfOrNull(RoomNameTag::parse)
|
||||
|
||||
fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse)
|
||||
|
||||
fun image() = tags.firstNotNullOfOrNull(ImageTag::parse)
|
||||
|
||||
fun status() = checkStatus(tags.firstNotNullOfOrNull(StatusTag::parseEnum))
|
||||
|
||||
fun isLive() = status() != StatusTag.STATUS.CLOSED
|
||||
|
||||
fun service() = tags.firstNotNullOfOrNull(ServiceUrlTag::parse)
|
||||
|
||||
fun endpoint() = tags.firstNotNullOfOrNull(EndpointUrlTag::parse)
|
||||
|
||||
fun relays() = tags.mapNotNull(RelayListTag::parse).flatten()
|
||||
|
||||
fun allRelayUrls() = tags.mapNotNull(RelayListTag::parse).flatten()
|
||||
|
||||
fun participantKeys(): List<HexKey> = tags.mapNotNull(ParticipantTag::parseKey)
|
||||
|
||||
fun participants() = tags.mapNotNull(ParticipantTag::parse)
|
||||
|
||||
fun checkStatus(eventStatus: StatusTag.STATUS?): StatusTag.STATUS? =
|
||||
if (eventStatus != StatusTag.STATUS.CLOSED && createdAt < TimeUtils.eightHoursAgo()) {
|
||||
StatusTag.STATUS.CLOSED
|
||||
} else {
|
||||
eventStatus
|
||||
}
|
||||
|
||||
fun participantsIntersect(keySet: Set<String>): Boolean = keySet.contains(pubKey) || tags.any(ParticipantTag::isIn, keySet)
|
||||
|
||||
companion object Companion {
|
||||
const val KIND = 30312
|
||||
const val ALT = "Interactive room event"
|
||||
|
||||
suspend fun create(
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
): MeetingSpaceEvent {
|
||||
val tags = arrayOf(AltTag.assemble(ALT))
|
||||
return signer.sign(createdAt, KIND, tags, "")
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class EndpointUrlTag {
|
||||
companion object Companion {
|
||||
const val TAG_NAME = "endpoint"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(url: String) = arrayOf(TAG_NAME, url)
|
||||
}
|
||||
}
|
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
|
||||
class MeetingSpaceTag(
|
||||
val address: Address,
|
||||
val relayHint: NormalizedRelayUrl? = null,
|
||||
) {
|
||||
fun countMemory(): Long = 2 * pointerSizeInBytes + address.countMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0)
|
||||
|
||||
fun toTag() = Address.assemble(address.kind, address.pubKeyHex, address.dTag)
|
||||
|
||||
fun toTagArray() = assemble(address, relayHint)
|
||||
|
||||
fun toTagIdOnly() = assemble(address, null)
|
||||
|
||||
companion object Companion {
|
||||
const val TAG_NAME = "a"
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
addressId: String,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == addressId
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
address: MeetingSpaceTag,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == address.toTag()
|
||||
|
||||
@JvmStatic
|
||||
fun isIn(
|
||||
tag: Array<String>,
|
||||
addressIds: Set<String>,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in addressIds
|
||||
|
||||
@JvmStatic
|
||||
fun isTaggedWithKind(
|
||||
tag: Array<String>,
|
||||
kind: String,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && Address.isOfKind(tag[1], kind)
|
||||
|
||||
@JvmStatic
|
||||
fun parse(
|
||||
aTagId: String,
|
||||
relay: String?,
|
||||
) = Address.parse(aTagId)?.let {
|
||||
MeetingSpaceTag(it, relay?.let { RelayUrlNormalizer.normalizeOrNull(it) })
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): MeetingSpaceTag? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return parse(tag[1], tag.getOrNull(2))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseValidAddress(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return Address.parse(tag[1])?.toValue()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAddress(tag: Array<String>): Address? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return Address.parse(tag[1])
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAddressId(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAsHint(tag: Array<String>): AddressHint? {
|
||||
ensure(tag.has(2)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
ensure(tag[1].contains(':')) { return null }
|
||||
ensure(tag[2].isNotEmpty()) { return null }
|
||||
|
||||
val relayHint = RelayUrlNormalizer.normalizeOrNull(tag[2])
|
||||
ensure(relayHint != null) { return null }
|
||||
|
||||
return AddressHint(tag[1], relayHint)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
aTagId: HexKey,
|
||||
relay: NormalizedRelayUrl?,
|
||||
) = arrayOfNotNull(TAG_NAME, aTagId, relay?.url)
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
address: Address,
|
||||
relay: NormalizedRelayUrl?,
|
||||
) = arrayOfNotNull(TAG_NAME, address.toValue(), relay?.url)
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
kind: Int,
|
||||
pubKey: String,
|
||||
dTag: String,
|
||||
relay: NormalizedRelayUrl?,
|
||||
) = assemble(Address.assemble(kind, pubKey, dTag), relay)
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class RecordingTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "recording"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(url: String) = arrayOf(TAG_NAME, url)
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class RelayListTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "relays"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): List<NormalizedRelayUrl>? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
val relays =
|
||||
tag.mapIndexedNotNull { index, s ->
|
||||
if (index == 0) null else RelayUrlNormalizer.normalizeOrNull(s)
|
||||
}
|
||||
|
||||
if (relays.isEmpty()) return null
|
||||
|
||||
return relays
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(urls: List<NormalizedRelayUrl>) = arrayOf(TAG_NAME) + urls.map { it.url }.toTypedArray()
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class RoomNameTag {
|
||||
companion object Companion {
|
||||
const val TAG_NAME = "room"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(title: String) = arrayOf(TAG_NAME, title)
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class ServiceUrlTag {
|
||||
companion object Companion {
|
||||
const val TAG_NAME = "service"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(url: String) = arrayOf(TAG_NAME, url)
|
||||
}
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class StatusTag {
|
||||
enum class STATUS(
|
||||
val code: String,
|
||||
) {
|
||||
OPEN("open"),
|
||||
PRIVATE("private"),
|
||||
CLOSED("closed"),
|
||||
;
|
||||
|
||||
fun toTagArray() = assemble(this)
|
||||
|
||||
companion object {
|
||||
fun parse(code: String): STATUS? =
|
||||
when (code) {
|
||||
STATUS.OPEN.code -> STATUS.OPEN
|
||||
STATUS.PRIVATE.code -> STATUS.PRIVATE
|
||||
STATUS.CLOSED.code -> STATUS.CLOSED
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "status"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseEnum(tag: Array<String>): STATUS? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return STATUS.parse(tag[1])
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(status: STATUS) = arrayOf(TAG_NAME, status.code)
|
||||
}
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.presence
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.MeetingSpaceTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.presence.tags.HandRaisedTag
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
class MeetingRoomPresenceEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||
AddressHintProvider {
|
||||
override fun addressHints(): List<AddressHint> = tags.mapNotNull(MeetingSpaceTag::parseAsHint)
|
||||
|
||||
override fun linkedAddressIds(): List<String> = tags.mapNotNull(MeetingSpaceTag::parseAddressId)
|
||||
|
||||
fun interactiveRoom() = tags.firstNotNullOfOrNull(MeetingSpaceTag::parse)
|
||||
|
||||
fun handRaised() = tags.firstNotNullOfOrNull(HandRaisedTag::parse)
|
||||
|
||||
companion object Companion {
|
||||
const val KIND = 10312
|
||||
const val ALT = "Room Presence tag"
|
||||
|
||||
fun createAddress(
|
||||
pubKey: HexKey,
|
||||
dtag: String,
|
||||
): Address = Address(KIND, pubKey, dtag)
|
||||
|
||||
fun createAddressATag(
|
||||
pubKey: HexKey,
|
||||
dtag: String,
|
||||
): ATag = ATag(KIND, pubKey, dtag, null)
|
||||
|
||||
fun createAddressTag(
|
||||
pubKey: HexKey,
|
||||
dtag: String,
|
||||
): String = Address.assemble(KIND, pubKey, dtag)
|
||||
|
||||
fun build(
|
||||
root: MeetingRoomEvent,
|
||||
handRaised: Boolean?,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<MeetingRoomPresenceEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, "", createdAt) {
|
||||
alt(root.title() ?: ALT)
|
||||
|
||||
roomMeeting(MeetingSpaceTag(root.address(), root.relays().firstOrNull()))
|
||||
|
||||
handRaised?.let {
|
||||
handRaised(it)
|
||||
}
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.presence
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.tags.MeetingSpaceTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.presence.tags.HandRaisedTag
|
||||
|
||||
fun TagArrayBuilder<MeetingRoomPresenceEvent>.roomMeeting(rep: MeetingSpaceTag) = addUnique(rep.toTagArray())
|
||||
|
||||
fun TagArrayBuilder<MeetingRoomPresenceEvent>.roomMeeting(rep: EventHintBundle<MeetingRoomEvent>) = addUnique(rep.toATag().toATagArray())
|
||||
|
||||
fun TagArrayBuilder<MeetingRoomPresenceEvent>.handRaised(raised: Boolean) = addUnique(HandRaisedTag.assemble(raised))
|
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.presence.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class HandRaisedTag {
|
||||
companion object Companion {
|
||||
const val TAG_NAME = "hand"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Boolean? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1] == "1"
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(handRaised: Boolean) = arrayOf(TAG_NAME, if (handRaised) "1" else "0")
|
||||
}
|
||||
}
|
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.presence.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
|
||||
class MeetingRoomTag(
|
||||
val address: Address,
|
||||
val relayHint: NormalizedRelayUrl? = null,
|
||||
) {
|
||||
fun countMemory(): Long = 2 * pointerSizeInBytes + address.countMemory() + (relayHint?.url?.bytesUsedInMemory() ?: 0)
|
||||
|
||||
fun toTag() = Address.assemble(address.kind, address.pubKeyHex, address.dTag)
|
||||
|
||||
fun toTagArray() = assemble(address, relayHint)
|
||||
|
||||
fun toTagIdOnly() = assemble(address, null)
|
||||
|
||||
companion object Companion {
|
||||
const val TAG_NAME = "a"
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
addressId: String,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == addressId
|
||||
|
||||
@JvmStatic
|
||||
fun isTagged(
|
||||
tag: Array<String>,
|
||||
address: MeetingRoomTag,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == address.toTag()
|
||||
|
||||
@JvmStatic
|
||||
fun isIn(
|
||||
tag: Array<String>,
|
||||
addressIds: Set<String>,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in addressIds
|
||||
|
||||
@JvmStatic
|
||||
fun isTaggedWithKind(
|
||||
tag: Array<String>,
|
||||
kind: String,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && Address.isOfKind(tag[1], kind)
|
||||
|
||||
@JvmStatic
|
||||
fun parse(
|
||||
aTagId: String,
|
||||
relay: String?,
|
||||
) = Address.parse(aTagId)?.let {
|
||||
MeetingRoomTag(it, relay?.let { RelayUrlNormalizer.normalizeOrNull(it) })
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): MeetingRoomTag? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return parse(tag[1], tag.getOrNull(2))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseValidAddress(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return Address.parse(tag[1])?.toValue()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAddress(tag: Array<String>): Address? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return Address.parse(tag[1])
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAddressId(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseAsHint(tag: Array<String>): AddressHint? {
|
||||
ensure(tag.has(2)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
ensure(tag[1].contains(':')) { return null }
|
||||
ensure(tag[2].isNotEmpty()) { return null }
|
||||
|
||||
val relayHint = RelayUrlNormalizer.normalizeOrNull(tag[2])
|
||||
ensure(relayHint != null) { return null }
|
||||
|
||||
return AddressHint(tag[1], relayHint)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
aTagId: HexKey,
|
||||
relay: NormalizedRelayUrl?,
|
||||
) = arrayOfNotNull(TAG_NAME, aTagId, relay?.url)
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
address: Address,
|
||||
relay: NormalizedRelayUrl?,
|
||||
) = arrayOfNotNull(TAG_NAME, address.toValue(), relay?.url)
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
kind: Int,
|
||||
pubKey: String,
|
||||
dTag: String,
|
||||
relay: NormalizedRelayUrl?,
|
||||
) = assemble(Address.assemble(kind, pubKey, dTag), relay)
|
||||
}
|
||||
}
|
@@ -24,13 +24,10 @@ import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.any
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
|
||||
import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.SummaryTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
|
||||
@@ -38,6 +35,8 @@ import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.CurrentParticipantsTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.EndsTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.ParticipantTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.PinnedEventTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.RecordingTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.RelayListTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.StartsTag
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags.StatusTag
|
||||
@@ -55,15 +54,26 @@ class LiveActivitiesEvent(
|
||||
sig: HexKey,
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig),
|
||||
EventHintProvider,
|
||||
AddressHintProvider,
|
||||
PubKeyHintProvider {
|
||||
override fun eventHints() = tags.mapNotNull(ETag::parseAsHint) + tags.mapNotNull(QTag::parseEventAsHint)
|
||||
override fun eventHints(): List<EventIdHint> {
|
||||
val pinnedEvents = pinned()
|
||||
if (pinnedEvents.isEmpty()) return emptyList()
|
||||
|
||||
override fun linkedEventIds() = tags.mapNotNull(ETag::parseId) + tags.mapNotNull(QTag::parseEventId)
|
||||
val relays = allRelayUrls()
|
||||
|
||||
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) + tags.mapNotNull(QTag::parseAddressAsHint)
|
||||
return if (relays.isNotEmpty()) {
|
||||
pinnedEvents
|
||||
.map { eventId ->
|
||||
relays.map { relay ->
|
||||
EventIdHint(eventId, relay)
|
||||
}
|
||||
}.flatten()
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun linkedAddressIds() = tags.mapNotNull(ATag::parseAddressId) + tags.mapNotNull(QTag::parseAddressId)
|
||||
override fun linkedEventIds() = tags.mapNotNull(PinnedEventTag::parse)
|
||||
|
||||
override fun pubKeyHints() = tags.mapNotNull(ParticipantTag::parseAsHint)
|
||||
|
||||
@@ -77,6 +87,8 @@ class LiveActivitiesEvent(
|
||||
|
||||
fun streaming() = tags.firstNotNullOfOrNull(StreamingTag::parse)
|
||||
|
||||
fun recording() = tags.firstNotNullOfOrNull(RecordingTag::parse)
|
||||
|
||||
fun starts() = tags.firstNotNullOfOrNull(StartsTag::parse)
|
||||
|
||||
fun ends() = tags.firstNotNullOfOrNull(EndsTag::parse)
|
||||
@@ -93,7 +105,7 @@ class LiveActivitiesEvent(
|
||||
|
||||
fun participants() = tags.mapNotNull(ParticipantTag::parse)
|
||||
|
||||
fun relays() = tags.mapNotNull(RelayListTag::parse)
|
||||
fun relays() = tags.mapNotNull(RelayListTag::parse).flatten()
|
||||
|
||||
fun allRelayUrls() = tags.mapNotNull(RelayListTag::parse).flatten()
|
||||
|
||||
@@ -103,6 +115,8 @@ class LiveActivitiesEvent(
|
||||
|
||||
fun hosts() = tags.mapNotNull(ParticipantTag::parseHost)
|
||||
|
||||
fun pinned() = tags.mapNotNull(PinnedEventTag::parse)
|
||||
|
||||
fun checkStatus(eventStatus: StatusTag.STATUS?): StatusTag.STATUS? =
|
||||
if (eventStatus == StatusTag.STATUS.LIVE && createdAt < TimeUtils.eightHoursAgo()) {
|
||||
StatusTag.STATUS.ENDED
|
||||
|
@@ -35,6 +35,7 @@ enum class ROLE(
|
||||
val code: String,
|
||||
) {
|
||||
HOST("host"),
|
||||
MODERATOR("moderator"),
|
||||
SPEAKER("speaker"),
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
@Immutable
|
||||
class PinnedEventTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "pinned"
|
||||
|
||||
fun isIn(
|
||||
tag: Array<String>,
|
||||
eventIds: Set<HexKey>,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in eventIds
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Tag): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].length == 64) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(eventId: HexKey) = arrayOfNotNull(TAG_NAME, eventId)
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class RecordingTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "recording"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(url: String) = arrayOf(TAG_NAME, url)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user