#include "./tox_contact_model2.hpp" #include #include #include "./components.hpp" #include #include #include ToxContactModel2::ToxContactModel2(Contact3Registry& cr, ToxI& t, ToxEventProviderI& tep) : _cr(cr), _t(t), _tep(tep) { _tep.subscribe(this, Tox_Event::TOX_EVENT_FRIEND_CONNECTION_STATUS); _tep.subscribe(this, Tox_Event::TOX_EVENT_FRIEND_STATUS); _tep.subscribe(this, Tox_Event::TOX_EVENT_FRIEND_NAME); // TODO: conf _tep.subscribe(this, Tox_Event::TOX_EVENT_GROUP_SELF_JOIN); _tep.subscribe(this, Tox_Event::TOX_EVENT_GROUP_PEER_JOIN); _tep.subscribe(this, Tox_Event::TOX_EVENT_GROUP_PEER_EXIT); _tep.subscribe(this, Tox_Event::TOX_EVENT_GROUP_PEER_NAME); // add self Contact3 c = entt::null; // TODO: if self exists c = _cr.create(); _cr.emplace(c, this); _cr.emplace(c); _cr.emplace(c, _t.toxSelfGetName()); _friend_self = c; // fill in contacts for (const uint32_t f_id : _t.toxSelfGetFriendList()) { getContactFriend(f_id); } for (const uint32_t g_id : _t.toxGroupGetList()) { getContactGroup(g_id); } } Contact3Handle ToxContactModel2::getContactFriend(uint32_t friend_number) { Contact3 c = entt::null; // first check contacts with friend id // TODO: lookup table //_cr.view().each([&c, friend_number](const Contact3 e, const Contact::Components::ToxFriendEphemeral& f_id) { for (const auto e : _cr.view()) { if (_cr.get(e).friend_number == friend_number) { c = e; break; } } if (_cr.valid(c)) { return {_cr, c}; } // else check by pubkey auto f_key_opt = _t.toxFriendGetPublicKey(friend_number); assert(f_key_opt.has_value()); // TODO: handle gracefully? const ToxKey& f_key = f_key_opt.value(); //_cr.view().each([&c, &f_key](const Contact3 e, const Contact::Components::ToxFriendPersistent& f_key_comp) { for (const auto e : _cr.view()) { if (f_key == _cr.get(e).key) { c = e; break; } } if (_cr.valid(c)) { return {_cr, c}; } // else, new ent c = _cr.create(); _cr.emplace(c); _cr.emplace(c, this); _cr.emplace(c, friend_number); _cr.emplace(c, f_key); _cr.emplace(c, _friend_self); _cr.emplace(c, _t.toxFriendGetName(friend_number).value_or("")); std::cout << "TCM2: created friend contact " << friend_number << "\n"; return {_cr, c}; } Contact3Handle ToxContactModel2::getContactGroup(uint32_t group_number) { Contact3 c = entt::null; // first check contacts with group_number // TODO: lookup table //_cr.view().each([&c, group_number](const Contact3 e, const Contact::Components::ToxGroupEphemeral& g_e) { for (const auto e : _cr.view()) { if (_cr.get(e).group_number == group_number) { c = e; break; } } if (_cr.valid(c)) { return {_cr, c}; } // else check by pubkey auto g_key_opt = _t.toxGroupGetChatId(group_number); assert(g_key_opt.has_value()); // TODO: handle gracefully? const ToxKey& g_key = g_key_opt.value(); //_cr.view().each([&c, &g_key](const Contact3 e, const Contact::Components::ToxGroupPersistent& g_key_comp) { for (const auto e : _cr.view()) { if (g_key == _cr.get(e).chat_id) { c = e; break; } } if (_cr.valid(c)) { return {_cr, c}; } // else, new ent c = _cr.create(); _cr.emplace(c, this); _cr.emplace(c); _cr.emplace(c); // start empty _cr.emplace(c, group_number); _cr.emplace(c, g_key); _cr.emplace(c, _t.toxGroupGetName(group_number).value_or("")); _cr.emplace( c, _t.toxGroupIsConnected(group_number).value_or(false) ? Contact::Components::ConnectionState::State::cloud : Contact::Components::ConnectionState::State::disconnected ); auto self_opt = _t.toxGroupSelfGetPeerId(group_number); if (self_opt.has_value()) { _cr.emplace(c, getContactGroupPeer(group_number, self_opt.value())); } else { std::cerr << "TCM2 error: getting self for group" << group_number << "!!\n"; } std::cout << "TCM2: created group contact " << group_number << "\n"; return {_cr, c}; } Contact3Handle ToxContactModel2::getContactGroupPeer(uint32_t group_number, uint32_t peer_number) { Contact3 c = entt::null; Contact3Handle group_c = getContactGroup(group_number); assert(static_cast(group_c)); // first check contacts with peer id // TODO: lookup table //_cr.view().each([&c, group_number, peer_number](const Contact3 e, const Contact::Components::ToxGroupPeerEphemeral& p_comp) { for (const auto e : _cr.view()) { const auto& p_comp = _cr.get(e); if (p_comp.group_number == group_number && p_comp.peer_number == peer_number) { c = e; break; } } if (_cr.valid(c)) { return {_cr, c}; } const auto& g_key = group_c.get().chat_id; // else check by key auto [g_p_key_opt, _] = _t.toxGroupPeerGetPublicKey(group_number, peer_number); if (!g_p_key_opt.has_value()) { } //assert(g_p_key_opt.has_value()); // TODO: handle gracefully? if (!g_p_key_opt.has_value()) { // if the key could not be retreived, that means the peer has exited (idk why the earlier search did not work, it should have) // also exit here, to not create, pubkey less <.< std::cerr << "TCM2 error: we did not have offline peer in db, which is worrying\n"; return {}; } const ToxKey& g_p_key = g_p_key_opt.value(); //_cr.view().each([&c, &g_key, &g_p_key](const Contact3 e, const Contact::Components::ToxGroupPeerPersistent& g_p_key_comp) { for (const auto e : _cr.view()) { const auto& g_p_key_comp = _cr.get(e); if (g_p_key == g_p_key_comp.peer_key && g_key == g_p_key_comp.chat_id) { c = e; break; } } if (_cr.valid(c)) { return {_cr, c}; } // else, new ent c = _cr.create(); _cr.emplace(c, group_c); { // add sub to parent auto& parent_sub_list = group_c.get_or_emplace().subs; if (std::find(parent_sub_list.cbegin(), parent_sub_list.cend(), c) == parent_sub_list.cend()) { parent_sub_list.push_back(c); } } _cr.emplace(c, this); _cr.emplace(c, group_number, peer_number); _cr.emplace(c, g_key, g_p_key); const auto name_opt = std::get<0>(_t.toxGroupPeerGetName(group_number, peer_number)); if (name_opt.has_value()) { _cr.emplace(c, name_opt.value()); } { // self // TODO: this is very flaky auto self_number_opt = _t.toxGroupSelfGetPeerId(group_number); if (peer_number == self_number_opt.value()) { _cr.emplace(c); } else { _cr.emplace(c, getContactGroupPeer(group_number, self_number_opt.value())); } } std::cout << "TCM2: created group peer contact " << group_number << " " << peer_number << "\n"; return {_cr, c}; } //Contact3Handle ToxContactModel2::getContactGroupPeer(const ToxKey& group_key, const ToxKey& peer_key) { //return {}; //} Contact3Handle ToxContactModel2::getContactGroupPeer(uint32_t group_number, const ToxKey& peer_key) { Contact3 c = entt::null; Contact3Handle group_c = getContactGroup(group_number); assert(static_cast(group_c)); const auto& g_key = group_c.get().chat_id; // search by key //_cr.view().each([&c, &g_key, &peer_key](const Contact3 e, const Contact::Components::ToxGroupPeerPersistent& g_p_key_comp) { for (const auto e : _cr.view()) { const auto& g_p_key_comp = _cr.get(e); if (peer_key == g_p_key_comp.peer_key && g_key == g_p_key_comp.chat_id) { c = e; break; } } if (_cr.valid(c)) { return {_cr, c}; } // TODO: maybe not create contacts via history sync // else, new ent c = _cr.create(); _cr.emplace(c, group_c); _cr.emplace(c, this); //_cr.emplace(c, group_number, peer_number); _cr.emplace(c, g_key, peer_key); //_cr.emplace(c, ""); //_cr.emplace(c, std::get<0>(_t.toxGroupPeerGetName(group_number, peer_number)).value_or("")); { // self // TODO: this is very flaky auto self_number_opt = _t.toxGroupSelfGetPeerId(group_number); _cr.emplace(c, getContactGroupPeer(group_number, self_number_opt.value())); } std::cout << "TCM2: created group peer contact via pubkey " << group_number << "\n"; return {_cr, c}; } bool ToxContactModel2::onToxEvent(const Tox_Event_Friend_Connection_Status* e) { const Tox_Connection connection_status = tox_event_friend_connection_status_get_connection_status(e); auto c = getContactFriend(tox_event_friend_connection_status_get_friend_number(e)); c.emplace_or_replace( (connection_status == TOX_CONNECTION_NONE) ? Contact::Components::ConnectionState::State::disconnected : (connection_status == TOX_CONNECTION_UDP) ? Contact::Components::ConnectionState::State::direct : Contact::Components::ConnectionState::State::cloud ); if (connection_status == TOX_CONNECTION_NONE) { c.remove(); } return false; } bool ToxContactModel2::onToxEvent(const Tox_Event_Friend_Status* e) { //tox_event_friend_status_get_status(e); //TOX_USER_STATUS_NONE, //TOX_USER_STATUS_AWAY, //TOX_USER_STATUS_BUSY, //auto c = getContactFriend(tox_event_friend_status_get_friend_number(e)); return false; } bool ToxContactModel2::onToxEvent(const Tox_Event_Friend_Name* e) { const std::string_view name { reinterpret_cast(tox_event_friend_name_get_name(e)), tox_event_friend_name_get_name_length(e) }; auto c = getContactFriend(tox_event_friend_name_get_friend_number(e)); c.emplace_or_replace(std::string{name}); return false; // return true? } bool ToxContactModel2::onToxEvent(const Tox_Event_Group_Self_Join* e) { const uint32_t group_number = tox_event_group_self_join_get_group_number(e); if (const auto self_id_opt = _t.toxGroupSelfGetPeerId(group_number); self_id_opt.has_value()) { auto c = getContactGroupPeer(group_number, self_id_opt.value()); c.emplace_or_replace(); c.emplace_or_replace(Contact::Components::ConnectionState::State::direct); } else { assert(false); } return false; } bool ToxContactModel2::onToxEvent(const Tox_Event_Group_Peer_Join* e) { const uint32_t group_number = tox_event_group_peer_join_get_group_number(e); const uint32_t peer_number = tox_event_group_peer_join_get_peer_id(e); auto c = getContactGroupPeer(group_number, peer_number); // ensure its set c.emplace_or_replace(group_number, peer_number); auto [peer_state_opt, _] = _t.toxGroupPeerGetConnectionStatus(group_number, peer_number); c.emplace_or_replace( (peer_state_opt.value_or(TOX_CONNECTION_NONE) == TOX_CONNECTION_NONE) ? Contact::Components::ConnectionState::State::disconnected : (peer_state_opt.value_or(TOX_CONNECTION_NONE) == TOX_CONNECTION_UDP) ? Contact::Components::ConnectionState::State::direct : Contact::Components::ConnectionState::State::cloud ); // update name const auto name_opt = std::get<0>(_t.toxGroupPeerGetName(group_number, peer_number)); if (name_opt.has_value()) { _cr.emplace_or_replace(c, name_opt.value()); } return false; } bool ToxContactModel2::onToxEvent(const Tox_Event_Group_Peer_Exit* e) { const uint32_t group_number = tox_event_group_peer_exit_get_group_number(e); const uint32_t peer_number = tox_event_group_peer_exit_get_peer_id(e); const auto exit_type = tox_event_group_peer_exit_get_exit_type(e); // set name? // we dont care about the part messae? if (exit_type == Tox_Group_Exit_Type::TOX_GROUP_EXIT_TYPE_SELF_DISCONNECTED) { // you disconnected intentionally, or you where kicked // TODO: we need to remove all ToxGroupPeerEphemeral components of that group } else { auto c = getContactGroupPeer(group_number, peer_number); if (!static_cast(c)) { return false; // we dont track this contact ????? } c.emplace_or_replace(Contact::Components::ConnectionState::State::disconnected); c.remove(); // they where kicked // exit_type == Tox_Group_Exit_Type::TOX_GROUP_EXIT_TYPE_KICK } return false; } bool ToxContactModel2::onToxEvent(const Tox_Event_Group_Peer_Name* e) { const uint32_t group_number = tox_event_group_peer_name_get_group_number(e); const uint32_t peer_number = tox_event_group_peer_name_get_peer_id(e); const std::string_view name { reinterpret_cast(tox_event_group_peer_name_get_name(e)), tox_event_group_peer_name_get_name_length(e) }; auto c = getContactGroupPeer(group_number, peer_number); c.emplace_or_replace(std::string{name}); return false; }