inital commit, add tox_contacts (version 2)

This commit is contained in:
Green Sky 2023-07-23 13:23:49 +02:00
commit df87cd9c8a
No known key found for this signature in database
7 changed files with 587 additions and 0 deletions

20
CMakeLists.txt Normal file
View File

@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(solanaceae)
add_library(solanaceae_tox_contacts
./solanaceae/tox_contacts/components.hpp
./solanaceae/tox_contacts/components_id.inl
./solanaceae/tox_contacts/tox_contact_model2.hpp
./solanaceae/tox_contacts/tox_contact_model2.cpp
)
target_include_directories(solanaceae_tox_contacts PUBLIC .)
target_compile_features(solanaceae_tox_contacts PUBLIC cxx_std_17)
target_link_libraries(solanaceae_tox_contacts PUBLIC
solanaceae_util
solanaceae_contact
solanaceae_toxcore
)

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
The Code is under the following License, if not stated otherwise:
MIT License
Copyright (c) 2023 Erik Scholz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

6
README.md Normal file
View File

@ -0,0 +1,6 @@
`plant !`
provides tox contacts and tox messages
requires solanaceae_toxcore

View File

@ -0,0 +1,58 @@
#pragma once
#include <solanaceae/toxcore/tox_key.hpp>
namespace Contact::Components {
// ==========
// friend
// ==========
struct ToxFriendPersistent {
ToxKey key;
};
struct ToxFriendEphemeral {
uint32_t friend_number;
};
// ==========
// TODO: conference (old groups)
// ==========
struct ToxConfPersistent {
ToxKey key;
};
struct ToxConfEhpemeral {
uint32_t id;
};
// ==========
// groups (ngc)
// ==========
struct ToxGroupPersistent {
ToxKey chat_id;
};
struct ToxGroupEphemeral {
uint32_t group_number;
};
struct ToxGroupPeerPersistent {
ToxKey chat_id;
ToxKey peer_key;
};
struct ToxGroupPeerEphemeral {
uint32_t group_number;
uint32_t peer_number;
};
} // Contact::Components
#include "./components_id.inl"

View File

@ -0,0 +1,25 @@
#include "./components.hpp"
#include <entt/core/type_info.hpp>
// TODO: move more central
#define DEFINE_COMP_ID(x) \
template<> \
constexpr entt::id_type entt::type_hash<x>::value() noexcept { \
using namespace entt::literals; \
return #x##_hs; \
}
// cross compiler stable ids
DEFINE_COMP_ID(Contact::Components::ToxFriendPersistent)
DEFINE_COMP_ID(Contact::Components::ToxFriendEphemeral)
DEFINE_COMP_ID(Contact::Components::ToxConfPersistent)
DEFINE_COMP_ID(Contact::Components::ToxConfEhpemeral)
DEFINE_COMP_ID(Contact::Components::ToxGroupPersistent)
DEFINE_COMP_ID(Contact::Components::ToxGroupEphemeral)
DEFINE_COMP_ID(Contact::Components::ToxGroupPeerPersistent)
DEFINE_COMP_ID(Contact::Components::ToxGroupPeerEphemeral)
#undef DEFINE_COMP_ID

View File

