Save inbound marmotInnerEvent
This commit is contained in:
@@ -5,45 +5,77 @@ import androidx.room3.RoomDatabase
|
||||
import androidx.room3.TypeConverters
|
||||
import androidx.room3.immediateTransaction
|
||||
import androidx.room3.useWriterConnection
|
||||
import at.torch.compose.database.dao.MarmotInnerEventDao
|
||||
import at.torch.compose.database.dao.MarmotKeyPackageBundleDao
|
||||
import at.torch.compose.database.dao.MarmotKeyPackageDao
|
||||
import at.torch.compose.database.model.BroadcastNostrEventReceipt
|
||||
import at.torch.compose.database.model.BroadcastNostrEventRequest
|
||||
import at.torch.compose.database.model.ChatMessage
|
||||
import at.torch.compose.database.model.ChatMessageBroadcastNostrEventReceiptRelation
|
||||
import at.torch.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation
|
||||
import at.torch.compose.database.model.ChatMessageNostrEventRelation
|
||||
import at.torch.compose.database.model.ChatRoom
|
||||
import at.torch.compose.database.model.Connection
|
||||
import at.torch.compose.database.model.GiftWrapMessage
|
||||
import at.torch.compose.database.model.GiftWrapPayload
|
||||
import at.torch.compose.database.model.GiftWrapSeal
|
||||
import at.torch.compose.database.model.InReplyToRelation
|
||||
import at.torch.compose.database.model.MarmotKeyPackage
|
||||
import at.torch.compose.database.model.MarmotKeyPackageBundle
|
||||
import at.torch.compose.database.model.Mention
|
||||
import at.torch.compose.database.model.NegentropySynchronizeRequest
|
||||
import at.torch.compose.database.model.NegentropySynchronizeResult
|
||||
import at.torch.compose.database.model.NostrEvent
|
||||
import at.torch.compose.database.model.NostrEventRelay
|
||||
import at.torch.compose.database.model.Participant
|
||||
import at.torch.compose.database.model.Post
|
||||
import at.torch.compose.database.model.Profile
|
||||
import at.torch.compose.database.model.QuotedRelation
|
||||
import at.torch.compose.database.model.Reaction
|
||||
import at.torch.compose.database.model.RecentSearch
|
||||
import at.torch.compose.database.model.Relay
|
||||
import at.torch.compose.database.model.RepostedRelation
|
||||
import at.torch.compose.database.model.SynchronizeNostrEventRequest
|
||||
import at.torch.compose.database.model.SynchronizeNostrEventResult
|
||||
import at.torch.compose.database.model.UnsignedNostrEvent
|
||||
import at.torch.compose.database.model.Zap
|
||||
import kotlin.time.Instant
|
||||
|
||||
val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L)
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
at.torch.compose.database.model.BroadcastNostrEventReceipt::class,
|
||||
at.torch.compose.database.model.BroadcastNostrEventRequest::class,
|
||||
at.torch.compose.database.model.Connection::class,
|
||||
at.torch.compose.database.model.ChatMessage::class,
|
||||
at.torch.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation::class,
|
||||
at.torch.compose.database.model.ChatMessageBroadcastNostrEventReceiptRelation::class,
|
||||
at.torch.compose.database.model.ChatMessageNostrEventRelation::class,
|
||||
at.torch.compose.database.model.ChatRoom::class,
|
||||
at.torch.compose.database.model.GiftWrapMessage::class,
|
||||
at.torch.compose.database.model.GiftWrapSeal::class,
|
||||
at.torch.compose.database.model.GiftWrapPayload::class,
|
||||
at.torch.compose.database.model.InReplyToRelation::class,
|
||||
at.torch.compose.database.model.MarmotKeyPackage::class,
|
||||
at.torch.compose.database.model.MarmotKeyPackageBundle::class,
|
||||
at.torch.compose.database.model.Mention::class,
|
||||
at.torch.compose.database.model.NegentropySynchronizeRequest::class,
|
||||
at.torch.compose.database.model.NegentropySynchronizeResult::class,
|
||||
at.torch.compose.database.model.NostrEvent::class,
|
||||
at.torch.compose.database.model.NostrEventRelay::class,
|
||||
at.torch.compose.database.model.Participant::class,
|
||||
at.torch.compose.database.model.Post::class,
|
||||
at.torch.compose.database.model.Profile::class,
|
||||
at.torch.compose.database.model.QuotedRelation::class,
|
||||
at.torch.compose.database.model.Reaction::class,
|
||||
at.torch.compose.database.model.RecentSearch::class,
|
||||
at.torch.compose.database.model.Relay::class,
|
||||
at.torch.compose.database.model.RepostedRelation::class,
|
||||
at.torch.compose.database.model.SynchronizeNostrEventRequest::class,
|
||||
at.torch.compose.database.model.SynchronizeNostrEventResult::class,
|
||||
at.torch.compose.database.model.UnsignedNostrEvent::class,
|
||||
at.torch.compose.database.model.Zap::class
|
||||
BroadcastNostrEventReceipt::class,
|
||||
BroadcastNostrEventRequest::class,
|
||||
Connection::class,
|
||||
ChatMessage::class,
|
||||
ChatMessageBroadcastNostrEventRequestRelation::class,
|
||||
ChatMessageBroadcastNostrEventReceiptRelation::class,
|
||||
ChatMessageNostrEventRelation::class,
|
||||
ChatRoom::class,
|
||||
GiftWrapMessage::class,
|
||||
GiftWrapSeal::class,
|
||||
GiftWrapPayload::class,
|
||||
InReplyToRelation::class,
|
||||
MarmotKeyPackage::class,
|
||||
MarmotKeyPackageBundle::class,
|
||||
Mention::class,
|
||||
NegentropySynchronizeRequest::class,
|
||||
NegentropySynchronizeResult::class,
|
||||
NostrEvent::class,
|
||||
NostrEventRelay::class,
|
||||
Participant::class,
|
||||
Post::class,
|
||||
Profile::class,
|
||||
QuotedRelation::class,
|
||||
Reaction::class,
|
||||
RecentSearch::class,
|
||||
Relay::class,
|
||||
RepostedRelation::class,
|
||||
SynchronizeNostrEventRequest::class,
|
||||
SynchronizeNostrEventResult::class,
|
||||
UnsignedNostrEvent::class,
|
||||
Zap::class
|
||||
],
|
||||
version = 1
|
||||
)
|
||||
@@ -72,6 +104,8 @@ abstract class TorchDatabase: RoomDatabase() {
|
||||
|
||||
abstract fun inReplyToRelationDao(): at.torch.compose.database.dao.InReplyToRelationDao
|
||||
|
||||
abstract fun marmotInnerEventDao(): MarmotInnerEventDao
|
||||
|
||||
abstract fun marmotKeyPackageDao(): MarmotKeyPackageDao
|
||||
|
||||
abstract fun marmotKeyPackageBundleDao(): MarmotKeyPackageBundleDao
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package at.torch.compose.database.dao
|
||||
|
||||
import androidx.room3.Dao
|
||||
import androidx.room3.Query
|
||||
import androidx.room3.Upsert
|
||||
import at.torch.compose.database.model.MarmotInnerEvent
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface MarmotInnerEventDao {
|
||||
|
||||
@Upsert
|
||||
suspend fun upsert(marmotInnerEvent: MarmotInnerEvent)
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import com.vitorpamplona.quartz.marmot.mls.group.MlsGroupState
|
||||
import com.vitorpamplona.quartz.marmot.mls.messages.KeyPackageBundle
|
||||
import com.vitorpamplona.quartz.marmot.mls.messages.MlsKeyPackage
|
||||
import com.vitorpamplona.quartz.marmot.mls.tree.Credential
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
@@ -376,6 +377,7 @@ abstract class NostrDao(
|
||||
)?.let { groupEventResult ->
|
||||
|
||||
ChatMessage.fromGroupEventResult(
|
||||
database = database,
|
||||
activeKeyPair = activeKeyPair,
|
||||
groupEvent = groupEvent,
|
||||
groupEventResult = groupEventResult,
|
||||
@@ -385,7 +387,7 @@ abstract class NostrDao(
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: Process none chatMessages...
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package at.torch.compose.database.model
|
||||
import androidx.room3.Entity
|
||||
import androidx.room3.ForeignKey
|
||||
import androidx.room3.PrimaryKey
|
||||
import at.torch.compose.database.TorchDatabase
|
||||
import at.torch.compose.database.model.traits.LocalStoreEntity
|
||||
import at.torch.compose.database.model.traits.SoftDeletableEntity
|
||||
import at.torch.compose.database.model.traits.TimestampedEntity
|
||||
@@ -33,6 +34,18 @@ import kotlin.time.Instant
|
||||
childColumns = ["giftWrapPayloadId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
ForeignKey(
|
||||
entity = GroupEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["groupEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
ForeignKey(
|
||||
entity = MarmotInnerEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["marmotInnerEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
)
|
||||
data class ChatMessage(
|
||||
@@ -44,6 +57,7 @@ data class ChatMessage(
|
||||
|
||||
val giftWrapPayloadId: HexKey?,
|
||||
val groupEventId: HexKey?,
|
||||
val marmotInnerEventId: HexKey?,
|
||||
val chatRoomId: String,
|
||||
|
||||
val replyToMessageId: Long? = null,
|
||||
@@ -60,7 +74,8 @@ data class ChatMessage(
|
||||
): TimestampedEntity, LocalStoreEntity, UserViewableEntity, SoftDeletableEntity {
|
||||
|
||||
companion object {
|
||||
fun fromGroupEventResult(
|
||||
suspend fun fromGroupEventResult(
|
||||
database: TorchDatabase,
|
||||
activeKeyPair: KeyPair,
|
||||
groupEvent: GroupEvent,
|
||||
groupEventResult: GroupEventResult
|
||||
@@ -73,12 +88,25 @@ data class ChatMessage(
|
||||
throw GiftWrapImpersonationException("" +
|
||||
"The innerEvent pubkey (${event?.pubKey}) doesn't match the groupEvent pubkey ${groupEvent.pubKey}")
|
||||
}
|
||||
database.marmotInnerEventDao().upsert(
|
||||
MarmotInnerEvent(
|
||||
id = event.id,
|
||||
publicKey = event.pubKey,
|
||||
marmotGroupEventId = groupEvent.id,
|
||||
tags = event.tags,
|
||||
content = event.content,
|
||||
chatRoomId = groupEventResult.groupId,
|
||||
kind = event.kind,
|
||||
createdAt = Instant.fromEpochSeconds(event.createdAt)
|
||||
)
|
||||
)
|
||||
when (event.kind) {
|
||||
ChatEvent.KIND -> {
|
||||
ChatMessage(
|
||||
giftWrapPayloadId = null,
|
||||
messageType = "message",
|
||||
groupEventId = groupEvent.id,
|
||||
marmotInnerEventId = event.id,
|
||||
senderPublicKey = event.pubKey,
|
||||
isUserMessage = activeKeyPair.pubKey.toHex() == groupEvent.pubKey,
|
||||
chatRoomId = groupEventResult.groupId,
|
||||
@@ -91,6 +119,7 @@ data class ChatMessage(
|
||||
giftWrapPayloadId = null,
|
||||
messageType = "unsupported",
|
||||
groupEventId = groupEvent.id,
|
||||
marmotInnerEventId = event.id,
|
||||
senderPublicKey = groupEvent.pubKey,
|
||||
isUserMessage = activeKeyPair.pubKey.toHex() == groupEvent.pubKey,
|
||||
chatRoomId = groupEventResult.groupId,
|
||||
@@ -104,6 +133,7 @@ data class ChatMessage(
|
||||
ChatMessage(
|
||||
giftWrapPayloadId = null,
|
||||
groupEventId = groupEvent.id,
|
||||
marmotInnerEventId = null,
|
||||
messageType = "pendingCommit",
|
||||
senderPublicKey = groupEvent.pubKey,
|
||||
isUserMessage = activeKeyPair.pubKey.toHex() == groupEvent.pubKey,
|
||||
@@ -116,6 +146,7 @@ data class ChatMessage(
|
||||
ChatMessage(
|
||||
giftWrapPayloadId = null,
|
||||
groupEventId = groupEvent.id,
|
||||
marmotInnerEventId = null,
|
||||
messageType = "processedCommit",
|
||||
senderPublicKey = groupEvent.pubKey,
|
||||
isUserMessage = activeKeyPair.pubKey.toHex() == groupEvent.pubKey,
|
||||
@@ -155,6 +186,7 @@ data class ChatMessage(
|
||||
ChatMessage(
|
||||
giftWrapPayloadId = null,
|
||||
groupEventId = groupEvent.id,
|
||||
marmotInnerEventId = null,
|
||||
messageType = "proposalStaged",
|
||||
senderPublicKey = groupEvent.pubKey,
|
||||
isUserMessage = activeKeyPair.pubKey.toHex() == groupEvent.pubKey,
|
||||
@@ -167,6 +199,7 @@ data class ChatMessage(
|
||||
ChatMessage(
|
||||
giftWrapPayloadId = null,
|
||||
groupEventId = groupEvent.id,
|
||||
marmotInnerEventId = null,
|
||||
messageType = "undecryptableOuterLayer",
|
||||
senderPublicKey = groupEvent.pubKey,
|
||||
isUserMessage = activeKeyPair.pubKey.toHex() == groupEvent.pubKey,
|
||||
|
||||
@@ -41,13 +41,13 @@ import kotlin.time.Instant
|
||||
],
|
||||
)
|
||||
data class MarmotInnerEvent( // TODO: Rename this to GiftWrapPayload...
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Long = 0,
|
||||
@PrimaryKey
|
||||
val id: HexKey,
|
||||
|
||||
/**
|
||||
* Sender Public Key
|
||||
*/
|
||||
val publicKey: String,
|
||||
val publicKey: HexKey,
|
||||
|
||||
val chatRoomId: String,
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ package at.torch.compose.database.repository
|
||||
|
||||
import at.torch.compose.database.model.ChatMessage
|
||||
import at.torch.compose.database.model.GiftWrapPayload
|
||||
import at.torch.compose.database.model.MarmotInnerEvent
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.vitorpamplona.quartz.marmot.mls.group.MlsGroup
|
||||
import com.vitorpamplona.quartz.marmot.mls.group.MlsGroupState
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Kind
|
||||
@@ -11,9 +14,11 @@ import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nipC7Chats.ChatEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -99,46 +104,97 @@ class DatabaseChatRepository(
|
||||
messageType: Kind
|
||||
) {
|
||||
logger.i("sendChatMessage($text): $localChatRoom")
|
||||
|
||||
val receiverTags = localChatRoom.localParticipants.filter { participant ->
|
||||
participant.participant.participantPublicKey != localChatRoom.chatRoom.userPublicKey // TODO: Allow this where it's notes to self...
|
||||
}.map { localParticipant ->
|
||||
PTag.assemble(
|
||||
localParticipant.participant.participantPublicKey,
|
||||
localParticipant.participant.relayHint?.let { NormalizedRelayUrl(it) }
|
||||
val mlsGroup = localChatRoom.chatRoom.mlsGroupState?.let { mlsGroupState ->
|
||||
MlsGroup.restore(
|
||||
MlsGroupState.decodeTls(
|
||||
mlsGroupState.hexToByteArray()
|
||||
)
|
||||
)
|
||||
}
|
||||
if (mlsGroup != null) {
|
||||
// Create ChatRumor...
|
||||
val createdAt = Clock.System.now().epochSeconds
|
||||
val marmotInnerEventKind = if (messageType == ChatMessageEvent.KIND) {
|
||||
ChatEvent.KIND // nip-17 chatMessageEvent.kind is in MLS chatMessage.kind
|
||||
} else {
|
||||
messageType
|
||||
}
|
||||
|
||||
val createdAt = Clock.System.now().epochSeconds
|
||||
val giftWrapPayloadId = EventHasher.hashId(
|
||||
pubKey = localChatRoom.chatRoom.userPublicKey,
|
||||
createdAt = createdAt,
|
||||
tags = receiverTags.toTypedArray(),
|
||||
content = text,
|
||||
kind = messageType
|
||||
)
|
||||
val marmotInnerEventId = EventHasher.hashId(
|
||||
pubKey = localChatRoom.chatRoom.userPublicKey,
|
||||
createdAt = createdAt,
|
||||
tags = emptyArray(),
|
||||
content = text,
|
||||
kind = marmotInnerEventKind
|
||||
)
|
||||
|
||||
database.giftWrapPayloadDao().upsert(
|
||||
GiftWrapPayload(
|
||||
id = giftWrapPayloadId,
|
||||
kind = messageType,
|
||||
database.marmotInnerEventDao().upsert(
|
||||
MarmotInnerEvent(
|
||||
id = marmotInnerEventId,
|
||||
publicKey = localChatRoom.chatRoom.userPublicKey,
|
||||
createdAt = Instant.fromEpochSeconds(createdAt),
|
||||
tags = emptyArray(),
|
||||
content = text,
|
||||
chatRoomId = localChatRoom.chatRoom.id,
|
||||
kind = marmotInnerEventKind
|
||||
)
|
||||
)
|
||||
|
||||
database.chatMessageDao().upsert(
|
||||
ChatMessage(
|
||||
content = text,
|
||||
chatRoomId = localChatRoom.chatRoom.id,
|
||||
senderPublicKey = localChatRoom.chatRoom.userPublicKey,
|
||||
isUserMessage = true,
|
||||
giftWrapPayloadId = null,
|
||||
groupEventId = null,
|
||||
marmotInnerEventId = marmotInnerEventId,
|
||||
)
|
||||
)
|
||||
} else {
|
||||
// Send GiftWrapMessage
|
||||
val receiverTags = localChatRoom.localParticipants.filter { participant ->
|
||||
participant.participant.participantPublicKey != localChatRoom.chatRoom.userPublicKey // TODO: Allow this where it's notes to self...
|
||||
}.map { localParticipant ->
|
||||
PTag.assemble(
|
||||
localParticipant.participant.participantPublicKey,
|
||||
localParticipant.participant.relayHint?.let { NormalizedRelayUrl(it) }
|
||||
)
|
||||
}
|
||||
|
||||
val createdAt = Clock.System.now().epochSeconds
|
||||
val giftWrapPayloadId = EventHasher.hashId(
|
||||
pubKey = localChatRoom.chatRoom.userPublicKey,
|
||||
createdAt = createdAt,
|
||||
tags = receiverTags.toTypedArray(),
|
||||
createdAt = Instant.fromEpochSeconds(createdAt),
|
||||
content = text,
|
||||
chatRoomId = localChatRoom.chatRoom.id,
|
||||
publicKey = localChatRoom.chatRoom.userPublicKey
|
||||
kind = messageType
|
||||
)
|
||||
)
|
||||
database.chatMessageDao().upsert(
|
||||
ChatMessage(
|
||||
content = text,
|
||||
chatRoomId = localChatRoom.chatRoom.id,
|
||||
senderPublicKey = localChatRoom.chatRoom.userPublicKey,
|
||||
isUserMessage = true,
|
||||
giftWrapPayloadId = giftWrapPayloadId,
|
||||
groupEventId = null
|
||||
|
||||
database.giftWrapPayloadDao().upsert(
|
||||
GiftWrapPayload(
|
||||
id = giftWrapPayloadId,
|
||||
kind = messageType,
|
||||
tags = receiverTags.toTypedArray(),
|
||||
createdAt = Instant.fromEpochSeconds(createdAt),
|
||||
content = text,
|
||||
chatRoomId = localChatRoom.chatRoom.id,
|
||||
publicKey = localChatRoom.chatRoom.userPublicKey
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
database.chatMessageDao().upsert(
|
||||
ChatMessage(
|
||||
content = text,
|
||||
chatRoomId = localChatRoom.chatRoom.id,
|
||||
senderPublicKey = localChatRoom.chatRoom.userPublicKey,
|
||||
isUserMessage = true,
|
||||
giftWrapPayloadId = giftWrapPayloadId,
|
||||
groupEventId = null,
|
||||
marmotInnerEventId = null
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateChatRoomSubject(
|
||||
|
||||
Reference in New Issue
Block a user