Compare commits

...

20 Commits

Author SHA1 Message Date
9e42508428 port to contact4 2025-03-10 20:40:25 +01:00
e77e57fa78 fix and update nix flake 2025-01-12 21:26:37 +01:00
bc09f94a70 pkgconf for windows ci/cd 2025-01-12 16:32:08 +01:00
7f07b90a79 fix toxcore cmake (for the most part) 2025-01-12 16:13:46 +01:00
f160ad1e0b change cmake to align it closer to tomato
this is unlikely to compile outside of ecosystem !
2025-01-12 15:44:19 +01:00
754310ef00 use sr 2024-10-25 13:04:12 +02:00
1a032ce260 update to rmmi 2024-10-06 11:51:02 +02:00
77fffb744e fix for new tox transfers ctr 2024-07-31 18:35:08 +02:00
4a59a83eca init name from config if empty 2024-06-12 21:06:24 +02:00
bb9da772bc correct fwd decls 2024-05-28 09:43:22 +02:00
446a4939f5 symbol visiblity and entt export 2024-05-19 11:48:03 +02:00
71d93329c5 add object store and provide to plugins 2024-04-12 22:53:40 +02:00
4b7ef79f38 improve tox client (prevent excessive saving)
and reorder construction/destruction order of plugin manager in main
2024-04-10 12:06:11 +02:00
4b39541d93 enable ngc tox saving again 2024-04-10 11:53:58 +02:00
618ed4915e also update cmake fetch (oops) 2024-04-03 16:27:47 +02:00
da9067ab34 update flake and deps 2024-04-03 15:55:13 +02:00
4e6193a09e update tox private impl 2024-04-03 14:50:06 +02:00
2c80406795 more cleanup and make mc configurable 2024-02-04 15:12:03 +01:00
e8e9126faa small main refactor + msg cleanser interval 2024-02-04 13:50:44 +01:00
80bf8f5afd remove entt from external, contact/messege3 manage it themself 2024-02-04 13:41:20 +01:00
23 changed files with 270 additions and 327 deletions

View File

@ -73,7 +73,7 @@ jobs:
submodules: recursive
- name: Install Dependencies
run: vcpkg install libsodium:x64-windows-static pthreads:x64-windows-static
run: vcpkg install pkgconf:x64-windows libsodium:x64-windows-static pthreads:x64-windows-static
# setup vs env
- uses: ilammy/msvc-dev-cmd@v1
@ -81,7 +81,7 @@ jobs:
arch: amd64
- name: Configure CMake
run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static
run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DPKG_CONFIG_EXECUTABLE=C:/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4

View File

@ -59,7 +59,7 @@ jobs:
submodules: recursive
- name: Install Dependencies
run: vcpkg install libsodium:x64-windows-static pthreads:x64-windows-static
run: vcpkg install pkgconf:x64-windows libsodium:x64-windows-static pthreads:x64-windows-static
# setup vs env
- uses: ilammy/msvc-dev-cmd@v1
@ -67,7 +67,7 @@ jobs:
arch: amd64
- name: Configure CMake
run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static
run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DPKG_CONFIG_EXECUTABLE=C:/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4

View File