@ -0,0 +1,409 @@
#include "./tox_contact_model2.hpp"
#include <solanaceae/toxcore/tox_interface.hpp>
#include <solanaceae/contact/components.hpp>
#include "./components.hpp"
#include <algorithm>
#include <string_view>
#include <iostream>
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<Contact::Components::ContactModel>(c, this);
_cr.emplace<Contact::Components::TagSelfStrong>(c);
_cr.emplace<Contact::Components::Name>(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<Contact::Components::ToxFriendEphemeral>().each([&c, friend_number](const Contact3 e, const Contact::Components::ToxFriendEphemeral& f_id) {
for (const auto e : _cr.view<Contact::Components::ToxFriendEphemeral>()) {
if (_cr.get<Contact::Components::ToxFriendEphemeral>(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<Contact::Components::ToxFriendPersistent>().each([&c, &f_key](const Contact3 e, const Contact::Components::ToxFriendPersistent& f_key_comp) {
for (const auto e : _cr.view<Contact::Components::ToxFriendPersistent>()) {
if (f_key == _cr.get<Contact::Components::ToxFriendPersistent>(e).key) {
c = e;
break;
}
}
if (_cr.valid(c)) {
return {_cr, c};
}
// else, new ent
c = _cr.create();
_cr.emplace<Contact::Components::TagBig>(c);
_cr.emplace<Contact::Components::ContactModel>(c, this);
_cr.emplace<Contact::Components::ToxFriendEphemeral>(c, friend_number);
_cr.emplace<Contact::Components::ToxFriendPersistent>(c, f_key);
_cr.emplace<Contact::Components::Self>(c, _friend_self);
_cr.emplace<Contact::Components::Name>(c, _t.toxFriendGetName(friend_number).value_or("<unk>"));
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<Contact::Components::ToxGroupEphemeral>().each([&c, group_number](const Contact3 e, const Contact::Components::ToxGroupEphemeral& g_e) {
for (const auto e : _cr.view<Contact::Components::ToxGroupEphemeral>()) {
if (_cr.get<Contact::Components::ToxGroupEphemeral>(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<Contact::Components::ToxGroupPersistent>().each([&c, &g_key](const Contact3 e, const Contact::Components::ToxGroupPersistent& g_key_comp) {
for (const auto e : _cr.view<Contact::Components::ToxGroupPersistent>()) {
if (g_key == _cr.get<Contact::Components::ToxGroupPersistent>(e).chat_id) {
c = e;
break;
}
}
if (_cr.valid(c)) {
return {_cr, c};
}
// else, new ent
c = _cr.create();
_cr.emplace<Contact::Components::ContactModel>(c, this);
_cr.emplace<Contact::Components::TagBig>(c);
_cr.emplace<Contact::Components::ParentOf>(c); // start empty
_cr.emplace<Contact::Components::ToxGroupEphemeral>(c, group_number);
_cr.emplace<Contact::Components::ToxGroupPersistent>(c, g_key);
_cr.emplace<Contact::Components::Name>(c, _t.toxGroupGetName(group_number).value_or("<unk>"));
_cr.emplace<Contact::Components::ConnectionState>(
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<Contact::Components::Self>(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<bool>(group_c));
// first check contacts with peer id
// TODO: lookup table
//_cr.view<Contact::Components::ToxGroupPeerEphemeral>().each([&c, group_number, peer_number](const Contact3 e, const Contact::Components::ToxGroupPeerEphemeral& p_comp) {
for (const auto e : _cr.view<Contact::Components::ToxGroupPeerEphemeral>()) {
const auto& p_comp = _cr.get<Contact::Components::ToxGroupPeerEphemeral>(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<Contact::Components::ToxGroupPersistent>().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<Contact::Components::ToxGroupPeerPersistent>().each([&c, &g_key, &g_p_key](const Contact3 e, const Contact::Components::ToxGroupPeerPersistent& g_p_key_comp) {
for (const auto e : _cr.view<Contact::Components::ToxGroupPeerPersistent>()) {
const auto& g_p_key_comp = _cr.get<Contact::Components::ToxGroupPeerPersistent>(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<Contact::Components::Parent>(c, group_c);
{ // add sub to parent
auto& parent_sub_list = group_c.get_or_emplace<Contact::Components::ParentOf>().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<Contact::Components::ContactModel>(c, this);
_cr.emplace<Contact::Components::ToxGroupPeerEphemeral>(c, group_number, peer_number);
_cr.emplace<Contact::Components::ToxGroupPeerPersistent>(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<Contact::Components::Name>(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<Contact::Components::TagSelfStrong>(c);
} else {
_cr.emplace<Contact::Components::Self>(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<bool>(group_c));
const auto& g_key = group_c.get<Contact::Components::ToxGroupPersistent>().chat_id;
// search by key
//_cr.view<Contact::Components::ToxGroupPeerPersistent>().each([&c, &g_key, &peer_key](const Contact3 e, const Contact::Components::ToxGroupPeerPersistent& g_p_key_comp) {
for (const auto e : _cr.view<Contact::Components::ToxGroupPeerPersistent>()) {
const auto& g_p_key_comp = _cr.get<Contact::Components::ToxGroupPeerPersistent>(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<Contact::Components::Parent>(c, group_c);
_cr.emplace<Contact::Components::ContactModel>(c, this);
//_cr.emplace<Contact::Components::ToxGroupPeerEphemeral>(c, group_number, peer_number);
_cr.emplace<Contact::Components::ToxGroupPeerPersistent>(c, g_key, peer_key);
//_cr.emplace<Contact::Components::Name>(c, "<unk>");
//_cr.emplace<Contact::Components::Name>(c, std::get<0>(_t.toxGroupPeerGetName(group_number, peer_number)).value_or("<unk>"));
{ // self
// TODO: this is very flaky
auto self_number_opt = _t.toxGroupSelfGetPeerId(group_number);
_cr.emplace<Contact::Components::Self>(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<Contact::Components::ConnectionState>(
(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<Contact::Components::ToxFriendEphemeral>();
}
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<const char*>(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<Contact::Components::Name>(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<Contact::Components::TagSelfStrong>();
c.emplace_or_replace<Contact::Components::ConnectionState>(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<Contact::Components::ToxGroupPeerEphemeral>(group_number, peer_number);
auto [peer_state_opt, _] = _t.toxGroupPeerGetConnectionStatus(group_number, peer_number);
c.emplace_or_replace<Contact::Components::ConnectionState>(
(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<Contact::Components::Name>(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<bool>(c)) {
return false; // we dont track this contact ?????
}
c.emplace_or_replace<Contact::Components::ConnectionState>(Contact::Components::ConnectionState::State::disconnected);
c.remove<Contact::Components::ToxGroupPeerEphemeral>();
// 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<const char*>(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<Contact::Components::Name>(std::string{name});
return false;
}

View File

@ -0,0 +1,45 @@
#pragma once
#include <solanaceae/toxcore/tox_event_interface.hpp>
#include <solanaceae/contact/contact_model3.hpp>
#include <solanaceae/toxcore/tox_key.hpp>
// fwd
struct ToxI;
// tox contact model for ContactModel3I
class ToxContactModel2 : public ContactModel3I, public ToxEventI {
Contact3Registry& _cr;
ToxI& _t;
ToxEventProviderI& _tep;
Contact3 _friend_self;
public:
ToxContactModel2(Contact3Registry& cr, ToxI& t, ToxEventProviderI& tep);
virtual ~ToxContactModel2(void) {}
// TODO: continually fetch group peer connection state, since JF does not want to add cb/event
//void iterate(void);
public: // util for tox code
// also creates if non existant
Contact3Handle getContactFriend(uint32_t friend_number);
Contact3Handle getContactGroup(uint32_t group_number);
Contact3Handle getContactGroupPeer(uint32_t group_number, uint32_t peer_number);
//Contact3Handle getContactGroupPeer(const ToxKey& group_key, const ToxKey& peer_key);
Contact3Handle getContactGroupPeer(uint32_t group_number, const ToxKey& peer_key);
protected: // tox events
bool onToxEvent(const Tox_Event_Friend_Connection_Status* e) override;
bool onToxEvent(const Tox_Event_Friend_Status* e) override;
bool onToxEvent(const Tox_Event_Friend_Name* e) override;
bool onToxEvent(const Tox_Event_Group_Self_Join* e) override;
bool onToxEvent(const Tox_Event_Group_Peer_Join* e) override;
bool onToxEvent(const Tox_Event_Group_Peer_Exit* e) override;
bool onToxEvent(const Tox_Event_Group_Peer_Name* e) override;
};