diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip64Chess/ChessAction.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip64Chess/ChessAction.kt index 82a966095..2e5ac8ace 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip64Chess/ChessAction.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip64Chess/ChessAction.kt @@ -20,11 +20,13 @@ */ package com.vitorpamplona.amethyst.model.nip64Chess +import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip64Chess.Color import com.vitorpamplona.quartz.nip64Chess.GameResult import com.vitorpamplona.quartz.nip64Chess.GameTermination import com.vitorpamplona.quartz.nip64Chess.accept.LiveChessGameAcceptEvent +import com.vitorpamplona.quartz.nip64Chess.baseEvent.tags.OpponentTag import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent @@ -46,7 +48,7 @@ class ChessAction { suspend fun createChallenge( gameId: String, playerColor: Color, - opponentPubkey: String? = null, + opponent: OpponentTag? = null, timeControl: String? = null, signer: NostrSigner, ): LiveChessGameChallengeEvent = @@ -54,7 +56,7 @@ class ChessAction { LiveChessGameChallengeEvent.build( gameId = gameId, playerColor = playerColor, - opponentPubkey = opponentPubkey, + opponent = opponent, timeControl = timeControl, ), ) @@ -69,15 +71,13 @@ class ChessAction { */ suspend fun acceptChallenge( gameId: String, - challengeEventId: String, - challengerPubkey: String, + challengeEvent: EventHintBundle, signer: NostrSigner, ): LiveChessGameAcceptEvent = signer.sign( LiveChessGameAcceptEvent.build( gameId = gameId, - challengeEventId = challengeEventId, - challengerPubkey = challengerPubkey, + challengeEvent = challengeEvent, ), ) @@ -88,7 +88,7 @@ class ChessAction { * @param moveNumber Move number (1-based) * @param san Move in Standard Algebraic Notation * @param fen Resulting position in FEN notation - * @param opponentPubkey Opponent's pubkey + * @param opponent Opponent's pubkey * @param comment Optional move comment * @param signer Nostr signer */ @@ -97,7 +97,7 @@ class ChessAction { moveNumber: Int, san: String, fen: String, - opponentPubkey: String, + opponent: OpponentTag, comment: String = "", signer: NostrSigner, ): LiveChessMoveEvent = @@ -107,7 +107,7 @@ class ChessAction { moveNumber = moveNumber, san = san, fen = fen, - opponentPubkey = opponentPubkey, + opponent = opponent, comment = comment, ), ) @@ -119,7 +119,7 @@ class ChessAction { * @param result Game result (1-0, 0-1, or 1/2-1/2) * @param termination How the game ended * @param winnerPubkey Pubkey of winner (if applicable) - * @param opponentPubkey Opponent's pubkey + * @param opponent Opponent's pubkey * @param pgn Optional PGN of complete game * @param signer Nostr signer */ @@ -128,7 +128,7 @@ class ChessAction { result: GameResult, termination: GameTermination, winnerPubkey: String? = null, - opponentPubkey: String, + opponent: OpponentTag, pgn: String = "", signer: NostrSigner, ): LiveChessGameEndEvent = @@ -138,7 +138,7 @@ class ChessAction { result = result, termination = termination, winnerPubkey = winnerPubkey, - opponentPubkey = opponentPubkey, + opponent = opponent, pgn = pgn, ), ) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/LiveChessGameAcceptEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/LiveChessGameAcceptEvent.kt index 16872f468..5309c7758 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/LiveChessGameAcceptEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/LiveChessGameAcceptEvent.kt @@ -21,11 +21,14 @@ package com.vitorpamplona.quartz.nip64Chess.accept 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.EventHintBundle import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip64Chess.baseEvent.BaseChessEvent +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent import com.vitorpamplona.quartz.utils.TimeUtils /** @@ -46,25 +49,22 @@ class LiveChessGameAcceptEvent( tags: Array>, content: String, sig: HexKey, -) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { +) : BaseChessEvent(id, pubKey, createdAt, KIND, tags, content, sig) { fun challengeEventId() = tags.challengeEventId() - fun challengerPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) - companion object { const val KIND = 30065 const val ALT_DESCRIPTION = "Chess game acceptance" fun build( gameId: String, - challengeEventId: String, - challengerPubkey: String, + challengeEvent: EventHintBundle, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { - add(arrayOf("d", gameId)) - challengeEvent(challengeEventId) - add(arrayOf("p", challengerPubkey)) + dTag(gameId) + challenge(challengeEvent) + challenger(challengeEvent) alt(ALT_DESCRIPTION) initializer() } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/TagArrayBuilderExt.kt index 4f0e0391c..6ee3602cd 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/TagArrayBuilderExt.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/TagArrayBuilderExt.kt @@ -21,6 +21,11 @@ package com.vitorpamplona.quartz.nip64Chess.accept import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle import com.vitorpamplona.quartz.nip64Chess.accept.tags.ChallengeEventTag +import com.vitorpamplona.quartz.nip64Chess.accept.tags.toOpponentTag +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent -fun TagArrayBuilder.challengeEvent(challengeEventId: String) = add(ChallengeEventTag.assemble(challengeEventId)) +fun TagArrayBuilder.challenge(challengeHint: EventHintBundle) = addUnique(ChallengeEventTag.assemble(challengeHint)) + +fun TagArrayBuilder.challenger(challengeHint: EventHintBundle) = add(challengeHint.toOpponentTag().toTagArray()) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/tags/ChallengeEventTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/tags/ChallengeEventTag.kt index 803b0e869..200fcc4d6 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/tags/ChallengeEventTag.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/tags/ChallengeEventTag.kt @@ -20,21 +20,87 @@ */ package com.vitorpamplona.quartz.nip64Chess.accept.tags +import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle +import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer +import com.vitorpamplona.quartz.nip01Core.tags.events.GenericETag +import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent +import com.vitorpamplona.quartz.nip64Chess.baseEvent.tags.OpponentTag +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent +import com.vitorpamplona.quartz.utils.arrayOfNotNull import com.vitorpamplona.quartz.utils.ensure -class ChallengeEventTag { +data class ChallengeEventTag( + override val eventId: HexKey, +) : GenericETag { + override var relay: NormalizedRelayUrl? = null + override var author: HexKey? = null + + constructor(eventId: HexKey, relayHint: NormalizedRelayUrl? = null, authorPubKeyHex: HexKey? = null) : this(eventId) { + this.relay = relayHint + this.author = authorPubKeyHex + } + + fun toNEvent(): String = NEvent.create(eventId, author, null, relay) + + override fun toTagArray() = assemble(eventId, relay, author) + companion object { const val TAG_NAME = "e" - fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].length == 64 + fun isTagged(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].length == 64 - fun parse(tag: Array): String? { - ensure(tag.has(1) && tag[0] == TAG_NAME) { return null } + fun isTagged( + tag: Array, + eventId: HexKey, + ) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == eventId + + fun isTagged( + tag: Array, + eventIds: Set, + ) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in eventIds + + fun parse(tag: Array): ChallengeEventTag? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].length == 64) { return null } + + val hint = tag.getOrNull(2)?.let { RelayUrlNormalizer.normalizeOrNull(it) } + + return ChallengeEventTag(tag[1], hint, tag.getOrNull(3)) + } + + fun parseId(tag: Array): String? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } ensure(tag[1].length == 64) { return null } return tag[1] } - fun assemble(challengeEventId: String) = arrayOf(TAG_NAME, challengeEventId) + fun parseAsHint(tag: Array): EventIdHint? { + ensure(tag.has(2)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].length == 64) { return null } + ensure(tag[2].isNotEmpty()) { return null } + + val hint = RelayUrlNormalizer.normalizeOrNull(tag[2]) + + ensure(hint != null) { return null } + + return EventIdHint(tag[1], hint) + } + + fun assemble( + eventId: HexKey, + relay: NormalizedRelayUrl?, + author: HexKey?, + ) = arrayOfNotNull(TAG_NAME, eventId, relay?.url, author) + + fun assemble(eventHint: EventHintBundle) = assemble(eventHint.event.id, eventHint.relay, eventHint.event.pubKey) } } + +fun EventHintBundle.toOpponentTag() = OpponentTag(event.pubKey, authorHomeRelay) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/baseEvent/BaseChessEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/baseEvent/BaseChessEvent.kt new file mode 100644 index 000000000..ef872583f --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/baseEvent/BaseChessEvent.kt @@ -0,0 +1,38 @@ +/* + * 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.nip64Chess.baseEvent + +import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent +import com.vitorpamplona.quartz.nip01Core.core.HexKey + +open class BaseChessEvent( + id: HexKey, + pubKey: HexKey, + createdAt: Long, + kind: Int, + tags: Array>, + content: String, + sig: HexKey, +) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) { + fun opponent() = tags.opponent() + + fun opponentPubkey() = tags.opponentKey() +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/baseEvent/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/baseEvent/TagArrayBuilderExt.kt new file mode 100644 index 000000000..75722abcd --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/baseEvent/TagArrayBuilderExt.kt @@ -0,0 +1,26 @@ +/* + * 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.nip64Chess.baseEvent + +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip64Chess.baseEvent.tags.OpponentTag + +fun TagArrayBuilder.opponent(player: OpponentTag) = addUnique(player.toTagArray()) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/baseEvent/TagArrayExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/baseEvent/TagArrayExt.kt new file mode 100644 index 000000000..bcafdf529 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/baseEvent/TagArrayExt.kt @@ -0,0 +1,28 @@ +/* + * 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.nip64Chess.baseEvent + +import com.vitorpamplona.quartz.nip01Core.core.TagArray +import com.vitorpamplona.quartz.nip64Chess.baseEvent.tags.OpponentTag + +fun TagArray.opponent() = firstNotNullOfOrNull(OpponentTag::parse) + +fun TagArray.opponentKey() = firstNotNullOfOrNull(OpponentTag::parseKey) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/baseEvent/tags/OpponentTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/baseEvent/tags/OpponentTag.kt new file mode 100644 index 000000000..f37f4a999 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/baseEvent/tags/OpponentTag.kt @@ -0,0 +1,84 @@ +/* + * 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.nip64Chess.baseEvent.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.nip01Core.hints.types.PubKeyHint +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer +import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag +import com.vitorpamplona.quartz.utils.arrayOfNotNull +import com.vitorpamplona.quartz.utils.ensure + +@Immutable +data class OpponentTag( + override val pubKey: HexKey, + override val relayHint: NormalizedRelayUrl? = null, +) : PubKeyReferenceTag { + fun toTagArray() = assemble(pubKey, relayHint) + + companion object { + const val TAG_NAME = "p" + + fun isTagged( + tag: Array, + key: HexKey, + ): Boolean = tag.has(1) && tag[0] == TAG_NAME && tag[1] == key + + fun parse(tag: Tag): OpponentTag? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].length == 64) { return null } + + val hint = tag.getOrNull(2)?.let { RelayUrlNormalizer.Companion.normalizeOrNull(it) } + + return OpponentTag(tag[1], hint) + } + + fun parseKey(tag: Array): HexKey? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].length == 64) { return null } + return tag[1] + } + + fun parseAsHint(tag: Array): PubKeyHint? { + ensure(tag.has(2)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].length == 64) { return null } + ensure(tag[2].isNotEmpty()) { return null } + + val hint = RelayUrlNormalizer.Companion.normalizeOrNull(tag[2]) + + ensure(hint != null) { return null } + + return PubKeyHint(tag[1], hint) + } + + fun assemble( + pubkey: HexKey, + relayHint: NormalizedRelayUrl?, + ) = arrayOfNotNull(TAG_NAME, pubkey, relayHint?.url) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/LiveChessGameChallengeEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/LiveChessGameChallengeEvent.kt index 98cc21fa7..acb6d5a45 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/LiveChessGameChallengeEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/LiveChessGameChallengeEvent.kt @@ -21,13 +21,17 @@ package com.vitorpamplona.quartz.nip64Chess.challenge 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.signers.eventTemplate +import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip64Chess.Color +import com.vitorpamplona.quartz.nip64Chess.baseEvent.BaseChessEvent +import com.vitorpamplona.quartz.nip64Chess.baseEvent.opponent +import com.vitorpamplona.quartz.nip64Chess.baseEvent.tags.OpponentTag import com.vitorpamplona.quartz.utils.TimeUtils +import kotlin.let /** * Live Chess Game Challenge Event (Kind 30064) @@ -49,13 +53,13 @@ class LiveChessGameChallengeEvent( tags: Array>, content: String, sig: HexKey, -) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { +) : BaseChessEvent(id, pubKey, createdAt, KIND, tags, content, sig) { + fun gameId() = tags.dTag() + fun playerColor() = tags.playerColor() fun timeControl() = tags.timeControl() - fun opponentPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) - companion object { const val KIND = 30064 const val ALT_DESCRIPTION = "Chess game challenge" @@ -63,14 +67,14 @@ class LiveChessGameChallengeEvent( fun build( gameId: String, playerColor: Color, - opponentPubkey: String? = null, + opponent: OpponentTag? = null, timeControl: String? = null, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { - add(arrayOf("d", gameId)) + dTag(gameId) playerColor(playerColor) - opponentPubkey?.let { add(arrayOf("p", it)) } + opponent?.let { opponent(opponent) } timeControl?.let { timeControl(it) } alt(ALT_DESCRIPTION) initializer() diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/draw/LiveChessDrawOfferEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/draw/LiveChessDrawOfferEvent.kt index ab7c05076..1870f62b5 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/draw/LiveChessDrawOfferEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/draw/LiveChessDrawOfferEvent.kt @@ -21,11 +21,14 @@ package com.vitorpamplona.quartz.nip64Chess.draw 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.signers.eventTemplate +import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip64Chess.baseEvent.BaseChessEvent +import com.vitorpamplona.quartz.nip64Chess.baseEvent.opponent +import com.vitorpamplona.quartz.nip64Chess.baseEvent.tags.OpponentTag import com.vitorpamplona.quartz.utils.TimeUtils /** @@ -48,9 +51,7 @@ class LiveChessDrawOfferEvent( tags: Array>, content: String, sig: HexKey, -) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { - fun opponentPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) - +) : BaseChessEvent(id, pubKey, createdAt, KIND, tags, content, sig) { fun message(): String = content companion object { @@ -59,13 +60,13 @@ class LiveChessDrawOfferEvent( fun build( gameId: String, - opponentPubkey: String, + opponent: OpponentTag, message: String = "", createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, message, createdAt) { - add(arrayOf("d", gameId)) - add(arrayOf("p", opponentPubkey)) + dTag(gameId) + opponent(opponent) alt(ALT_DESCRIPTION) initializer() } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/LiveChessGameEndEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/LiveChessGameEndEvent.kt index f7f2773ec..6f90d1316 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/LiveChessGameEndEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/LiveChessGameEndEvent.kt @@ -21,13 +21,16 @@ package com.vitorpamplona.quartz.nip64Chess.end 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.signers.eventTemplate +import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip64Chess.GameResult import com.vitorpamplona.quartz.nip64Chess.GameTermination +import com.vitorpamplona.quartz.nip64Chess.baseEvent.BaseChessEvent +import com.vitorpamplona.quartz.nip64Chess.baseEvent.opponent +import com.vitorpamplona.quartz.nip64Chess.baseEvent.tags.OpponentTag import com.vitorpamplona.quartz.utils.TimeUtils /** @@ -52,15 +55,13 @@ class LiveChessGameEndEvent( tags: Array>, content: String, sig: HexKey, -) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { +) : BaseChessEvent(id, pubKey, createdAt, KIND, tags, content, sig) { fun result() = tags.result() fun termination() = tags.termination() fun winnerPubkey() = tags.winnerPubkey() - fun opponentPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) - fun pgn(): String = content companion object { @@ -72,16 +73,16 @@ class LiveChessGameEndEvent( result: GameResult, termination: GameTermination, winnerPubkey: String? = null, - opponentPubkey: String, + opponent: OpponentTag, pgn: String = "", createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, pgn, createdAt) { - add(arrayOf("d", gameId)) + dTag(gameId) result(result) termination(termination) winnerPubkey?.let { winner(it) } - add(arrayOf("p", opponentPubkey)) + opponent(opponent) alt("$ALT_DESCRIPTION: ${result.notation}") initializer() } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/LiveChessMoveEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/LiveChessMoveEvent.kt index 869930687..7c515aa41 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/LiveChessMoveEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/LiveChessMoveEvent.kt @@ -21,12 +21,16 @@ package com.vitorpamplona.quartz.nip64Chess.move 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.signers.eventTemplate +import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip64Chess.baseEvent.BaseChessEvent +import com.vitorpamplona.quartz.nip64Chess.baseEvent.opponent +import com.vitorpamplona.quartz.nip64Chess.baseEvent.tags.OpponentTag import com.vitorpamplona.quartz.utils.TimeUtils +import kotlinx.serialization.json.JsonNull.content /** * Live Chess Move Event (Kind 30066) @@ -50,7 +54,7 @@ class LiveChessMoveEvent( tags: Array>, content: String, sig: HexKey, -) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { +) : BaseChessEvent(id, pubKey, createdAt, KIND, tags, content, sig) { fun gameId() = tags.gameId() fun moveNumber() = tags.moveNumber() @@ -59,8 +63,6 @@ class LiveChessMoveEvent( fun fen() = tags.fen() - fun opponentPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) - fun comment(): String = content companion object { @@ -72,17 +74,17 @@ class LiveChessMoveEvent( moveNumber: Int, san: String, fen: String, - opponentPubkey: String, + opponent: OpponentTag, comment: String = "", createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, comment, createdAt) { - add(arrayOf("d", "$gameId-$moveNumber")) + dTag("$gameId-$moveNumber") gameId(gameId) moveNumber(moveNumber) san(san) fen(fen) - add(arrayOf("p", opponentPubkey)) + opponent(opponent) alt("$ALT_DESCRIPTION: $san") initializer() }