// DragonX Wallet - HushChat service (implementation). #include "chat_service.h" #include "chat_database.h" #include "chat_identity.h" // chatIdentityPublicKeyHex #include namespace dragonx::chat { ChatService::~ChatService() { clearIdentity(); } void ChatService::setIdentity(const ChatKeyPair& keys) { identity_ = keys; has_identity_ = true; } void ChatService::clearIdentity() { wipeChatKeyPair(identity_); has_identity_ = false; } int ChatService::ingest(const std::vector& metadata, const std::unordered_map& txTimestamps, std::int64_t fallbackTimestamp) { if (!has_identity_) return 0; int added = 0; for (const auto& meta : metadata) { ChatMessage message; message.direction = ChatDirection::Incoming; message.txid = meta.txid; message.conversation_id = meta.conversation_id; message.peer_zaddr = meta.reply_zaddr; message.peer_public_key_hex = meta.sender_public_key_hex; const auto timeIt = txTimestamps.find(meta.txid); message.timestamp = timeIt != txTimestamps.end() ? timeIt->second : fallbackTimestamp; message.payload_position = meta.payload_position; if (meta.type == HushChatHeaderType::ContactRequest) { message.kind = ChatMessageKind::ContactRequest; message.body = meta.payload_memo; // plaintext request text } else { message.kind = ChatMessageKind::Message; std::string plaintext; const ChatCryptoStatus status = decryptIncoming( identity_, meta.sender_public_key_hex, meta.secretstream_header_hex, meta.payload_memo, plaintext); if (status != ChatCryptoStatus::Ok) continue; // drop undecryptable silently message.body = std::move(plaintext); } // In-memory store dedups (txid+position); only persist the genuinely new ones. On the next // session loadFromDatabase() repopulates the store, so re-scanning the chain re-ingests but // the store dedup prevents a duplicate write. if (store_.append(message)) { if (db_) db_->append(message); ++added; } } return added; } void ChatService::loadFromDatabase() { if (!db_) return; for (const auto& message : db_->load()) { store_.append(message); } } std::string ChatService::identityPublicKeyHex() const { if (!has_identity_) return {}; return chatIdentityPublicKeyHex(identity_); } ChatComposeStatus ChatService::composeMessage(const std::string& myReplyZaddr, const std::string& peerPublicKeyHex, const std::string& peerZaddr, const std::string& conversationId, const std::string& plaintext, OutgoingChatMemos& out) const { if (!has_identity_) return ChatComposeStatus::MissingField; return buildOutgoingMessage(identity_, chatIdentityPublicKeyHex(identity_), myReplyZaddr, peerPublicKeyHex, peerZaddr, conversationId, plaintext, out); } ChatComposeStatus ChatService::composeContactRequest(const std::string& myReplyZaddr, const std::string& peerZaddr, const std::string& conversationId, const std::string& requestText, OutgoingChatMemos& out) const { if (!has_identity_) return ChatComposeStatus::MissingField; return buildOutgoingContactRequest(chatIdentityPublicKeyHex(identity_), myReplyZaddr, peerZaddr, conversationId, requestText, out); } bool ChatService::recordOutgoing(const ChatMessage& message) { if (store_.append(message)) { if (db_) db_->append(message); return true; } return false; } } // namespace dragonx::chat