mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-04-08 20:08:06 +02:00
support for sending and receiving nip 44 dms
This commit is contained in:
parent
a7aa3705ef
commit
6303573dd9
@ -16,6 +16,7 @@ import com.vitorpamplona.amethyst.service.relays.Constants
|
||||
import com.vitorpamplona.amethyst.service.relays.FeedType
|
||||
import com.vitorpamplona.amethyst.service.relays.Relay
|
||||
import com.vitorpamplona.amethyst.service.relays.RelayPool
|
||||
import com.vitorpamplona.amethyst.ui.actions.SignerType
|
||||
import com.vitorpamplona.amethyst.ui.components.BundledUpdate
|
||||
import com.vitorpamplona.amethyst.ui.note.combineWith
|
||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||
@ -114,7 +115,8 @@ class Account(
|
||||
AmberUtils.content = ""
|
||||
AmberUtils.decrypt(
|
||||
content,
|
||||
keyPair.pubKey.toHexKey()
|
||||
keyPair.pubKey.toHexKey(),
|
||||
blockList?.id() ?: ""
|
||||
)
|
||||
blockList?.decryptedContent = AmberUtils.content
|
||||
live.invalidateData()
|
||||
@ -282,7 +284,7 @@ class Account(
|
||||
return
|
||||
}
|
||||
|
||||
if (note.event is ChatMessageEvent && !loginWithAmber) {
|
||||
if (note.event is ChatMessageEvent) {
|
||||
val event = note.event as ChatMessageEvent
|
||||
val users = event.recipientsPubKey().plus(event.pubKey).toSet().toList()
|
||||
|
||||
@ -290,14 +292,48 @@ class Account(
|
||||
val emojiUrl = EmojiUrl.decode(reaction)
|
||||
if (emojiUrl != null) {
|
||||
note.event?.let {
|
||||
val giftWraps = NIP24Factory().createReactionWithinGroup(
|
||||
emojiUrl = emojiUrl,
|
||||
originalNote = it,
|
||||
to = users,
|
||||
from = keyPair
|
||||
)
|
||||
if (loginWithAmber) {
|
||||
val senderPublicKey = keyPair.pubKey.toHexKey()
|
||||
|
||||
broadcastPrivately(giftWraps)
|
||||
var senderReaction = ReactionEvent.create(
|
||||
emojiUrl,
|
||||
it,
|
||||
keyPair
|
||||
)
|
||||
|
||||
AmberUtils.openAmber(senderReaction)
|
||||
if (AmberUtils.content.isBlank()) return
|
||||
senderReaction = ReactionEvent.create(senderReaction, AmberUtils.content)
|
||||
|
||||
val giftWraps = users.plus(senderPublicKey).map {
|
||||
val gossip = Gossip.create(senderReaction)
|
||||
val content = Gossip.toJson(gossip)
|
||||
AmberUtils.encrypt(content, it, SignerType.NIP44_ENCRYPT)
|
||||
|
||||
var sealedEvent = SealedGossipEvent.create(
|
||||
encryptedContent = AmberUtils.content,
|
||||
pubKey = senderPublicKey
|
||||
)
|
||||
AmberUtils.openAmber(sealedEvent)
|
||||
if (AmberUtils.content.isBlank()) return
|
||||
sealedEvent = SealedGossipEvent.create(sealedEvent, AmberUtils.content)
|
||||
|
||||
GiftWrapEvent.create(
|
||||
event = sealedEvent,
|
||||
recipientPubKey = it
|
||||
)
|
||||
}
|
||||
|
||||
broadcastPrivately(giftWraps)
|
||||
} else {
|
||||
val giftWraps = NIP24Factory().createReactionWithinGroup(
|
||||
emojiUrl = emojiUrl,
|
||||
originalNote = it,
|
||||
to = users,
|
||||
from = keyPair
|
||||
)
|
||||
broadcastPrivately(giftWraps)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
@ -305,14 +341,51 @@ class Account(
|
||||
}
|
||||
|
||||
note.event?.let {
|
||||
val giftWraps = NIP24Factory().createReactionWithinGroup(
|
||||
content = reaction,
|
||||
originalNote = it,
|
||||
to = users,
|
||||
from = keyPair
|
||||
)
|
||||
if (loginWithAmber) {
|
||||
val senderPublicKey = keyPair.pubKey.toHexKey()
|
||||
|
||||
broadcastPrivately(giftWraps)
|
||||
var senderReaction = ReactionEvent.create(
|
||||
reaction,
|
||||
it,
|
||||
keyPair
|
||||
)
|
||||
|
||||
AmberUtils.openAmber(senderReaction)
|
||||
if (AmberUtils.content.isBlank()) return
|
||||
senderReaction = ReactionEvent.create(senderReaction, AmberUtils.content)
|
||||
|
||||
val newUsers = users.plus(senderPublicKey)
|
||||
newUsers.forEach {
|
||||
val gossip = Gossip.create(senderReaction)
|
||||
val content = Gossip.toJson(gossip)
|
||||
AmberUtils.content = ""
|
||||
AmberUtils.encrypt(content, it, SignerType.NIP44_ENCRYPT)
|
||||
|
||||
var sealedEvent = SealedGossipEvent.create(
|
||||
encryptedContent = AmberUtils.content,
|
||||
pubKey = senderPublicKey
|
||||
)
|
||||
AmberUtils.openAmber(sealedEvent)
|
||||
if (AmberUtils.content.isBlank()) return
|
||||
sealedEvent = SealedGossipEvent.create(sealedEvent, AmberUtils.content)
|
||||
|
||||
val giftWraps = GiftWrapEvent.create(
|
||||
event = sealedEvent,
|
||||
recipientPubKey = it
|
||||
)
|
||||
|
||||
broadcastPrivately(listOf(giftWraps))
|
||||
}
|
||||
} else {
|
||||
val giftWraps = NIP24Factory().createReactionWithinGroup(
|
||||
content = reaction,
|
||||
originalNote = it,
|
||||
to = users,
|
||||
from = keyPair
|
||||
)
|
||||
|
||||
broadcastPrivately(giftWraps)
|
||||
}
|
||||
}
|
||||
return
|
||||
} else {
|
||||
@ -1373,32 +1446,67 @@ class Account(
|
||||
wantsToMarkAsSensitive: Boolean,
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null
|
||||
): List<GiftWrapEvent>? {
|
||||
// TODO: add support for amber
|
||||
if (!isWriteable() && !loginWithAmber) return null
|
||||
) {
|
||||
if (!isWriteable() && !loginWithAmber) return
|
||||
|
||||
val repliesToHex = listOfNotNull(replyingTo?.idHex).ifEmpty { null }
|
||||
val mentionsHex = mentions?.map { it.pubkeyHex }
|
||||
|
||||
val signedEvents = NIP24Factory().createMsgNIP24(
|
||||
msg = message,
|
||||
to = toUsers,
|
||||
subject = subject,
|
||||
replyTos = repliesToHex,
|
||||
mentions = mentionsHex,
|
||||
zapReceiver = zapReceiver,
|
||||
markAsSensitive = wantsToMarkAsSensitive,
|
||||
zapRaiserAmount = zapRaiserAmount,
|
||||
geohash = geohash,
|
||||
from = keyPair.privKey!!
|
||||
)
|
||||
|
||||
if (loginWithAmber) {
|
||||
return signedEvents
|
||||
}
|
||||
var chatMessageEvent = ChatMessageEvent.create(
|
||||
msg = message,
|
||||
to = toUsers,
|
||||
keyPair = keyPair,
|
||||
subject = subject,
|
||||
replyTos = repliesToHex,
|
||||
mentions = mentionsHex,
|
||||
zapReceiver = zapReceiver,
|
||||
markAsSensitive = wantsToMarkAsSensitive,
|
||||
zapRaiserAmount = zapRaiserAmount,
|
||||
geohash = geohash
|
||||
)
|
||||
|
||||
broadcastPrivately(signedEvents)
|
||||
return null
|
||||
AmberUtils.openAmber(chatMessageEvent)
|
||||
if (AmberUtils.content.isBlank()) return
|
||||
chatMessageEvent = ChatMessageEvent.create(chatMessageEvent, AmberUtils.content)
|
||||
val senderPublicKey = keyPair.pubKey.toHexKey()
|
||||
toUsers.plus(senderPublicKey).toSet().forEach {
|
||||
val gossip = Gossip.create(chatMessageEvent)
|
||||
val content = Gossip.toJson(gossip)
|
||||
AmberUtils.content = ""
|
||||
AmberUtils.encrypt(content, it, SignerType.NIP44_ENCRYPT)
|
||||
if (AmberUtils.content.isNotBlank()) {
|
||||
var sealedEvent = SealedGossipEvent.create(
|
||||
encryptedContent = AmberUtils.content,
|
||||
pubKey = senderPublicKey
|
||||
)
|
||||
AmberUtils.openAmber(sealedEvent)
|
||||
if (AmberUtils.content.isBlank()) return
|
||||
sealedEvent = SealedGossipEvent.create(sealedEvent, AmberUtils.content)
|
||||
|
||||
val giftWraps = GiftWrapEvent.create(
|
||||
event = sealedEvent,
|
||||
recipientPubKey = it
|
||||
)
|
||||
broadcastPrivately(listOf(giftWraps))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val signedEvents = NIP24Factory().createMsgNIP24(
|
||||
msg = message,
|
||||
to = toUsers,
|
||||
subject = subject,
|
||||
replyTos = repliesToHex,
|
||||
mentions = mentionsHex,
|
||||
zapReceiver = zapReceiver,
|
||||
markAsSensitive = wantsToMarkAsSensitive,
|
||||
zapRaiserAmount = zapRaiserAmount,
|
||||
geohash = geohash,
|
||||
keyPair = keyPair
|
||||
)
|
||||
|
||||
broadcastPrivately(signedEvents)
|
||||
}
|
||||
}
|
||||
|
||||
fun broadcastPrivately(signedEvents: List<GiftWrapEvent>) {
|
||||
@ -1407,13 +1515,33 @@ class Account(
|
||||
|
||||
// Only keep in cache the GiftWrap for the account.
|
||||
if (it.recipientPubKey() == keyPair.pubKey.toHexKey()) {
|
||||
it.cachedGift(keyPair.privKey!!)?.let {
|
||||
if (it is SealedGossipEvent) {
|
||||
it.cachedGossip(keyPair.privKey!!)?.let {
|
||||
if (loginWithAmber) {
|
||||
AmberUtils.content = ""
|
||||
AmberUtils.decrypt(it.content, it.pubKey, it.id, SignerType.NIP44_DECRYPT)
|
||||
val decryptedContent = AmberUtils.cachedDecryptedContent[it.id] ?: ""
|
||||
if (decryptedContent.isEmpty()) return
|
||||
it.cachedGift(keyPair.pubKey, decryptedContent)?.let { cached ->
|
||||
if (cached is SealedGossipEvent) {
|
||||
AmberUtils.content = ""
|
||||
AmberUtils.decrypt(cached.content, cached.pubKey, cached.id, SignerType.NIP44_DECRYPT)
|
||||
val localDecryptedContent = AmberUtils.cachedDecryptedContent[cached.id] ?: ""
|
||||
if (localDecryptedContent.isEmpty()) return
|
||||
cached.cachedGossip(keyPair.pubKey, localDecryptedContent)?.let { gossip ->
|
||||
LocalCache.justConsume(gossip, null)
|
||||
}
|
||||
} else {
|
||||
LocalCache.justConsume(it, null)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
it.cachedGift(keyPair.privKey!!)?.let {
|
||||
if (it is SealedGossipEvent) {
|
||||
it.cachedGossip(keyPair.privKey!!)?.let {
|
||||
LocalCache.justConsume(it, null)
|
||||
}
|
||||
} else {
|
||||
LocalCache.justConsume(it, null)
|
||||
}
|
||||
} else {
|
||||
LocalCache.justConsume(it, null)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2026,7 +2154,7 @@ class Account(
|
||||
AmberUtils.content
|
||||
} else {
|
||||
AmberUtils.content = ""
|
||||
AmberUtils.decrypt(content, keyPair.pubKey.toHexKey())
|
||||
AmberUtils.decrypt(content, keyPair.pubKey.toHexKey(), blockList?.id ?: "")
|
||||
if (AmberUtils.content.isBlank()) return
|
||||
val decryptedContent = AmberUtils.content
|
||||
AmberUtils.content = ""
|
||||
@ -2110,7 +2238,7 @@ class Account(
|
||||
AmberUtils.content
|
||||
} else {
|
||||
AmberUtils.content = ""
|
||||
AmberUtils.decrypt(content, keyPair.pubKey.toHexKey())
|
||||
AmberUtils.decrypt(content, keyPair.pubKey.toHexKey(), blockList.id)
|
||||
if (AmberUtils.content.isBlank()) return
|
||||
val decryptedContent = AmberUtils.content
|
||||
AmberUtils.content = ""
|
||||
@ -2356,12 +2484,30 @@ class Account(
|
||||
}
|
||||
|
||||
fun unwrap(event: GiftWrapEvent): Event? {
|
||||
if (!isWriteable()) return null
|
||||
if (!isWriteable() && !loginWithAmber) return null
|
||||
|
||||
if (loginWithAmber) {
|
||||
AmberUtils.content = ""
|
||||
AmberUtils.decrypt(event.content, event.pubKey, event.id, SignerType.NIP44_DECRYPT)
|
||||
val decryptedContent = AmberUtils.cachedDecryptedContent[event.id] ?: ""
|
||||
if (decryptedContent.isEmpty()) return null
|
||||
return event.cachedGift(keyPair.pubKey, decryptedContent)
|
||||
}
|
||||
|
||||
return event.cachedGift(keyPair.privKey!!)
|
||||
}
|
||||
|
||||
fun unseal(event: SealedGossipEvent): Event? {
|
||||
if (!isWriteable()) return null
|
||||
if (!isWriteable() && !loginWithAmber) return null
|
||||
|
||||
if (loginWithAmber) {
|
||||
AmberUtils.content = ""
|
||||
AmberUtils.decrypt(event.content, event.pubKey, event.id, SignerType.NIP44_DECRYPT)
|
||||
val decryptedContent = AmberUtils.cachedDecryptedContent[event.id] ?: ""
|
||||
if (decryptedContent.isEmpty()) return null
|
||||
return event.cachedGossip(keyPair.pubKey, decryptedContent)
|
||||
}
|
||||
|
||||
return event.cachedGossip(keyPair.privKey!!)
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,8 @@ object AmberUtils {
|
||||
data: String,
|
||||
type: SignerType,
|
||||
intentResult: ActivityResultLauncher<Intent>,
|
||||
pubKey: HexKey
|
||||
pubKey: HexKey,
|
||||
id: String
|
||||
) {
|
||||
ServiceManager.shouldPauseService = false
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$data"))
|
||||
@ -31,6 +32,7 @@ object AmberUtils {
|
||||
}
|
||||
intent.putExtra("type", signerType)
|
||||
intent.putExtra("pubKey", pubKey)
|
||||
intent.putExtra("id", id)
|
||||
intent.`package` = "com.greenart7c3.nostrsigner.debug"
|
||||
intentResult.launch(intent)
|
||||
}
|
||||
@ -44,7 +46,8 @@ object AmberUtils {
|
||||
event.toJson(),
|
||||
SignerType.SIGN_EVENT,
|
||||
IntentUtils.activityResultLauncher,
|
||||
""
|
||||
"",
|
||||
event.id()
|
||||
)
|
||||
while (isActivityRunning) {
|
||||
// do nothing
|
||||
@ -59,6 +62,7 @@ object AmberUtils {
|
||||
"",
|
||||
SignerType.GET_PUBLIC_KEY,
|
||||
IntentUtils.activityResultLauncher,
|
||||
"",
|
||||
""
|
||||
)
|
||||
while (isActivityRunning) {
|
||||
@ -66,14 +70,15 @@ object AmberUtils {
|
||||
}
|
||||
}
|
||||
|
||||
fun decrypt(encryptedContent: String, pubKey: HexKey) {
|
||||
fun decrypt(encryptedContent: String, pubKey: HexKey, id: String, signerType: SignerType = SignerType.NIP04_DECRYPT) {
|
||||
if (content.isBlank()) {
|
||||
isActivityRunning = true
|
||||
openAmber(
|
||||
encryptedContent,
|
||||
SignerType.NIP04_DECRYPT,
|
||||
signerType,
|
||||
IntentUtils.activityResultLauncher,
|
||||
pubKey
|
||||
pubKey,
|
||||
id
|
||||
)
|
||||
while (isActivityRunning) {
|
||||
// do nothing
|
||||
@ -81,14 +86,15 @@ object AmberUtils {
|
||||
}
|
||||
}
|
||||
|
||||
fun encrypt(decryptedContent: String, pubKey: HexKey) {
|
||||
fun encrypt(decryptedContent: String, pubKey: HexKey, signerType: SignerType = SignerType.NIP04_ENCRYPT) {
|
||||
if (content.isBlank()) {
|
||||
isActivityRunning = true
|
||||
openAmber(
|
||||
decryptedContent,
|
||||
SignerType.NIP04_ENCRYPT,
|
||||
signerType,
|
||||
IntentUtils.activityResultLauncher,
|
||||
pubKey
|
||||
pubKey,
|
||||
""
|
||||
)
|
||||
while (isActivityRunning) {
|
||||
// do nothing
|
||||
|
@ -34,6 +34,10 @@ object IntentUtils {
|
||||
}
|
||||
|
||||
val event = it.data?.getStringExtra("signature") ?: ""
|
||||
val id = it.data?.getStringExtra("id") ?: ""
|
||||
if (id.isNotBlank()) {
|
||||
AmberUtils.cachedDecryptedContent[id] = event
|
||||
}
|
||||
AmberUtils.content = event
|
||||
AmberUtils.isActivityRunning = false
|
||||
ServiceManager.shouldPauseService = true
|
||||
|
@ -8,6 +8,7 @@ import com.vitorpamplona.amethyst.service.relays.EOSEAccount
|
||||
import com.vitorpamplona.amethyst.service.relays.JsonFilter
|
||||
import com.vitorpamplona.amethyst.service.relays.Relay
|
||||
import com.vitorpamplona.amethyst.service.relays.TypedFilter
|
||||
import com.vitorpamplona.amethyst.ui.actions.SignerType
|
||||
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.events.BadgeAwardEvent
|
||||
import com.vitorpamplona.quartz.events.BadgeProfilesEvent
|
||||
@ -29,6 +30,10 @@ import com.vitorpamplona.quartz.events.RepostEvent
|
||||
import com.vitorpamplona.quartz.events.SealedGossipEvent
|
||||
import com.vitorpamplona.quartz.events.StatusEvent
|
||||
import com.vitorpamplona.quartz.events.TextNoteEvent
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
lateinit var account: Account
|
||||
@ -144,6 +149,7 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
latestEOSEs.addOrUpdate(account.userProfile(), account.defaultNotificationFollowList, relayUrl, time)
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
override fun consume(event: Event, relay: Relay) {
|
||||
if (LocalCache.justVerify(event)) {
|
||||
if (event is GiftWrapEvent) {
|
||||
@ -152,6 +158,17 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
event.cachedGift(privateKey)?.let {
|
||||
this.consume(it, relay)
|
||||
}
|
||||
} else if (account.loginWithAmber) {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
AmberUtils.content = ""
|
||||
AmberUtils.decrypt(event.content, event.pubKey, event.id, SignerType.NIP44_DECRYPT)
|
||||
val decryptedContent = AmberUtils.cachedDecryptedContent[event.id] ?: ""
|
||||
if (decryptedContent.isNotBlank()) {
|
||||
event.cachedGift(account.keyPair.pubKey, decryptedContent)?.let {
|
||||
consume(it, relay)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,6 +178,22 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
event.cachedGossip(privateKey)?.let {
|
||||
LocalCache.justConsume(it, relay)
|
||||
}
|
||||
} else if (account.loginWithAmber) {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
AmberUtils.content = ""
|
||||
AmberUtils.decrypt(
|
||||
event.content,
|
||||
event.pubKey,
|
||||
event.id,
|
||||
SignerType.NIP44_DECRYPT
|
||||
)
|
||||
val decryptedContent = AmberUtils.cachedDecryptedContent[event.id] ?: ""
|
||||
if (decryptedContent.isNotBlank()) {
|
||||
event.cachedGossip(account.keyPair.pubKey, decryptedContent)?.let {
|
||||
LocalCache.justConsume(it, relay)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't store sealed gossips to avoid rebroadcasting by mistake.
|
||||
|
@ -7,8 +7,10 @@ import com.vitorpamplona.amethyst.LocalPreferences
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.AmberUtils
|
||||
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.sendDMNotification
|
||||
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.sendZapNotification
|
||||
import com.vitorpamplona.amethyst.ui.actions.SignerType
|
||||
import com.vitorpamplona.amethyst.ui.note.showAmount
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import com.vitorpamplona.quartz.events.ChatMessageEvent
|
||||
@ -47,17 +49,59 @@ class EventNotificationConsumer(private val applicationContext: Context) {
|
||||
|
||||
return when (event) {
|
||||
is GiftWrapEvent -> {
|
||||
val key = account.keyPair.privKey ?: return null
|
||||
event.cachedGift(key)?.let {
|
||||
unwrapAndConsume(it, account)
|
||||
val key = account.keyPair.privKey
|
||||
if (key != null) {
|
||||
event.cachedGift(key)?.let {
|
||||
unwrapAndConsume(it, account)
|
||||
}
|
||||
} else if (account.loginWithAmber) {
|
||||
AmberUtils.content = ""
|
||||
AmberUtils.decrypt(
|
||||
event.content,
|
||||
event.pubKey,
|
||||
event.id,
|
||||
SignerType.NIP44_DECRYPT
|
||||
)
|
||||
val decryptedContent = AmberUtils.cachedDecryptedContent[event.id] ?: ""
|
||||
if (decryptedContent.isNotBlank()) {
|
||||
event.cachedGift(account.keyPair.pubKey, decryptedContent)?.let {
|
||||
LocalCache.justConsume(it, null)
|
||||
it
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
is SealedGossipEvent -> {
|
||||
val key = account.keyPair.privKey ?: return null
|
||||
event.cachedGossip(key)?.let {
|
||||
// this is not verifiable
|
||||
LocalCache.justConsume(it, null)
|
||||
it
|
||||
val key = account.keyPair.privKey
|
||||
if (key != null) {
|
||||
event.cachedGossip(key)?.let {
|
||||
// this is not verifiable
|
||||
LocalCache.justConsume(it, null)
|
||||
it
|
||||
}
|
||||
} else if (account.loginWithAmber) {
|
||||
AmberUtils.content = ""
|
||||
AmberUtils.decrypt(
|
||||
event.content,
|
||||
event.pubKey,
|
||||
event.id,
|
||||
SignerType.NIP44_DECRYPT
|
||||
)
|
||||
val decryptedContent = AmberUtils.cachedDecryptedContent[event.id] ?: ""
|
||||
if (decryptedContent.isNotBlank()) {
|
||||
event.cachedGossip(account.keyPair.pubKey, decryptedContent)?.let {
|
||||
LocalCache.justConsume(it, null)
|
||||
it
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
|
@ -87,7 +87,7 @@ fun SignerDialog(
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
AmberUtils.openAmber(data, type, intentResult, pubKey)
|
||||
AmberUtils.openAmber(data, type, intentResult, pubKey, "")
|
||||
}
|
||||
|
||||
Dialog(
|
||||
@ -178,7 +178,7 @@ fun SignerDialog(
|
||||
)
|
||||
Button(
|
||||
shape = ButtonBorder,
|
||||
onClick = { AmberUtils.openAmber(data, type, intentResult, pubKey) }
|
||||
onClick = { AmberUtils.openAmber(data, type, intentResult, pubKey, "") }
|
||||
) {
|
||||
Text("Open Amber")
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ object BookmarkPrivateFeedFilter : FeedFilter<Note>() {
|
||||
|
||||
if (account.loginWithAmber) {
|
||||
if (AmberUtils.content.isBlank()) {
|
||||
AmberUtils.decrypt(bookmarks?.content ?: "", account.keyPair.pubKey.toHexKey())
|
||||
AmberUtils.decrypt(bookmarks?.content ?: "", account.keyPair.pubKey.toHexKey(), "")
|
||||
bookmarks?.decryptedContent = AmberUtils.content
|
||||
}
|
||||
|
||||
|
@ -301,7 +301,8 @@ private fun UserRoomCompose(
|
||||
event?.content() ?: "",
|
||||
SignerType.NIP04_DECRYPT,
|
||||
activityLauncher,
|
||||
(event as PrivateDmEvent).talkingWith(accountViewModel.userProfile().pubkeyHex)
|
||||
(event as PrivateDmEvent).talkingWith(accountViewModel.userProfile().pubkeyHex),
|
||||
event.id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -668,7 +668,8 @@ private fun RenderRegularTextNote(
|
||||
event?.content() ?: "",
|
||||
SignerType.NIP04_DECRYPT,
|
||||
activityLauncher,
|
||||
(event as PrivateDmEvent).talkingWith(accountViewModel.userProfile().pubkeyHex)
|
||||
(event as PrivateDmEvent).talkingWith(accountViewModel.userProfile().pubkeyHex),
|
||||
event.id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1596,7 +1596,8 @@ private fun RenderPrivateMessage(
|
||||
event?.content() ?: "",
|
||||
SignerType.NIP04_DECRYPT,
|
||||
activityLauncher,
|
||||
(event as PrivateDmEvent).talkingWith(accountViewModel.userProfile().pubkeyHex)
|
||||
(event as PrivateDmEvent).talkingWith(accountViewModel.userProfile().pubkeyHex),
|
||||
event.id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -452,7 +452,8 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
|
||||
val bookmarks = accountViewModel.userProfile().latestBookmarkList
|
||||
AmberUtils.decrypt(
|
||||
bookmarks?.content ?: "",
|
||||
accountViewModel.account.keyPair.pubKey.toHexKey()
|
||||
accountViewModel.account.keyPair.pubKey.toHexKey(),
|
||||
bookmarks?.id ?: ""
|
||||
)
|
||||
bookmarks?.decryptedContent = AmberUtils.content
|
||||
AmberUtils.content = ""
|
||||
@ -474,7 +475,8 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
|
||||
val bookmarks = accountViewModel.userProfile().latestBookmarkList
|
||||
AmberUtils.decrypt(
|
||||
bookmarks?.content ?: "",
|
||||
accountViewModel.account.keyPair.pubKey.toHexKey()
|
||||
accountViewModel.account.keyPair.pubKey.toHexKey(),
|
||||
bookmarks?.id ?: ""
|
||||
)
|
||||
bookmarks?.decryptedContent = AmberUtils.content
|
||||
AmberUtils.content = ""
|
||||
@ -497,7 +499,8 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
|
||||
val bookmarks = accountViewModel.userProfile().latestBookmarkList
|
||||
AmberUtils.decrypt(
|
||||
bookmarks?.content ?: "",
|
||||
accountViewModel.account.keyPair.pubKey.toHexKey()
|
||||
accountViewModel.account.keyPair.pubKey.toHexKey(),
|
||||
bookmarks?.id ?: ""
|
||||
)
|
||||
bookmarks?.decryptedContent = AmberUtils.content
|
||||
AmberUtils.content = ""
|
||||
@ -522,7 +525,8 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
|
||||
val bookmarks = accountViewModel.userProfile().latestBookmarkList
|
||||
AmberUtils.decrypt(
|
||||
bookmarks?.content ?: "",
|
||||
accountViewModel.account.keyPair.pubKey.toHexKey()
|
||||
accountViewModel.account.keyPair.pubKey.toHexKey(),
|
||||
bookmarks?.id ?: ""
|
||||
)
|
||||
bookmarks?.decryptedContent = AmberUtils.content
|
||||
AmberUtils.content = ""
|
||||
|
@ -296,7 +296,6 @@ fun ChatroomScreen(
|
||||
PrivateMessageEditFieldRow(newPostModel, isPrivate = true, accountViewModel) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
if (newPostModel.nip24 || room.users.size > 1 || replyTo.value?.event is ChatMessageEvent) {
|
||||
// TODO: add support for amber
|
||||
accountViewModel.account.sendNIP24PrivateMessage(
|
||||
message = newPostModel.message.text,
|
||||
toUsers = room.users.toList(),
|
||||
@ -639,7 +638,6 @@ fun NewSubjectView(onClose: () -> Unit, accountViewModel: AccountViewModel, room
|
||||
PostButton(
|
||||
onPost = {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
// TODO: add support for amber
|
||||
accountViewModel.account.sendNIP24PrivateMessage(
|
||||
message = message.value,
|
||||
toUsers = room.users.toList(),
|
||||
|
@ -5,6 +5,7 @@ import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||
import com.vitorpamplona.quartz.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import kotlinx.collections.immutable.ImmutableSet
|
||||
import kotlinx.collections.immutable.toImmutableSet
|
||||
@ -61,7 +62,7 @@ class ChatMessageEvent(
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null,
|
||||
privateKey: ByteArray,
|
||||
keyPair: KeyPair,
|
||||
createdAt: Long = TimeUtils.now()
|
||||
): ChatMessageEvent {
|
||||
val content = msg
|
||||
@ -91,10 +92,17 @@ class ChatMessageEvent(
|
||||
tags.add(listOf("subject", it))
|
||||
}
|
||||
|
||||
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
|
||||
val pubKey = keyPair.pubKey.toHexKey()
|
||||
val id = generateId(pubKey, createdAt, ClassifiedsEvent.kind, tags, content)
|
||||
val sig = CryptoUtils.sign(id, privateKey)
|
||||
return ChatMessageEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
|
||||
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
|
||||
return ChatMessageEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
|
||||
}
|
||||
|
||||
fun create(
|
||||
unsignedEvent: ChatMessageEvent,
|
||||
signature: String
|
||||
): ChatMessageEvent {
|
||||
return ChatMessageEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,19 @@ class GiftWrapEvent(
|
||||
return myInnerEvent
|
||||
}
|
||||
|
||||
fun cachedGift(pubKey: ByteArray, decryptedContent: String): Event? {
|
||||
val hex = pubKey.toHexKey()
|
||||
if (cachedInnerEvent.contains(hex)) return cachedInnerEvent[hex]
|
||||
|
||||
val myInnerEvent = unwrap(decryptedContent)
|
||||
if (myInnerEvent is WrappedEvent) {
|
||||
myInnerEvent.host = this
|
||||
}
|
||||
|
||||
cachedInnerEvent = cachedInnerEvent + Pair(hex, myInnerEvent)
|
||||
return myInnerEvent
|
||||
}
|
||||
|
||||
fun unwrap(privKey: ByteArray) = try {
|
||||
plainContent(privKey)?.let { fromJson(it) }
|
||||
} catch (e: Exception) {
|
||||
@ -43,6 +56,13 @@ class GiftWrapEvent(
|
||||
null
|
||||
}
|
||||
|
||||
fun unwrap(decryptedContent: String) = try {
|
||||
plainContent(decryptedContent)?.let { fromJson(it) }
|
||||
} catch (e: Exception) {
|
||||
// Log.e("UnwrapError", "Couldn't Decrypt the content", e)
|
||||
null
|
||||
}
|
||||
|
||||
private fun plainContent(privKey: ByteArray): String? {
|
||||
if (content.isEmpty()) return null
|
||||
|
||||
@ -60,6 +80,11 @@ class GiftWrapEvent(
|
||||
}
|
||||
}
|
||||
|
||||
private fun plainContent(decryptedContent: String): String? {
|
||||
if (decryptedContent.isEmpty()) return null
|
||||
return decryptedContent
|
||||
}
|
||||
|
||||
fun recipientPubKey() = tags.firstOrNull { it.size > 1 && it[0] == "p" }?.get(1)
|
||||
|
||||
companion object {
|
||||
|
@ -9,7 +9,7 @@ class NIP24Factory {
|
||||
fun createMsgNIP24(
|
||||
msg: String,
|
||||
to: List<HexKey>,
|
||||
from: ByteArray,
|
||||
keyPair: KeyPair,
|
||||
subject: String? = null,
|
||||
replyTos: List<String>? = null,
|
||||
mentions: List<String>? = null,
|
||||
@ -18,12 +18,12 @@ class NIP24Factory {
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null
|
||||
): List<GiftWrapEvent> {
|
||||
val senderPublicKey = CryptoUtils.pubkeyCreate(from).toHexKey()
|
||||
val senderPublicKey = keyPair.pubKey.toHexKey()
|
||||
|
||||
val senderMessage = ChatMessageEvent.create(
|
||||
msg = msg,
|
||||
to = to,
|
||||
privateKey = from,
|
||||
keyPair = keyPair,
|
||||
subject = subject,
|
||||
replyTos = replyTos,
|
||||
mentions = mentions,
|
||||
@ -38,7 +38,7 @@ class NIP24Factory {
|
||||
event = SealedGossipEvent.create(
|
||||
event = senderMessage,
|
||||
encryptTo = it,
|
||||
privateKey = from
|
||||
privateKey = keyPair.privKey!!
|
||||
),
|
||||
recipientPubKey = it
|
||||
)
|
||||
@ -46,7 +46,7 @@ class NIP24Factory {
|
||||
}
|
||||
|
||||
fun createReactionWithinGroup(content: String, originalNote: EventInterface, to: List<HexKey>, from: KeyPair): List<GiftWrapEvent> {
|
||||
val senderPublicKey = CryptoUtils.pubkeyCreate(from.privKey!!).toHexKey()
|
||||
val senderPublicKey = from.pubKey.toHexKey()
|
||||
|
||||
val senderReaction = ReactionEvent.create(
|
||||
content,
|
||||
@ -59,7 +59,7 @@ class NIP24Factory {
|
||||
event = SealedGossipEvent.create(
|
||||
event = senderReaction,
|
||||
encryptTo = it,
|
||||
privateKey = from.privKey
|
||||
privateKey = from.privKey!!
|
||||
),
|
||||
recipientPubKey = it
|
||||
)
|
||||
@ -67,7 +67,7 @@ class NIP24Factory {
|
||||
}
|
||||
|
||||
fun createReactionWithinGroup(emojiUrl: EmojiUrl, originalNote: EventInterface, to: List<HexKey>, from: KeyPair): List<GiftWrapEvent> {
|
||||
val senderPublicKey = CryptoUtils.pubkeyCreate(from.privKey!!).toHexKey()
|
||||
val senderPublicKey = from.pubKey.toHexKey()
|
||||
|
||||
val senderReaction = ReactionEvent.create(
|
||||
emojiUrl,
|
||||
@ -80,7 +80,7 @@ class NIP24Factory {
|
||||
event = SealedGossipEvent.create(
|
||||
event = senderReaction,
|
||||
encryptTo = it,
|
||||
privateKey = from.privKey
|
||||
privateKey = from.privKey!!
|
||||
),
|
||||
recipientPubKey = it
|
||||
)
|
||||
|
@ -38,6 +38,20 @@ class SealedGossipEvent(
|
||||
return event
|
||||
}
|
||||
|
||||
fun cachedGossip(pubKey: ByteArray, decryptedContent: String): Event? {
|
||||
val hex = pubKey.toHexKey()
|
||||
if (cachedInnerEvent.contains(hex)) return cachedInnerEvent[hex]
|
||||
|
||||
val gossip = unseal(decryptedContent)
|
||||
val event = gossip?.mergeWith(this)
|
||||
if (event is WrappedEvent) {
|
||||
event.host = host ?: this
|
||||
}
|
||||
|
||||
cachedInnerEvent = cachedInnerEvent + Pair(hex, event)
|
||||
return event
|
||||
}
|
||||
|
||||
fun unseal(privKey: ByteArray): Gossip? = try {
|
||||
plainContent(privKey)?.let { Gossip.fromJson(it) }
|
||||
} catch (e: Exception) {
|
||||
@ -45,6 +59,18 @@ class SealedGossipEvent(
|
||||
null
|
||||
}
|
||||
|
||||
fun unseal(decryptedContent: String): Gossip? = try {
|
||||
plainContent(decryptedContent)?.let { Gossip.fromJson(it) }
|
||||
} catch (e: Exception) {
|
||||
Log.w("GossipEvent", "Fail to decrypt or parse Gossip", e)
|
||||
null
|
||||
}
|
||||
|
||||
private fun plainContent(decryptedContent: String): String? {
|
||||
if (decryptedContent.isEmpty()) return null
|
||||
return decryptedContent
|
||||
}
|
||||
|
||||
private fun plainContent(privKey: ByteArray): String? {
|
||||
if (content.isEmpty()) return null
|
||||
|
||||
@ -95,6 +121,23 @@ class SealedGossipEvent(
|
||||
val sig = CryptoUtils.sign(id, privateKey)
|
||||
return SealedGossipEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
|
||||
}
|
||||
|
||||
fun create(
|
||||
encryptedContent: String,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long = TimeUtils.randomWithinAWeek()
|
||||
): SealedGossipEvent {
|
||||
val tags = listOf<List<String>>()
|
||||
val id = generateId(pubKey, createdAt, kind, tags, encryptedContent)
|
||||
return SealedGossipEvent(id.toHexKey(), pubKey, createdAt, tags, encryptedContent, "")
|
||||
}
|
||||
|
||||
fun create(
|
||||
unsignedEvent: SealedGossipEvent,
|
||||
signature: String
|
||||
): SealedGossipEvent {
|
||||
return SealedGossipEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user