@ -2,16 +2,6 @@ cmake_minimum_required(VERSION 3.14...3.24 FATAL_ERROR)
include(FetchContent)
# TODO: move entt dep into solanaceae_contact
if (NOT TARGET EnTT::EnTT)
FetchContent_Declare(EnTT
GIT_REPOSITORY https://github.com/skypjack/entt.git
GIT_TAG v3.12.2
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(EnTT)
endif()
if (NOT TARGET solanaceae_util)
FetchContent_Declare(solanaceae_util
GIT_REPOSITORY https://github.com/Green-Sky/solanaceae_util.git
@ -48,6 +38,15 @@ if (NOT TARGET solanaceae_plugin)
FetchContent_MakeAvailable(solanaceae_plugin)
endif()
if (NOT TARGET solanaceae_object_store)
FetchContent_Declare(solanaceae_object_store
GIT_REPOSITORY https://github.com/Green-Sky/solanaceae_object_store.git
GIT_TAG master
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(solanaceae_object_store)
endif()
add_subdirectory(./toxcore)
if (NOT TARGET solanaceae_toxcore)

View File

@ -1,169 +1,54 @@
cmake_minimum_required(VERSION 3.14...3.24 FATAL_ERROR)
cmake_minimum_required(VERSION 3.13...3.16 FATAL_ERROR)
if (NOT TARGET toxcore)
# for find sodium
# HACK: support old libsodium find
# libs should handle this case themselfs
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
set(EXPERIMENTAL_API ON CACHE BOOL "" FORCE)
set(UNITTEST OFF CACHE BOOL "" FORCE)
set(BOOTSTRAP_DAEMON OFF CACHE BOOL "" FORCE)
#if (TOMATO_TOX_AV)
# set(BUILD_TOXAV ON CACHE BOOL "" FORCE)
# set(MUST_BUILD_TOXAV ON CACHE BOOL "" FORCE)
#endif()
include(FetchContent)
FetchContent_Declare(toxcore
# waiting for events on master
GIT_REPOSITORY https://github.com/Green-Sky/c-toxcore.git
GIT_TAG ngc_events
GIT_REPOSITORY https://github.com/TokTok/c-toxcore.git
GIT_TAG v0.2.20
EXCLUDE_FROM_ALL
)
FetchContent_GetProperties(toxcore)
if(NOT toxcore_POPULATED)
FetchContent_Populate(toxcore)
# pupulates and adds cmake subdir
FetchContent_MakeAvailable(toxcore)
# TODO: should this all be in the populated if??
#set(TOX_DIR "${CMAKE_CURRENT_SOURCE_DIR}/c-toxcore/")
set(TOX_DIR "${toxcore_SOURCE_DIR}/")
# TODO: shared
add_library(toxcore STATIC
${TOX_DIR}third_party/cmp/cmp.c
${TOX_DIR}third_party/cmp/cmp.h
#message("II BUILD_TOXAV: ${BUILD_TOXAV}")
${TOX_DIR}toxcore/announce.c
${TOX_DIR}toxcore/announce.h
${TOX_DIR}toxcore/bin_pack.c
${TOX_DIR}toxcore/bin_pack.h
${TOX_DIR}toxcore/bin_unpack.c
${TOX_DIR}toxcore/bin_unpack.h
${TOX_DIR}toxcore/ccompat.c
${TOX_DIR}toxcore/ccompat.h
${TOX_DIR}toxcore/crypto_core.c
${TOX_DIR}toxcore/crypto_core.h
${TOX_DIR}toxcore/DHT.c
${TOX_DIR}toxcore/DHT.h
${TOX_DIR}toxcore/events/conference_connected.c
${TOX_DIR}toxcore/events/conference_invite.c
${TOX_DIR}toxcore/events/conference_message.c
${TOX_DIR}toxcore/events/conference_peer_list_changed.c
${TOX_DIR}toxcore/events/conference_peer_name.c
${TOX_DIR}toxcore/events/conference_title.c
${TOX_DIR}toxcore/events/events_alloc.c
${TOX_DIR}toxcore/events/events_alloc.h
${TOX_DIR}toxcore/events/file_chunk_request.c
${TOX_DIR}toxcore/events/file_recv.c
${TOX_DIR}toxcore/events/file_recv_chunk.c
${TOX_DIR}toxcore/events/file_recv_control.c
${TOX_DIR}toxcore/events/friend_connection_status.c
${TOX_DIR}toxcore/events/friend_lossless_packet.c
${TOX_DIR}toxcore/events/friend_lossy_packet.c
${TOX_DIR}toxcore/events/friend_message.c
${TOX_DIR}toxcore/events/friend_name.c
${TOX_DIR}toxcore/events/friend_read_receipt.c
${TOX_DIR}toxcore/events/friend_request.c
${TOX_DIR}toxcore/events/friend_status.c
${TOX_DIR}toxcore/events/friend_status_message.c
${TOX_DIR}toxcore/events/friend_typing.c
${TOX_DIR}toxcore/events/self_connection_status.c
${TOX_DIR}toxcore/events/group_custom_packet.c
${TOX_DIR}toxcore/events/group_custom_private_packet.c
${TOX_DIR}toxcore/events/group_invite.c
${TOX_DIR}toxcore/events/group_join_fail.c
${TOX_DIR}toxcore/events/group_message.c
${TOX_DIR}toxcore/events/group_moderation.c
${TOX_DIR}toxcore/events/group_password.c
${TOX_DIR}toxcore/events/group_peer_exit.c
${TOX_DIR}toxcore/events/group_peer_join.c
${TOX_DIR}toxcore/events/group_peer_limit.c
${TOX_DIR}toxcore/events/group_peer_name.c
${TOX_DIR}toxcore/events/group_peer_status.c
${TOX_DIR}toxcore/events/group_privacy_state.c
${TOX_DIR}toxcore/events/group_private_message.c
${TOX_DIR}toxcore/events/group_self_join.c
${TOX_DIR}toxcore/events/group_topic.c
${TOX_DIR}toxcore/events/group_topic_lock.c
${TOX_DIR}toxcore/events/group_voice_state.c
${TOX_DIR}toxcore/forwarding.c
${TOX_DIR}toxcore/forwarding.h
${TOX_DIR}toxcore/friend_connection.c
${TOX_DIR}toxcore/friend_connection.h
${TOX_DIR}toxcore/friend_requests.c
${TOX_DIR}toxcore/friend_requests.h
${TOX_DIR}toxcore/group.c
${TOX_DIR}toxcore/group.h
${TOX_DIR}toxcore/group_announce.c
${TOX_DIR}toxcore/group_announce.h
${TOX_DIR}toxcore/group_moderation.c
${TOX_DIR}toxcore/group_moderation.h
${TOX_DIR}toxcore/group_chats.c
${TOX_DIR}toxcore/group_chats.h
${TOX_DIR}toxcore/group_common.h
${TOX_DIR}toxcore/group_connection.c
${TOX_DIR}toxcore/group_connection.h
${TOX_DIR}toxcore/group_onion_announce.c
${TOX_DIR}toxcore/group_onion_announce.h
${TOX_DIR}toxcore/group_pack.c
${TOX_DIR}toxcore/group_pack.h
${TOX_DIR}toxcore/LAN_discovery.c
${TOX_DIR}toxcore/LAN_discovery.h
${TOX_DIR}toxcore/list.c
${TOX_DIR}toxcore/list.h
${TOX_DIR}toxcore/logger.c
${TOX_DIR}toxcore/logger.h
${TOX_DIR}toxcore/Messenger.c
${TOX_DIR}toxcore/Messenger.h
${TOX_DIR}toxcore/mem.c
${TOX_DIR}toxcore/mem.h
${TOX_DIR}toxcore/mono_time.c
${TOX_DIR}toxcore/mono_time.h
${TOX_DIR}toxcore/net_crypto.c
${TOX_DIR}toxcore/net_crypto.h
${TOX_DIR}toxcore/network.c
${TOX_DIR}toxcore/network.h
${TOX_DIR}toxcore/onion_announce.c
${TOX_DIR}toxcore/onion_announce.h
${TOX_DIR}toxcore/onion.c
${TOX_DIR}toxcore/onion_client.c
${TOX_DIR}toxcore/onion_client.h
${TOX_DIR}toxcore/onion.h
${TOX_DIR}toxcore/ping_array.c
${TOX_DIR}toxcore/ping_array.h
${TOX_DIR}toxcore/ping.c
${TOX_DIR}toxcore/ping.h
${TOX_DIR}toxcore/shared_key_cache.c
${TOX_DIR}toxcore/shared_key_cache.h
${TOX_DIR}toxcore/state.c
${TOX_DIR}toxcore/state.h
${TOX_DIR}toxcore/TCP_client.c
${TOX_DIR}toxcore/TCP_client.h
${TOX_DIR}toxcore/TCP_common.c
${TOX_DIR}toxcore/TCP_common.h
${TOX_DIR}toxcore/TCP_connection.c
${TOX_DIR}toxcore/TCP_connection.h
${TOX_DIR}toxcore/TCP_server.c
${TOX_DIR}toxcore/TCP_server.h
${TOX_DIR}toxcore/timed_auth.c
${TOX_DIR}toxcore/timed_auth.h
${TOX_DIR}toxcore/tox_api.c
${TOX_DIR}toxcore/tox.c
${TOX_DIR}toxcore/tox_dispatch.c
${TOX_DIR}toxcore/tox_dispatch.h
${TOX_DIR}toxcore/tox_event.c
${TOX_DIR}toxcore/tox_event.h
${TOX_DIR}toxcore/tox_events.c
${TOX_DIR}toxcore/tox_events.h
${TOX_DIR}toxcore/tox.h
${TOX_DIR}toxcore/tox_private.c
${TOX_DIR}toxcore/tox_private.h
${TOX_DIR}toxcore/tox_unpack.c
${TOX_DIR}toxcore/tox_unpack.h
${TOX_DIR}toxcore/util.c
${TOX_DIR}toxcore/util.h
# the sad case
add_library(toxcore INTERFACE)
${TOX_DIR}toxencryptsave/defines.h
${TOX_DIR}toxencryptsave/toxencryptsave.c
${TOX_DIR}toxencryptsave/toxencryptsave.h
)
if (TARGET toxcore_static)
target_link_libraries(toxcore INTERFACE toxcore_static)
# TODO: add the others
# the ideal case
#add_library(toxcore ALIAS toxcore_static)
else()
target_link_libraries(toxcore INTERFACE toxcore_shared)
# the ideal case
#add_library(toxcore ALIAS toxcore_shared)
endif()
# HACK: "install" api headers into binary dir
configure_file(
${TOX_DIR}toxcore/tox.h
${toxcore_BINARY_DIR}/include/tox/tox.h
@ -179,54 +64,37 @@ if (NOT TARGET toxcore)
${toxcore_BINARY_DIR}/include/tox/tox_private.h
@ONLY
)
configure_file(
${TOX_DIR}toxencryptsave/toxencryptsave.h
${toxcore_BINARY_DIR}/include/tox/toxencryptsave.h
@ONLY
)
#configure_file(
# ${TOX_DIR}toxav/toxav.h
# ${toxcore_BINARY_DIR}/include/tox/toxav.h
# @ONLY
#)
target_include_directories(toxcore PRIVATE "${TOX_DIR}toxcore")
target_include_directories(toxcore INTERFACE "${TOX_DIR}") #toxencryptsave
target_include_directories(toxcore PUBLIC "${toxcore_BINARY_DIR}/include/")
target_include_directories(toxcore INTERFACE ${toxcore_BINARY_DIR}/include/)
target_compile_definitions(toxcore PUBLIC USE_IPV6=1)
#target_compile_definitions(toxcore PUBLIC MIN_LOGGER_LEVEL=LOGGER_LEVEL_DEBUG)
target_compile_definitions(toxcore PUBLIC MIN_LOGGER_LEVEL=LOGGER_LEVEL_INFO)
find_package(unofficial-sodium CONFIG QUIET)
find_package(sodium QUIET)
if(unofficial-sodium_FOUND) # vcpkg
if(TARGET unofficial-sodium::sodium)
target_link_libraries(toxcore unofficial-sodium::sodium)
target_link_libraries(toxcore INTERFACE unofficial-sodium::sodium)
endif()
if(TARGET unofficial-sodium::sodium_config_public)
target_link_libraries(toxcore unofficial-sodium::sodium_config_public)
target_link_libraries(toxcore INTERFACE unofficial-sodium::sodium_config_public)
endif()
elseif(sodium_FOUND)
target_link_libraries(toxcore sodium)
target_link_libraries(toxcore INTERFACE sodium)
else()
message(SEND_ERROR "missing libsodium")
endif()
if(WIN32)
target_link_libraries(toxcore ws2_32 iphlpapi)
endif()
find_package(pthreads QUIET)
if(TARGET PThreads4W::PThreads4W)
target_link_libraries(toxcore PThreads4W::PThreads4W)
else()
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(toxcore Threads::Threads)
endif()
add_executable(DHT_Bootstrap EXCLUDE_FROM_ALL
${TOX_DIR}other/DHT_bootstrap.c
${TOX_DIR}other/bootstrap_node_packets.h
${TOX_DIR}other/bootstrap_node_packets.c
${TOX_DIR}testing/misc_tools.h
${TOX_DIR}testing/misc_tools.c
)
target_link_libraries(DHT_Bootstrap toxcore)
#if(BUILD_TOXAV)
# set_target_properties(toxcore PROPERTIES TOX_HAS_TOXAV ON)
#endif()
endif(NOT toxcore_POPULATED)
endif(NOT TARGET toxcore)

8
flake.lock generated
View File

@ -20,16 +20,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1694553957,
"narHash": "sha256-8o15HEax53lBJjjcr5VHMpuuT6vBcrzSNB6y2iGlPaU=",
"lastModified": 1736692550,
"narHash": "sha256-7tk8xH+g0sJkKLTJFOxphJxxOjMDFMWv24nXslaU2ro=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e7fe745d22df5fa282b321e577fe18d4f62e0f0b",
"rev": "7c4869c47090dd7f9f1bdfb49a22aea026996815",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-23.05",
"ref": "release-24.11",
"repo": "nixpkgs",
"type": "github"
}

View File

@ -4,7 +4,7 @@
# https://youtu.be/7ZeTP_S6ZVI
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/release-23.05";
nixpkgs.url = "github:NixOS/nixpkgs/release-24.11";
flake-utils.url = "github:numtide/flake-utils";
};
@ -18,8 +18,8 @@
toxcore-src = pkgs.fetchFromGitHub {
owner = "TokTok"; repo = "c-toxcore";
fetchSubmodules = true;
rev = "f1df709b8792da4c0e946d826b11df77d565064d"; # iphydf:ordered-events pr
hash = "sha256-P3+Y+IBy932TtR+j25G6yzQqfDLvdLv/c+9fvF1b5LY=";
rev = "v0.2.20";
hash = "sha256-jJk3K1wDG84NtMQBmNvSDRMPYUsYIzjE0emiZ4Ugbhk=";
};
entt-src = pkgs.fetchFromGitHub {
owner = "skypjack"; repo = "entt";
@ -28,33 +28,38 @@
};
solanaceae_util-src = pkgs.fetchFromGitHub {
owner = "Green-Sky"; repo = "solanaceae_util";
rev = "2b20c2d2a45ad1005e794c704b3fc831ca1d3830";
hash = "sha256-eLDIFQOA3NjdjK1MPpdNJl9mYQgtJK9IV4CaOyenae8=";
rev = "6cbcc9463ce3c4344e06d74d7df67175ada83b5f";
hash = "sha256-puWLMzCWLBQEsslIJE9sGA7fqp8v93J4zixyu64TqhY=";
};
solanaceae_contact-src = pkgs.fetchFromGitHub {
owner = "Green-Sky"; repo = "solanaceae_contact";
rev = "2d73c7272c3c254086fa067ccfdfb4072c20f7c9";
hash = "sha256-t6UWKMvWqXGvmsjx1JSc6fOawrzzXndaOpBntoKhLW0=";
rev = "e2917c497c91f91f8febcd1f43e462fad8359305";
hash = "sha256-gM9aT+lk+TmgiLyIeaGvHSpo55C+RNEnJvzFPLXrVMc=";
};
solanaceae_message3-src = pkgs.fetchFromGitHub {
owner = "Green-Sky"; repo = "solanaceae_message3";
rev = "1a036c2321e06d4c36f3e2148e67dfe6aa379296";
hash = "sha256-vcUN7tsy6E1P5juhW7pj9OUtZLytZcpPijlIj/iTBgk=";
rev = "e55fb46027f16a1bc078f797ae9fcc7609d15659";
hash = "sha256-BdwHBCXWLiVL26djyasQEPW1PbwKCkeXdUivkPaQD3c=";
};
solanaceae_plugin-src = pkgs.fetchFromGitHub {
owner = "Green-Sky"; repo = "solanaceae_plugin";
rev = "96bab0200f5b13671756abe7e3132ed78aaa2a40";
hash = "sha256-UTOdTiZLsiFs/3SZdrxfynX9OYVlDXEeG6VFIHXIxlA=";
rev = "54cd23433df4acedede51e932f27d16fe4f35548";
hash = "sha256-Yy58w1PJFzIiN8kjqe7zerG9HewcdlBcN9P/YsjFCQs=";
};
solanaceae_toxcore-src = pkgs.fetchFromGitHub {
owner = "Green-Sky"; repo = "solanaceae_toxcore";
rev = "54084b5a53e1617ff9b0c225880b0f1d60fe65ea";
hash = "sha256-yuOuZZAacnUUPbxL01sUx/r210E8TJQfgVZms6YROVk=";
rev = "727c341899a82c911a27a5cac6d09bb23ce06b1d";
hash = "sha256-pI0ZKX6h/DMC9m0z4yC38kqGRP34ES12U9LcuO14fO0=";
};
solanaceae_tox-src = pkgs.fetchFromGitHub {
owner = "Green-Sky"; repo = "solanaceae_tox";
rev = "c01d91144ce10486ff6e98a2e6e8cc5e20a5c412";
hash = "sha256-oCDx/bK433AZ5+xx0kq/NudylTMQK+6ycddaQRse/+0=";
rev = "8ad10978b96837eb7949f32ef433c5b37c2aa458";
hash = "sha256-0eV8tf1CDG1xHxKmDHBMSP/5hMoB2qRagOj1meHcEFY=";
};
solanaceae_object_store-src = pkgs.fetchFromGitHub {
owner = "Green-Sky"; repo = "solanaceae_object_store";
rev = "18d2888e3452074245375f329d90520ac250b595";
hash = "sha256-vfFepPHj58c8YBSa8G8bJjD6gcCj65T0Kx5aEhqsjok=";
};
pname = "totato";
@ -78,7 +83,9 @@
"-DFETCHCONTENT_SOURCE_DIR_SOLANACEAE_PLUGIN=${solanaceae_plugin-src}"
"-DFETCHCONTENT_SOURCE_DIR_SOLANACEAE_TOXCORE=${solanaceae_toxcore-src}"
"-DFETCHCONTENT_SOURCE_DIR_SOLANACEAE_TOX=${solanaceae_tox-src}"
"-DFETCHCONTENT_SOURCE_DIR_SOLANACEAE_OBJECT_STORE=${solanaceae_object_store-src}"
"-DFETCHCONTENT_SOURCE_DIR_JSON=${pkgs.nlohmann_json.src}" # we care less about version here
"-DFETCHCONTENT_SOURCE_DIR_ZSTD=${pkgs.zstd.src}"
];
# TODO: replace with install command

