Organizes Chess class and common tags

This commit is contained in:
Vitor Pamplona
2026-03-04 09:19:39 -05:00
parent 4060bef2be
commit 46dcd105c8
12 changed files with 310 additions and 55 deletions
@@ -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<Array<String>>,
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<LiveChessGameChallengeEvent>,
createdAt: Long = TimeUtils.now(),
initializer: TagArrayBuilder<LiveChessGameAcceptEvent>.() -> Unit = {},
) = eventTemplate(KIND, "", createdAt) {
add(arrayOf("d", gameId))
challengeEvent(challengeEventId)
add(arrayOf("p", challengerPubkey))
dTag(gameId)
challenge(challengeEvent)
challenger(challengeEvent)
alt(ALT_DESCRIPTION)
initializer()
}
@@ -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<LiveChessGameAcceptEvent>.challengeEvent(challengeEventId: String) = add(ChallengeEventTag.assemble(challengeEventId))
fun TagArrayBuilder<LiveChessGameAcceptEvent>.challenge(challengeHint: EventHintBundle<LiveChessGameChallengeEvent>) = addUnique(ChallengeEventTag.assemble(challengeHint))
fun TagArrayBuilder<LiveChessGameAcceptEvent>.challenger(challengeHint: EventHintBundle<LiveChessGameChallengeEvent>) = add(challengeHint.toOpponentTag().toTagArray())
@@ -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<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].length == 64
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].length == 64
fun parse(tag: Array<String>): String? {
ensure(tag.has(1) && tag[0] == TAG_NAME) { return null }
fun isTagged(
tag: Array<String>,
eventId: HexKey,
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == eventId
fun isTagged(
tag: Array<String>,
eventIds: Set<HexKey>,
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in eventIds
fun parse(tag: Array<String>): 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>): 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<String>): 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<LiveChessGameChallengeEvent>) = assemble(eventHint.event.id, eventHint.relay, eventHint.event.pubKey)
}
}
fun EventHintBundle<LiveChessGameChallengeEvent>.toOpponentTag() = OpponentTag(event.pubKey, authorHomeRelay)
@@ -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<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
fun opponent() = tags.opponent()
fun opponentPubkey() = tags.opponentKey()
}
@@ -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 <T : BaseChessEvent> TagArrayBuilder<T>.opponent(player: OpponentTag) = addUnique(player.toTagArray())
@@ -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)
@@ -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<String>,
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<String>): 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<String>): 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)
}
}
@@ -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<Array<String>>,
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<LiveChessGameChallengeEvent>.() -> 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()
@@ -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<Array<String>>,
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<LiveChessDrawOfferEvent>.() -> Unit = {},
) = eventTemplate(KIND, message, createdAt) {
add(arrayOf("d", gameId))
add(arrayOf("p", opponentPubkey))
dTag(gameId)
opponent(opponent)
alt(ALT_DESCRIPTION)
initializer()
}
@@ -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<Array<String>>,
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<LiveChessGameEndEvent>.() -> 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()
}
@@ -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<Array<String>>,
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<LiveChessMoveEvent>.() -> 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()
}