Migrates to MuteList 10000

This commit is contained in:
Vitor Pamplona
2023-11-23 10:46:56 -05:00
parent 954330064d
commit 70964e680a
22 changed files with 340 additions and 164 deletions
@@ -18,8 +18,11 @@ class EmojiPackSelectionEvent(
content: String,
sig: HexKey
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
override fun dTag() = fixedDTag
companion object {
const val kind = 10030
const val fixedDTag = ""
fun create(
listOfEmojiPacks: List<ATag>?,
@@ -66,6 +66,7 @@ class EventFactory {
LnZapRequestEvent.kind -> LnZapRequestEvent(id, pubKey, createdAt, tags, content, sig)
LongTextNoteEvent.kind -> LongTextNoteEvent(id, pubKey, createdAt, tags, content, sig)
MetadataEvent.kind -> MetadataEvent(id, pubKey, createdAt, tags, content, sig)
MuteListEvent.kind -> MuteListEvent(id, pubKey, createdAt, tags, content, sig)
NNSEvent.kind -> NNSEvent(id, pubKey, createdAt, tags, content, sig)
PeopleListEvent.kind -> PeopleListEvent(id, pubKey, createdAt, tags, content, sig)
PinListEvent.kind -> PinListEvent(id, pubKey, createdAt, tags, content, sig)
@@ -8,6 +8,8 @@ import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.toImmutableSet
@Immutable
abstract class GeneralListEvent(
@@ -34,6 +36,27 @@ abstract class GeneralListEvent(
return privateTagsCache
}
fun filterTagList(key: String, privateTags: List<List<String>>?): ImmutableSet<String> {
val privateUserList = privateTags?.let {
it.filter { it.size > 1 && it[0] == key }.map { it[1] }.toSet()
} ?: emptySet()
val publicUserList = tags.filter { it.size > 1 && it[0] == key }.map { it[1] }.toSet()
return (privateUserList + publicUserList).toImmutableSet()
}
fun isTagged(key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, onReady: (Boolean) -> Unit) {
return if (isPrivate) {
privateTagsOrEmpty(signer = signer) {
onReady(
it.any { it.size > 1 && it[0] == key && it[1] == tag }
)
}
} else {
onReady(isTagged(key, tag))
}
}
fun privateTags(signer: NostrSigner, onReady: (List<List<String>>) -> Unit) {
if (content.isBlank()) return
@@ -10,6 +10,7 @@ import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import kotlinx.collections.immutable.ImmutableSet
import java.util.UUID
@Immutable
@@ -20,96 +21,195 @@ class MuteListEvent(
tags: List<List<String>>,
content: String,
sig: HexKey
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
) : GeneralListEvent(id, pubKey, createdAt, kind, tags, content, sig) {
@Transient
private var privateTagsCache: List<List<String>>? = null
var publicAndPrivateUserCache: ImmutableSet<HexKey>? = null
@Transient
var publicAndPrivateWordCache: ImmutableSet<String>? = null
private fun privateTags(signer: NostrSigner, onReady: (List<List<String>>) -> Unit) {
if (content.isBlank()) return
override fun dTag() = fixedDTag
privateTagsCache?.let {
onReady(it)
return
fun publicAndPrivateUsersAndWords(signer: NostrSigner, onReady: (PeopleListEvent.UsersAndWords) -> Unit) {
publicAndPrivateUserCache?.let { userList ->
publicAndPrivateWordCache?.let { wordList ->
onReady(PeopleListEvent.UsersAndWords(userList, wordList))
return
}
}
try {
signer.nip04Decrypt(content, pubKey) {
privateTagsCache = mapper.readValue<List<List<String>>>(it)
privateTagsCache?.let {
onReady(it)
privateTagsOrEmpty(signer) {
publicAndPrivateUserCache = filterTagList("p", it)
publicAndPrivateWordCache = filterTagList("word", it)
publicAndPrivateUserCache?.let { userList ->
publicAndPrivateWordCache?.let { wordList ->
onReady(
PeopleListEvent.UsersAndWords(userList, wordList)
)
}
}
} catch (e: Throwable) {
Log.w("MuteList", "Error parsing the JSON ${e.message}")
}
}
fun privateTaggedUsers(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(it.filter { it.size > 1 && it[0] == "p" }.map { it[1] } )
}
fun privateHashtags(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(it.filter { it.size > 1 && it[0] == "t" }.map { it[1] } )
}
fun privateGeohashes(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(it.filter { it.size > 1 && it[0] == "g" }.map { it[1] } )
}
fun privateTaggedEvents(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(it.filter { it.size > 1 && it[0] == "e" }.map { it[1] } )
}
fun privateTaggedAddresses(signer: NostrSigner, onReady: (List<ATag>) -> Unit) = privateTags(signer) {
onReady(
it.filter { it.firstOrNull() == "a" }.mapNotNull {
val aTagValue = it.getOrNull(1)
val relay = it.getOrNull(2)
if (aTagValue != null) ATag.parse(aTagValue, relay) else null
}
)
}
companion object {
const val kind = 10000
const val fixedDTag = ""
fun blockListFor(pubKeyHex: HexKey): String {
return "10000:$pubKeyHex:"
}
fun createListWithTag(key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
if (isPrivate) {
encryptTags(listOf(listOf(key, tag)), signer) { encryptedTags ->
create(
content = encryptedTags,
tags = emptyList(),
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
} else {
create(
content = "",
tags = listOf(listOf(key, tag)),
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
fun createListWithUser(pubKeyHex: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
return createListWithTag("p", pubKeyHex, isPrivate, signer, createdAt, onReady)
}
fun createListWithWord(word: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
return createListWithTag("word", word, isPrivate, signer, createdAt, onReady)
}
fun addUsers(earlierVersion: MuteListEvent, listPubKeyHex: List<String>, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
if (isPrivate) {
earlierVersion.privateTagsOrEmpty(signer) { privateTags ->
encryptTags(
privateTags = privateTags.plus(
listPubKeyHex.map {
listOf("p", it)
}
),
signer = signer
) { encryptedTags ->
create(
content = encryptedTags,
tags = earlierVersion.tags,
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.plus(
listPubKeyHex.map {
listOf("p", it)
}
),
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
fun addWord(earlierVersion: MuteListEvent, word: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
return addTag(earlierVersion, "word", word, isPrivate, signer, createdAt, onReady)
}
fun addUser(earlierVersion: MuteListEvent, pubKeyHex: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
return addTag(earlierVersion, "p", pubKeyHex, isPrivate, signer, createdAt, onReady)
}
fun addTag(earlierVersion: MuteListEvent, key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
earlierVersion.isTagged(key, tag, isPrivate, signer) { isTagged ->
if (!isTagged) {
if (isPrivate) {
earlierVersion.privateTagsOrEmpty(signer) { privateTags ->
encryptTags(
privateTags = privateTags.plus(element = listOf(key, tag)),
signer = signer
) { encryptedTags ->
create(
content = encryptedTags,
tags = earlierVersion.tags,
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.plus(element = listOf(key, tag)),
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
}
}
fun removeWord(earlierVersion: MuteListEvent, word: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
return removeTag(earlierVersion, "word", word, isPrivate, signer, createdAt, onReady)
}
fun removeUser(earlierVersion: MuteListEvent, pubKeyHex: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
return removeTag(earlierVersion, "p", pubKeyHex, isPrivate, signer, createdAt, onReady)
}
fun removeTag(earlierVersion: MuteListEvent, key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
earlierVersion.isTagged(key, tag, isPrivate, signer) { isTagged ->
if (isTagged) {
if (isPrivate) {
earlierVersion.privateTagsOrEmpty(signer) { privateTags ->
encryptTags(
privateTags = privateTags.filter { it.size > 1 && !(it[0] == key && it[1] == tag) },
signer = signer
) { encryptedTags ->
create(
content = encryptedTags,
tags = earlierVersion.tags.filter { it.size > 1 && !(it[0] == key && it[1] == tag) },
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.filter { it.size > 1 && !(it[0] == key && it[1] == tag) },
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
}
}
fun create(
events: List<String>? = null,
users: List<String>? = null,
addresses: List<ATag>? = null,
privEvents: List<String>? = null,
privUsers: List<String>? = null,
privAddresses: List<ATag>? = null,
content: String,
tags: List<List<String>>,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (MuteListEvent) -> Unit
) {
val privTags = mutableListOf<List<String>>()
privEvents?.forEach {
privTags.add(listOf("e", it))
}
privUsers?.forEach {
privTags.add(listOf("p", it))
}
privAddresses?.forEach {
privTags.add(listOf("a", it.toTag()))
}
val msg = mapper.writeValueAsString(privTags)
val tags = mutableListOf<List<String>>()
events?.forEach {
tags.add(listOf("e", it))
}
users?.forEach {
tags.add(listOf("p", it))
}
addresses?.forEach {
tags.add(listOf("a", it.toTag()))
}
signer.nip04Encrypt(msg, signer.pubKey) { content ->
signer.sign(createdAt, kind, tags, content, onReady)
}
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -24,15 +24,6 @@ class PeopleListEvent(
@Transient
var publicAndPrivateWordCache: ImmutableSet<String>? = null
fun filterTagList(key: String, privateTags: List<List<String>>?): ImmutableSet<String> {
val privateUserList = privateTags?.let {
it.filter { it.size > 1 && it[0] == key }.map { it[1] }.toSet()
} ?: emptySet()
val publicUserList = tags.filter { it.size > 1 && it[0] == key }.map { it[1] }.toSet()
return (privateUserList + publicUserList).toImmutableSet()
}
fun publicAndPrivateWords(signer: NostrSigner, onReady: (ImmutableSet<String>) -> Unit) {
publicAndPrivateWordCache?.let {
onReady(it)
@@ -62,7 +53,10 @@ class PeopleListEvent(
}
@Immutable
data class UsersAndWords(val users: ImmutableSet<String>, val words: ImmutableSet<String>)
data class UsersAndWords(
val users: ImmutableSet<String> = persistentSetOf(),
val words: ImmutableSet<String> = persistentSetOf()
)
fun publicAndPrivateUsersAndWords(signer: NostrSigner, onReady: (UsersAndWords) -> Unit) {
publicAndPrivateUserCache?.let { userList ->
@@ -86,18 +80,6 @@ class PeopleListEvent(
}
}
fun isTagged(key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, onReady: (Boolean) -> Unit) {
return if (isPrivate) {
privateTagsOrEmpty(signer = signer) {
onReady(
it.any { it.size > 1 && it[0] == key && it[1] == tag }
)
}
} else {
onReady(isTagged(key, tag))
}
}
fun isTaggedWord(word: String, isPrivate: Boolean, signer: NostrSigner, onReady: (Boolean) -> Unit) = isTagged( "word", word, isPrivate, signer, onReady)
fun isTaggedUser(idHex: String, isPrivate: Boolean, signer: NostrSigner, onReady: (Boolean) -> Unit) = isTagged( "p", idHex, isPrivate, signer, onReady)