View File

@ -25,10 +25,15 @@ add_executable(totato
./fun_commands.cpp
)
set_target_properties(totato PROPERTIES POSITION_INDEPENDENT_CODE ON)
# probably not enough
target_compile_definitions(totato PUBLIC ENTT_API_EXPORT)
target_compile_features(totato PUBLIC cxx_std_17)
target_link_libraries(totato PUBLIC
solanaceae_util
solanaceae_contact
solanaceae_contact_impl
solanaceae_message3
solanaceae_plugin
@ -37,6 +42,8 @@ target_link_libraries(totato PUBLIC
solanaceae_tox_contacts
solanaceae_tox_messages
solanaceae_object_store
nlohmann_json::nlohmann_json
)

View File

@ -2,21 +2,18 @@
#include "./tox_client.hpp"
AutoDirty::AutoDirty(ToxClient& tc) : _tc(tc), _tep_sr(_tc.newSubRef(this)) {
// TODO: add more events
void AutoDirty::subscribe(void) {
_tc.subscribe(this, Tox_Event_Type::TOX_EVENT_SELF_CONNECTION_STATUS);
_tc.subscribe(this, Tox_Event_Type::TOX_EVENT_FRIEND_CONNECTION_STATUS);
_tc.subscribe(this, Tox_Event_Type::TOX_EVENT_FRIEND_REQUEST);
_tc.subscribe(this, Tox_Event_Type::TOX_EVENT_GROUP_INVITE);
_tc.subscribe(this, Tox_Event_Type::TOX_EVENT_GROUP_SELF_JOIN);
_tc.subscribe(this, Tox_Event_Type::TOX_EVENT_GROUP_PEER_JOIN);
_tc.subscribe(this, Tox_Event_Type::TOX_EVENT_GROUP_PEER_EXIT);
_tc.subscribe(this, Tox_Event_Type::TOX_EVENT_CONFERENCE_INVITE);
}
AutoDirty::AutoDirty(ToxClient& tc) : _tc(tc) {
subscribe();
_tep_sr
.subscribe(Tox_Event_Type::TOX_EVENT_SELF_CONNECTION_STATUS)
.subscribe(Tox_Event_Type::TOX_EVENT_FRIEND_CONNECTION_STATUS)
.subscribe(Tox_Event_Type::TOX_EVENT_FRIEND_REQUEST)
.subscribe(Tox_Event_Type::TOX_EVENT_GROUP_INVITE)
.subscribe(Tox_Event_Type::TOX_EVENT_GROUP_SELF_JOIN)
.subscribe(Tox_Event_Type::TOX_EVENT_GROUP_PEER_JOIN)
.subscribe(Tox_Event_Type::TOX_EVENT_GROUP_PEER_EXIT)
.subscribe(Tox_Event_Type::TOX_EVENT_CONFERENCE_INVITE)
;
}
bool AutoDirty::onToxEvent(const Tox_Event_Self_Connection_Status*) {

View File

@ -8,8 +8,7 @@ class ToxClient;
// sets ToxClient dirty on some events
class AutoDirty : public ToxEventI {
ToxClient& _tc;
void subscribe(void); // private
ToxEventProviderI::SubscriptionReference _tep_sr;
public:
AutoDirty(ToxClient& tc);

View File

@ -1,6 +1,6 @@
#include "./managment_commands.hpp"
#include <solanaceae/contact/contact_model3.hpp>
#include <solanaceae/contact/contact_store_i.hpp>
#include <solanaceae/util/config_model.hpp>
#include <solanaceae/message3/message_command_dispatcher.hpp>
@ -13,8 +13,8 @@
void registerConfigCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
Contact3Registry& cr,
RegistryMessageModel& rmm
ContactStore4I& cs,
RegistryMessageModelI& rmm
) {
mcd.registerCommand(
"Config", "conf",

View File

@ -1,6 +1,6 @@
#pragma once
#include <solanaceae/contact/contact_model3.hpp>
#include <solanaceae/contact/fwd.hpp>
#include <solanaceae/message3/registry_message_model.hpp>
// fwd
@ -10,7 +10,7 @@ struct ConfigModelI;
void registerConfigCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
Contact3Registry& cr,
RegistryMessageModel& rmm
ContactStore4I& cs,
RegistryMessageModelI& rmm
);

View File

@ -1,19 +1,20 @@
#include "./managment_commands.hpp"
#include <random>
#include <solanaceae/contact/contact_model3.hpp>
//#include <solanaceae/util/config_model.hpp>
#include <solanaceae/contact/contact_store_i.hpp>
#include <solanaceae/message3/message_command_dispatcher.hpp>
#include <solanaceae/message3/components.hpp>
//#include <solanaceae/contact/components.hpp>
#include <solanaceae/message3/components.hpp>
#include <random>
void registerFunCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
Contact3Registry& cr,
RegistryMessageModel& rmm
ContactStore4I& cs,
RegistryMessageModelI& rmm
) {
mcd.registerCommand(
"fun", "fun",

View File

@ -1,6 +1,5 @@
#pragma once
#include <solanaceae/contact/contact_model3.hpp>
#include <solanaceae/message3/registry_message_model.hpp>
// fwd
@ -10,7 +9,7 @@ struct ConfigModelI;
void registerFunCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
Contact3Registry& cr,
RegistryMessageModel& rmm
ContactStore4I& cs,
RegistryMessageModelI& rmm
);

View File

@ -1,6 +1,7 @@
#include <solanaceae/object_store/object_store.hpp>
#include <solanaceae/util/simple_config_model.hpp>
#include <solanaceae/contact/contact_model3.hpp>
#include <solanaceae/message3/registry_message_model.hpp>
#include <solanaceae/contact/contact_store_impl.hpp>
#include <solanaceae/message3/registry_message_model_impl.hpp>
#include <solanaceae/message3/message_time_sort.hpp>
#include <solanaceae/plugin/plugin_manager.hpp>
#include <solanaceae/toxcore/tox_event_logger.hpp>
@ -28,6 +29,9 @@
#include <nlohmann/json.hpp>
#include <entt/entt.hpp>
#include <entt/fwd.hpp>
#include <cstdint>
#include <cmath>
#include <chrono>
@ -44,6 +48,16 @@
#include <signal.h>
#endif
// why is min not variadic?
template<typename... T>
float min_var(float v0, T... args) {
if constexpr (sizeof...(args) == 0) {
return v0;
} else {
return std::min(v0, min_var(args...));
}
}
std::atomic_bool quit = false;
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) || defined (_WIN32)
@ -127,6 +141,8 @@ int main(int argc, char** argv) {
const auto started_at = std::chrono::steady_clock::now();
auto last_time_tick = std::chrono::steady_clock::now();
ObjectStore2 os;
std::string config_path {"config.json"};
// totato <config.json> -p <path/to/plugin.so>
@ -169,11 +185,11 @@ int main(int argc, char** argv) {
// TODO: name
}
Contact3Registry cr;
RegistryMessageModel rmm{cr};
ContactStore4Impl cs;
RegistryMessageModelImpl rmm{cs};
MessageTimeSort mts{rmm};
MessageCleanser mc{cr, rmm};
MessageCommandDispatcher mcd{cr, rmm, conf};
MessageCleanser mc{cs, rmm, conf};
MessageCommandDispatcher mcd{cs, rmm, conf};
{ // setup basic commands for bot
mcd.registerCommand(
@ -188,17 +204,16 @@ int main(int argc, char** argv) {
}
PluginManager pm;
ToxEventLogger tel{std::cout}; // TODO: config
// TODO: password?
ToxClient tc{conf, conf.get_string("tox", "save_file_path").value(), ""};
tel.subscribeAll(tc);
ToxEventLogger tel{tc, std::cout}; // TODO: config
tel.subscribeAll();
{ // name stuff
auto name = tc.toxSelfGetName();
if (name.empty()) {
name = "totato";
name = conf.get_string("tox", "name").value_or("totato");
}
conf.set("tox", "name", name);
tc.setSelfName(name); // TODO: this is ugly
@ -208,14 +223,18 @@ int main(int argc, char** argv) {
ToxPrivateImpl tpi{tc.getTox()};
AutoDirty ad{tc};
ToxContactModel2 tcm{cr, tc, tc};
ToxMessageManager tmm{rmm, cr, tcm, tc, tc};
ToxTransferManager ttm{rmm, cr, tcm, tc, tc};
ToxContactModel2 tcm{cs, tc, tc};
ToxMessageManager tmm{rmm, cs, tcm, tc, tc};
ToxTransferManager ttm{rmm, cs, tcm, tc, tc, os};
PluginManager pm;
{ // setup plugin instances
g_provideInstance<ObjectStore2>("ObjectStore2", "host", &os);
g_provideInstance<ConfigModelI>("ConfigModelI", "host", &conf);
g_provideInstance<Contact3Registry>("Contact3Registry", "1", "host", &cr);
g_provideInstance<RegistryMessageModel>("RegistryMessageModel", "host", &rmm);
g_provideInstance<ContactStore4I>("ContactStore4I", "host", &cs);
g_provideInstance<RegistryMessageModelI>("RegistryMessageModelI", "host", &rmm);
g_provideInstance<MessageCommandDispatcher>("MessageCommandDispatcher", "host", &mcd);
g_provideInstance<ToxI>("ToxI", "host", &tc);
@ -248,11 +267,11 @@ int main(int argc, char** argv) {
}
}
registerManagementCommands(mcd, conf, cr, rmm);
registerManagementCommands(mcd, conf, cs, rmm);
// TODO: finish impl
//registerConfigCommands(mcd, conf, cr, rmm);
registerToxCommands(mcd, conf, cr, rmm, tc, tpi);
registerFunCommands(mcd, conf, cr, rmm);
//registerConfigCommands(mcd, conf, cs, rmm);
registerToxCommands(mcd, conf, cs, rmm, tc, tpi);
registerFunCommands(mcd, conf, cs, rmm);
mcd.registerCommand(
"totato", "",
@ -303,26 +322,22 @@ int main(int argc, char** argv) {
const bool tick = time_delta_tick >= last_min_interval;
if (tick) {
quit = !tc.iterate();
quit = !tc.iterate(time_delta_tick);
tcm.iterate(time_delta_tick);
ttm.iterate();
mts.iterate();
const float pm_interval = pm.tick(time_delta_tick);
mc.iterate(time_delta_tick);
const float mc_interval = mc.iterate(time_delta_tick);
const float mcd_interval = mcd.iterate(time_delta_tick);
const float tox_interval = std::pow(tc.toxIterationInterval(), 1.6f) / 1000.f;
last_min_interval = std::min<float>(
tox_interval,
pm_interval
);
last_min_interval = std::min<float>(
last_min_interval,
mcd_interval
last_min_interval = min_var(
pm_interval,
mc_interval,
mcd_interval,
tox_interval
);
// dont sleep and do an extra check

View File

@ -1,6 +1,6 @@
#include "./managment_commands.hpp"
#include <solanaceae/contact/contact_model3.hpp>
#include <solanaceae/contact/contact_store_i.hpp>
#include <solanaceae/util/config_model.hpp>
#include <solanaceae/message3/message_command_dispatcher.hpp>
@ -12,25 +12,22 @@
#include <iostream>
#include <algorithm>
static std::optional<Contact3> getContactFromIDStr(
Contact3Registry& cr,
static std::optional<Contact4> getContactFromIDStr(
ContactStore4I& cs,
std::string_view id_str
) {
const std::vector<uint8_t> id = hex2bin(std::string{id_str});
const auto view = cr.view<Contact::Components::ID>();
const auto found_contact = std::find_if(view.begin(), view.end(), [&id, &view](const Contact3 c) -> bool {
return view.get<Contact::Components::ID>(c).data == id;
});
const std::vector<uint8_t> id = hex2bin(id_str);
const auto found_contact = cs.getOneContactByID(ByteSpan{id});
if (found_contact != view.end()) {
return *found_contact;
if (static_cast<bool>(found_contact)) {
return found_contact;
} else {
return std::nullopt;
}
}
static std::string getStatusFromContact(
Contact3Handle c
ContactHandle4 c
) {
std::string status_str;
@ -53,8 +50,8 @@ static std::string getStatusFromContact(
bool handleContactAddToGroup(
ConfigModelI& conf,
Contact3Registry& cr,
RegistryMessageModel& rmm,
ContactStore4I& cs,
RegistryMessageModelI& rmm,
std::string_view params,
Message3Handle m,
@ -73,7 +70,7 @@ bool handleContactAddToGroup(
return true;
}
const auto target_opt = getContactFromIDStr(cr, params);
const auto target_opt = getContactFromIDStr(cs, params);
if (!target_opt.has_value()) {
rmm.sendText(
contact_from,
@ -99,7 +96,7 @@ bool handleContactAddToGroup(
reply += "' to the ";
reply += group;
reply += "s.\n";
reply += getStatusFromContact(Contact3Handle{cr, target_opt.value()});
reply += getStatusFromContact(cs.contactHandle(target_opt.value()));
rmm.sendText(
contact_from,
@ -112,14 +109,14 @@ bool handleContactAddToGroup(
void registerManagementCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
Contact3Registry& cr,
RegistryMessageModel& rmm
ContactStore4I& cs,
RegistryMessageModelI& rmm
) {
mcd.registerCommand(
"Management", "manage",
"admin-add",
[&](std::string_view params, Message3Handle m) -> bool {
return handleContactAddToGroup(conf, cr, rmm, params, m, "admin");
return handleContactAddToGroup(conf, cs, rmm, params, m, "admin");
},
"Add an admin by ID.",
MessageCommandDispatcher::Perms::ADMIN
@ -129,7 +126,7 @@ void registerManagementCommands(
"Management", "manage",
"mod-add",
[&](std::string_view params, Message3Handle m) -> bool {
return handleContactAddToGroup(conf, cr, rmm, params, m, "moderator");
return handleContactAddToGroup(conf, cs, rmm, params, m, "moderator");
},
"Add a moderator by ID.",
MessageCommandDispatcher::Perms::ADMIN

View File

@ -1,6 +1,6 @@
#pragma once
#include <solanaceae/contact/contact_model3.hpp>
#include <solanaceae/contact/fwd.hpp>
#include <solanaceae/message3/registry_message_model.hpp>
// fwd
@ -10,7 +10,7 @@ struct ConfigModelI;
void registerManagementCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
Contact3Registry& cr,
RegistryMessageModel& rmm
ContactStore4I& cs,
RegistryMessageModelI& rmm
);

View File

@ -1,20 +1,28 @@
#include "./message_cleanser.hpp"
#include <solanaceae/contact/contact_store_i.hpp>
#include <solanaceae/contact/components.hpp>
#include <solanaceae/message3/components.hpp>
#include <solanaceae/util/utils.hpp>
#include <iostream>
#include <chrono>
#include <cstdint>
MessageCleanser::MessageCleanser(Contact3Registry& cr, RegistryMessageModel& rmm) : _cr(cr), _rmm(rmm) {
// config
// has MessageCleanser::old_age
MessageCleanser::MessageCleanser(
ContactStore4I& cs,
RegistryMessageModelI& rmm,
ConfigModelI& conf
) : _cs(cs), _rmm(rmm), _conf(conf) {
if (!_conf.has_int("MessageCleanser", "old_age_minutes")) {
_conf.set("MessageCleanser", "old_age_minutes", int64_t(_old_age_default));
}
}
MessageCleanser::~MessageCleanser(void) {
}
void MessageCleanser::iterate(float time_delta) {
float MessageCleanser::iterate(float time_delta) {
_timer += time_delta;
if (_timer >= _interval) {
_timer = 0.f;
@ -23,17 +31,29 @@ void MessageCleanser::iterate(float time_delta) {
uint64_t now_ts = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
uint64_t deleted_count{0};
const auto& cr = _cs.registry();
// TODO: iterate rmm directly
//_rmm.get();
// workaround by iterating contacts
for (const auto& c : _cr.view<Contact::Components::TagBig>()) {
for (const auto& c : cr.view<Contact::Components::TagBig>()) {
if (auto* reg = _rmm.get(c); reg != nullptr) {
float old_age {0.f};
{ // old age from conf
// TODO: find some way to extract this (maybe map into contact reg?)
if (const auto* id_comp = cr.try_get<Contact::Components::ID>(c); id_comp != nullptr) {
const auto id_hex = bin2hex(id_comp->data);
old_age = _conf.get_int("MessageCleanser", "old_age_minutes", id_hex).value_or(_old_age_default);
} else {
old_age = _conf.get_int("MessageCleanser", "old_age_minutes").value_or(_old_age_default);
}
old_age *= 60.f; // to sec
}
std::vector<Message3> to_remove;
// assuming all messages have Timestamp comp
reg->view<Message::Components::Timestamp>().each([this, now_ts, &to_remove](const Message3 ent, const Message::Components::Timestamp& ts) {
if (now_ts >= ts.ts + static_cast<uint64_t>(_old_age*1000.f)) {
reg->view<Message::Components::Timestamp>().each([this, now_ts, old_age, &to_remove](const Message3 ent, const Message::Components::Timestamp& ts) {
if (now_ts >= ts.ts + static_cast<uint64_t>(old_age*1000.f)) {
to_remove.push_back(ent);
}
});
@ -44,12 +64,13 @@ void MessageCleanser::iterate(float time_delta) {
_rmm.throwEventDestroy(*reg, c);
reg->destroy(c);
}
deleted_count += to_remove.size();
if (!to_remove.empty()) {
std::cout << "MC: cleaned up " << to_remove.size() << ", age >= " << old_age << "sec\n";
}
}
}
}
if (deleted_count > 0) {
std::cout << "MC: cleaned up " << deleted_count << "\n";
}
}
return _interval - _timer;
}

View File

@ -1,18 +1,25 @@
#pragma once
#include <solanaceae/message3/registry_message_model.hpp>
#include <solanaceae/util/config_model.hpp>
class MessageCleanser {
Contact3Registry& _cr;
RegistryMessageModel& _rmm;
ContactStore4I& _cs;
RegistryMessageModelI& _rmm;
ConfigModelI& _conf;
static constexpr int64_t _old_age_default{150}; // minutes
float _old_age{150.f*60.f}; // max 150min
float _interval{3.f*60.f}; // every 3min
float _timer{0.f};
public:
MessageCleanser(Contact3Registry& cr, RegistryMessageModel& rmm);
MessageCleanser(
ContactStore4I& cs,
RegistryMessageModelI& rmm,
ConfigModelI& conf
);
~MessageCleanser(void);
void iterate(float time_delta);
float iterate(float time_delta);
};

View File

@ -4,7 +4,7 @@
#include <exception>
#include <stdexcept>
#include <system_error>
#include <toxencryptsave/toxencryptsave.h>
#include <tox/toxencryptsave.h>
#include <sodium.h>
@ -102,6 +102,7 @@ ToxClient::ToxClient(ConfigModelI& conf, std::string_view save_path, std::string
tox_options_set_end_port(options, conf.get_int("tox", "end_port").value_or(0));
tox_options_set_tcp_port(options, conf.get_int("tox", "tcp_port").value_or(0));
tox_options_set_hole_punching_enabled(options, conf.get_bool("tox", "hole_punching_enabled").value_or(true));
tox_options_set_experimental_groups_persistence(options, true);
TOX_ERR_NEW err_new;
_tox = tox_new(options, &err_new);
@ -154,10 +155,13 @@ ToxClient::ToxClient(ConfigModelI& conf, std::string_view save_path, std::string
}
ToxClient::~ToxClient(void) {
if (_tox_profile_dirty) {
saveToxProfile();
}
tox_kill(_tox);
}
bool ToxClient::iterate(void) {
bool ToxClient::iterate(float time_delta) {
Tox_Err_Events_Iterate err_e_it = TOX_ERR_EVENTS_ITERATE_OK;
auto* events = tox_events_iterate(_tox, false, &err_e_it);
if (err_e_it == TOX_ERR_EVENTS_ITERATE_OK && events != nullptr) {
@ -169,7 +173,8 @@ bool ToxClient::iterate(void) {
tox_events_free(events);
if (_tox_profile_dirty) {
_save_heat -= time_delta;
if (_tox_profile_dirty && _save_heat <= 0.f) {
saveToxProfile();
}
@ -213,5 +218,6 @@ void ToxClient::saveToxProfile(void) {
}
_tox_profile_dirty = false;
_save_heat = 10.f;
}

View File

@ -24,6 +24,7 @@ class ToxClient : public ToxDefaultImpl, public ToxEventProviderBase {
std::string _tox_profile_path;
std::string _tox_profile_password;
bool _tox_profile_dirty {true}; // set in callbacks
float _save_heat {0.f};
public:
ToxClient(ConfigModelI& conf, std::string_view save_path, std::string_view save_password);
@ -35,7 +36,7 @@ class ToxClient : public ToxDefaultImpl, public ToxEventProviderBase {
void setDirty(void) { _tox_profile_dirty = true; }
// returns false when we shoul stop the program
bool iterate(void);
bool iterate(float time_delta);
void stop(void); // let it know it should exit
void setToxProfilePath(const std::string& new_path) { _tox_profile_path = new_path; }
@ -48,4 +49,3 @@ class ToxClient : public ToxDefaultImpl, public ToxEventProviderBase {
void saveToxProfile(void);
};

View File

@ -1,6 +1,6 @@
#include "./tox_commands.hpp"
#include <solanaceae/contact/contact_model3.hpp>
#include <solanaceae/contact/contact_store_i.hpp>
#include <solanaceae/util/config_model.hpp>
#include <solanaceae/util/utils.hpp>
#include <solanaceae/toxcore/tox_interface.hpp>
@ -17,8 +17,8 @@
void registerToxCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
Contact3Registry& cr,
RegistryMessageModel& rmm,
ContactStore4I& cs,
RegistryMessageModelI& rmm,
ToxI& t,
ToxPrivateI& tp
) {
@ -43,6 +43,8 @@ void registerToxCommands(
reply += "\ndht-closenum-announce-capable:";
reply += std::to_string(tp.toxDHTGetNumCloselistAnnounceCapable());
const auto& cr = cs.registry();
if (cr.all_of<Contact::Components::ToxFriendEphemeral>(contact_from)) {
const auto con_opt = t.toxFriendGetConnectionStatus(cr.get<Contact::Components::ToxFriendEphemeral>(contact_from).friend_number);
if (!con_opt.has_value() || con_opt.value() == Tox_Connection::TOX_CONNECTION_NONE) {
@ -174,6 +176,8 @@ void registerToxCommands(
return true;
}
const auto& cr = cs.registry();
// get current group
if (!cr.all_of<Contact::Components::Parent>(contact_from)) {
rmm.sendText(

View File

@ -1,19 +1,19 @@
#pragma once
#include <solanaceae/contact/contact_model3.hpp>
#include <solanaceae/contact/fwd.hpp>
#include <solanaceae/message3/registry_message_model.hpp>
// fwd
class MessageCommandDispatcher;
struct ConfigModelI;
class ToxI;
class ToxPrivateI;
struct ToxI;
struct ToxPrivateI;
void registerToxCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
Contact3Registry& cr,
RegistryMessageModel& rmm,
ContactStore4I& cs,
RegistryMessageModelI& rmm,
ToxI& t,
ToxPrivateI& tp
);

View File

@ -16,4 +16,20 @@ struct ToxPrivateImpl : public ToxPrivateI {
uint16_t toxDHTGetNumCloselistAnnounceCapable(void) override {
return tox_dht_get_num_closelist_announce_capable(_tox);
}
std::tuple<std::optional<std::string>, Tox_Err_Group_Peer_Query> toxGroupPeerGetIPAddress(uint32_t group_number, uint32_t peer_id) override {
Tox_Err_Group_Peer_Query err = TOX_ERR_GROUP_PEER_QUERY_OK;
size_t str_size = tox_group_peer_get_ip_address_size(_tox, group_number, peer_id, &err);
if (err != TOX_ERR_GROUP_PEER_QUERY_OK) {
return {std::nullopt, err};
}
std::string ip_str(str_size, '\0');
tox_group_peer_get_ip_address(_tox, group_number, peer_id, reinterpret_cast<uint8_t*>(ip_str.data()), &err);
if (err == TOX_ERR_GROUP_PEER_QUERY_OK) {
return {ip_str, err};
} else {
return {std::nullopt, err};
}
}
};