support for sending and receiving nip 44 dms

This commit is contained in:
greenart7c3 2023-09-06 18:53:11 -03:00
parent a7aa3705ef
commit 6303573dd9
16 changed files with 399 additions and 85 deletions

View File

@ -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!!)
}

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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 -> {

View File

@ -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")
}

View File

@ -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
}

View File

@ -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
)
}
}

View File

@ -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
)
}
}

View File

@ -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
)
}
}

View File

@ -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 = ""

View File

@ -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(),

View File

@ -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)
}
}
}

View File

@ -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 {

View File

@ -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
)

View File

@ -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)
}
}
}