diff --git a/src/call.cpp b/src/call.cpp index 5b568d5b9a581d622c7eae640538090c5a210c5a..da9f76528333fd9013cef698ba06430ac8fafdda 100644 --- a/src/call.cpp +++ b/src/call.cpp @@ -22,6 +22,7 @@ #include "call.h" #include "account.h" +#include "jamidht/jamiaccount.h" #include "manager.h" #ifdef ENABLE_PLUGIN #include "plugin/jamipluginmanager.h" @@ -122,6 +123,22 @@ Call::Call(const std::shared_ptr<Account>& account, std::chrono::seconds(timeout)); } + if (!isSubcall() && getCallType() == CallType::OUTGOING) { + if (cnx_state == ConnectionState::CONNECTED && duration_start_ == time_point::min()) + duration_start_ = clock::now(); + else if (cnx_state == ConnectionState::DISCONNECTED) { + if (auto jamiAccount = std::dynamic_pointer_cast<JamiAccount>( + getAccount().lock())) { + auto duration = duration_start_ == time_point::min() + ? 0 + : std::chrono::duration_cast<std::chrono::milliseconds>( + clock::now() - duration_start_) + .count(); + jamiAccount->addCallHistoryMessage(getPeerNumber(), duration); + } + } + } + // kill pending subcalls at disconnect if (call_state == CallState::OVER) hangupCalls(safePopSubcalls(), 0); diff --git a/src/call.h b/src/call.h index 43147dfd8b84f3540dfefa69426b8cb27afbb9a3..24a86e270da7e5990d0195e3b19c8aceb64d4107 100644 --- a/src/call.h +++ b/src/call.h @@ -396,6 +396,9 @@ protected: mutable ConfInfo confInfo_ {}; private: + using clock = std::chrono::steady_clock; + using time_point = clock::time_point; + bool validStateTransition(CallState newState); void checkPendingIM(); @@ -433,6 +436,7 @@ private: std::string peerDisplayName_ {}; time_t timestamp_start_ {0}; + time_point duration_start_ {time_point::min()}; ///< MultiDevice: message received by subcall to merged yet MsgList pendingInMessages_; diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index a56372339bbe08c761ea838000793f7628ba7952..24ee9c0e1a4b1b832373ce8b3708d5005106efd4 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -3789,6 +3789,13 @@ JamiAccount::requestPeerConnection( isVCard, channeledConnectedCb, onChanneledCancelled); + //auto convId = getOneToOneConversation(info.peer); + //if (!convId.empty()) { + // Json::Value value; + // value["tid"] = std::to_string(tid); + // value["type"] = "application/data-transfer+json"; + // sendMessage(convId, value); + //} } void @@ -4262,11 +4269,23 @@ JamiAccount::sendMessage(const std::string& conversationId, const std::string& parent, const std::string& type, bool announce) +{ + Json::Value json; + json["body"] = message; + json["type"] = type; + sendMessage(conversationId, json, parent, announce); +} + +void +JamiAccount::sendMessage(const std::string& conversationId, + const Json::Value& value, + const std::string& parent, + bool announce) { std::lock_guard<std::mutex> lk(conversationsMtx_); auto conversation = conversations_.find(conversationId); if (conversation != conversations_.end() && conversation->second) { - auto commitId = conversation->second->sendMessage(message, type, parent); + auto commitId = conversation->second->sendMessage(value, parent); if (!announce) return; if (!commitId.empty()) { @@ -5267,4 +5286,19 @@ JamiAccount::announceMemberMessage(const std::string& convId, action); } } + +void +JamiAccount::addCallHistoryMessage(const std::string& uri, uint64_t duration_ms) +{ + auto finalUri = uri.substr(0, uri.find("@ring.dht")); + auto convId = getOneToOneConversation(finalUri); + if (!convId.empty()) { + Json::Value value; + value["to"] = finalUri; + value["type"] = "application/call-history+json"; + value["duration"] = std::to_string(duration_ms); + sendMessage(convId, value); + } +} + } // namespace jami diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h index e4f1e7055dd54d788bf8ba1dc3068919862e0fbd..4bbe027c557eb6e9abdc74f722d1cbc0a4703344 100644 --- a/src/jamidht/jamiaccount.h +++ b/src/jamidht/jamiaccount.h @@ -525,11 +525,21 @@ public: const std::string& conversationId); // Message send/load + void sendMessage(const std::string& conversationId, + const Json::Value& value, + const std::string& parent = "", + bool announce = true); void sendMessage(const std::string& conversationId, const std::string& message, const std::string& parent = "", const std::string& type = "text/plain", bool announce = true); + /** + * Add to the related conversation the call history message + * @param uri Peer number + * @param duration_ms The call duration in ms + */ + void addCallHistoryMessage(const std::string& uri, uint64_t duration_ms); uint32_t loadConversationMessages(const std::string& conversationId, const std::string& fromMessage = "", size_t n = 0); @@ -963,15 +973,16 @@ private: void sendMessageNotification(const Conversation& conversation, const std::string& commitId, bool sync); + + void announceMemberMessage(const std::string& convId, + const std::map<std::string, std::string>& message) const; + /** * Get related conversation with member * @param uri The member to search for * @return the conversation id if found else empty */ std::string getOneToOneConversation(const std::string& uri) const; - - void announceMemberMessage(const std::string& convId, - const std::map<std::string, std::string>& message) const; }; static inline std::ostream& diff --git a/test/unitTest/conversation/conversation.cpp b/test/unitTest/conversation/conversation.cpp index 9cad8f1255e7adc5f4417d089778f76434f1c0ab..f9345749904ca6617d7b307724173fe2cc29dfc3 100644 --- a/test/unitTest/conversation/conversation.cpp +++ b/test/unitTest/conversation/conversation.cpp @@ -730,7 +730,7 @@ ConversationTest::testSendMessage() // Wait that alice sees Bob cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageAliceReceived == 1; }); - aliceAccount->sendMessage(convId, "hi"); + aliceAccount->sendMessage(convId, std::string("hi")); cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived == 1; }); DRing::unregisterSignalHandlers(); } @@ -763,7 +763,7 @@ ConversationTest::testSendMessageTriggerMessageReceived() cv.wait_for(lk, std::chrono::seconds(30)); CPPUNIT_ASSERT(conversationReady); - aliceAccount->sendMessage(convId, "hi"); + aliceAccount->sendMessage(convId, std::string("hi")); cv.wait_for(lk, std::chrono::seconds(30), [&] { return messageReceived == 1; }); CPPUNIT_ASSERT(messageReceived == 1); DRing::unregisterSignalHandlers(); @@ -819,9 +819,9 @@ ConversationTest::testMergeTwoDifferentHeads() ConversationRepository repo(carlaAccount, convId); repo.join(); - aliceAccount->sendMessage(convId, "hi"); - aliceAccount->sendMessage(convId, "sup"); - aliceAccount->sendMessage(convId, "jami"); + aliceAccount->sendMessage(convId, std::string("hi")); + aliceAccount->sendMessage(convId, std::string("sup")); + aliceAccount->sendMessage(convId, std::string("jami")); // Start Carla, should merge and all messages should be there Manager::instance().sendRegister(carlaId, true); @@ -982,7 +982,7 @@ ConversationTest::testSendMessageToMultipleParticipants() + DIR_SEPARATOR_STR + "conversations" + DIR_SEPARATOR_STR + convId; CPPUNIT_ASSERT(fileutils::isDirectory(repoPath)); - aliceAccount->sendMessage(convId, "hi"); + aliceAccount->sendMessage(convId, std::string("hi")); CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(60), [&]() { return messageReceivedBob >= 1 && messageReceivedCarla >= 1; })); @@ -1045,19 +1045,19 @@ ConversationTest::testPingPongMessages() CPPUNIT_ASSERT(fileutils::isDirectory(repoPath)); messageBobReceived = 0; messageAliceReceived = 0; - aliceAccount->sendMessage(convId, "ping"); + aliceAccount->sendMessage(convId, std::string("ping")); CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived == 1 && messageAliceReceived == 1; })); - bobAccount->sendMessage(convId, "pong"); + bobAccount->sendMessage(convId, std::string("pong")); CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived == 2 && messageAliceReceived == 2; })); - bobAccount->sendMessage(convId, "ping"); + bobAccount->sendMessage(convId, std::string("ping")); CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived == 3 && messageAliceReceived == 3; })); - aliceAccount->sendMessage(convId, "pong"); + aliceAccount->sendMessage(convId, std::string("pong")); CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived == 4 && messageAliceReceived == 4; })); @@ -1357,7 +1357,7 @@ ConversationTest::testBannedMemberCannotSendMessage() // Now check that alice doesn't receive a message from Bob aliceMessageReceived = false; - bobAccount->sendMessage(convId, "hi"); + bobAccount->sendMessage(convId, std::string("hi")); CPPUNIT_ASSERT( !cv.wait_for(lk, std::chrono::seconds(30), [&]() { return aliceMessageReceived; })); } @@ -1465,7 +1465,7 @@ ConversationTest::generateFakeVote(std::shared_ptr<JamiAccount> account, wbuilder["indentation"] = ""; cr.commitMessage(Json::writeString(wbuilder, json)); - account->sendMessage(convId, "trigger the fake history to be pulled"); + account->sendMessage(convId, std::string("trigger the fake history to be pulled")); } void @@ -1507,7 +1507,7 @@ ConversationTest::generateFakeInvite(std::shared_ptr<JamiAccount> account, wbuilder["indentation"] = ""; cr.commitMessage(Json::writeString(wbuilder, json)); - account->sendMessage(convId, "trigger the fake history to be pulled"); + account->sendMessage(convId, std::string("trigger the fake history to be pulled")); } void ConversationTest::addAll(std::shared_ptr<JamiAccount> account, const std::string& convId) @@ -1649,7 +1649,7 @@ ConversationTest::testMemberCannotBanOther() generateFakeVote(carlaAccount, convId, bobUri); CPPUNIT_ASSERT(!cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived; })); - aliceAccount->sendMessage(convId, "hi"); + aliceAccount->sendMessage(convId, std::string("hi")); CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived; })); } @@ -1832,7 +1832,7 @@ ConversationTest::testPlainTextNoBadFile() cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageAliceReceived == 1; }); addFile(aliceAccount, convId, "BADFILE"); - aliceAccount->sendMessage(convId, "hi"); + aliceAccount->sendMessage(convId, std::string("hi")); // Check not received due to the unwanted file CPPUNIT_ASSERT( !cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived == 1; })); @@ -1924,7 +1924,7 @@ ConversationTest::testVoteNoBadFile() })); messageCarlaReceived = false; - bobAccount->sendMessage(convId, "final"); + bobAccount->sendMessage(convId, std::string("final")); CPPUNIT_ASSERT( cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageCarlaReceived; })); }