Massive refactoring of Quartz to:
- Define each tag in their own class. - Allow extension functions to additional responsibilities to other classes - Migrate from hardcoded tag filters in events to the Tag's parser and assemble functions. - Migrate hardcoded event.create to builders that use extension functions - Restructures threading infrastructure for NIP-10 - Decouple the event signing from the Event building functions via event templates - Create classes to represent Tags and TagArrays and use extension functions to add domain-related methods to the tag array of each nip. - Uses external functions on event template builders to better point to which functions and which tags can be used in which event kinds. - Separates Event kinds in packages inside each nip. - Improves support for NIP-89 - Correctly establishes which imeta params can be used in each nip (video, picture, files) - Decouples the iMeta builder from any nip. - Fixes mute list word and user removal when inserted from a different client. - Migrates the Account class to avoiding having to build each Event inside of it
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
package com.vitorpamplona.quartz
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import org.junit.Assert.assertEquals
|
||||
|
||||
@@ -24,8 +24,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.hasValidSignature
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.verify
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@@ -48,7 +48,7 @@ class LargeDBSignatureCheck {
|
||||
|
||||
var counter = 0
|
||||
eventArray.forEach {
|
||||
assertTrue(it.hasValidSignature())
|
||||
assertTrue(it.verify())
|
||||
counter++
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class LargeDBSignatureCheck {
|
||||
var counter = 0
|
||||
eventArray.forEach {
|
||||
if (it.sig != "") {
|
||||
assertTrue(it.hasValidSignature())
|
||||
assertTrue(it.verify())
|
||||
}
|
||||
counter++
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.quartz.nip01Core
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.utils.sha256Hash
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import org.junit.Assert.assertEquals
|
||||
|
||||
+14
-13
@@ -20,20 +20,19 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip03Timestamp.ots
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.OtsResolver
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import junit.framework.TestCase.fail
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class OtsTest {
|
||||
val otsEvent = "{\"content\":\"AE9wZW5UaW1lc3RhbXBzAABQcm9vZgC/ieLohOiSlAEIqCiiW0FlsU9lqK5f1A+cL6CGJ1Ah4V/A1yNJY/stUE3wECJz6ng/QxU5Z6xwaMx97qkI//AQqJv8bEMrGTplGWRv5qm4DgjxIHkcQqzpL0Fjr9VBAAijDe0IsQYpOhw1SIjZIgQa6i16CPEEZck7CvAIxR0AloJzCZoAg9/jDS75DI4uLWh0dHBzOi8vYWxpY2UuYnRjLmNhbGVuZGFyLm9wZW50aW1lc3RhbXBzLm9yZ//wEOwPtjIkKI1hmtv9t1kuxZcI8QRlyTsK8Ahl0wrCSggZzgCD3+MNLvkMjiwraHR0cHM6Ly9ib2IuYnRjLmNhbGVuZGFyLm9wZW50aW1lc3RhbXBzLm9yZ//wEE1dVGa8JCuf2ek0c5ybDKII8SCBoVz8Sal45Kd1O8STWIGJTcl5JPtAZBZitqk3BE9MqAjxBGXJOwrwCHoGVgAZi9q9AIPf4w0u+QyOKShodHRwczovL2Zpbm5leS5jYWxlbmRhci5ldGVybml0eXdhbGwuY29t8BAFZFXFYg7DJJ0OzjmJ0FKWCPEEZck7CvAI0M49IcBR5bf/AIPf4w0u+QyOIyJodHRwczovL2J0Yy5jYWxlbmRhci5jYXRhbGxheHkuY29tCPEgoE3IfYTmxxo4W/x/QYp/NGX6Wu93gSQkwbpjpOhZORcI8SDWLLurVQaXHdUuwivCfTfuxYCaq+AzypSGqLDAVocrEgjwIBfgjta16y13Gp4etQOCa9YiKEcM+/9AieG/vZolr3IDCPAgMR2zFCb384CEi8tVuI2fHgLT3I9zpe7oqJTzCcEqxWEI8SAJSdgeeosr7IxdOt8r7f0ipWc8FI6GAhgep8zSRgWikAjxIGxYmtCsC79Tx4z4YsT1WuMo+ycMkwhGQsQltF597cchCPAgCLrBf9vR1Aex6yY+vSkXAvLjMKdMqM/a1g8zNPwLeJcI8SBDCbTk4CczTuiIyZeUyYRVh31BZdjaSd2nU/pBQxu+6QjwIOwqE9/WqGC6CHH0i+tr7edvYX5PstSDf08KmnMqsqCoCPEgQIdEBg358vfek3Qjfyrgl51iCU6WUWmThsGLPDTcB0QI8SAX4dp64iI8pBx+zBqAQwUN6XgZ1cEfT8+2vha/9I1vzwjxWQEAAAAB8YJxvxJJth8OnxIV6UOXveIZAcJPTHcAkWgnucpuYqYAAAAAAP3///8CRhoMAAAAAAAWABTUUZK82uAvbU3vVyaaPIOddZBicwAAAAAAAAAAImog8ARCqgwACAjwIB9TcMDLhzgeS1Uw647lNvCfWECkkUvfrrOe6nay0sGdCAjwICYfs90sbPggoMICyOHGYbmOzop2L9mlnh4xqiBLY7yPCAjxINDiVWOBHnRmGJleQdB9myvJAJbNJ9kciZlTOkgJy89mCAjwIHfxqDLdwycj1Vtyth2CaSDdLQwiey9oV6Xov4stLpWNCAjxIGurJYpKJnKp9+y7MAdC+gXgHOiAu5P3RRUFW9l5hGaCCAjxICXqs9hdY0QMP/MNeqlt6s7xaIYtEXZ1CLvou5gaZNEICAjxIHdYxVeI76NXbT2zHcv6lw+v819Ooib7KWxc1GAsiX2fCAjwICPWdi3uBXOlIdmYi+V9C7wAqLyGE4DMoHD+GtvLizqiCAjwIOF2vENtWN5okEMMS+JSf1SGTY9yYP9j0JjXLbC1s+N1CAjxINWsgCtsPxhRNbe372k8/20WDbiL9e8934hGF256DvRECAjwII3mi+Li06j10ORxg0dYMkcsyGb115Jiqq1YEV3K/u+aCAgABYiWDXPXGQEDw9Qy\",\"created_at\":1707690688,\"id\":\"759f9da5846e936fab06766a524b36ba71c03bbc69ad0944fb8ee4bb1f3dd705\",\"kind\":1040,\"pubkey\":\"82fbb08c8ef45c4d71c88368d0ae805bc62fb92f166ab04a0b7a0c83d8cbc29a\",\"sig\":\"07c7896c8cbb97b5d7483097590c9d31b73f35c1ad9e752002bb5c1776cbd852e1d32704333d6930c9bc3e40f8b899a1f2e9f91cc3bf797d86acdecba7792576\",\"tags\":[[\"e\",\"a828a25b4165b14f65a8ae5fd40f9c2fa086275021e15fc0d7234963fb2d504d\"],[\"p\",\"595ca8eaace5899cb6ab7e2542bfc972136376f2eabc09287f1857eb8f167e53\"],[\"alt\",\"NIP-03 time stamp\"]]}"
|
||||
val otsEvent2 = "{\"content\":\"AE9wZW5UaW1lc3RhbXBzAABQcm9vZgC/ieLohOiSlAEIqGNPU2jhd4no+zg2ytDkuf5PIoivr8KHI8BL68aKGNbwENyCNtiEN98IzIZgEu3cl6YI//AQ6TkSRd3BTGhDHCK1KkJc+AjxIAHaizG++NNL3Vm13BJrIhT7Br6tEYpb0TVRGaadgiUMCPAgOSDREH9v1Y50UHu79LfC4Lcd9WklQJzRQpw+Unb/pyII8QRltDD58AgqrxfAVrLw7QCD3+MNLvkMji4taHR0cHM6Ly9hbGljZS5idGMuY2FsZW5kYXIub3BlbnRpbWVzdGFtcHMub3Jn//AQQMq/CLpGwY60nmddPS7OVgjxIDKxqd9nl+Mej41vP52Wd7gv7004r3n1rFGDObS8icRvCPAgH9TB/kwvXJEEw+h9Ce6fLaI3MORjtTEge0GbAefT6W4I8QRltDD58AhRcoU3gAo/swCD3+MNLvkMjiwraHR0cHM6Ly9ib2IuYnRjLmNhbGVuZGFyLm9wZW50aW1lc3RhbXBzLm9yZ//wECWtWsKo0uvSr8BYonjs3DEI8CBlsh2ng1Spl0K4oStYElGuMJsjd2uo5nXB+apo5A7ipwjxIM8oxynBwNA+QS/X7Ebtl1kyhFgfoOQioASNfCBzZ4gaCPEEZbQw+fAId6Yd5cw5gioAg9/jDS75DI4pKGh0dHBzOi8vZmlubmV5LmNhbGVuZGFyLmV0ZXJuaXR5d2FsbC5jb23wEJmPzXQbxv0AFTIyjTWjMskI8CAurbkrfrBtlinZXSDxj+m/oIkze57hGjTSxu1Xs87XYQjwIPk/LMD0zIgKoEE2dfeoYrrdHuO6dwmghTwUFajH2QzkCPEEZbQw+fAIE+Pq1/Wmdpj/AIPf4w0u+QyOIyJodHRwczovL2J0Yy5jYWxlbmRhci5jYXRhbGxheHkuY29tCPAgB2CbqkV7VpjRKIl3Ea6cBmB/EHcSN/YCgcc1E+mc07QI8CAfpkZ2Hh4Rukz3x4il3tZqQtlDlbna+I6so2t2YSEmMQjwIJOv32jbsMa2HJwpleRCKLEhgYOoHCSfpv1ZO0YNNNFsCPAgLMM7eFfCjokQfU4gdU5WpG/wBLkO9lDRF0GktL6ujt8I8SCRxJ0bC1PQ8qFmI/1jh8AS5d1/6VRJNMt1Hz41QmNr3QjwIBmgrKBF+OZ3y+XOMv2E7IZ4WwLr2u2H+ehsBfy7cPlICPEgm4ZMCSXzZVWu40d+zk2edaur6KOauo8X7V2KaFBR1VoI8SBKVVOiyq6IFqGn/15kLwk7L8upMAIZ0znjhYxYqSTQCQjwIMBD1twPZ33GxbwTiuOCeJPkoP++6R2wYpCii8UBTdgwCPAg84VkgMXwrt2xxRoeC1/6CtsFctki+w3m8Rs5/6g/IhEI8CCnzZQDhJyicX7bS7U8PMUObuC9Y4TXe+4THoXBMMXkxwjxIMZ0oAvshpcwowR3qPEDbwKZ6B4NPSU4Hz/+4PnD74gnCPAgIfLZEKqAvkMNXfakXoNq1UVqGSzL4Z86z5GzUfbvw8UI8SC8KoIeLvjd4vJ/xhNVphakPRd80YKeNkeYEuVH8k2EtAjwIPRtinLLxzt8iuw0XZtpTDzEstZOTNYVm+Bi3fEzdeIuCPFZAQAAAAE8vsasINN5DKon0KakX2HNdCB126ZLKXrw4PfvyEfqbwAAAAAA/f///wLPGw8AAAAAABYAFCvgxlh6msa4ZOtgvlc5KiZCx7IvAAAAAAAAAAAiaiDwBKygDAAICPEgB/2DJ3s6gMky/PceGZocTFRXjZUiCCAhHGYwQk/8yrUICPEgDuSd6+PJHUMuEHyLcKFxw7xfvRHRfInjkV3/Zy3BxqAICPEgZDgQ+4VXzlOIkGoO8EVxDgs2cWaeh4EEiaqa/y50gKAICPAgFI+zIuYcMF69GmPQVsXa9oy8eng7MeRZdIxArQyeX3oICPEgX5HYIuImpiSTEapgEssEW4l+W+4aRfNCG3pZf7z0hCoICPEgPkAbOSjFdtS4NT7MXgMYVQoQhI1JZtdFxUu4J3NTt7IICPEguW3qyuyGjctu5d9rM9P9ZCs/ZK4vAc+z21b9ygklgWAICPEgu4e2645xtvGhI1Zzuiv23vRhwE8uC9vj1TAgNg/C8UcICPAgwoQX8X0nY4HoQLRsJ0z8JCWQDzRh2iL2QXEb8z3gbjIICPAgzTwlPRtStsLJWhz3Q/0l8tMnrPSHVuh+zCiGk95dW2MICPAgGcBEuYZyzFNapHOfnJ9Q515QzO2VbIRhlVI0vIhd4jwICPAgidRMoM2pA+KmVJenVrLcbollsbUg9lL9bmv1C1dSxswICPAgZinGakwhbHdanTaRJeBkEUlbhfNokvj8b5KneyG+wzIICAAFiJYNc9cZAQOtwTI=\",\"created_at\":1706324334,\"id\":\"2ad074ddb7724eb13b4244b49cf2321b1057f37fdf8ce102e6329b839cf763a9\",\"kind\":1040,\"pubkey\":\"82fbb08c8ef45c4d71c88368d0ae805bc62fb92f166ab04a0b7a0c83d8cbc29a\",\"sig\":\"ad7274bb32ba9e9cfdbd52f4887e8a2fda1047c75a7185b2ab7ff254ebac14ed48a2b60737494d655e24c9400eeeec7e29293a77bfcaafaecd94b350c9a2c22b\",\"tags\":[[\"e\",\"a8634f5368e17789e8fb3836cad0e4b9fe4f2288afafc28723c04bebc68a18d6\"],[\"p\",\"c31e22c3715c1bde5608b7e0d04904f22f5fc453ba1806d21c9f2382e1e58c6c\"],[\"alt\",\"NIP-03 time stamp\"]]}"
|
||||
@@ -44,37 +43,39 @@ class OtsTest {
|
||||
@Test
|
||||
fun verifyNostrEvent() {
|
||||
val ots = Event.fromJson(otsEvent) as OtsEvent
|
||||
println(ots.info())
|
||||
println(OtsResolver.info(ots.otsByteArray()))
|
||||
assertEquals(1707688818L, ots.verify())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyNostrEvent2() {
|
||||
val ots = Event.Companion.fromJson(otsEvent2) as OtsEvent
|
||||
println(ots.info())
|
||||
println(OtsResolver.info(ots.otsByteArray()))
|
||||
assertEquals(1706322179L, ots.verify())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyNostrPendingEvent() {
|
||||
val ots = Event.Companion.fromJson(otsPendingEvent) as OtsEvent
|
||||
println(ots.info())
|
||||
println(OtsResolver.info(ots.otsByteArray()))
|
||||
assertEquals(null, ots.verify())
|
||||
|
||||
val eventId =
|
||||
ots.digestEvent() ?: run {
|
||||
ots.digestEventId() ?: run {
|
||||
fail("Should not be null")
|
||||
return
|
||||
}
|
||||
|
||||
val upgraded = OtsEvent.upgrade(ots.content, eventId)
|
||||
val upgraded = OtsEvent.upgrade(ots.otsByteArray(), eventId)
|
||||
|
||||
assertNotNull(upgraded)
|
||||
|
||||
val signer = NostrSignerInternal(KeyPair())
|
||||
|
||||
var newOts: OtsEvent? = null
|
||||
val countDownLatch = CountDownLatch(1)
|
||||
|
||||
OtsEvent.create(eventId, upgraded, signer) {
|
||||
signer.sign(OtsEvent.build(eventId, upgraded!!)) {
|
||||
newOts = it
|
||||
countDownLatch.countDown()
|
||||
}
|
||||
@@ -82,7 +83,7 @@ class OtsTest {
|
||||
Assert.assertTrue(countDownLatch.await(1, TimeUnit.SECONDS))
|
||||
|
||||
println(newOts!!.toJson())
|
||||
println(newOts!!.info())
|
||||
println(OtsResolver.info(newOts!!.otsByteArray()))
|
||||
|
||||
assertEquals(1708879025L, newOts!!.verify())
|
||||
}
|
||||
@@ -94,7 +95,7 @@ class OtsTest {
|
||||
|
||||
val countDownLatch = CountDownLatch(1)
|
||||
|
||||
OtsEvent.create(otsEvent2Digest, OtsEvent.stamp(otsEvent2Digest), signer) {
|
||||
signer.sign(OtsEvent.build(otsEvent2Digest, OtsEvent.stamp(otsEvent2Digest))) {
|
||||
ots = it
|
||||
countDownLatch.countDown()
|
||||
}
|
||||
@@ -102,7 +103,7 @@ class OtsTest {
|
||||
Assert.assertTrue(countDownLatch.await(1, TimeUnit.SECONDS))
|
||||
|
||||
println(ots!!.toJson())
|
||||
println(ots!!.info())
|
||||
println(OtsResolver.info(ots!!.otsByteArray()))
|
||||
|
||||
assertEquals(null, ots!!.verify())
|
||||
}
|
||||
|
||||
+7
-7
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip04Dm
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.Nip01
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
@@ -29,10 +29,10 @@ import junit.framework.TestCase.assertTrue
|
||||
import org.junit.Test
|
||||
import java.security.SecureRandom
|
||||
|
||||
class Nip04Test {
|
||||
class Nip04EncryptionTest {
|
||||
private val random = SecureRandom()
|
||||
private val nip01 = Nip01(Secp256k1.get(), random)
|
||||
private val nip04 = Nip04(Secp256k1.get(), random)
|
||||
private val nip04 = Nip04Encryption(Secp256k1.get(), random)
|
||||
|
||||
val sk1 = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe".hexToByteArray()
|
||||
val sk2 = "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220".hexToByteArray()
|
||||
@@ -89,17 +89,17 @@ class Nip04Test {
|
||||
|
||||
@Test
|
||||
fun isNIP04Encode() {
|
||||
assertTrue(Nip04.isNIP04("Xj/oZZolaItdyQ5v7xYFpA==?iv=+a6zagBp+mr5m1aFbHQ8lA=="))
|
||||
assertTrue(Nip04.isNIP04("zJxfaJ32rN5Dg1ODjOlEew==?iv=EV5bUjcc4OX2Km/zPp4ndQ=="))
|
||||
assertTrue(Nip04Encryption.isNIP04("Xj/oZZolaItdyQ5v7xYFpA==?iv=+a6zagBp+mr5m1aFbHQ8lA=="))
|
||||
assertTrue(Nip04Encryption.isNIP04("zJxfaJ32rN5Dg1ODjOlEew==?iv=EV5bUjcc4OX2Km/zPp4ndQ=="))
|
||||
assertTrue(
|
||||
Nip04.isNIP04("6f8dMstm+udOu7yipSn33orTmwQpWbtfuY95NH+eTU1kArysWJIDkYgI2D25EAGIDJsNd45jOJ2NbVOhFiL3ZP/NWsTwXokk34iyHyA/lkjzugQ1bHXoMD1fP/Ay4hB4al1NHb8HXHKZaxPrErwdRDb8qa/I6dXb/1xxyVvNQBHHvmsM5yIFaPwnCN1DZqXf2KbTA/Ekz7Hy+7R+Sy3TXLQDFpWYqykppkXc7Fs0qSuPRyxz5+anuN0dxZa9GTwTEnBrZPbthKkNRrvZMdTGJ6WumOh9aUq8OJJWy9aOgsXvs7qjN1UqcCqQqYaVnEOhCaqWNDsVtsFrVDj+SaLIBvCiomwF4C4nIgngJ5I69tx0UNI0q+ZnvOGQZ7m1PpW2NYP7Yw43HJNdeUEQAmdCPnh/PJwzLTnIxHmQU7n7SPlMdV0SFa6H8y2HHvex697GAkyE5t8c2uO24OnqIwF1tR3blIqXzTSRl0GA6QvrSj2p4UtnWjvF7xT7RiIEyTtgU/AsihTrXyXzWWZaIBJogpgw6erlZqWjCH7sZy/WoGYEiblobOAqMYxax6vRbeuGtoYksr/myX+x9rfLrYuoDRTw4woXOLmMrrj+Mf0TbAgc3SjdkqdsPU1553rlSqIEZXuFgoWmxvVQDtekgTYyS97G81TDSK9nTJT5ilku8NVq2LgtBXGwsNIw/xekcOUzJke3kpnFPutNaexR1VF3ohIuqRKYRGcd8ADJP2lfwMcaGRiplAmFoaVS1YUhQwYFNq9rMLf7YauRGV4BJg/t9srdGxf5RoKCvRo+XM/nLxxysTR9MVaEP/3lDqjwChMxs+eWfLHE5vRWV8hUEqdrWNZV29gsx5nQpzJ4PARGZVu310pQzc6JAlc2XAhhFk6RamkYJnmCSMnb/RblzIATBi2kNrCVAlaXIon188inB62rEpZGPkRIP7PUfu27S/elLQHBHeGDsxOXsBRo1gl3te+raoBHsxo6zvRnYbwdAQa5taDE63eh+fT6kFI+xYmXNAQkU8Dp0MVhEh4JQI06Ni/AKrvYpC95TXXIphZcF+/Pv/vaGkhG2X9S3uhugwWK?iv=2vWkOQQi0WynNJz/aZ4k2g=="),
|
||||
Nip04Encryption.isNIP04("6f8dMstm+udOu7yipSn33orTmwQpWbtfuY95NH+eTU1kArysWJIDkYgI2D25EAGIDJsNd45jOJ2NbVOhFiL3ZP/NWsTwXokk34iyHyA/lkjzugQ1bHXoMD1fP/Ay4hB4al1NHb8HXHKZaxPrErwdRDb8qa/I6dXb/1xxyVvNQBHHvmsM5yIFaPwnCN1DZqXf2KbTA/Ekz7Hy+7R+Sy3TXLQDFpWYqykppkXc7Fs0qSuPRyxz5+anuN0dxZa9GTwTEnBrZPbthKkNRrvZMdTGJ6WumOh9aUq8OJJWy9aOgsXvs7qjN1UqcCqQqYaVnEOhCaqWNDsVtsFrVDj+SaLIBvCiomwF4C4nIgngJ5I69tx0UNI0q+ZnvOGQZ7m1PpW2NYP7Yw43HJNdeUEQAmdCPnh/PJwzLTnIxHmQU7n7SPlMdV0SFa6H8y2HHvex697GAkyE5t8c2uO24OnqIwF1tR3blIqXzTSRl0GA6QvrSj2p4UtnWjvF7xT7RiIEyTtgU/AsihTrXyXzWWZaIBJogpgw6erlZqWjCH7sZy/WoGYEiblobOAqMYxax6vRbeuGtoYksr/myX+x9rfLrYuoDRTw4woXOLmMrrj+Mf0TbAgc3SjdkqdsPU1553rlSqIEZXuFgoWmxvVQDtekgTYyS97G81TDSK9nTJT5ilku8NVq2LgtBXGwsNIw/xekcOUzJke3kpnFPutNaexR1VF3ohIuqRKYRGcd8ADJP2lfwMcaGRiplAmFoaVS1YUhQwYFNq9rMLf7YauRGV4BJg/t9srdGxf5RoKCvRo+XM/nLxxysTR9MVaEP/3lDqjwChMxs+eWfLHE5vRWV8hUEqdrWNZV29gsx5nQpzJ4PARGZVu310pQzc6JAlc2XAhhFk6RamkYJnmCSMnb/RblzIATBi2kNrCVAlaXIon188inB62rEpZGPkRIP7PUfu27S/elLQHBHeGDsxOXsBRo1gl3te+raoBHsxo6zvRnYbwdAQa5taDE63eh+fT6kFI+xYmXNAQkU8Dp0MVhEh4JQI06Ni/AKrvYpC95TXXIphZcF+/Pv/vaGkhG2X9S3uhugwWK?iv=2vWkOQQi0WynNJz/aZ4k2g=="),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isNIP04EncodeWithBug() {
|
||||
assertTrue(
|
||||
Nip04.isNIP04(
|
||||
Nip04Encryption.isNIP04(
|
||||
"QOAYBWa88ConWs2C4kSvNqAcowCtg0ZRtAl7FyLSv9VMaJH4oCiDx0h8VLBnV97HdE4lv" +
|
||||
"TW7AYC1eEw8/t1dbe0qRc3XrOt7MrPAO8yqpy1/3lFB1+10kip0+KdgT8Quvv02wTP8Dqi" +
|
||||
"xpr2fliAIG2ONvDn+O5V0q9aVUN9HitgL/myTyR0T42edmxWeZoMBEOKvJyO80FekSsgVL" +
|
||||
@@ -21,82 +21,49 @@
|
||||
package com.vitorpamplona.quartz.nip10Notes
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEventIds
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
|
||||
import com.vitorpamplona.quartz.nip01Core.verify
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class CitationTests {
|
||||
val json =
|
||||
"""
|
||||
{
|
||||
"content": "Astral:\n\nhttps://void.cat/d/A5Fba5B1bcxwEmeyoD9nBs.webp\n\nIris:\n\nhttps://void.cat/d/44hTcVvhRps6xYYs99QsqA.webp\n\nSnort:\n\nhttps://void.cat/d/4nJD5TRePuQChM5tzteYbU.webp\n\nAmethyst agrees with Astral which I suspect are both wrong. nostr:npub13sx6fp3pxq5rl70x0kyfmunyzaa9pzt5utltjm0p8xqyafndv95q3saapa nostr:npub1v0lxxxxutpvrelsksy8cdhgfux9l6a42hsj2qzquu2zk7vc9qnkszrqj49 nostr:npub1g53mukxnjkcmr94fhryzkqutdz2ukq4ks0gvy5af25rgmwsl4ngq43drvk nostr:npub1gcxzte5zlkncx26j68ez60fzkvtkm9e0vrwdcvsjakxf9mu9qewqlfnj5z ",
|
||||
"created_at": 1683596206,
|
||||
"id": "98b574c3527f0ffb30b7271084e3f07480733c7289f8de424d29eae82e36c758",
|
||||
"kind": 1,
|
||||
"pubkey": "46fcbe3065eaf1ae7811465924e48923363ff3f526bd6f73d7c184b16bd8ce4d",
|
||||
"sig": "4aa5264965018fa12a326686ad3d3bd8beae3218dcc83689b19ca1e6baeb791531943c15363aa6707c7c0c8b2d601deca1f20c32078b2872d356cdca03b04cce",
|
||||
"tags": [
|
||||
[
|
||||
"e",
|
||||
"27ac621d7dc4a932e1a79f984308e7d20656dd6fddb2ce9cdfcb6a67b9a7bcc3",
|
||||
"",
|
||||
"root"
|
||||
],
|
||||
[
|
||||
"e",
|
||||
"be7245af96210a0dd048cab4ad38e52dbd6c09a53ea21a7edb6be8898e5727cc",
|
||||
"",
|
||||
"reply"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"22aa81510ee63fe2b16cae16e0921f78e9ba9882e2868e7e63ad6d08ae9b5954"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"22aa81510ee63fe2b16cae16e0921f78e9ba9882e2868e7e63ad6d08ae9b5954"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"ec4d241c334311b3a304433ee3442be29d0e88e7ec19b85edf2bba29b93565e2"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"0fe0b18b4dbf0e0aa40fcd47209b2a49b3431fc453b460efcf45ca0bd16bd6ac"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"4523be58d395b1b196a9b8c82b038b6895cb02b683d0c253a955068dba1facd0"
|
||||
],
|
||||
[
|
||||
"p",
|
||||
"460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c"
|
||||
]
|
||||
],
|
||||
"seenOn": [
|
||||
"wss://nostr.wine/"
|
||||
]
|
||||
}
|
||||
"""
|
||||
val note =
|
||||
TextNoteEvent(
|
||||
"98b574c3527f0ffb30b7271084e3f07480733c7289f8de424d29eae82e36c758",
|
||||
"46fcbe3065eaf1ae7811465924e48923363ff3f526bd6f73d7c184b16bd8ce4d",
|
||||
1683596206,
|
||||
arrayOf(
|
||||
arrayOf("e", "27ac621d7dc4a932e1a79f984308e7d20656dd6fddb2ce9cdfcb6a67b9a7bcc3", "", "root"),
|
||||
arrayOf("e", "be7245af96210a0dd048cab4ad38e52dbd6c09a53ea21a7edb6be8898e5727cc", "", "reply"),
|
||||
arrayOf("p", "22aa81510ee63fe2b16cae16e0921f78e9ba9882e2868e7e63ad6d08ae9b5954"),
|
||||
arrayOf("p", "22aa81510ee63fe2b16cae16e0921f78e9ba9882e2868e7e63ad6d08ae9b5954"),
|
||||
arrayOf("p", "3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24"),
|
||||
arrayOf("p", "ec4d241c334311b3a304433ee3442be29d0e88e7ec19b85edf2bba29b93565e2"),
|
||||
arrayOf("p", "0fe0b18b4dbf0e0aa40fcd47209b2a49b3431fc453b460efcf45ca0bd16bd6ac"),
|
||||
arrayOf("p", "8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168"),
|
||||
arrayOf("p", "63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed"),
|
||||
arrayOf("p", "4523be58d395b1b196a9b8c82b038b6895cb02b683d0c253a955068dba1facd0"),
|
||||
arrayOf("p", "460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c"),
|
||||
),
|
||||
"Astral:\n\nhttps://void.cat/d/A5Fba5B1bcxwEmeyoD9nBs.webp\n\nIris:\n\nhttps://void.cat/d/44hTcVvhRps6xYYs99QsqA.webp\n\nSnort:\n\nhttps://void.cat/d/4nJD5TRePuQChM5tzteYbU.webp\n\nAmethyst agrees with Astral which I suspect are both wrong. nostr:npub13sx6fp3pxq5rl70x0kyfmunyzaa9pzt5utltjm0p8xqyafndv95q3saapa nostr:npub1v0lxxxxutpvrelsksy8cdhgfux9l6a42hsj2qzquu2zk7vc9qnkszrqj49 nostr:npub1g53mukxnjkcmr94fhryzkqutdz2ukq4ks0gvy5af25rgmwsl4ngq43drvk nostr:npub1gcxzte5zlkncx26j68ez60fzkvtkm9e0vrwdcvsjakxf9mu9qewqlfnj5z ",
|
||||
"4aa5264965018fa12a326686ad3d3bd8beae3218dcc83689b19ca1e6baeb791531943c15363aa6707c7c0c8b2d601deca1f20c32078b2872d356cdca03b04cce",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun parseEvent() {
|
||||
val event = EventMapper.fromJson(json) as TextNoteEvent
|
||||
fun verifyEvent() {
|
||||
assertTrue(note.verify())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCitedUsers() {
|
||||
val expectedCitations =
|
||||
setOf(
|
||||
"8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168",
|
||||
@@ -105,6 +72,75 @@ class CitationTests {
|
||||
"460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c",
|
||||
)
|
||||
|
||||
assertEquals(expectedCitations, event.citedUsers())
|
||||
assertEquals(expectedCitations, note.citedUsers())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTagsWithoutCitations() {
|
||||
val expectedTagsWithoutCitations =
|
||||
listOf(
|
||||
"27ac621d7dc4a932e1a79f984308e7d20656dd6fddb2ce9cdfcb6a67b9a7bcc3",
|
||||
"be7245af96210a0dd048cab4ad38e52dbd6c09a53ea21a7edb6be8898e5727cc",
|
||||
)
|
||||
|
||||
assertEquals(expectedTagsWithoutCitations, note.tagsWithoutCitations())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTaggedEventIds() {
|
||||
val eventIds =
|
||||
listOf(
|
||||
"27ac621d7dc4a932e1a79f984308e7d20656dd6fddb2ce9cdfcb6a67b9a7bcc3",
|
||||
"be7245af96210a0dd048cab4ad38e52dbd6c09a53ea21a7edb6be8898e5727cc",
|
||||
)
|
||||
|
||||
assertEquals(eventIds, note.taggedEventIds())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTaggedEvents() {
|
||||
val eventIds =
|
||||
listOf(
|
||||
ETag("27ac621d7dc4a932e1a79f984308e7d20656dd6fddb2ce9cdfcb6a67b9a7bcc3"),
|
||||
ETag("be7245af96210a0dd048cab4ad38e52dbd6c09a53ea21a7edb6be8898e5727cc"),
|
||||
)
|
||||
|
||||
assertEquals(eventIds, note.taggedEvents())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTaggedUsers() {
|
||||
val eventIds =
|
||||
listOf(
|
||||
PTag("22aa81510ee63fe2b16cae16e0921f78e9ba9882e2868e7e63ad6d08ae9b5954"),
|
||||
PTag("22aa81510ee63fe2b16cae16e0921f78e9ba9882e2868e7e63ad6d08ae9b5954"),
|
||||
PTag("3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24"),
|
||||
PTag("ec4d241c334311b3a304433ee3442be29d0e88e7ec19b85edf2bba29b93565e2"),
|
||||
PTag("0fe0b18b4dbf0e0aa40fcd47209b2a49b3431fc453b460efcf45ca0bd16bd6ac"),
|
||||
PTag("8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168"),
|
||||
PTag("63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed"),
|
||||
PTag("4523be58d395b1b196a9b8c82b038b6895cb02b683d0c253a955068dba1facd0"),
|
||||
PTag("460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c"),
|
||||
)
|
||||
|
||||
assertEquals(eventIds, note.taggedUsers())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTaggedUserIds() {
|
||||
val eventIds =
|
||||
listOf(
|
||||
"22aa81510ee63fe2b16cae16e0921f78e9ba9882e2868e7e63ad6d08ae9b5954",
|
||||
"22aa81510ee63fe2b16cae16e0921f78e9ba9882e2868e7e63ad6d08ae9b5954",
|
||||
"3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24",
|
||||
"ec4d241c334311b3a304433ee3442be29d0e88e7ec19b85edf2bba29b93565e2",
|
||||
"0fe0b18b4dbf0e0aa40fcd47209b2a49b3431fc453b460efcf45ca0bd16bd6ac",
|
||||
"8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168",
|
||||
"63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed",
|
||||
"4523be58d395b1b196a9b8c82b038b6895cb02b683d0c253a955068dba1facd0",
|
||||
"460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c",
|
||||
)
|
||||
|
||||
assertEquals(eventIds, note.taggedUserIds())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.nip10Notes
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
|
||||
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ThreadingTests {
|
||||
@Test
|
||||
fun testLegacyEvent() {
|
||||
val note =
|
||||
TextNoteEvent(
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
arrayOf(
|
||||
arrayOf("p", "4ca4f5533e40da5e0508796d409e6bb35a50b26fc304345617ab017183d83ac0"),
|
||||
arrayOf("p", "534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec"),
|
||||
arrayOf("p", "77ce56f89d1228f7ff3743ce1ad1b254857b9008564727ebd5a1f317362f6ca7"),
|
||||
arrayOf("e", "89f220b63465c93542b1a78caa3a952cf4f196e91a50596493c8093c533ebc4d"),
|
||||
arrayOf("e", "090c037b2e399ee74d9f134758928948dd9154413ca1a1acb37155046e03a051"),
|
||||
arrayOf("e", "567b7c11f0fe582361e3cea6fcc7609a8942dfe196ee1b98d5604c93fbeea976"),
|
||||
arrayOf("e", "49aff7ae6daeaaa2777931b90f9bb29f6cb01c5a3d7d88c8ba82d890f264afb4"),
|
||||
arrayOf("e", "5e081ebb19153357d7c31e8a10b9ceeef29313f58dc8d701f66727fab02aef64"),
|
||||
arrayOf("e", "bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631"),
|
||||
arrayOf("e", "b857504288c18a15950dd05b9e8772c62ca6289d5aac373c0a8ee5b132e94e7c"),
|
||||
),
|
||||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
val taggedUsers =
|
||||
listOf(
|
||||
PTag("4ca4f5533e40da5e0508796d409e6bb35a50b26fc304345617ab017183d83ac0"),
|
||||
PTag("534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec"),
|
||||
PTag("77ce56f89d1228f7ff3743ce1ad1b254857b9008564727ebd5a1f317362f6ca7"),
|
||||
)
|
||||
|
||||
assertEquals(taggedUsers, note.taggedUsers())
|
||||
|
||||
val expectedReply = MarkedETag("b857504288c18a15950dd05b9e8772c62ca6289d5aac373c0a8ee5b132e94e7c", null, MarkedETag.MARKER.REPLY)
|
||||
|
||||
// check replies
|
||||
assertEquals(null, note.markedReply())
|
||||
assertEquals(expectedReply, note.unmarkedReply())
|
||||
assertEquals(expectedReply, note.reply())
|
||||
|
||||
val replyTos =
|
||||
listOf(
|
||||
"89f220b63465c93542b1a78caa3a952cf4f196e91a50596493c8093c533ebc4d",
|
||||
"090c037b2e399ee74d9f134758928948dd9154413ca1a1acb37155046e03a051",
|
||||
"567b7c11f0fe582361e3cea6fcc7609a8942dfe196ee1b98d5604c93fbeea976",
|
||||
"49aff7ae6daeaaa2777931b90f9bb29f6cb01c5a3d7d88c8ba82d890f264afb4",
|
||||
"5e081ebb19153357d7c31e8a10b9ceeef29313f58dc8d701f66727fab02aef64",
|
||||
"bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631",
|
||||
"b857504288c18a15950dd05b9e8772c62ca6289d5aac373c0a8ee5b132e94e7c",
|
||||
)
|
||||
|
||||
assertEquals(emptyList<String>(), note.markedReplyTos())
|
||||
assertEquals(replyTos, note.unmarkedReplyTos())
|
||||
|
||||
assertEquals(expectedReply.eventId, note.replyingTo())
|
||||
assertEquals(expectedReply.eventId, note.replyingToAddressOrEvent())
|
||||
|
||||
// check root
|
||||
val expectedRoot = MarkedETag("89f220b63465c93542b1a78caa3a952cf4f196e91a50596493c8093c533ebc4d", null, MarkedETag.MARKER.ROOT)
|
||||
|
||||
assertEquals(null, note.markedRoot())
|
||||
assertEquals(expectedRoot, note.unmarkedRoot())
|
||||
assertEquals(expectedRoot, note.root())
|
||||
|
||||
// check quotes
|
||||
assertEquals(emptySet<String>(), note.citedUsers())
|
||||
assertEquals(emptySet<String>(), note.findCitations())
|
||||
assertEquals(replyTos, note.tagsWithoutCitations())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMarkedEvent() {
|
||||
val note =
|
||||
TextNoteEvent(
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
arrayOf(
|
||||
arrayOf("p", "4ca4f5533e40da5e0508796d409e6bb35a50b26fc304345617ab017183d83ac0"),
|
||||
arrayOf("p", "534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec"),
|
||||
arrayOf("e", "77ce56f89d1228f7ff3743ce1ad1b254857b9008564727ebd5a1f317362f6ca7"),
|
||||
arrayOf("e", "bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631", "", "root"),
|
||||
arrayOf("e", "b857504288c18a15950dd05b9e8772c62ca6289d5aac373c0a8ee5b132e94e7c", "", "reply"),
|
||||
),
|
||||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
val taggedUsers =
|
||||
listOf(
|
||||
PTag("4ca4f5533e40da5e0508796d409e6bb35a50b26fc304345617ab017183d83ac0"),
|
||||
PTag("534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec"),
|
||||
)
|
||||
|
||||
assertEquals(taggedUsers, note.taggedUsers())
|
||||
|
||||
val expectedReply = MarkedETag("b857504288c18a15950dd05b9e8772c62ca6289d5aac373c0a8ee5b132e94e7c", null, MarkedETag.MARKER.REPLY)
|
||||
val expectedReplyWrong = MarkedETag("77ce56f89d1228f7ff3743ce1ad1b254857b9008564727ebd5a1f317362f6ca7", null, MarkedETag.MARKER.REPLY)
|
||||
|
||||
// check replies
|
||||
assertEquals(expectedReply, note.markedReply())
|
||||
assertEquals(expectedReplyWrong, note.unmarkedReply())
|
||||
assertEquals(expectedReply, note.reply())
|
||||
|
||||
val markedReplies =
|
||||
listOf(
|
||||
"bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631",
|
||||
"b857504288c18a15950dd05b9e8772c62ca6289d5aac373c0a8ee5b132e94e7c",
|
||||
)
|
||||
|
||||
val unmarkedReplies =
|
||||
listOf(
|
||||
"77ce56f89d1228f7ff3743ce1ad1b254857b9008564727ebd5a1f317362f6ca7",
|
||||
)
|
||||
|
||||
assertEquals(markedReplies, note.markedReplyTos())
|
||||
assertEquals(unmarkedReplies, note.unmarkedReplyTos())
|
||||
|
||||
assertEquals(expectedReply.eventId, note.replyingTo())
|
||||
assertEquals(expectedReply.eventId, note.replyingToAddressOrEvent())
|
||||
|
||||
// check root
|
||||
val expectedRoot = MarkedETag("bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631", null, MarkedETag.MARKER.ROOT)
|
||||
|
||||
val expectedRootWrong = MarkedETag("77ce56f89d1228f7ff3743ce1ad1b254857b9008564727ebd5a1f317362f6ca7", null, MarkedETag.MARKER.ROOT)
|
||||
|
||||
assertEquals(expectedRoot, note.markedRoot())
|
||||
assertEquals(expectedRootWrong, note.unmarkedRoot())
|
||||
assertEquals(expectedRoot, note.root())
|
||||
|
||||
// check quotes
|
||||
assertEquals(emptySet<String>(), note.citedUsers())
|
||||
assertEquals(emptySet<String>(), note.findCitations())
|
||||
assertEquals(markedReplies, note.tagsWithoutCitations())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMarkedInverted() {
|
||||
val note =
|
||||
TextNoteEvent(
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
arrayOf(
|
||||
arrayOf("p", "4ca4f5533e40da5e0508796d409e6bb35a50b26fc304345617ab017183d83ac0", "wss://goiaba.com"),
|
||||
arrayOf("p", "534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec"),
|
||||
arrayOf("p", "77ce56f89d1228f7ff3743ce1ad1b254857b9008564727ebd5a1f317362f6ca7"),
|
||||
arrayOf("e", "5e081ebb19153357d7c31e8a10b9ceeef29313f58dc8d701f66727fab02aef64", "", "reply"),
|
||||
arrayOf("e", "bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631", "wss://banana.com", "root", "4ca4f5533e40da5e0508796d409e6bb35a50b26fc304345617ab017183d83ac0"),
|
||||
arrayOf("e", "b857504288c18a15950dd05b9e8772c62ca6289d5aac373c0a8ee5b132e94e7c"),
|
||||
),
|
||||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
val taggedUsers =
|
||||
listOf(
|
||||
PTag("4ca4f5533e40da5e0508796d409e6bb35a50b26fc304345617ab017183d83ac0", "wss://goiaba.com"),
|
||||
PTag("534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec"),
|
||||
PTag("77ce56f89d1228f7ff3743ce1ad1b254857b9008564727ebd5a1f317362f6ca7"),
|
||||
)
|
||||
|
||||
assertEquals(taggedUsers, note.taggedUsers())
|
||||
|
||||
val expectedReply = MarkedETag("5e081ebb19153357d7c31e8a10b9ceeef29313f58dc8d701f66727fab02aef64", null, MarkedETag.MARKER.REPLY)
|
||||
val expectedReplyWrong = MarkedETag("b857504288c18a15950dd05b9e8772c62ca6289d5aac373c0a8ee5b132e94e7c", null, MarkedETag.MARKER.REPLY)
|
||||
|
||||
// check replies
|
||||
assertEquals(expectedReply, note.markedReply())
|
||||
assertEquals(expectedReplyWrong, note.unmarkedReply())
|
||||
assertEquals(expectedReply, note.reply())
|
||||
|
||||
val markedReplies =
|
||||
listOf(
|
||||
"bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631",
|
||||
"5e081ebb19153357d7c31e8a10b9ceeef29313f58dc8d701f66727fab02aef64",
|
||||
)
|
||||
|
||||
val unmarkedReplies =
|
||||
listOf(
|
||||
"b857504288c18a15950dd05b9e8772c62ca6289d5aac373c0a8ee5b132e94e7c",
|
||||
)
|
||||
|
||||
assertEquals(markedReplies, note.markedReplyTos())
|
||||
assertEquals(unmarkedReplies, note.unmarkedReplyTos())
|
||||
|
||||
assertEquals(expectedReply.eventId, note.replyingTo())
|
||||
assertEquals(expectedReply.eventId, note.replyingToAddressOrEvent())
|
||||
|
||||
// check root
|
||||
val expectedRoot = MarkedETag("bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631", null, MarkedETag.MARKER.ROOT)
|
||||
val expectedRootWrong = MarkedETag("b857504288c18a15950dd05b9e8772c62ca6289d5aac373c0a8ee5b132e94e7c", null, MarkedETag.MARKER.ROOT)
|
||||
|
||||
assertEquals(expectedRoot, note.markedRoot())
|
||||
assertEquals(expectedRootWrong, note.unmarkedRoot())
|
||||
assertEquals(expectedRoot, note.root())
|
||||
|
||||
// check quotes
|
||||
assertEquals(emptySet<String>(), note.citedUsers())
|
||||
assertEquals(emptySet<String>(), note.findCitations())
|
||||
assertEquals(markedReplies, note.tagsWithoutCitations())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnlyRoot() {
|
||||
val note =
|
||||
TextNoteEvent(
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
arrayOf(
|
||||
arrayOf("p", "534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec", "wss://banana.com"),
|
||||
arrayOf("e", "9abbfd9b9ac5ecdab45d14b8bf8d746139ea039e931a1b376d19a239f1946590", "", "root", "534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec"),
|
||||
),
|
||||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
val taggedUsers =
|
||||
listOf(
|
||||
PTag("534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec", "wss://banana.com"),
|
||||
)
|
||||
|
||||
assertEquals(taggedUsers, note.taggedUsers())
|
||||
|
||||
val expectedReply = MarkedETag("9abbfd9b9ac5ecdab45d14b8bf8d746139ea039e931a1b376d19a239f1946590", null, MarkedETag.MARKER.REPLY)
|
||||
|
||||
// check replies
|
||||
assertEquals(null, note.markedReply())
|
||||
assertEquals(null, note.unmarkedReply())
|
||||
assertEquals(null, note.reply())
|
||||
|
||||
val replyTos =
|
||||
listOf(
|
||||
"9abbfd9b9ac5ecdab45d14b8bf8d746139ea039e931a1b376d19a239f1946590",
|
||||
)
|
||||
|
||||
assertEquals(replyTos, note.markedReplyTos())
|
||||
assertEquals(emptyList<String>(), note.unmarkedReplyTos())
|
||||
|
||||
assertEquals(expectedReply.eventId, note.replyingTo())
|
||||
assertEquals(expectedReply.eventId, note.replyingToAddressOrEvent())
|
||||
|
||||
// check root
|
||||
val expectedRoot = MarkedETag("9abbfd9b9ac5ecdab45d14b8bf8d746139ea039e931a1b376d19a239f1946590", null, MarkedETag.MARKER.ROOT, "534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec")
|
||||
|
||||
assertEquals(expectedRoot, note.markedRoot())
|
||||
assertEquals(null, note.unmarkedRoot())
|
||||
assertEquals(expectedRoot, note.root())
|
||||
|
||||
// check quotes
|
||||
assertEquals(emptySet<String>(), note.citedUsers())
|
||||
assertEquals(emptySet<String>(), note.findCitations())
|
||||
assertEquals(replyTos, note.tagsWithoutCitations())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnlyReply() {
|
||||
val note =
|
||||
TextNoteEvent(
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
arrayOf(
|
||||
arrayOf("p", "534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec", "wss://banana.com"),
|
||||
arrayOf("e", "9abbfd9b9ac5ecdab45d14b8bf8d746139ea039e931a1b376d19a239f1946590", "", "reply", "534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec"),
|
||||
),
|
||||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
val taggedUsers =
|
||||
listOf(
|
||||
PTag("534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec", "wss://banana.com"),
|
||||
)
|
||||
|
||||
assertEquals(taggedUsers, note.taggedUsers())
|
||||
|
||||
val expectedReply = MarkedETag("9abbfd9b9ac5ecdab45d14b8bf8d746139ea039e931a1b376d19a239f1946590", null, MarkedETag.MARKER.REPLY)
|
||||
|
||||
// check replies
|
||||
assertEquals(expectedReply, note.markedReply())
|
||||
assertEquals(null, note.unmarkedReply())
|
||||
assertEquals(expectedReply, note.reply())
|
||||
|
||||
val replyTos =
|
||||
listOf(
|
||||
"9abbfd9b9ac5ecdab45d14b8bf8d746139ea039e931a1b376d19a239f1946590",
|
||||
)
|
||||
|
||||
assertEquals(replyTos, note.markedReplyTos())
|
||||
assertEquals(emptyList<String>(), note.unmarkedReplyTos())
|
||||
|
||||
assertEquals(expectedReply.eventId, note.replyingTo())
|
||||
assertEquals(expectedReply.eventId, note.replyingToAddressOrEvent())
|
||||
|
||||
// check root
|
||||
val expectedRoot = MarkedETag("9abbfd9b9ac5ecdab45d14b8bf8d746139ea039e931a1b376d19a239f1946590", null, MarkedETag.MARKER.ROOT, "534780e44da7b494485e85cd4cca6af4f6caa1627472432b6f2a4ece0e9e54ec")
|
||||
|
||||
assertEquals(null, note.markedRoot())
|
||||
assertEquals(null, note.unmarkedRoot())
|
||||
assertEquals(null, note.root())
|
||||
|
||||
// check quotes
|
||||
assertEquals(emptySet<String>(), note.citedUsers())
|
||||
assertEquals(emptySet<String>(), note.findCitations())
|
||||
assertEquals(replyTos, note.tagsWithoutCitations())
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.nip17Dm
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip17Dm.files.encryption.AESGCM
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.quartz.nip17Dm
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
|
||||
import kotlinx.collections.immutable.persistentSetOf
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
+12
-10
@@ -22,11 +22,11 @@ package com.vitorpamplona.quartz.nip19Bech32
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.experimental.medical.FhirResourceEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.hasValidSignature
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
||||
import com.vitorpamplona.quartz.nip01Core.verify
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEmbed
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
@@ -52,7 +52,9 @@ class NIP19EmbedTests {
|
||||
|
||||
val countDownLatch = CountDownLatch(1)
|
||||
|
||||
TextNoteEvent.create("I like this. It could solve the ninvite problem in #1062, and it seems like it could be applied very broadly to limit the spread of events that shouldn't stand on their own or need to be private. The one question I have is how long are these embeds? If it's 50 lines of text, that breaks the human readable (or at least parseable) requirement of kind 1s. Also, encoding json in a tlv is silly, we should at least use the tlv to reduce the payload size.", isDraft = false, signer = signer) {
|
||||
signer.sign(
|
||||
TextNoteEvent.build("I like this. It could solve the ninvite problem in #1062, and it seems like it could be applied very broadly to limit the spread of events that shouldn't stand on their own or need to be private. The one question I have is how long are these embeds? If it's 50 lines of text, that breaks the human readable (or at least parseable) requirement of kind 1s. Also, encoding json in a tlv is silly, we should at least use the tlv to reduce the payload size."),
|
||||
) {
|
||||
textNote = it
|
||||
countDownLatch.countDown()
|
||||
}
|
||||
@@ -67,7 +69,7 @@ class NIP19EmbedTests {
|
||||
|
||||
val decodedNote = (Nip19Parser.uriToRoute(bech32)?.entity as NEmbed).event
|
||||
|
||||
assertTrue(decodedNote.hasValidSignature())
|
||||
assertTrue(decodedNote.verify())
|
||||
|
||||
assertEquals(textNote!!.toJson(), decodedNote.toJson())
|
||||
}
|
||||
@@ -83,7 +85,7 @@ class NIP19EmbedTests {
|
||||
|
||||
val countDownLatch = CountDownLatch(1)
|
||||
|
||||
FhirResourceEvent.create(fhirPayload = visionPrescriptionFhir, signer = signer) {
|
||||
signer.sign(FhirResourceEvent.build(visionPrescriptionFhir)) {
|
||||
eyeglassesPrescriptionEvent = it
|
||||
countDownLatch.countDown()
|
||||
}
|
||||
@@ -99,7 +101,7 @@ class NIP19EmbedTests {
|
||||
|
||||
val decodedNote = (Nip19Parser.uriToRoute(bech32)?.entity as NEmbed).event
|
||||
|
||||
assertTrue(decodedNote.hasValidSignature())
|
||||
assertTrue(decodedNote.verify())
|
||||
|
||||
assertEquals(eyeglassesPrescriptionEvent!!.toJson(), decodedNote.toJson())
|
||||
}
|
||||
@@ -115,7 +117,7 @@ class NIP19EmbedTests {
|
||||
|
||||
val countDownLatch = CountDownLatch(1)
|
||||
|
||||
FhirResourceEvent.create(fhirPayload = visionPrescriptionBundle, signer = signer) {
|
||||
signer.sign(FhirResourceEvent.build(visionPrescriptionBundle)) {
|
||||
eyeglassesPrescriptionEvent = it
|
||||
countDownLatch.countDown()
|
||||
}
|
||||
@@ -131,7 +133,7 @@ class NIP19EmbedTests {
|
||||
|
||||
val decodedNote = (Nip19Parser.uriToRoute(bech32)?.entity as NEmbed).event
|
||||
|
||||
assertTrue(decodedNote.hasValidSignature())
|
||||
assertTrue(decodedNote.verify())
|
||||
|
||||
assertEquals(eyeglassesPrescriptionEvent!!.toJson(), decodedNote.toJson())
|
||||
}
|
||||
@@ -147,7 +149,7 @@ class NIP19EmbedTests {
|
||||
|
||||
val countDownLatch = CountDownLatch(1)
|
||||
|
||||
FhirResourceEvent.create(fhirPayload = visionPrescriptionBundle2, signer = signer) {
|
||||
signer.sign(FhirResourceEvent.build(visionPrescriptionBundle2)) {
|
||||
eyeglassesPrescriptionEvent = it
|
||||
countDownLatch.countDown()
|
||||
}
|
||||
@@ -163,7 +165,7 @@ class NIP19EmbedTests {
|
||||
|
||||
val decodedNote = (Nip19Parser.uriToRoute(bech32)?.entity as NEmbed).event
|
||||
|
||||
assertTrue(decodedNote.hasValidSignature())
|
||||
assertTrue(decodedNote.verify())
|
||||
|
||||
assertEquals(eyeglassesPrescriptionEvent!!.toJson(), decodedNote.toJson())
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ package com.vitorpamplona.quartz.nip44Encryption
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.Nip01
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.utils.sha256Hash
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
package com.vitorpamplona.quartz.nip46RemoteSigner
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ReadWrite
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import org.junit.Ignore
|
||||
@@ -219,7 +219,7 @@ internal class Nip46Test {
|
||||
|
||||
@Test
|
||||
fun testRelaysResponse() {
|
||||
val expected = BunkerResponseGetRelays(relays = mapOf("url" to ContactListEvent.ReadWrite(true, false)))
|
||||
val expected = BunkerResponseGetRelays(relays = mapOf("url" to ReadWrite(true, false)))
|
||||
val actual = encodeDecodeEvent(expected)
|
||||
|
||||
assertEquals(expected.id, actual.id)
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
package com.vitorpamplona.quartz.nip57Zaps
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
|
||||
+25
-20
@@ -22,16 +22,17 @@ package com.vitorpamplona.quartz.nip59GiftWraps
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.checkSignature
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
|
||||
import com.vitorpamplona.quartz.nip17Dm.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.NIP17Factory
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.GiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.SealedRumorEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotEquals
|
||||
@@ -54,9 +55,11 @@ class GiftWrapEventTest {
|
||||
// Requires 3 tests
|
||||
val countDownLatch = CountDownLatch(3)
|
||||
|
||||
NIP17Factory().createMsgNIP17(
|
||||
message,
|
||||
listOf(receiver.pubKey),
|
||||
NIP17Factory().createMessageNIP17(
|
||||
ChatMessageEvent.build(
|
||||
message,
|
||||
listOf(PTag(receiver.pubKey, null)),
|
||||
),
|
||||
sender,
|
||||
) { events ->
|
||||
countDownLatch.countDown()
|
||||
@@ -114,9 +117,11 @@ class GiftWrapEventTest {
|
||||
|
||||
val countDownLatch = CountDownLatch(receivers.size + 2)
|
||||
|
||||
NIP17Factory().createMsgNIP17(
|
||||
message,
|
||||
receivers.map { it.pubKey },
|
||||
NIP17Factory().createMessageNIP17(
|
||||
ChatMessageEvent.build(
|
||||
message,
|
||||
receivers.map { PTag(it.pubKey, null) },
|
||||
),
|
||||
sender,
|
||||
) { events ->
|
||||
countDownLatch.countDown()
|
||||
@@ -167,11 +172,11 @@ class GiftWrapEventTest {
|
||||
var giftWrapEventToSender: GiftWrapEvent? = null
|
||||
var giftWrapEventToReceiver: GiftWrapEvent? = null
|
||||
|
||||
ChatMessageEvent.create(
|
||||
msg = "Hi There!",
|
||||
isDraft = false,
|
||||
to = listOf(receiver.pubKey),
|
||||
signer = sender,
|
||||
sender.sign(
|
||||
ChatMessageEvent.build(
|
||||
msg = "Hi There!",
|
||||
to = listOf(PTag(receiver.pubKey, null)),
|
||||
),
|
||||
) { senderMessage ->
|
||||
// MsgFor the Receiver
|
||||
|
||||
@@ -306,11 +311,11 @@ class GiftWrapEventTest {
|
||||
var giftWrapEventToReceiverA: GiftWrapEvent? = null
|
||||
var giftWrapEventToReceiverB: GiftWrapEvent? = null
|
||||
|
||||
ChatMessageEvent.create(
|
||||
msg = "Who is going to the party tonight?",
|
||||
isDraft = false,
|
||||
to = listOf(receiverA.pubKey, receiverB.pubKey),
|
||||
signer = sender,
|
||||
sender.sign(
|
||||
ChatMessageEvent.build(
|
||||
msg = "Who is going to the party tonight?",
|
||||
to = listOf(PTag(receiverA.pubKey), PTag(receiverB.pubKey)),
|
||||
),
|
||||
) { senderMessage ->
|
||||
SealedRumorEvent.create(
|
||||
event = senderMessage,
|
||||
|
||||
@@ -21,8 +21,9 @@
|
||||
package com.vitorpamplona.quartz
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.Nip01
|
||||
import com.vitorpamplona.quartz.nip04Dm.Nip04
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip04Dm.Nip04Encoder
|
||||
import com.vitorpamplona.quartz.nip04Dm.Nip04Encryption
|
||||
import com.vitorpamplona.quartz.nip06KeyDerivation.Nip06
|
||||
import com.vitorpamplona.quartz.nip44Encryption.Nip44
|
||||
import com.vitorpamplona.quartz.nip44Encryption.Nip44v2
|
||||
@@ -38,7 +39,7 @@ object CryptoUtils {
|
||||
|
||||
val nip01 = Nip01(secp256k1, random)
|
||||
val nip06 = Nip06(secp256k1)
|
||||
val nip04 = Nip04(secp256k1, random)
|
||||
val nip04 = Nip04Encryption(secp256k1, random)
|
||||
val nip44 = Nip44(secp256k1, random, nip04)
|
||||
val nip49 = Nip49(secp256k1, random)
|
||||
|
||||
@@ -84,7 +85,7 @@ object CryptoUtils {
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? =
|
||||
if (Nip04.isNIP04(msg)) {
|
||||
if (Nip04Encryption.isNIP04(msg)) {
|
||||
decryptNIP04(msg, privateKey, pubKey)
|
||||
} else {
|
||||
decryptNIP44(msg, privateKey, pubKey)
|
||||
@@ -100,7 +101,7 @@ object CryptoUtils {
|
||||
fun encryptNIP04(
|
||||
msg: String,
|
||||
sharedSecret: ByteArray,
|
||||
): Nip04.EncryptedInfo = nip04.encrypt(msg, sharedSecret)
|
||||
): Nip04Encoder = nip04.encrypt(msg, sharedSecret)
|
||||
|
||||
fun decryptNIP04(
|
||||
msg: String,
|
||||
@@ -109,7 +110,7 @@ object CryptoUtils {
|
||||
): String = nip04.decrypt(msg, privateKey, pubKey)
|
||||
|
||||
fun decryptNIP04(
|
||||
encryptedInfo: Nip04.EncryptedInfo,
|
||||
encryptedInfo: Nip04Encoder,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String = nip04.decrypt(encryptedInfo, privateKey, pubKey)
|
||||
|
||||
@@ -22,49 +22,49 @@ package com.vitorpamplona.quartz
|
||||
|
||||
import com.vitorpamplona.quartz.blossom.BlossomAuthorizationEvent
|
||||
import com.vitorpamplona.quartz.blossom.BlossomServersEvent
|
||||
import com.vitorpamplona.quartz.experimental.audio.AudioHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.audio.AudioTrackEvent
|
||||
import com.vitorpamplona.quartz.experimental.audio.header.AudioHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.audio.track.AudioTrackEvent
|
||||
import com.vitorpamplona.quartz.experimental.edits.PrivateOutboxRelayListEvent
|
||||
import com.vitorpamplona.quartz.experimental.edits.TextNoteModificationEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryPrologueEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryReadingStateEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStorySceneEvent
|
||||
import com.vitorpamplona.quartz.experimental.medical.FhirResourceEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.FileStorageEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.nns.NNSEvent
|
||||
import com.vitorpamplona.quartz.experimental.profileGallery.ProfileGalleryEntryEvent
|
||||
import com.vitorpamplona.quartz.experimental.relationshipStatus.RelationshipStatusEvent
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.EventHasher
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.MetadataEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.ChatMessageEncryptedFileHeaderEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.ChatMessageRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
|
||||
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
|
||||
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.ChannelCreateEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.ChannelHideMessageEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.ChannelListEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.ChannelMessageEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.ChannelMetadataEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.ChannelMuteUserEvent
|
||||
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiPackEvent
|
||||
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiPackSelectionEvent
|
||||
import com.vitorpamplona.quartz.nip34Git.GitIssueEvent
|
||||
import com.vitorpamplona.quartz.nip34Git.GitPatchEvent
|
||||
import com.vitorpamplona.quartz.nip34Git.GitReplyEvent
|
||||
import com.vitorpamplona.quartz.nip34Git.GitRepositoryEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelHideMessageEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMuteUserEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
||||
import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent
|
||||
import com.vitorpamplona.quartz.nip30CustomEmoji.selection.EmojiPackSelectionEvent
|
||||
import com.vitorpamplona.quartz.nip34Git.issue.GitIssueEvent
|
||||
import com.vitorpamplona.quartz.nip34Git.patch.GitPatchEvent
|
||||
import com.vitorpamplona.quartz.nip34Git.reply.GitReplyEvent
|
||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
|
||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
|
||||
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
|
||||
@@ -82,8 +82,8 @@ import com.vitorpamplona.quartz.nip52Calendar.CalendarDateSlotEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.CalendarEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.CalendarRSVPEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.CalendarTimeSlotEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
|
||||
@@ -92,28 +92,27 @@ import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.BadgeAwardEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.BadgeDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.BadgeProfilesEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.GiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.SealedRumorEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip68Picture.PictureEvent
|
||||
import com.vitorpamplona.quartz.nip71Video.VideoHorizontalEvent
|
||||
import com.vitorpamplona.quartz.nip71Video.VideoVerticalEvent
|
||||
import com.vitorpamplona.quartz.nip71Video.VideoViewEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.CommunityListEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.CommunityPostApprovalEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.approval.CommunityPostApprovalEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip75ZapGoals.GoalEvent
|
||||
import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataEvent
|
||||
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.AppDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.AppRecommendationEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.recommendation.AppRecommendationEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryRequestEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryResponseEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.NIP90StatusEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.NIP90UserDiscoveryRequestEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.NIP90UserDiscoveryResponseEvent
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
|
||||
import com.vitorpamplona.quartz.nip96FileStorage.FileServersEvent
|
||||
import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent
|
||||
import com.vitorpamplona.quartz.nip98HttpAuth.HTTPAuthorizationEvent
|
||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||
|
||||
@@ -251,7 +250,6 @@ class EventFactory {
|
||||
TorrentCommentEvent.KIND -> TorrentCommentEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
VideoHorizontalEvent.KIND -> VideoHorizontalEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
VideoVerticalEvent.KIND -> VideoVerticalEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
VideoViewEvent.KIND -> VideoViewEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
WikiNoteEvent.KIND -> WikiNoteEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
else -> {
|
||||
factories[kind]?.let {
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
@@ -58,7 +58,7 @@ class BlossomServersEvent(
|
||||
servers
|
||||
.map {
|
||||
arrayOf("server", it)
|
||||
}.plusElement(AltTagSerializer.toTagArray(ALT))
|
||||
}.plusElement(AltTag.assemble(ALT))
|
||||
.toTypedArray()
|
||||
|
||||
fun updateRelayList(
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.audio
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip36SensitiveContent.ContentWarningSerializer
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
class AudioHeaderEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun download() = tags.firstOrNull { it.size > 1 && it[0] == DOWNLOAD_URL }?.get(1)
|
||||
|
||||
fun stream() = tags.firstOrNull { it.size > 1 && it[0] == STREAM_URL }?.get(1)
|
||||
|
||||
fun wavefrom() =
|
||||
tags
|
||||
.firstOrNull { it.size > 1 && it[0] == WAVEFORM }
|
||||
?.get(1)
|
||||
?.let { EventMapper.mapper.readValue<List<Int>>(it) }
|
||||
|
||||
companion object {
|
||||
const val KIND = 1808
|
||||
const val ALT = "Audio header"
|
||||
|
||||
private const val DOWNLOAD_URL = "download_url"
|
||||
private const val STREAM_URL = "stream_url"
|
||||
private const val WAVEFORM = "waveform"
|
||||
|
||||
fun create(
|
||||
description: String,
|
||||
downloadUrl: String,
|
||||
streamUrl: String? = null,
|
||||
wavefront: String? = null,
|
||||
sensitiveContent: Boolean? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (AudioHeaderEvent) -> Unit,
|
||||
) {
|
||||
val tags =
|
||||
listOfNotNull(
|
||||
downloadUrl.let { arrayOf(DOWNLOAD_URL, it) },
|
||||
streamUrl?.let { arrayOf(STREAM_URL, it) },
|
||||
wavefront?.let { arrayOf(WAVEFORM, it) },
|
||||
sensitiveContent?.let {
|
||||
if (it) {
|
||||
ContentWarningSerializer.toTagArray()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
AltTagSerializer.toTagArray(ALT),
|
||||
).toTypedArray()
|
||||
|
||||
signer.sign(createdAt, KIND, tags, description, onReady)
|
||||
}
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.audio.header
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.audio.header.tags.DownloadUrlTag
|
||||
import com.vitorpamplona.quartz.experimental.audio.header.tags.StreamUrlTag
|
||||
import com.vitorpamplona.quartz.experimental.audio.header.tags.WaveformTag
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
class AudioHeaderEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun download() = tags.firstNotNullOfOrNull(DownloadUrlTag::parse)
|
||||
|
||||
fun stream() = tags.firstNotNullOfOrNull(StreamUrlTag::parse)
|
||||
|
||||
fun wavefrom() = tags.firstNotNullOfOrNull(WaveformTag::parse)
|
||||
|
||||
companion object {
|
||||
const val KIND = 1808
|
||||
const val ALT = "Audio header"
|
||||
|
||||
fun build(
|
||||
description: String,
|
||||
downloadUrl: String,
|
||||
streamUrl: String? = null,
|
||||
wavefront: List<Int>? = null,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<AudioHeaderEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, description, createdAt) {
|
||||
alt(ALT)
|
||||
downloadUrl.let { downloadUrl(it) }
|
||||
streamUrl?.let { streamUrl(it) }
|
||||
wavefront?.let { wavefront(it) }
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.audio.header
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.audio.header.tags.DownloadUrlTag
|
||||
import com.vitorpamplona.quartz.experimental.audio.header.tags.StreamUrlTag
|
||||
import com.vitorpamplona.quartz.experimental.audio.header.tags.WaveformTag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
|
||||
fun TagArrayBuilder<AudioHeaderEvent>.downloadUrl(downloadUrlTag: String) = addUnique(DownloadUrlTag.assemble(downloadUrlTag))
|
||||
|
||||
fun TagArrayBuilder<AudioHeaderEvent>.streamUrl(streamUrl: String) = addUnique(StreamUrlTag.assemble(streamUrl))
|
||||
|
||||
fun TagArrayBuilder<AudioHeaderEvent>.wavefront(wave: List<Int>) = addUnique(WaveformTag.assemble(wave))
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.audio.header.tags
|
||||
|
||||
class DownloadUrlTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "download_url"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(url: String) = arrayOf(TAG_NAME, url)
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.audio.header.tags
|
||||
|
||||
class StreamUrlTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "stream_url"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(url: String) = arrayOf(TAG_NAME, url)
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.audio.header.tags
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
|
||||
|
||||
class WaveformTag(
|
||||
val wave: List<Int>,
|
||||
) {
|
||||
fun toTagArray() = assemble(wave)
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "waveform"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): WaveformTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
if (tag[1].isEmpty()) return null
|
||||
val wave = runCatching { EventMapper.mapper.readValue<List<Int>>(tag[1]) }.getOrNull()
|
||||
if (wave.isNullOrEmpty()) return null
|
||||
return WaveformTag(wave)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(wave: List<Int>) = arrayOf(TAG_NAME, EventMapper.mapper.writeValueAsString(wave))
|
||||
}
|
||||
}
|
||||
+30
-35
@@ -18,14 +18,22 @@
|
||||
* 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.experimental.audio
|
||||
package com.vitorpamplona.quartz.experimental.audio.track
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.audio.track.tags.CoverTag
|
||||
import com.vitorpamplona.quartz.experimental.audio.track.tags.MediaTag
|
||||
import com.vitorpamplona.quartz.experimental.audio.track.tags.ParticipantTag
|
||||
import com.vitorpamplona.quartz.experimental.audio.track.tags.PriceTag
|
||||
import com.vitorpamplona.quartz.experimental.audio.track.tags.TypeTag
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import java.util.UUID
|
||||
|
||||
@Immutable
|
||||
class AudioTrackEvent(
|
||||
@@ -36,53 +44,40 @@ class AudioTrackEvent(
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun participants() = tags.filter { it.size > 1 && it[0] == "p" }.map { Participant(it[1], it.getOrNull(2)) }
|
||||
fun participants() = tags.mapNotNull(ParticipantTag::parse)
|
||||
|
||||
fun type() = tags.firstOrNull { it.size > 1 && it[0] == TYPE }?.get(1)
|
||||
fun type() = tags.firstNotNullOfOrNull(TypeTag::parse)
|
||||
|
||||
fun price() = tags.firstOrNull { it.size > 1 && it[0] == PRICE }?.get(1)
|
||||
fun price() = tags.firstNotNullOfOrNull(PriceTag::parse)
|
||||
|
||||
fun cover() = tags.firstOrNull { it.size > 1 && it[0] == COVER }?.get(1)
|
||||
fun cover() = tags.firstNotNullOfOrNull(CoverTag::parse)
|
||||
|
||||
// fun subject() = tags.firstOrNull { it.size > 1 && it[0] == SUBJECT }?.get(1)
|
||||
fun media() = tags.firstOrNull { it.size > 1 && it[0] == MEDIA }?.get(1)
|
||||
fun media() = tags.firstNotNullOfOrNull(MediaTag::parse)
|
||||
|
||||
companion object {
|
||||
const val KIND = 31337
|
||||
const val ALT = "Audio track"
|
||||
const val ALT_DESCRIPTION = "Audio track"
|
||||
|
||||
private const val TYPE = "c"
|
||||
private const val PRICE = "price"
|
||||
private const val COVER = "cover"
|
||||
private const val SUBJECT = "subject"
|
||||
private const val MEDIA = "media"
|
||||
|
||||
fun create(
|
||||
fun build(
|
||||
type: String,
|
||||
media: String,
|
||||
price: String? = null,
|
||||
cover: String? = null,
|
||||
subject: String? = null,
|
||||
signer: NostrSigner,
|
||||
dTag: String = UUID.randomUUID().toString(),
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (AudioTrackEvent) -> Unit,
|
||||
) {
|
||||
val tags =
|
||||
listOfNotNull(
|
||||
arrayOf(MEDIA, media),
|
||||
arrayOf(TYPE, type),
|
||||
price?.let { arrayOf(PRICE, it) },
|
||||
cover?.let { arrayOf(COVER, it) },
|
||||
subject?.let { arrayOf(SUBJECT, it) },
|
||||
AltTagSerializer.toTagArray(ALT),
|
||||
).toTypedArray()
|
||||
initializer: TagArrayBuilder<AudioTrackEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, "", createdAt) {
|
||||
dTag(dTag)
|
||||
alt(ALT_DESCRIPTION)
|
||||
|
||||
signer.sign(createdAt, KIND, tags, "", onReady)
|
||||
type(type)
|
||||
media(media)
|
||||
price?.let { price(it) }
|
||||
cover?.let { cover(it) }
|
||||
subject?.let { subject(it) }
|
||||
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable data class Participant(
|
||||
val key: String,
|
||||
val role: String?,
|
||||
)
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.audio.track
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.audio.track.tags.CoverTag
|
||||
import com.vitorpamplona.quartz.experimental.audio.track.tags.MediaTag
|
||||
import com.vitorpamplona.quartz.experimental.audio.track.tags.PriceTag
|
||||
import com.vitorpamplona.quartz.experimental.audio.track.tags.TypeTag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip14Subject.SubjectTag
|
||||
|
||||
fun TagArrayBuilder<AudioTrackEvent>.price(price: String) = addUnique(PriceTag.assemble(price))
|
||||
|
||||
fun TagArrayBuilder<AudioTrackEvent>.cover(coverUrl: String) = addUnique(CoverTag.assemble(coverUrl))
|
||||
|
||||
fun TagArrayBuilder<AudioTrackEvent>.media(mediaUrl: String) = addUnique(MediaTag.assemble(mediaUrl))
|
||||
|
||||
fun TagArrayBuilder<AudioTrackEvent>.type(type: String) = addUnique(TypeTag.assemble(type))
|
||||
|
||||
fun TagArrayBuilder<AudioTrackEvent>.subject(subject: String) = addUnique(SubjectTag.assemble(subject))
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.audio.track.tags
|
||||
|
||||
class CoverTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "cover"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(url: String) = arrayOf(TAG_NAME, url)
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.audio.track.tags
|
||||
|
||||
class MediaTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "media"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(url: String) = arrayOf(TAG_NAME, url)
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.audio.track.tags
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Tag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.isNotName
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
|
||||
@Immutable
|
||||
data class ParticipantTag(
|
||||
override val pubKey: String,
|
||||
override val relayHint: String?,
|
||||
) : PubKeyReferenceTag {
|
||||
fun toTagArray() = assemble(pubKey, relayHint)
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "p"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Tag): ParticipantTag? {
|
||||
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
|
||||
if (tag[1].length != 64) return null
|
||||
return ParticipantTag(tag[1], tag.getOrNull(2))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseKey(tag: Tag): String? {
|
||||
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
|
||||
if (tag[1].length != 64) return null
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
pubkey: HexKey,
|
||||
relayHint: String?,
|
||||
) = arrayOfNotNull(TAG_NAME, pubkey, relayHint)
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.audio.track.tags
|
||||
|
||||
class PriceTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "price"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(url: String) = arrayOf(TAG_NAME, url)
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.audio.track.tags
|
||||
|
||||
class TypeTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "c"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(url: String) = arrayOf(TAG_NAME, url)
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.bounties
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
|
||||
import com.vitorpamplona.quartz.nip10Notes.tags.markedETag
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* This is a huge hack from back in the days...
|
||||
*/
|
||||
class BountyAddValueEvent {
|
||||
companion object {
|
||||
const val KIND = 1
|
||||
const val ALT_DESCRIPTION = "Add Value to Bounty"
|
||||
|
||||
const val BOUNTY_HASH_TAG = "bounty-added-reward"
|
||||
|
||||
fun build(
|
||||
amount: BigDecimal,
|
||||
bountyRoot: EventHintBundle<TextNoteEvent>,
|
||||
bountyRootAuthor: PTag,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
): EventTemplate<TextNoteEvent> {
|
||||
val tags = TagArrayBuilder<TextNoteEvent>()
|
||||
tags.markedETag(bountyRoot.toMarkedETag(MarkedETag.MARKER.ROOT))
|
||||
tags.hashtag(BOUNTY_HASH_TAG)
|
||||
tags.pTag(bountyRootAuthor)
|
||||
tags.alt(ALT_DESCRIPTION)
|
||||
return EventTemplate(createdAt, KIND, tags.build(), amount.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,15 @@
|
||||
package com.vitorpamplona.quartz.experimental.bounties
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.firstMapTagged
|
||||
import java.math.BigDecimal
|
||||
|
||||
fun Event.getReward(): BigDecimal? = tags.firstMapTagged("reward") { runCatching { BigDecimal(it[1]) }.getOrNull() }
|
||||
fun Event.bountyBaseReward(): BigDecimal? = tags.bountyBaseReward()
|
||||
|
||||
fun Event.hasAdditionalReward(): Boolean = tags.hasAdditionalReward()
|
||||
|
||||
fun Event.addedRewardValue(): BigDecimal? =
|
||||
if (hasAdditionalReward()) {
|
||||
runCatching { BigDecimal(content) }.getOrNull()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.bounties
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
class RewardTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "reward"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): BigDecimal? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
|
||||
return runCatching { BigDecimal(tag[1]) }.getOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(amount: BigDecimal) = arrayOf(TAG_NAME, amount.toString())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.bounties
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.bounties.BountyAddValueEvent.Companion.BOUNTY_HASH_TAG
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.isTaggedHash
|
||||
|
||||
fun TagArray.bountyBaseReward() = this.firstNotNullOfOrNull(RewardTag::parse)
|
||||
|
||||
fun TagArray.hasAdditionalReward() = this.isTaggedHash(BOUNTY_HASH_TAG)
|
||||
+2
-2
@@ -30,7 +30,7 @@ import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
@@ -100,7 +100,7 @@ class PrivateOutboxRelayListEvent(
|
||||
|
||||
companion object {
|
||||
const val KIND = 10013
|
||||
val TAGS = arrayOf(AltTagSerializer.toTagArray("Relay list to store private content from this author"))
|
||||
val TAGS = arrayOf(AltTag.assemble("Relay list to store private content from this author"))
|
||||
|
||||
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
|
||||
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.firstTaggedEvent
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
@@ -64,7 +64,7 @@ class TextNoteModificationEvent(
|
||||
tags.add(arrayOf("summary", it))
|
||||
}
|
||||
|
||||
tags.add(AltTagSerializer.toTagArray(ALT))
|
||||
tags.add(AltTag.assemble(ALT))
|
||||
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), content, onReady)
|
||||
}
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.forks
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
|
||||
|
||||
fun BaseThreadedEvent.isAFork() = tags.any { it.size > 3 && (it[0] == "a" || it[0] == "e") && it[3] == "fork" }
|
||||
|
||||
fun BaseThreadedEvent.forkFromAddress() =
|
||||
tags.firstOrNull { it.size > 3 && it[0] == "a" && it[3] == "fork" }?.let {
|
||||
val aTagValue = it[1]
|
||||
val relay = it.getOrNull(2)
|
||||
|
||||
ATag.parse(aTagValue, relay)
|
||||
}
|
||||
|
||||
fun BaseThreadedEvent.forkFromVersion() = tags.firstNotNullOfOrNull(MarkedETag::parseFork)
|
||||
|
||||
fun BaseThreadedEvent.isForkFromAddressWithPubkey(authorHex: HexKey) = tags.any { it.size > 3 && it[0] == "a" && it[3] == "fork" && it[1].contains(authorHex) }
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.forks
|
||||
|
||||
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
|
||||
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag.MARKER
|
||||
|
||||
fun MarkedETag.Companion.parseFork(tag: Array<String>): MarkedETag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
if (tag[ORDER_MARKER] != MARKER.FORK.code) return null
|
||||
// ["e", id hex, relay hint, marker, pubkey]
|
||||
return MarkedETag(
|
||||
tag[ORDER_EVT_ID],
|
||||
tag[ORDER_RELAY],
|
||||
tag[ORDER_MARKER],
|
||||
tag.getOrNull(
|
||||
ORDER_PUBKEY,
|
||||
),
|
||||
)
|
||||
}
|
||||
+4
-10
@@ -20,28 +20,22 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.inlineMetadata
|
||||
|
||||
import com.vitorpamplona.quartz.nip92IMeta.IMetaTag
|
||||
import java.net.URI
|
||||
import java.net.URLDecoder
|
||||
import java.net.URLEncoder
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
class Nip54InlineMetadata {
|
||||
fun createUrl(header: IMetaTag): String =
|
||||
createUrl(
|
||||
header.url,
|
||||
header.properties,
|
||||
)
|
||||
|
||||
fun createUrl(
|
||||
url: String,
|
||||
tags: Map<String, String>,
|
||||
tags: Map<String, List<String>>,
|
||||
): String {
|
||||
val extension =
|
||||
tags
|
||||
.mapNotNull {
|
||||
if (it.key != "url") {
|
||||
"${it.key}=${URLEncoder.encode(it.value, "utf-8")}"
|
||||
val value = it.value.firstOrNull()
|
||||
if (it.key != "url" && value != null) {
|
||||
"${it.key}=${URLEncoder.encode(value, "utf-8")}"
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
+8
-83
@@ -20,23 +20,12 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.interactiveStories
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.tags.StoryOptionTag
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashMipMap
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.buildHashtagTags
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.buildUrlRefs
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findURLs
|
||||
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrl
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip36SensitiveContent.ContentWarningSerializer
|
||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetupSerializer
|
||||
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.ZapRaiserSerializer
|
||||
import com.vitorpamplona.quartz.nip92IMeta.IMetaTag
|
||||
import com.vitorpamplona.quartz.nip92IMeta.Nip92MediaAttachments
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.SummaryTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
|
||||
|
||||
open class InteractiveStoryBaseEvent(
|
||||
id: HexKey,
|
||||
@@ -47,75 +36,11 @@ open class InteractiveStoryBaseEvent(
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun title() = tags.firstTagValue("title")
|
||||
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
|
||||
|
||||
fun summary() = tags.firstTagValue("summary")
|
||||
fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse)
|
||||
|
||||
fun image() = tags.firstTagValue("image")
|
||||
fun image() = tags.firstNotNullOfOrNull(ImageTag::parse)
|
||||
|
||||
fun options() =
|
||||
tags
|
||||
.filter { it.size > 2 && it[0] == "option" }
|
||||
.mapNotNull { ATag.parse(it[2], it.getOrNull(3))?.let { aTag -> StoryOption(it[1], aTag) } }
|
||||
|
||||
companion object {
|
||||
fun generalTags(
|
||||
content: String,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
): Array<Array<String>> {
|
||||
val tags = mutableListOf<Array<String>>()
|
||||
|
||||
tags.addAll(buildHashtagTags(findHashtags(content)))
|
||||
tags.addAll(buildUrlRefs(findURLs(content)))
|
||||
zapReceiver?.forEach { tags.add(ZapSplitSetupSerializer.toTagArray(it)) }
|
||||
zapRaiserAmount?.let { tags.add(ZapRaiserSerializer.toTagArray(it)) }
|
||||
|
||||
if (markAsSensitive) {
|
||||
tags.add(ContentWarningSerializer.toTagArray())
|
||||
}
|
||||
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
emojis?.forEach { tags.add(it.toTagArray()) }
|
||||
return tags.toTypedArray()
|
||||
}
|
||||
|
||||
fun makeTags(
|
||||
baseId: String,
|
||||
alt: String,
|
||||
title: String,
|
||||
summary: String? = null,
|
||||
image: String? = null,
|
||||
options: List<StoryOption> = emptyList(),
|
||||
): Array<Array<String>> =
|
||||
(
|
||||
listOfNotNull(
|
||||
arrayOf("d", baseId),
|
||||
arrayOf("title", title),
|
||||
summary?.let { arrayOf("summary", it) },
|
||||
image?.let { arrayOf("image", it) },
|
||||
AltTagSerializer.toTagArray(alt),
|
||||
) +
|
||||
options.map {
|
||||
val relayUrl = it.address.relay
|
||||
if (relayUrl != null) {
|
||||
arrayOf("option", it.option, it.address.toTag(), relayUrl)
|
||||
} else {
|
||||
arrayOf("option", it.option, it.address.toTag())
|
||||
}
|
||||
}
|
||||
).toTypedArray()
|
||||
}
|
||||
fun options() = tags.mapNotNull(StoryOptionTag::parse)
|
||||
}
|
||||
|
||||
class StoryOption(
|
||||
val option: String,
|
||||
val address: ATag,
|
||||
)
|
||||
|
||||
+15
-26
@@ -20,12 +20,14 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.interactiveStories
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.tags.StoryOptionTag
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
|
||||
import com.vitorpamplona.quartz.nip22Comments.RootScope
|
||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
||||
import com.vitorpamplona.quartz.nip92IMeta.IMetaTag
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
class InteractiveStoryPrologueEvent(
|
||||
@@ -39,7 +41,7 @@ class InteractiveStoryPrologueEvent(
|
||||
RootScope {
|
||||
companion object {
|
||||
const val KIND = 30296
|
||||
const val ALT = "The prologue of an interative story called "
|
||||
const val ALT = "The prologue of an interactive story called "
|
||||
|
||||
fun createAddressATag(
|
||||
pubKey: HexKey,
|
||||
@@ -51,32 +53,19 @@ class InteractiveStoryPrologueEvent(
|
||||
dtag: String,
|
||||
): String = ATag.assembleATagId(KIND, pubKey, dtag)
|
||||
|
||||
fun create(
|
||||
fun build(
|
||||
baseId: String,
|
||||
title: String,
|
||||
content: String,
|
||||
options: List<StoryOption>,
|
||||
summary: String? = null,
|
||||
image: String? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
signer: NostrSigner,
|
||||
options: List<StoryOptionTag>,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
isDraft: Boolean,
|
||||
onReady: (InteractiveStoryPrologueEvent) -> Unit,
|
||||
) {
|
||||
val tags =
|
||||
makeTags(baseId, ALT + title, title, summary, image, options) +
|
||||
generalTags(content, zapReceiver, markAsSensitive, zapRaiserAmount, geohash, imetas)
|
||||
|
||||
if (isDraft) {
|
||||
signer.assembleRumor(createdAt, KIND, tags, content, onReady)
|
||||
} else {
|
||||
signer.sign(createdAt, KIND, tags, content, onReady)
|
||||
initializer: TagArrayBuilder<InteractiveStoryPrologueEvent>.() -> Unit = {},
|
||||
): EventTemplate<InteractiveStoryPrologueEvent> =
|
||||
eventTemplate(KIND, content, createdAt) {
|
||||
dTag(baseId)
|
||||
title(title)
|
||||
options(options)
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+49
-53
@@ -21,15 +21,22 @@
|
||||
package com.vitorpamplona.quartz.experimental.interactiveStories
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.tags.ReadStatusTag
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.tags.RootSceneTag
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.firstTag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.core.builder
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.SummaryTag
|
||||
import com.vitorpamplona.quartz.nip99Classifieds.tags.StatusTag
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
|
||||
|
||||
@Immutable
|
||||
class InteractiveStoryReadingStateEvent(
|
||||
@@ -40,17 +47,17 @@ class InteractiveStoryReadingStateEvent(
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun title() = tags.firstTagValue("title")
|
||||
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
|
||||
|
||||
fun summary() = tags.firstTagValue("summary")
|
||||
fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse)
|
||||
|
||||
fun image() = tags.firstTagValue("image")
|
||||
fun image() = tags.firstNotNullOfOrNull(ImageTag::parse)
|
||||
|
||||
fun status() = tags.firstTagValue("status")
|
||||
fun status() = tags.firstNotNullOfOrNull(StatusTag::parse)
|
||||
|
||||
fun root() = tags.firstTag("A")?.let { ATag.parse(it[1], it.getOrNull(2)) }
|
||||
fun root() = tags.firstNotNullOfOrNull(RootSceneTag::parse)
|
||||
|
||||
fun currentScene() = tags.firstTag("a")?.let { ATag.parse(it[1], it.getOrNull(2)) }
|
||||
fun currentScene() = tags.firstNotNullOfOrNull(ATag::parse)
|
||||
|
||||
companion object {
|
||||
const val KIND = 30298
|
||||
@@ -71,71 +78,60 @@ class InteractiveStoryReadingStateEvent(
|
||||
base: InteractiveStoryReadingStateEvent,
|
||||
currentScene: InteractiveStoryBaseEvent,
|
||||
currentSceneRelay: String?,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (InteractiveStoryReadingStateEvent) -> Unit,
|
||||
) {
|
||||
): EventTemplate<InteractiveStoryReadingStateEvent> {
|
||||
val rootTag = base.dTag()
|
||||
val sceneTag = currentScene.addressTag()
|
||||
val sceneTag = currentScene.aTag(currentSceneRelay)
|
||||
|
||||
val status =
|
||||
if (rootTag == sceneTag) {
|
||||
"new"
|
||||
if (rootTag == sceneTag.toTag()) {
|
||||
ReadStatusTag.STATUS.NEW
|
||||
} else if (currentScene.options().isEmpty()) {
|
||||
"done"
|
||||
ReadStatusTag.STATUS.DONE
|
||||
} else {
|
||||
"reading"
|
||||
ReadStatusTag.STATUS.READING
|
||||
}
|
||||
|
||||
val tags =
|
||||
base.tags.filter { it[0] != "a" && it[0] != "status" } +
|
||||
listOf(
|
||||
removeTrailingNullsAndEmptyOthers("a", sceneTag, currentSceneRelay),
|
||||
arrayOf("status", status),
|
||||
)
|
||||
val updatedTags =
|
||||
base.tags.builder {
|
||||
currentScene(sceneTag)
|
||||
status(status)
|
||||
}
|
||||
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), "", onReady)
|
||||
return EventTemplate(createdAt, KIND, updatedTags, "")
|
||||
}
|
||||
|
||||
fun create(
|
||||
fun build(
|
||||
root: InteractiveStoryBaseEvent,
|
||||
rootRelay: String?,
|
||||
currentScene: InteractiveStoryBaseEvent,
|
||||
currentSceneRelay: String?,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (InteractiveStoryReadingStateEvent) -> Unit,
|
||||
) {
|
||||
val rootTag = root.addressTag()
|
||||
val sceneTag = currentScene.addressTag()
|
||||
initializer: TagArrayBuilder<InteractiveStoryReadingStateEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, "", createdAt) {
|
||||
val rootTag = root.aTag(rootRelay)
|
||||
val sceneTag = currentScene.aTag(currentSceneRelay)
|
||||
val status =
|
||||
if (rootTag == sceneTag) {
|
||||
"new"
|
||||
ReadStatusTag.STATUS.NEW
|
||||
} else if (currentScene.options().isEmpty()) {
|
||||
"done"
|
||||
ReadStatusTag.STATUS.DONE
|
||||
} else {
|
||||
"reading"
|
||||
ReadStatusTag.STATUS.READING
|
||||
}
|
||||
|
||||
val tags =
|
||||
listOfNotNull(
|
||||
arrayOf("d", rootTag),
|
||||
AltTagSerializer.toTagArray(root.title()?.let { ALT2 + it } ?: ALT1),
|
||||
root.title()?.let { arrayOf("title", it) },
|
||||
root.summary()?.let { arrayOf("summary", it) },
|
||||
root.image()?.let { arrayOf("image", it) },
|
||||
removeTrailingNullsAndEmptyOthers("A", rootTag, rootRelay),
|
||||
removeTrailingNullsAndEmptyOthers("a", sceneTag, currentSceneRelay),
|
||||
arrayOf("status", status),
|
||||
).toTypedArray()
|
||||
dTag(rootTag.toTag())
|
||||
alt(root.title()?.let { ALT2 + it } ?: ALT1)
|
||||
|
||||
signer.sign(createdAt, KIND, tags, "", onReady)
|
||||
rootScene(rootTag)
|
||||
currentScene(sceneTag)
|
||||
status(status)
|
||||
|
||||
root.title()?.let { storyTitle(it) }
|
||||
root.summary()?.let { storyImage(it) }
|
||||
root.image()?.let { storySummary(it) }
|
||||
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
|
||||
enum class ReadingStatus {
|
||||
NEW,
|
||||
READING,
|
||||
DONE,
|
||||
}
|
||||
}
|
||||
|
||||
+12
-23
@@ -20,12 +20,13 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.interactiveStories
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.tags.StoryOptionTag
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
|
||||
import com.vitorpamplona.quartz.nip22Comments.RootScope
|
||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
||||
import com.vitorpamplona.quartz.nip92IMeta.IMetaTag
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
class InteractiveStorySceneEvent(
|
||||
@@ -51,30 +52,18 @@ class InteractiveStorySceneEvent(
|
||||
dtag: String,
|
||||
): String = ATag.assembleATagId(KIND, pubKey, dtag)
|
||||
|
||||
fun create(
|
||||
fun build(
|
||||
baseId: String,
|
||||
title: String,
|
||||
content: String,
|
||||
options: List<StoryOption>,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
signer: NostrSigner,
|
||||
options: List<StoryOptionTag>,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
isDraft: Boolean,
|
||||
onReady: (InteractiveStorySceneEvent) -> Unit,
|
||||
) {
|
||||
val tags =
|
||||
makeTags(baseId, ALT + title, title, options = options) +
|
||||
generalTags(content, zapReceiver, markAsSensitive, zapRaiserAmount, geohash, imetas)
|
||||
|
||||
if (isDraft) {
|
||||
signer.assembleRumor(createdAt, KIND, tags, content, onReady)
|
||||
} else {
|
||||
signer.sign(createdAt, KIND, tags, content, onReady)
|
||||
}
|
||||
initializer: TagArrayBuilder<InteractiveStorySceneEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, content, createdAt) {
|
||||
dTag(baseId)
|
||||
title(title)
|
||||
options(options)
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.interactiveStories
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.tags.ReadStatusTag
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.tags.StoryOptionTag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.PublishedAtTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.SummaryTag
|
||||
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
|
||||
|
||||
fun <T : InteractiveStoryBaseEvent> TagArrayBuilder<T>.title(title: String) = addUnique(TitleTag.assemble(title))
|
||||
|
||||
fun <T : InteractiveStoryBaseEvent> TagArrayBuilder<T>.option(option: StoryOptionTag) = add(option.toTagArray())
|
||||
|
||||
fun <T : InteractiveStoryBaseEvent> TagArrayBuilder<T>.options(options: List<StoryOptionTag>) = addAll(options.map { it.toTagArray() })
|
||||
|
||||
fun <T : InteractiveStoryBaseEvent> TagArrayBuilder<T>.publishedAt(publishedAt: Long) = addUnique(PublishedAtTag.assemble(publishedAt))
|
||||
|
||||
fun TagArrayBuilder<InteractiveStoryPrologueEvent>.summary(summary: String) = addUnique(SummaryTag.assemble(summary))
|
||||
|
||||
fun TagArrayBuilder<InteractiveStoryPrologueEvent>.image(imageUrl: String) = add(ImageTag.assemble(imageUrl))
|
||||
|
||||
fun TagArrayBuilder<InteractiveStoryPrologueEvent>.images(imageUrls: List<String>) = addAll(imageUrls.map { ImageTag.assemble(it) })
|
||||
|
||||
fun TagArrayBuilder<InteractiveStoryReadingStateEvent>.storyTitle(title: String) = addUnique(TitleTag.assemble(title))
|
||||
|
||||
fun TagArrayBuilder<InteractiveStoryReadingStateEvent>.storySummary(summary: String) = addUnique(SummaryTag.assemble(summary))
|
||||
|
||||
fun TagArrayBuilder<InteractiveStoryReadingStateEvent>.storyImage(imageUrl: String) = add(ImageTag.assemble(imageUrl))
|
||||
|
||||
fun TagArrayBuilder<InteractiveStoryReadingStateEvent>.storyImages(imageUrls: List<String>) = addAll(imageUrls.map { ImageTag.assemble(it) })
|
||||
|
||||
fun TagArrayBuilder<InteractiveStoryReadingStateEvent>.rootScene(scene: ATag) = addUnique(scene.toATagArray())
|
||||
|
||||
fun TagArrayBuilder<InteractiveStoryReadingStateEvent>.currentScene(scene: ATag) = addUnique(scene.toATagArray())
|
||||
|
||||
fun TagArrayBuilder<InteractiveStoryReadingStateEvent>.status(status: ReadStatusTag.STATUS) = addUnique(status.toTagArray())
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.interactiveStories.tags
|
||||
|
||||
class ReadStatusTag {
|
||||
enum class STATUS(
|
||||
val value: String,
|
||||
) {
|
||||
NEW("new"),
|
||||
READING("reading"),
|
||||
DONE("done"),
|
||||
;
|
||||
|
||||
fun toTagArray() = assemble(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "status"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(status: STATUS) = arrayOf(TAG_NAME, status.value)
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.interactiveStories.tags
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
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.pointerSizeInBytes
|
||||
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
|
||||
|
||||
@Immutable
|
||||
data class RootSceneTag(
|
||||
val kind: Int,
|
||||
val pubKeyHex: String,
|
||||
val dTag: String,
|
||||
) {
|
||||
var relay: String? = null
|
||||
|
||||
constructor(
|
||||
kind: Int,
|
||||
pubKeyHex: HexKey,
|
||||
dTag: String,
|
||||
relayHint: String?,
|
||||
) : this(kind, pubKeyHex, dTag) {
|
||||
this.relay = relayHint
|
||||
}
|
||||
|
||||
fun countMemory(): Long =
|
||||
5 * pointerSizeInBytes + // 7 fields, 4 bytes each reference (32bit)
|
||||
8L + // kind
|
||||
pubKeyHex.bytesUsedInMemory() +
|
||||
dTag.bytesUsedInMemory() +
|
||||
(relay?.bytesUsedInMemory() ?: 0)
|
||||
|
||||
fun toTag() = assembleATagId(kind, pubKeyHex, dTag)
|
||||
|
||||
fun toTagArray() = removeTrailingNullsAndEmptyOthers(TAG_NAME, toTag(), relay)
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "A"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
fun assembleATagId(
|
||||
kind: Int,
|
||||
pubKeyHex: HexKey,
|
||||
dTag: String,
|
||||
) = Address.assemble(kind, pubKeyHex, dTag)
|
||||
|
||||
fun parse(
|
||||
aTagId: String,
|
||||
relay: String?,
|
||||
) = Address.parse(aTagId)?.let { RootSceneTag(it.kind, it.pubKeyHex, it.dTag, relay) }
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): RootSceneTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return parse(tag[1], tag.getOrNull(2))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
aTagId: HexKey,
|
||||
relay: String?,
|
||||
) = arrayOfNotNull(TAG_NAME, aTagId, relay)
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
kind: Int,
|
||||
pubKeyHex: String,
|
||||
dTag: String,
|
||||
relay: String?,
|
||||
) = arrayOfNotNull(TAG_NAME, assembleATagId(kind, pubKeyHex, dTag), relay)
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.interactiveStories.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.utils.arrayOfNotNull
|
||||
|
||||
class StoryOptionTag(
|
||||
val option: String,
|
||||
val address: ATag,
|
||||
) {
|
||||
fun toTagArray() = assemble(option, address)
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "option"
|
||||
const val TAG_SIZE = 3
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): StoryOptionTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
|
||||
return ATag.parse(tag[2], tag.getOrNull(3))?.let { StoryOptionTag(tag[1], it) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
title: String,
|
||||
destination: ATag,
|
||||
) = arrayOfNotNull(TAG_NAME, title, destination.toTag(), destination.relay)
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
package com.vitorpamplona.quartz.experimental.limits
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip13Pow.pow
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
|
||||
+10
-8
@@ -21,9 +21,12 @@
|
||||
package com.vitorpamplona.quartz.experimental.medical
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent.Companion.ALT
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
@@ -37,16 +40,15 @@ class FhirResourceEvent(
|
||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
companion object {
|
||||
const val KIND = 82
|
||||
const val ALT_DESCRIPTION = "Medical data"
|
||||
|
||||
fun create(
|
||||
fun build(
|
||||
fhirPayload: String,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (FhirResourceEvent) -> Unit,
|
||||
) {
|
||||
val tags = mutableListOf<Array<String>>()
|
||||
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), fhirPayload, onReady)
|
||||
initializer: TagArrayBuilder<FhirResourceEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, fhirPayload, createdAt) {
|
||||
alt(ALT)
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.nip95
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip36SensitiveContent.ContentWarningSerializer
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.Dimension
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
class FileStorageHeaderEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun dataEventId() = tags.firstOrNull { it.size > 1 && it[0] == "e" }?.get(1)
|
||||
|
||||
fun mimeType() = tags.firstOrNull { it.size > 1 && it[0] == MIME_TYPE }?.get(1)
|
||||
|
||||
fun hash() = tags.firstOrNull { it.size > 1 && it[0] == HASH }?.get(1)
|
||||
|
||||
fun size() = tags.firstOrNull { it.size > 1 && it[0] == FILE_SIZE }?.get(1)
|
||||
|
||||
fun alt() = tags.firstOrNull { it.size > 1 && it[0] == ALT }?.get(1)
|
||||
|
||||
fun dimensions() = tags.firstOrNull { it.size > 1 && it[0] == DIMENSION }?.get(1)?.let { Dimension.parse(it) }
|
||||
|
||||
fun magnetURI() = tags.firstOrNull { it.size > 1 && it[0] == MAGNET_URI }?.get(1)
|
||||
|
||||
fun torrentInfoHash() = tags.firstOrNull { it.size > 1 && it[0] == TORRENT_INFOHASH }?.get(1)
|
||||
|
||||
fun blurhash() = tags.firstOrNull { it.size > 1 && it[0] == BLUR_HASH }?.get(1)
|
||||
|
||||
fun isOneOf(mimeTypes: Set<String>) = tags.any { it.size > 1 && it[0] == FileHeaderEvent.MIME_TYPE && mimeTypes.contains(it[1]) }
|
||||
|
||||
companion object {
|
||||
const val KIND = 1065
|
||||
const val ALT_DESCRIPTION = "Descriptors for a binary file"
|
||||
|
||||
private const val ENCRYPTION_KEY = "aes-256-gcm"
|
||||
private const val MIME_TYPE = "m"
|
||||
private const val FILE_SIZE = "size"
|
||||
private const val DIMENSION = "dim"
|
||||
private const val HASH = "x"
|
||||
private const val MAGNET_URI = "magnet"
|
||||
private const val TORRENT_INFOHASH = "i"
|
||||
private const val BLUR_HASH = "blurhash"
|
||||
private const val ALT = "alt"
|
||||
|
||||
fun create(
|
||||
storageEvent: FileStorageEvent,
|
||||
mimeType: String? = null,
|
||||
alt: String? = null,
|
||||
hash: String? = null,
|
||||
size: String? = null,
|
||||
dimensions: Dimension? = null,
|
||||
blurhash: String? = null,
|
||||
magnetURI: String? = null,
|
||||
torrentInfoHash: String? = null,
|
||||
sensitiveContent: Boolean? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (FileStorageHeaderEvent) -> Unit,
|
||||
) {
|
||||
val tags =
|
||||
listOfNotNull(
|
||||
arrayOf("e", storageEvent.id),
|
||||
mimeType?.let { arrayOf(MIME_TYPE, mimeType) },
|
||||
hash?.let { arrayOf(HASH, it) },
|
||||
alt?.let { arrayOf(ALT, it) } ?: AltTagSerializer.toTagArray(ALT_DESCRIPTION),
|
||||
size?.let { arrayOf(FILE_SIZE, it) },
|
||||
dimensions?.let { arrayOf(DIMENSION, it.toString()) },
|
||||
blurhash?.let { arrayOf(BLUR_HASH, it) },
|
||||
magnetURI?.let { arrayOf(MAGNET_URI, it) },
|
||||
torrentInfoHash?.let { arrayOf(TORRENT_INFOHASH, it) },
|
||||
sensitiveContent?.let {
|
||||
if (it) {
|
||||
ContentWarningSerializer.toTagArray()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
val content = alt ?: ""
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), content, onReady)
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
-29
@@ -18,14 +18,15 @@
|
||||
* 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.experimental.nip95
|
||||
package com.vitorpamplona.quartz.experimental.nip95.data
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import java.util.Base64
|
||||
|
||||
@@ -40,40 +41,26 @@ class FileStorageEvent(
|
||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
override fun isContentEncoded() = true
|
||||
|
||||
fun type() = tags.firstOrNull { it.size > 1 && it[0] == TYPE }?.get(1)
|
||||
|
||||
fun decode(): ByteArray? =
|
||||
try {
|
||||
Base64.getDecoder().decode(content)
|
||||
} catch (e: Exception) {
|
||||
Log.e("FileStorageEvent", "Unable to decode base 64 ${e.message} $content")
|
||||
null
|
||||
}
|
||||
fun decode(): ByteArray? = decode(content)
|
||||
|
||||
companion object {
|
||||
const val KIND = 1064
|
||||
const val ALT = "Binary data"
|
||||
|
||||
private const val TYPE = "type"
|
||||
private const val DECRYPT = "decrypt"
|
||||
fun decode(content: String): ByteArray? = runCatching { Base64.getDecoder().decode(content) }.getOrNull()
|
||||
|
||||
fun encode(bytes: ByteArray): String = Base64.getEncoder().encodeToString(bytes)
|
||||
|
||||
fun create(
|
||||
mimeType: String,
|
||||
fun build(
|
||||
data: ByteArray,
|
||||
signer: NostrSigner,
|
||||
mimeType: String? = null,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (FileStorageEvent) -> Unit,
|
||||
) {
|
||||
val tags =
|
||||
listOfNotNull(
|
||||
arrayOf(TYPE, mimeType),
|
||||
AltTagSerializer.toTagArray(ALT),
|
||||
)
|
||||
|
||||
val content = encode(data)
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), content, onReady)
|
||||
}
|
||||
initializer: TagArrayBuilder<FileStorageEvent>.() -> Unit = {},
|
||||
): EventTemplate<FileStorageEvent> =
|
||||
eventTemplate(KIND, encode(data), createdAt) {
|
||||
alt(ALT)
|
||||
mimeType?.let { mimeType(it) }
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.nip95.data
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
|
||||
|
||||
fun TagArrayBuilder<FileStorageEvent>.mimeType(mimeType: String) = add(MimeTypeTag.assemble(mimeType))
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.nip95.header
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.core.any
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.eTag
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ServiceTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.SizeTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.SummaryTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ThumbTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.TorrentInfoHash
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
class FileStorageHeaderEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun dataEvent() = tags.mapNotNull(ETag::parse)
|
||||
|
||||
fun dataEventIds() = tags.mapNotNull(ETag::parseId)
|
||||
|
||||
fun dataEventId() = tags.firstNotNullOfOrNull(ETag::parseId)
|
||||
|
||||
fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse)
|
||||
|
||||
fun hash() = tags.firstNotNullOfOrNull(HashTag::parse)
|
||||
|
||||
fun size() = tags.firstNotNullOfOrNull(SizeTag::parse)
|
||||
|
||||
fun dimensions() = tags.firstNotNullOfOrNull(DimensionTag::parse)
|
||||
|
||||
fun magnetURI() = tags.firstNotNullOfOrNull(MagnetTag::parse)
|
||||
|
||||
fun torrentInfoHash() = tags.firstNotNullOfOrNull(TorrentInfoHash::parse)
|
||||
|
||||
fun blurhash() = tags.firstNotNullOfOrNull(BlurhashTag::parse)
|
||||
|
||||
fun image() = tags.firstNotNullOfOrNull(ImageTag::parse)
|
||||
|
||||
fun thumb() = tags.firstNotNullOfOrNull(ThumbTag::parse)
|
||||
|
||||
fun service() = tags.firstNotNullOfOrNull(ServiceTag::parse)
|
||||
|
||||
fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse)
|
||||
|
||||
fun fallback() = tags.firstNotNullOfOrNull(FallbackTag::parse)
|
||||
|
||||
fun isOneOf(mimeTypes: Set<String>) = tags.any(MimeTypeTag::isIn, mimeTypes)
|
||||
|
||||
companion object {
|
||||
const val KIND = 1065
|
||||
const val ALT_DESCRIPTION = "Descriptors for a binary file"
|
||||
|
||||
fun build(
|
||||
storageEvent: EventHintBundle<FileStorageEvent>,
|
||||
caption: String?,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<FileStorageHeaderEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, caption ?: "", createdAt) {
|
||||
eTag(storageEvent.toETag())
|
||||
caption?.ifBlank { null }?.let { alt(caption) } ?: alt(ALT_DESCRIPTION)
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.nip95.header
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ServiceTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.SizeTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.SummaryTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ThumbTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.TorrentInfoHash
|
||||
|
||||
fun TagArrayBuilder<FileStorageHeaderEvent>.mimeType(mimeType: String) = add(MimeTypeTag.assemble(mimeType))
|
||||
|
||||
fun TagArrayBuilder<FileStorageHeaderEvent>.hash(hash: HexKey) = add(HashTag.assemble(hash))
|
||||
|
||||
fun TagArrayBuilder<FileStorageHeaderEvent>.fileSize(size: Int) = add(SizeTag.assemble(size))
|
||||
|
||||
fun TagArrayBuilder<FileStorageHeaderEvent>.dimension(dim: DimensionTag) = add(DimensionTag.assemble(dim))
|
||||
|
||||
fun TagArrayBuilder<FileStorageHeaderEvent>.blurhash(blurhash: String) = add(BlurhashTag.assemble(blurhash))
|
||||
|
||||
fun TagArrayBuilder<FileStorageHeaderEvent>.torrentInfohash(hash: String) = add(TorrentInfoHash.assemble(hash))
|
||||
|
||||
fun TagArrayBuilder<FileStorageHeaderEvent>.magnet(magnetUri: String) = add(MagnetTag.assemble(magnetUri))
|
||||
|
||||
fun TagArrayBuilder<FileStorageHeaderEvent>.image(imageUrl: HexKey) = add(ImageTag.assemble(imageUrl))
|
||||
|
||||
fun TagArrayBuilder<FileStorageHeaderEvent>.thumb(trumbUrl: HexKey) = add(ThumbTag.assemble(trumbUrl))
|
||||
|
||||
fun TagArrayBuilder<FileStorageHeaderEvent>.summary(summary: HexKey) = add(SummaryTag.assemble(summary))
|
||||
|
||||
fun TagArrayBuilder<FileStorageHeaderEvent>.fallback(fallbackUrl: HexKey) = add(FallbackTag.assemble(fallbackUrl))
|
||||
|
||||
fun TagArrayBuilder<FileStorageHeaderEvent>.service(service: HexKey) = add(ServiceTag.assemble(service))
|
||||
@@ -21,10 +21,14 @@
|
||||
package com.vitorpamplona.quartz.experimental.nns
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.nns.tags.IPv4Tag
|
||||
import com.vitorpamplona.quartz.experimental.nns.tags.IPv6Tag
|
||||
import com.vitorpamplona.quartz.experimental.nns.tags.VersionTag
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
@@ -36,23 +40,32 @@ class NNSEvent(
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun ip4() = tags.firstOrNull { it.size > 1 && it[0] == "ip4" }?.get(1)
|
||||
fun ip4() = tags.firstNotNullOfOrNull(IPv4Tag::parse)
|
||||
|
||||
fun ip6() = tags.firstOrNull { it.size > 1 && it[0] == "ip6" }?.get(1)
|
||||
fun ip6() = tags.firstNotNullOfOrNull(IPv6Tag::parse)
|
||||
|
||||
fun version() = tags.firstOrNull { it.size > 1 && it[0] == "version" }?.get(1)
|
||||
fun version() = tags.firstNotNullOfOrNull(VersionTag::parse)
|
||||
|
||||
companion object {
|
||||
const val KIND = 30053
|
||||
const val ALT = "DNS records"
|
||||
|
||||
fun create(
|
||||
signer: NostrSigner,
|
||||
fun build(
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (NNSEvent) -> Unit,
|
||||
) {
|
||||
val tags = arrayOf(AltTagSerializer.toTagArray(ALT))
|
||||
signer.sign(createdAt, KIND, tags, "", onReady)
|
||||
initializer: TagArrayBuilder<NNSEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, "", createdAt) {
|
||||
alt(ALT)
|
||||
initializer()
|
||||
}
|
||||
|
||||
fun build(
|
||||
ipv4: String,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<NNSEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, "", createdAt) {
|
||||
alt(ALT)
|
||||
ipv4(ipv4)
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.nns
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.nns.tags.IPv4Tag
|
||||
import com.vitorpamplona.quartz.experimental.nns.tags.IPv6Tag
|
||||
import com.vitorpamplona.quartz.experimental.nns.tags.VersionTag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
|
||||
fun TagArrayBuilder<NNSEvent>.ipv4(ip: String) = add(IPv4Tag.assemble(ip))
|
||||
|
||||
fun TagArrayBuilder<NNSEvent>.ipv6(ip: String) = add(IPv6Tag.assemble(ip))
|
||||
|
||||
fun TagArrayBuilder<NNSEvent>.version(version: String) = add(VersionTag.assemble(version))
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.nns.tags
|
||||
|
||||
class IPv4Tag {
|
||||
companion object {
|
||||
const val TAG_NAME = "ip4"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(ip: String) = arrayOf(TAG_NAME, ip)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.nns.tags
|
||||
|
||||
class IPv6Tag {
|
||||
companion object {
|
||||
const val TAG_NAME = "ip6"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(ip: String) = arrayOf(TAG_NAME, ip)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.nns.tags
|
||||
|
||||
class VersionTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "version"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(ip: String) = arrayOf(TAG_NAME, ip)
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -24,7 +24,7 @@ import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||
import com.vitorpamplona.quartz.nip51Lists.GeneralListEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@@ -142,7 +142,7 @@ class GalleryListEvent(
|
||||
if (tags.any { it.size > 1 && it[0] == "alt" }) {
|
||||
tags
|
||||
} else {
|
||||
tags + AltTagSerializer.toTagArray(ALT)
|
||||
tags + AltTag.assemble(ALT)
|
||||
}
|
||||
|
||||
signer.sign(createdAt, KIND, newTags, content, onReady)
|
||||
|
||||
+46
-75
@@ -23,10 +23,24 @@ package com.vitorpamplona.quartz.experimental.profileGallery
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip36SensitiveContent.ContentWarningSerializer
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.Dimension
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.core.any
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ServiceTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.SizeTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.SummaryTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ThumbTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.TorrentInfoHash
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.UrlTag
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
@@ -38,97 +52,54 @@ class ProfileGalleryEntryEvent(
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun url() = tags.firstOrNull { it.size > 1 && it[0] == URL }?.get(1)
|
||||
fun url() = tags.firstNotNullOfOrNull(UrlTag::parse)
|
||||
|
||||
fun urls() = tags.filter { it.size > 1 && it[0] == URL }.map { it[1] }
|
||||
fun urls() = tags.mapNotNull(UrlTag::parse)
|
||||
|
||||
fun mimeType() = tags.firstOrNull { it.size > 1 && it[0] == MIME_TYPE }?.get(1)
|
||||
fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse)
|
||||
|
||||
fun hash() = tags.firstOrNull { it.size > 1 && it[0] == HASH }?.get(1)
|
||||
fun hash() = tags.firstNotNullOfOrNull(HashTag::parse)
|
||||
|
||||
fun size() = tags.firstOrNull { it.size > 1 && it[0] == FILE_SIZE }?.get(1)
|
||||
fun size() = tags.firstNotNullOfOrNull(SizeTag::parse)
|
||||
|
||||
fun alt() = tags.firstOrNull { it.size > 1 && it[0] == ALT }?.get(1)
|
||||
fun dimensions() = tags.firstNotNullOfOrNull(DimensionTag::parse)
|
||||
|
||||
fun dimensions() = tags.firstOrNull { it.size > 1 && it[0] == DIMENSION }?.get(1)?.let { Dimension.parse(it) }
|
||||
fun magnetURI() = tags.firstNotNullOfOrNull(MagnetTag::parse)
|
||||
|
||||
fun magnetURI() = tags.firstOrNull { it.size > 1 && it[0] == MAGNET_URI }?.get(1)
|
||||
fun torrentInfoHash() = tags.firstNotNullOfOrNull(TorrentInfoHash::parse)
|
||||
|
||||
fun torrentInfoHash() = tags.firstOrNull { it.size > 1 && it[0] == TORRENT_INFOHASH }?.get(1)
|
||||
fun blurhash() = tags.firstNotNullOfOrNull(BlurhashTag::parse)
|
||||
|
||||
fun blurhash() = tags.firstOrNull { it.size > 1 && it[0] == BLUR_HASH }?.get(1)
|
||||
fun image() = tags.firstNotNullOfOrNull(ImageTag::parse)
|
||||
|
||||
fun hasUrl() = tags.any { it.size > 1 && it[0] == URL }
|
||||
fun thumb() = tags.firstNotNullOfOrNull(ThumbTag::parse)
|
||||
|
||||
fun fromEvent() = tags.firstOrNull { it.size > 1 && it[0] == "e" }?.get(1)
|
||||
fun service() = tags.firstNotNullOfOrNull(ServiceTag::parse)
|
||||
|
||||
fun hasFromEvent() = tags.any { it.size > 1 && it[0] == "e" }
|
||||
fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse)
|
||||
|
||||
fun isOneOf(mimeTypes: Set<String>) = tags.any { it.size > 1 && it[0] == MIME_TYPE && mimeTypes.contains(it[1]) }
|
||||
fun fallback() = tags.firstNotNullOfOrNull(FallbackTag::parse)
|
||||
|
||||
fun hasUrl() = tags.any(UrlTag::isTag)
|
||||
|
||||
fun isOneOf(mimeTypes: Set<String>) = tags.any(MimeTypeTag::isIn, mimeTypes)
|
||||
|
||||
fun fromEvent() = tags.firstNotNullOfOrNull(ETag::parseId)
|
||||
|
||||
fun hasFromEvent() = tags.any(ETag::isTagged)
|
||||
|
||||
companion object {
|
||||
const val KIND = 1163
|
||||
const val ALT_DESCRIPTION = "Profile Gallery Entry"
|
||||
|
||||
const val URL = "url"
|
||||
const val ENCRYPTION_KEY = "aes-256-gcm"
|
||||
const val MIME_TYPE = "m"
|
||||
const val FILE_SIZE = "size"
|
||||
const val DIMENSION = "dim"
|
||||
const val HASH = "x"
|
||||
const val MAGNET_URI = "magnet"
|
||||
const val TORRENT_INFOHASH = "i"
|
||||
const val BLUR_HASH = "blurhash"
|
||||
const val ORIGINAL_HASH = "ox"
|
||||
const val ALT = "alt"
|
||||
|
||||
fun create(
|
||||
fun build(
|
||||
url: String,
|
||||
eventid: String? = null,
|
||||
relayhint: String? = null,
|
||||
magnetUri: String? = null,
|
||||
mimeType: String? = null,
|
||||
alt: String? = null,
|
||||
hash: String? = null,
|
||||
size: String? = null,
|
||||
dimensions: Dimension? = null,
|
||||
blurhash: String? = null,
|
||||
originalHash: String? = null,
|
||||
magnetURI: String? = null,
|
||||
torrentInfoHash: String? = null,
|
||||
sensitiveContent: Boolean? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (ProfileGalleryEntryEvent) -> Unit,
|
||||
) {
|
||||
var etag = eventid?.let { arrayOf("e", it) }
|
||||
relayhint?.let { etag = etag?.plus(it) }
|
||||
|
||||
val tags =
|
||||
listOfNotNull(
|
||||
arrayOf(URL, url),
|
||||
eventid?.let { etag },
|
||||
magnetUri?.let { arrayOf(MAGNET_URI, it) },
|
||||
mimeType?.let { arrayOf(MIME_TYPE, it) },
|
||||
alt?.ifBlank { null }?.let { arrayOf(ALT, it) } ?: AltTagSerializer.toTagArray(ALT_DESCRIPTION),
|
||||
hash?.let { arrayOf(HASH, it) },
|
||||
size?.let { arrayOf(FILE_SIZE, it) },
|
||||
dimensions?.let { arrayOf(DIMENSION, it.toString()) },
|
||||
blurhash?.let { arrayOf(BLUR_HASH, it) },
|
||||
originalHash?.let { arrayOf(ORIGINAL_HASH, it) },
|
||||
magnetURI?.let { arrayOf(MAGNET_URI, it) },
|
||||
torrentInfoHash?.let { arrayOf(TORRENT_INFOHASH, it) },
|
||||
sensitiveContent?.let {
|
||||
if (it) {
|
||||
ContentWarningSerializer.toTagArray()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
val content = alt ?: ""
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), content, onReady)
|
||||
initializer: TagArrayBuilder<ProfileGalleryEntryEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, "", createdAt) {
|
||||
url(url)
|
||||
alt(ALT_DESCRIPTION)
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.profileGallery
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.BlurhashTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.FallbackTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ImageTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MagnetTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.OriginalHashTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ServiceTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.SizeTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.SummaryTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.ThumbTag
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.TorrentInfoHash
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.UrlTag
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.url(url: String) = add(UrlTag.assemble(url))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.mimeType(mimeType: String) = add(MimeTypeTag.assemble(mimeType))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.hash(hash: HexKey) = add(HashTag.assemble(hash))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.fileSize(size: Int) = add(SizeTag.assemble(size))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.dimension(dim: DimensionTag) = add(DimensionTag.assemble(dim))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.blurhash(blurhash: String) = add(BlurhashTag.assemble(blurhash))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.originalHash(hash: HexKey) = add(OriginalHashTag.assemble(hash))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.torrentInfohash(hash: String) = add(TorrentInfoHash.assemble(hash))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.magnet(magnetUri: String) = add(MagnetTag.assemble(magnetUri))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.image(imageUrl: String) = add(ImageTag.assemble(imageUrl))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.thumb(trumbUrl: String) = add(ThumbTag.assemble(trumbUrl))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.summary(summary: String) = add(SummaryTag.assemble(summary))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.fallback(fallbackUrl: String) = add(FallbackTag.assemble(fallbackUrl))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.service(service: String) = add(ServiceTag.assemble(service))
|
||||
|
||||
fun TagArrayBuilder<ProfileGalleryEntryEvent>.fromEvent(
|
||||
event: HexKey,
|
||||
relayHint: String?,
|
||||
) = add(ETag.assemble(event, relayHint, null))
|
||||
+30
-29
@@ -21,9 +21,16 @@
|
||||
package com.vitorpamplona.quartz.experimental.relationshipStatus
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.relationshipStatus.tags.PetnameTag
|
||||
import com.vitorpamplona.quartz.experimental.relationshipStatus.tags.RankTag
|
||||
import com.vitorpamplona.quartz.experimental.relationshipStatus.tags.SummaryTag
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.core.tagArray
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.nip51Lists.Nip51PrivateTags
|
||||
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@@ -36,48 +43,42 @@ class RelationshipStatusEvent(
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : PrivateTagArrayEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun rank() = tags.firstNotNullOfOrNull(RankTag::parse)
|
||||
|
||||
fun petname() = tags.firstNotNullOfOrNull(PetnameTag::parse)
|
||||
|
||||
fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse)
|
||||
|
||||
companion object {
|
||||
const val KIND = 30382
|
||||
const val ALT = "Relationship Status"
|
||||
|
||||
const val PETNAME = "petname"
|
||||
const val SUMMARY = "summary"
|
||||
|
||||
private fun create(
|
||||
content: String,
|
||||
tags: Array<Array<String>>,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (RelationshipStatusEvent) -> Unit,
|
||||
) {
|
||||
val newTags =
|
||||
if (tags.any { it.size > 1 && it[0] == "alt" }) {
|
||||
tags
|
||||
} else {
|
||||
tags + AltTagSerializer.toTagArray(ALT)
|
||||
}
|
||||
|
||||
signer.sign(createdAt, KIND, newTags, content, onReady)
|
||||
}
|
||||
|
||||
fun create(
|
||||
targetUser: HexKey,
|
||||
petname: String? = null,
|
||||
summary: String? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
publicInitializer: TagArrayBuilder<RelationshipStatusEvent>.() -> Unit = {},
|
||||
privateInitializer: TagArrayBuilder<RelationshipStatusEvent>.() -> Unit = {},
|
||||
onReady: (RelationshipStatusEvent) -> Unit,
|
||||
) {
|
||||
val tags = mutableListOf<Array<String>>()
|
||||
tags.add(arrayOf("d", targetUser))
|
||||
tags.add(AltTagSerializer.toTagArray(ALT))
|
||||
val publicTags =
|
||||
tagArray {
|
||||
alt(ALT)
|
||||
dTag(targetUser)
|
||||
publicInitializer()
|
||||
}
|
||||
|
||||
val privateTags = mutableListOf<Array<String>>()
|
||||
petname?.let { privateTags.add(arrayOf(PETNAME, it)) }
|
||||
summary?.let { privateTags.add(arrayOf(SUMMARY, it)) }
|
||||
val privateTags =
|
||||
tagArray {
|
||||
petname?.let { petname(it) }
|
||||
summary?.let { summary(it) }
|
||||
privateInitializer()
|
||||
}
|
||||
|
||||
encryptTags(privateTags.toTypedArray(), signer) { content ->
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), content, onReady)
|
||||
Nip51PrivateTags.encryptNip44(privateTags, signer) { content ->
|
||||
signer.sign(createdAt, KIND, publicTags, content, onReady)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.relationshipStatus
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.relationshipStatus.tags.PetnameTag
|
||||
import com.vitorpamplona.quartz.experimental.relationshipStatus.tags.RankTag
|
||||
import com.vitorpamplona.quartz.experimental.relationshipStatus.tags.SummaryTag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
|
||||
fun TagArrayBuilder<RelationshipStatusEvent>.rank(rank: Int) = add(RankTag.assemble(rank))
|
||||
|
||||
fun TagArrayBuilder<RelationshipStatusEvent>.petname(name: String) = add(PetnameTag.assemble(name))
|
||||
|
||||
fun TagArrayBuilder<RelationshipStatusEvent>.summary(summary: String) = add(SummaryTag.assemble(summary))
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.relationshipStatus.tags
|
||||
|
||||
class PetnameTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "petname"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(petname: String) = arrayOf(TAG_NAME, petname)
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.relationshipStatus.tags
|
||||
|
||||
class RankTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "rank"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Int? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1].toIntOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(rank: Int) = arrayOf(TAG_NAME, rank.toString())
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.relationshipStatus.tags
|
||||
|
||||
class SummaryTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "summary"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): String? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(ip: String) = arrayOf(TAG_NAME, ip)
|
||||
}
|
||||
}
|
||||
+28
-106
@@ -21,132 +21,54 @@
|
||||
package com.vitorpamplona.quartz.experimental.zapPolls
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.tags.ClosedAtTag
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.tags.ConsensusThresholdTag
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.tags.MaximumTag
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.tags.MinimumTag
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.tags.PollOptionTag
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashMipMap
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseTextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrl
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip36SensitiveContent.ContentWarningSerializer
|
||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetupSerializer
|
||||
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.ZapRaiserSerializer
|
||||
import com.vitorpamplona.quartz.nip92IMeta.IMetaTag
|
||||
import com.vitorpamplona.quartz.nip92IMeta.Nip92MediaAttachments
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
const val POLL_OPTION = "poll_option"
|
||||
const val VALUE_MAXIMUM = "value_maximum"
|
||||
const val VALUE_MINIMUM = "value_minimum"
|
||||
const val CONSENSUS_THRESHOLD = "consensus_threshold"
|
||||
const val CLOSED_AT = "closed_at"
|
||||
|
||||
@Immutable
|
||||
class PollNoteEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: Array<Array<String>>,
|
||||
// ots: String?, TODO implement OTS: https://github.com/opentimestamps/java-opentimestamps
|
||||
content: String,
|
||||
sig: HexKey,
|
||||
) : BaseTextNoteEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun pollOptions() = tags.filter { it.size > 2 && it[0] == POLL_OPTION }.associate { it[1].toInt() to it[2] }
|
||||
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun pollOptionsArray() = tags.mapNotNull(PollOptionTag::parse)
|
||||
|
||||
fun minimumAmount() = tags.firstOrNull { it.size > 1 && it[0] == VALUE_MINIMUM }?.getOrNull(1)?.toLongOrNull()
|
||||
fun pollOptions() = pollOptionsArray().associate { it.index to it.descriptor }
|
||||
|
||||
fun maximumAmount() = tags.firstOrNull { it.size > 1 && it[0] == VALUE_MAXIMUM }?.getOrNull(1)?.toLongOrNull()
|
||||
fun minAmount() = tags.firstNotNullOfOrNull(MinimumTag::parse)
|
||||
|
||||
fun getTagLong(property: String): Long? {
|
||||
val number = tags.firstOrNull { it.size > 1 && it[0] == property }?.get(1)
|
||||
fun maxAmount() = tags.firstNotNullOfOrNull(MaximumTag::parse)
|
||||
|
||||
return if (number.isNullOrBlank() || number == "null") {
|
||||
null
|
||||
} else {
|
||||
number.toLong()
|
||||
}
|
||||
}
|
||||
fun closedAt() = tags.firstNotNullOfOrNull(ClosedAtTag::parse)
|
||||
|
||||
fun consensusThreshold() = tags.firstNotNullOfOrNull(ConsensusThresholdTag::parse)
|
||||
|
||||
companion object {
|
||||
const val KIND = 6969
|
||||
const val ALT = "Poll event"
|
||||
const val ALT_DESCRIPTION = "Poll event"
|
||||
|
||||
fun create(
|
||||
msg: String,
|
||||
replyTos: List<String>?,
|
||||
mentions: List<String>?,
|
||||
addresses: List<ATag>?,
|
||||
signer: NostrSigner,
|
||||
fun build(
|
||||
post: String,
|
||||
options: List<PollOptionTag>,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
pollOptions: Map<Int, String>,
|
||||
valueMaximum: Int?,
|
||||
valueMinimum: Int?,
|
||||
consensusThreshold: Int?,
|
||||
closedAt: Int?,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean,
|
||||
zapRaiserAmount: Long?,
|
||||
geohash: String? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
emojis: List<EmojiUrl>? = null,
|
||||
isDraft: Boolean,
|
||||
onReady: (PollNoteEvent) -> Unit,
|
||||
) {
|
||||
val tags = mutableListOf<Array<String>>()
|
||||
replyTos?.forEach { tags.add(arrayOf("e", it)) }
|
||||
mentions?.forEach { tags.add(arrayOf("p", it)) }
|
||||
addresses?.forEach { tags.add(arrayOf("a", it.toTag())) }
|
||||
pollOptions.forEach { poll_op ->
|
||||
tags.add(arrayOf(POLL_OPTION, poll_op.key.toString(), poll_op.value))
|
||||
}
|
||||
valueMaximum?.let { tags.add(arrayOf(VALUE_MAXIMUM, valueMaximum.toString())) }
|
||||
valueMinimum?.let { tags.add(arrayOf(VALUE_MINIMUM, valueMinimum.toString())) }
|
||||
consensusThreshold?.let {
|
||||
tags.add(arrayOf(CONSENSUS_THRESHOLD, consensusThreshold.toString()))
|
||||
}
|
||||
closedAt?.let { tags.add(arrayOf(CLOSED_AT, closedAt.toString())) }
|
||||
zapReceiver?.forEach { tags.add(ZapSplitSetupSerializer.toTagArray(it)) }
|
||||
zapRaiserAmount?.let { tags.add(ZapRaiserSerializer.toTagArray(it)) }
|
||||
|
||||
if (markAsSensitive) {
|
||||
tags.add(ContentWarningSerializer.toTagArray())
|
||||
}
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
emojis?.forEach { tags.add(it.toTagArray()) }
|
||||
tags.add(AltTagSerializer.toTagArray(ALT))
|
||||
|
||||
if (isDraft) {
|
||||
signer.assembleRumor(createdAt, KIND, tags.toTypedArray(), msg, onReady)
|
||||
} else {
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), msg, onReady)
|
||||
}
|
||||
initializer: TagArrayBuilder<PollNoteEvent>.() -> Unit = {},
|
||||
): EventTemplate<PollNoteEvent> {
|
||||
val tags = TagArrayBuilder<PollNoteEvent>()
|
||||
tags.pollOptions(options)
|
||||
tags.alt(ALT_DESCRIPTION)
|
||||
tags.apply(initializer)
|
||||
return EventTemplate(createdAt, KIND, tags.build(), post)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"id": <32-bytes lowercase hex-encoded sha256 of the serialized event data>
|
||||
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
|
||||
"created_at": <unix timestamp in seconds>,
|
||||
"kind": 6969,
|
||||
"tags": [
|
||||
["e", <32-bytes hex of the id of the poll event>, <primary poll host relay URL>],
|
||||
["p", <32-bytes hex of the key>, <primary poll host relay URL>],
|
||||
["poll_option", "0", "poll option 0 description string"],
|
||||
["poll_option", "1", "poll option 1 description string"],
|
||||
["poll_option", "n", "poll option <n> description string"],
|
||||
["value_maximum", "maximum satoshi value for inclusion in tally"],
|
||||
["value_minimum", "minimum satoshi value for inclusion in tally"],
|
||||
["consensus_threshold", "required percentage to attain consensus <0..100>"],
|
||||
["closed_at", "unix timestamp in seconds"],
|
||||
],
|
||||
"ots": <base64-encoded OTS file data>
|
||||
"content": <primary poll description string>,
|
||||
"sig": <64-bytes hex of the signature of the sha256 hash of the serialized event data, which is the same as the "id" field>
|
||||
}
|
||||
*/
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.zapPolls
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.tags.ClosedAtTag
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.tags.ConsensusThresholdTag
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.tags.MaximumTag
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.tags.MinimumTag
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.tags.PollOptionTag
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
|
||||
fun TagArrayBuilder<PollNoteEvent>.consensusThreshold(percentage: Double) = addUnique(ConsensusThresholdTag.assemble(percentage))
|
||||
|
||||
fun TagArrayBuilder<PollNoteEvent>.minAmount(value: Long) = addUnique(MinimumTag.assemble(value))
|
||||
|
||||
fun TagArrayBuilder<PollNoteEvent>.maxAmount(value: Long) = addUnique(MaximumTag.assemble(value))
|
||||
|
||||
fun TagArrayBuilder<PollNoteEvent>.closedAt(timestamp: Long) = addUnique(ClosedAtTag.assemble(timestamp))
|
||||
|
||||
fun TagArrayBuilder<PollNoteEvent>.pollOption(
|
||||
index: Int,
|
||||
description: String,
|
||||
) = add(PollOptionTag.assemble(index, description))
|
||||
|
||||
fun TagArrayBuilder<PollNoteEvent>.pollOptions(options: Map<Int, String>) = addAll(options.map { PollOptionTag.assemble(it.key, it.value) })
|
||||
|
||||
fun TagArrayBuilder<PollNoteEvent>.pollOptions(options: List<PollOptionTag>) = addAll(options.map { it.toTagArray() })
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.zapPolls.tags
|
||||
|
||||
class ClosedAtTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "closed_at"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Long? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1].toLongOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(timestamp: Long) = arrayOf(TAG_NAME, timestamp.toString())
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.zapPolls.tags
|
||||
|
||||
import kotlin.math.round
|
||||
|
||||
class ConsensusThresholdTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "consensus_threshold"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Double? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1].toLongOrNull()?.toDouble()?.div(100)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(percentage: Double) = arrayOf(TAG_NAME, (round(percentage * 100)).toString())
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.zapPolls.tags
|
||||
|
||||
class MaximumTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "value_maximum"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Long? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1].toLongOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(timestamp: Long) = arrayOf(TAG_NAME, timestamp.toString())
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.zapPolls.tags
|
||||
|
||||
class MinimumTag {
|
||||
companion object {
|
||||
const val TAG_NAME = "value_minimum"
|
||||
const val TAG_SIZE = 2
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): Long? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
return tag[1].toLongOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(timestamp: Long) = arrayOf(TAG_NAME, timestamp.toString())
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.experimental.zapPolls.tags
|
||||
|
||||
class PollOptionTag(
|
||||
val index: Int,
|
||||
val descriptor: String,
|
||||
) {
|
||||
fun toTagArray() = assemble(index, descriptor)
|
||||
|
||||
companion object {
|
||||
const val TAG_NAME = "poll_option"
|
||||
const val TAG_SIZE = 3
|
||||
|
||||
@JvmStatic
|
||||
fun parse(tag: Array<String>): PollOptionTag? {
|
||||
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
|
||||
|
||||
val index = tag[1].toIntOrNull() ?: return null
|
||||
|
||||
return PollOptionTag(index, tag[2])
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(
|
||||
index: Int,
|
||||
descriptor: String,
|
||||
) = arrayOf(TAG_NAME, index.toString(), descriptor)
|
||||
}
|
||||
}
|
||||
@@ -27,19 +27,19 @@ import com.vitorpamplona.quartz.utils.Hex
|
||||
|
||||
fun Event.generateId(): String = EventHasher.hashId(pubKey, createdAt, kind, tags, content)
|
||||
|
||||
fun Event.hasCorrectIDHash(): Boolean {
|
||||
fun Event.verifyId(): Boolean {
|
||||
if (id.isEmpty()) return false
|
||||
return id == generateId()
|
||||
}
|
||||
|
||||
fun Event.hasVerifiedSignature(): Boolean {
|
||||
fun Event.verifySignature(): Boolean {
|
||||
if (id.isEmpty() || sig.isEmpty()) return false
|
||||
return CryptoUtils.verifySignature(Hex.decode(sig), Hex.decode(id), Hex.decode(pubKey))
|
||||
}
|
||||
|
||||
/** Checks if the ID is correct and then if the pubKey's secret key signed the event. */
|
||||
fun Event.checkSignature() {
|
||||
if (!hasCorrectIDHash()) {
|
||||
if (!verifyId()) {
|
||||
throw Exception(
|
||||
"""
|
||||
|Unexpected ID.
|
||||
@@ -49,14 +49,14 @@ fun Event.checkSignature() {
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
if (!hasVerifiedSignature()) {
|
||||
if (!verifySignature()) {
|
||||
throw Exception("""Bad signature!""")
|
||||
}
|
||||
}
|
||||
|
||||
fun Event.hasValidSignature(): Boolean =
|
||||
fun Event.verify(): Boolean =
|
||||
try {
|
||||
hasCorrectIDHash() && hasVerifiedSignature()
|
||||
verifyId() && verifySignature()
|
||||
} catch (e: Exception) {
|
||||
Log.w("Event", "Event $id does not have a valid signature: ${toJson()}", e)
|
||||
false
|
||||
|
||||
@@ -22,6 +22,11 @@ package com.vitorpamplona.quartz.nip01Core
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
@@ -31,9 +36,11 @@ data class EventHintBundle<T : Event>(
|
||||
val event: T,
|
||||
) {
|
||||
var relay: String? = null
|
||||
var authorHomeRelay: String? = null
|
||||
|
||||
constructor(event: T, relayHint: String? = null) : this(event) {
|
||||
constructor(event: T, relayHint: String? = null, authorHomeRelay: String? = null) : this(event) {
|
||||
this.relay = relayHint
|
||||
this.authorHomeRelay = authorHomeRelay
|
||||
}
|
||||
|
||||
fun countMemory(): Long =
|
||||
@@ -43,9 +50,15 @@ data class EventHintBundle<T : Event>(
|
||||
|
||||
fun toNEvent(): String = NEvent.create(event.id, event.pubKey, event.kind, relay)
|
||||
|
||||
fun toTagArray(tag: String) = listOfNotNull(tag, event.id, relay, event.pubKey).toTypedArray()
|
||||
fun toETag() = ETag(event.id, relay, event.pubKey)
|
||||
|
||||
fun toETagArray() = toTagArray("e")
|
||||
fun toATag() = ATag(event.kind, event.pubKey, event.dTag(), relay)
|
||||
|
||||
fun toQTagArray() = toTagArray("q")
|
||||
fun toPTag() = PTag(event.pubKey, authorHomeRelay)
|
||||
|
||||
fun toMarkedETag(marker: MarkedETag.MARKER) = MarkedETag(event.id, relay, marker, event.pubKey)
|
||||
|
||||
fun toETagArray() = ETag.assemble(event.id, relay, event.pubKey)
|
||||
|
||||
fun toQTagArray() = ETag(event.id, relay, event.pubKey).toQTagArray()
|
||||
}
|
||||
|
||||
@@ -22,12 +22,15 @@ package com.vitorpamplona.quartz.nip01Core.core
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
|
||||
@Immutable
|
||||
interface AddressableEvent {
|
||||
interface AddressableEvent : IEvent {
|
||||
fun dTag(): String
|
||||
|
||||
fun address(relayHint: String? = null): ATag
|
||||
fun aTag(relayHint: String? = null): ATag
|
||||
|
||||
fun address(): Address
|
||||
|
||||
fun addressTag(): String
|
||||
}
|
||||
|
||||
+4
-1
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.nip01Core.core
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
|
||||
|
||||
@Immutable
|
||||
@@ -38,7 +39,9 @@ open class BaseAddressableEvent(
|
||||
AddressableEvent {
|
||||
override fun dTag() = tags.dTag()
|
||||
|
||||
override fun address(relayHint: String?) = ATag(kind, pubKey, dTag(), relayHint)
|
||||
override fun aTag(relayHint: String?) = ATag(kind, pubKey, dTag(), relayHint)
|
||||
|
||||
override fun address() = Address(kind, pubKey, dTag())
|
||||
|
||||
/**
|
||||
* Creates the tag in a memory efficient way (without creating the ATag class
|
||||
|
||||
+4
-1
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.nip01Core.core
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
|
||||
@Immutable
|
||||
open class BaseReplaceableEvent(
|
||||
@@ -36,7 +37,9 @@ open class BaseReplaceableEvent(
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
override fun dTag() = FIXED_D_TAG
|
||||
|
||||
override fun address(relayHint: String?) = ATag(kind, pubKey, FIXED_D_TAG, relayHint)
|
||||
override fun aTag(relayHint: String?) = ATag(kind, pubKey, FIXED_D_TAG, relayHint)
|
||||
|
||||
override fun address() = Address(kind, pubKey, dTag())
|
||||
|
||||
/**
|
||||
* Creates the tag in a memory efficient way (without creating the ATag class
|
||||
|
||||
@@ -39,7 +39,7 @@ open class Event(
|
||||
val tags: TagArray,
|
||||
val content: String,
|
||||
val sig: HexKey,
|
||||
) {
|
||||
) : IEvent {
|
||||
open fun isContentEncoded() = false
|
||||
|
||||
open fun countMemory(): Long =
|
||||
|
||||
+2
-6
@@ -18,10 +18,6 @@
|
||||
* 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.nip99Classifieds
|
||||
package com.vitorpamplona.quartz.nip01Core.core
|
||||
|
||||
data class Price(
|
||||
val amount: String,
|
||||
val currency: String?,
|
||||
val frequency: String?,
|
||||
)
|
||||
interface IEvent
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.nip01Core.core
|
||||
|
||||
typealias Tag = Array<String>
|
||||
|
||||
fun Tag.name() = this[0]
|
||||
|
||||
fun Tag.value() = this[1]
|
||||
|
||||
fun Tag.hasValue() = this[1].isNotEmpty()
|
||||
|
||||
fun Tag.nameOrNull() = if (size > 0) name() else null
|
||||
|
||||
fun Tag.valueOrNull() = if (size > 1) value() else null
|
||||
|
||||
fun Tag.isNameUnsafe(name: String): Boolean = name() == name
|
||||
|
||||
fun Tag.isValueUnsafe(value: String): Boolean = value() == value
|
||||
|
||||
fun Tag.isValueInUnsafe(values: Set<String>): Boolean = value() in values
|
||||
|
||||
fun Tag.match(name: String): Boolean = if (size > 0) isNameUnsafe(name) else false
|
||||
|
||||
fun Tag.isValue(value: String): Boolean = if (size > 1) isValueUnsafe(value) else false
|
||||
|
||||
fun Tag.match(
|
||||
name: String,
|
||||
value: String,
|
||||
minSize: Int,
|
||||
): Boolean = if (size >= minSize) isNameUnsafe(name) && isValueUnsafe(value) else false
|
||||
|
||||
fun Tag.match(
|
||||
name: String,
|
||||
values: Set<String>,
|
||||
minSize: Int,
|
||||
): Boolean = if (size >= minSize) isNameUnsafe(name) && isValueInUnsafe(values) else false
|
||||
|
||||
fun Tag.match(
|
||||
name: String,
|
||||
minSize: Int,
|
||||
): Boolean = if (size >= minSize) isNameUnsafe(name) else false
|
||||
|
||||
fun Tag.isNotName(
|
||||
name: String,
|
||||
minSize: Int,
|
||||
): Boolean = !match(name, minSize)
|
||||
|
||||
fun Tag.matchAndHasValue(
|
||||
name: String,
|
||||
minSize: Int,
|
||||
): Boolean = if (size >= minSize) isNameUnsafe(name) && hasValue() else false
|
||||
|
||||
fun Tag.valueIfMatches(
|
||||
name: String,
|
||||
minSize: Int,
|
||||
): String? = if (match(name, minSize)) value() else null
|
||||
|
||||
fun Tag.valueToIntIfMatches(
|
||||
name: String,
|
||||
minSize: Int,
|
||||
): Int? = if (match(name, minSize)) value().toIntOrNull() else null
|
||||
@@ -24,6 +24,8 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
|
||||
typealias TagArray = Array<Array<String>>
|
||||
|
||||
fun <T : Event> TagArray.builder(initializer: TagArrayBuilder<T>.() -> Unit = {}) = TagArrayBuilder<T>().addAll(this).apply(initializer).build()
|
||||
|
||||
/**
|
||||
* Performs the given [action] on each tag that matches the given [tagName].
|
||||
*/
|
||||
@@ -109,7 +111,7 @@ fun TagArray.mapValues(tagName: String) =
|
||||
* Returns the first non-null value produced by [transform] function being applied to all tags
|
||||
* that match the [tagName]
|
||||
*/
|
||||
fun <R> TagArray.firstMapTagged(
|
||||
fun <R> TagArray.firstMappedTag(
|
||||
tagName: String,
|
||||
transform: (tagValue: Array<String>) -> R,
|
||||
) = this.firstNotNullOfOrNull {
|
||||
@@ -161,7 +163,8 @@ fun TagArray.firstTagValueFor(vararg tagNames: String) = this.firstOrNull { it.s
|
||||
fun TagArray.isTagged(
|
||||
tagName: String,
|
||||
tagValue: String,
|
||||
) = this.any { it.size > 1 && it[0] == tagName && it[1] == tagValue }
|
||||
ignoreCase: Boolean = false,
|
||||
) = this.any { it.size > 1 && it[0] == tagName && it[1].equals(tagValue, ignoreCase) }
|
||||
|
||||
/**
|
||||
* Returns `true` if at least one tag matches the given [tagName] and is in [tagValues]
|
||||
@@ -171,6 +174,51 @@ fun TagArray.isAnyTagged(
|
||||
tagValues: Set<String>,
|
||||
) = this.any { it.size > 1 && it[0] == tagName && it[1] in tagValues }
|
||||
|
||||
fun <U> TagArray.any(
|
||||
predicate: (Array<String>, U) -> Boolean,
|
||||
extras: U,
|
||||
): Boolean {
|
||||
for (element in this) if (predicate(element, extras)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
public inline fun <T, U, R : Any> Array<out T>.firstNotNullOfOrNull(
|
||||
transform: (T, U) -> R?,
|
||||
extras: U,
|
||||
): R? {
|
||||
for (element in this) {
|
||||
val result = transform(element, extras)
|
||||
if (result != null) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if at least one tag matches the given [tagName] and is in [tagValues]
|
||||
*/
|
||||
fun TagArray.firstAnyLowercaseTaggedValue(
|
||||
tagName: String,
|
||||
tagValues: Set<String>,
|
||||
) = this.firstOrNull { it.size > 1 && it[0] == tagName && it[1].lowercase() in tagValues }?.getOrNull(1)
|
||||
|
||||
/**
|
||||
* Returns `true` if at least one tag matches the given [tagName] and is in [tagValues]
|
||||
*/
|
||||
fun TagArray.isAnyLowercaseTagged(
|
||||
tagName: String,
|
||||
tagValues: Set<String>,
|
||||
) = this.any { it.size > 1 && it[0] == tagName && it[1].lowercase() in tagValues }
|
||||
|
||||
/**
|
||||
* Returns `true` if at least one tag matches the given [tagName] and is in [tagValues]
|
||||
*/
|
||||
fun TagArray.firstAnyTaggedValue(
|
||||
tagName: String,
|
||||
tagValues: Set<String>,
|
||||
) = this.firstOrNull { it.size > 1 && it[0] == tagName && it[1] in tagValues }?.getOrNull(1)
|
||||
|
||||
/**
|
||||
* Returns `true` if at least one tag has value that contains [text]
|
||||
*/
|
||||
@@ -178,3 +226,15 @@ fun TagArray.tagValueContains(
|
||||
text: String,
|
||||
ignoreCase: Boolean = false,
|
||||
) = this.any { it.size > 1 && it[1].contains(text, ignoreCase) }
|
||||
|
||||
fun TagArray.containsAllTagNamesWithValues(names: Set<String>): Boolean {
|
||||
val remaining = names.toMutableSet()
|
||||
|
||||
this.forEach {
|
||||
if (it.size > 1) {
|
||||
remaining.remove(it[0])
|
||||
}
|
||||
}
|
||||
|
||||
return remaining.isEmpty()
|
||||
}
|
||||
|
||||
@@ -20,23 +20,75 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.core
|
||||
|
||||
class TagArrayBuilder {
|
||||
val tagList = mutableListOf<Array<String>>()
|
||||
class TagArrayBuilder<T : IEvent> {
|
||||
/**
|
||||
* keeps a tag list by tag names to treat tags that must be unique
|
||||
*/
|
||||
private val tagList = mutableMapOf<String, MutableList<Tag>>()
|
||||
|
||||
fun add(tag: Array<String>): TagArrayBuilder {
|
||||
tagList.add(tag)
|
||||
fun remove(tagName: String): TagArrayBuilder<T> {
|
||||
tagList.remove(tagName)
|
||||
return this
|
||||
}
|
||||
|
||||
fun addAll(tag: List<Array<String>>): TagArrayBuilder {
|
||||
tagList.addAll(tag)
|
||||
fun remove(
|
||||
tagName: String,
|
||||
tagValue: String,
|
||||
): TagArrayBuilder<T> {
|
||||
tagList[tagName]?.removeIf { it.value() == tagValue }
|
||||
if (tagList[tagName]?.isEmpty() == true) {
|
||||
tagList.remove(tagName)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun addAll(tag: Array<Array<String>>): TagArrayBuilder {
|
||||
tagList.addAll(tag)
|
||||
fun removeIf(
|
||||
predicate: (Tag, Tag) -> Boolean,
|
||||
toCompare: Tag,
|
||||
): TagArrayBuilder<T> {
|
||||
tagList[toCompare.name()]?.removeIf { predicate(it, toCompare) }
|
||||
if (tagList[toCompare.name()]?.isEmpty() == true) {
|
||||
tagList.remove(toCompare.name())
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun build() = tagList.toTypedArray()
|
||||
fun removeIf(
|
||||
tagName: String,
|
||||
tagValue: String,
|
||||
): TagArrayBuilder<T> {
|
||||
tagList[tagName]?.removeIf { it.value() == tagValue }
|
||||
if (tagList[tagName]?.isEmpty() == true) {
|
||||
tagList.remove(tagName)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun add(tag: Array<String>): TagArrayBuilder<T> {
|
||||
if (tag.isEmpty() || tag[0].isEmpty()) return this
|
||||
tagList.getOrPut(tag[0], ::mutableListOf).add(tag)
|
||||
return this
|
||||
}
|
||||
|
||||
fun addUnique(tag: Array<String>): TagArrayBuilder<T> {
|
||||
if (tag.isEmpty() || tag[0].isEmpty()) return this
|
||||
tagList[tag[0]] = mutableListOf(tag)
|
||||
return this
|
||||
}
|
||||
|
||||
fun addAll(tag: List<Array<String>>): TagArrayBuilder<T> {
|
||||
tag.forEach(::add)
|
||||
return this
|
||||
}
|
||||
|
||||
fun addAll(tag: Array<Array<String>>): TagArrayBuilder<T> {
|
||||
tag.forEach(::add)
|
||||
return this
|
||||
}
|
||||
|
||||
fun toTypedArray() = tagList.flatMap { it.value }.toTypedArray()
|
||||
|
||||
fun build() = toTypedArray()
|
||||
}
|
||||
|
||||
inline fun <T : Event> tagArray(initializer: TagArrayBuilder<T>.() -> Unit = {}): TagArray = TagArrayBuilder<T>().apply(initializer).build()
|
||||
|
||||
+2
-1
@@ -18,9 +18,10 @@
|
||||
* 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.nip01Core
|
||||
package com.vitorpamplona.quartz.nip01Core.crypto
|
||||
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
|
||||
class KeyPair(
|
||||
privKey: ByteArray? = null,
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.nip01Core
|
||||
package com.vitorpamplona.quartz.nip01Core.crypto
|
||||
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.utils.nextBytes
|
||||
@@ -35,9 +35,9 @@ import com.vitorpamplona.quartz.nip47WalletConnect.Request
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.RequestDeserializer
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.Response
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.ResponseDeserializer
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.Rumor
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.RumorDeserializer
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.RumorSerializer
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.rumors.Rumor
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.rumors.RumorDeserializer
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.rumors.RumorSerializer
|
||||
|
||||
class EventMapper {
|
||||
companion object {
|
||||
|
||||
+5
-4
@@ -18,17 +18,18 @@
|
||||
* 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.nip01Core
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata
|
||||
|
||||
import android.util.Log
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
|
||||
import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
|
||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||
import com.vitorpamplona.quartz.nip39ExtIdentities.updateClaims
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import java.io.ByteArrayInputStream
|
||||
@@ -69,7 +70,7 @@ class MetadataEvent(
|
||||
val tags = mutableListOf<Array<String>>()
|
||||
|
||||
tags.add(
|
||||
AltTagSerializer.toTagArray("User profile for ${name ?: currentJson.get("name").asText() ?: ""}"),
|
||||
AltTag.assemble("User profile for ${name ?: currentJson.get("name").asText() ?: ""}"),
|
||||
)
|
||||
|
||||
return signer.sign(createdAt, KIND, tags.toTypedArray(), writer.buffer.toString())
|
||||
@@ -119,7 +120,7 @@ class MetadataEvent(
|
||||
ObjectMapper().writeValue(writer, currentJson)
|
||||
|
||||
val tags = mutableListOf<Array<String>>()
|
||||
tags.add(AltTagSerializer.toTagArray("User profile for ${name ?: currentJson.get("name").asText() ?: ""}"))
|
||||
tags.add(AltTag.assemble("User profile for ${name ?: currentJson.get("name").asText() ?: ""}"))
|
||||
|
||||
latest?.updateClaims(twitter, github, mastodon)?.forEach {
|
||||
tags.add(it)
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.nip01Core
|
||||
package com.vitorpamplona.quartz.nip01Core.metadata
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.nip01Core.relays
|
||||
package com.vitorpamplona.quartz.nip01Core.relay
|
||||
|
||||
import androidx.collection.LruCache
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.nip01Core.relays
|
||||
package com.vitorpamplona.quartz.nip01Core.relay
|
||||
|
||||
enum class RelayState {
|
||||
// Websocket connected
|
||||
+18
-18
@@ -18,27 +18,27 @@
|
||||
* 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.nip01Core.relays
|
||||
package com.vitorpamplona.quartz.nip01Core.relay
|
||||
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.commands.toClient.AuthMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.commands.toClient.ClosedMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.commands.toClient.EoseMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.commands.toClient.EventMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.commands.toClient.NoticeMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.commands.toClient.NotifyMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.commands.toClient.OkMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.commands.toClient.ToClientParser
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.commands.toRelay.AuthCmd
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.commands.toRelay.CloseCmd
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.commands.toRelay.CountCmd
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.commands.toRelay.EventCmd
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.sockets.WebSocket
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.sockets.WebSocketListener
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.sockets.WebsocketBuilder
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.AuthMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.ClosedMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.EoseMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.EventMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.NoticeMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.NotifyMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.OkMessage
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.ToClientParser
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.AuthCmd
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.CloseCmd
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.CountCmd
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.EventCmd
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.sockets.WebSocket
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.sockets.WebSocketListener
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.sockets.WebsocketBuilder
|
||||
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
@@ -313,7 +313,7 @@ class SimpleClientRelay(
|
||||
if (isReady) {
|
||||
if (filters.isNotEmpty()) {
|
||||
writeToSocket(
|
||||
com.vitorpamplona.quartz.nip01Core.relays.commands.toRelay.ReqCmd
|
||||
com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.ReqCmd
|
||||
.toJson(requestId, filters),
|
||||
)
|
||||
afterEOSEPerSubscription[requestId] = false
|
||||
+2
-2
@@ -18,9 +18,9 @@
|
||||
* 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.nip01Core.relays
|
||||
package com.vitorpamplona.quartz.nip01Core.relay
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import java.util.UUID
|
||||
|
||||
class Subscription(
|
||||
+2
-2
@@ -18,10 +18,10 @@
|
||||
* 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.nip01Core.relays
|
||||
package com.vitorpamplona.quartz.nip01Core.relay
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.relays.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
|
||||
interface SubscriptionCollection {
|
||||
fun isActive(subscriptionId: String): Boolean
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.nip01Core.relays.commands.toClient
|
||||
package com.vitorpamplona.quartz.nip01Core.relay.commands.toClient
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.nip01Core.relays.commands.toClient
|
||||
package com.vitorpamplona.quartz.nip01Core.relay.commands.toClient
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.nip01Core.relays.commands.toClient
|
||||
package com.vitorpamplona.quartz.nip01Core.relay.commands.toClient
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.nip01Core.relays.commands.toClient
|
||||
package com.vitorpamplona.quartz.nip01Core.relay.commands.toClient
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
+1
-1
@@ -18,6 +18,6 @@
|
||||
* 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.nip01Core.relays.commands.toClient
|
||||
package com.vitorpamplona.quartz.nip01Core.relay.commands.toClient
|
||||
|
||||
interface Message
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.nip01Core.relays.commands.toClient
|
||||
package com.vitorpamplona.quartz.nip01Core.relay.commands.toClient
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user