Compare commits

..

49 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
949929f490 respect mcd interval 2024-02-03 17:39:04 +01:00
8b3af55834 new timing circuit (dynamic interval)
needs windows fix (f msvc stl)
2024-02-03 16:36:28 +01:00
343c8e7012 make msg cleanser throw delete events 2024-01-25 12:23:55 +01:00
e459e1c166 fix provided instance version 2024-01-18 21:43:55 +01:00
6f239880ee move mcd 2024-01-13 22:53:12 +01:00
e8f69c0948 dont execute commands that where written before it was started 2024-01-13 19:11:22 +01:00
0ea096c695 fix headers 2024-01-12 19:09:55 +01:00
bd1f46349b synced by changes 2024-01-12 18:52:22 +01:00
77936081b5 add quick uptime 2024-01-10 18:22:50 +01:00
6b1d949810 rest of the update 2023-12-27 12:24:25 +01:00
516b264a96 update to new ngc_events (missing nix updates) 2023-12-26 21:26:13 +01:00
8dcede652e update flake for toxcore update 2023-12-15 14:44:43 +01:00
ee638d3a10 load tox options from config 2023-12-15 13:03:27 +01:00
d00b3c6f8d add dht num closelist (with cap) to tox status command 2023-12-15 11:43:46 +01:00
de3f845918 fun dance (private reply for now) 2023-12-15 01:59:19 +01:00
113a9ec099 add command to invite friend to same group (untested) 2023-12-15 00:05:16 +01:00
f9a43c30cc cmake versions 2023-12-14 18:23:30 +01:00
a747053a48 make cmake project embedable 2023-12-14 18:13:58 +01:00
910029bf4f fix group messages and start config commands (disabled for now) 2023-12-14 17:56:52 +01:00
1c10d4da4c dont trust synced messages 2023-12-06 17:42:01 +01:00
c74ccbaff1 handle private messages 2023-12-05 20:53:15 +01:00
92cf42ead5 add management commands (add to admin or moderator list) 2023-12-05 19:45:04 +01:00
b0b117e615 add moderator 2023-12-05 10:41:23 +01:00
3b7f1ba312 try fix nix ci 2023-12-04 17:29:30 +01:00
632f4d3fe1 add admin from config 2023-12-04 17:17:28 +01:00
51c5d9b06c add permissions (only everyone is checked yet) 2023-12-04 16:53:38 +01:00
f83abab0a0 draft generated help 2023-12-04 02:16:37 +01:00
bd30bc8fd5 params parsing + basic tox commands !! no permissions yet 2023-12-03 20:01:47 +01:00
f9eae6ede9 enable experimental 2023-12-03 17:41:19 +01:00
26 changed files with 960 additions and 586 deletions

View File

@ -46,8 +46,11 @@ jobs:
with:
submodules: recursive
- name: Install Dependencies
run: sudo apt update && sudo apt -y install nix
#- name: Install Dependencies
## TODO: is 2.6 new enough?
#run: sudo apt update && sudo apt -y install nix
- uses: cachix/install-nix-action@v24
- name: Nix build
run: nix build '.#static'
@ -70,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
@ -78,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

@ -1,8 +1,18 @@
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
cmake_minimum_required(VERSION 3.14...3.24 FATAL_ERROR)
# cmake setup begin
project(tomato)
project(totato)
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
set(TOTATO_STANDALONE ON)
else()
set(TOTATO_STANDALONE OFF)
endif()
message("II TOTATO_STANDALONE " ${TOTATO_STANDALONE})
#option(TOTATO_BUILD_PLUGINS "Build the toxic_games plugins" ${TOTATO_STANDALONE})
if (TOTATO_STANDALONE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# defaulting to debug mode, if not specified
@ -17,10 +27,12 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
endif()
# external libs
add_subdirectory(./external) # before increasing warn levels, sad :(
if (TOTATO_STANDALONE)
set(CMAKE_CXX_EXTENSIONS OFF)
# bump up warning levels appropriately for clang, gcc & msvc
@ -45,6 +57,7 @@ elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
endif()
endif()
# cmake setup end

View File

@ -1,17 +1,7 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
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 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_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)
# HACK: "install" api headers into self
# this is dirty, should be binary dir
# 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";
};
@ -14,11 +14,12 @@
pkgs = import nixpkgs { inherit system; };
# get deps
# TODO: move to inputs and lock file
toxcore-src = pkgs.fetchFromGitHub {
owner = "Green-Sky"; repo = "c-toxcore";
owner = "TokTok"; repo = "c-toxcore";
fetchSubmodules = true;
rev = "d4b06edc2a35bad51b0f0950d74f61c8c70630ab"; # ngc_events
hash = "sha256-P7wTojRQRtvTx+h9+QcFdO381hniWWpAy5Yv63KWWZA=";
rev = "v0.2.20";
hash = "sha256-jJk3K1wDG84NtMQBmNvSDRMPYUsYIzjE0emiZ4Ugbhk=";
};
entt-src = pkgs.fetchFromGitHub {
owner = "skypjack"; repo = "entt";
@ -27,33 +28,38 @@
};
solanaceae_util-src = pkgs.fetchFromGitHub {
owner = "Green-Sky"; repo = "solanaceae_util";
rev = "57d7178e76b41c5d0f8117fc8fb5791b4108cdc0";
hash = "sha256-CjBj1iYlJsLMsZvp857FrsmStV4AJ7SD7L+hzOgZMpg=";
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 = "48fb5f0889404370006ae12b3637a77d7d4ba485";
hash = "sha256-kFA90EpAH/BciHDD7NwZs7KL1cDcGVQZCRjOazxMbvM=";
rev = "e55fb46027f16a1bc078f797ae9fcc7609d15659";
hash = "sha256-BdwHBCXWLiVL26djyasQEPW1PbwKCkeXdUivkPaQD3c=";
};
solanaceae_plugin-src = pkgs.fetchFromGitHub {
owner = "Green-Sky"; repo = "solanaceae_plugin";
rev = "eeab3109e76907a0e3e8c526956ff0fa9b3bd3a2";
hash = "sha256-knW8S4VK/U3xAWFyczSNXwx2ZA9hq2XSyr39Xh2Nsgs=";
rev = "54cd23433df4acedede51e932f27d16fe4f35548";
hash = "sha256-Yy58w1PJFzIiN8kjqe7zerG9HewcdlBcN9P/YsjFCQs=";
};
solanaceae_toxcore-src = pkgs.fetchFromGitHub {
owner = "Green-Sky"; repo = "solanaceae_toxcore";
rev = "d05875f489577e9a2c26234810058b41c3236cf7";
hash = "sha256-uQVEkTn9ww/LVhPOqrq/iKIFSaDC6/BNrYGNUg+LrzA=";
rev = "727c341899a82c911a27a5cac6d09bb23ce06b1d";
hash = "sha256-pI0ZKX6h/DMC9m0z4yC38kqGRP34ES12U9LcuO14fO0=";
};
solanaceae_tox-src = pkgs.fetchFromGitHub {
owner = "Green-Sky"; repo = "solanaceae_tox";
rev = "89e74b35f83d888f8aa2e5230811b8a5e2b101a7";
hash = "sha256-PQw2290ahYfU13tHGzBttwrvZBXK+wKh6UF4xfUaRWQ=";
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";
@ -65,7 +71,6 @@
cmake
ninja
pkg-config
git # cmake FetchContent
];
cmakeFlags = [
@ -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

@ -12,14 +12,28 @@ add_executable(totato
./message_cleanser.hpp
./message_cleanser.cpp
./message_command_dispatcher.hpp
./message_command_dispatcher.cpp
./managment_commands.hpp
./managment_commands.cpp
./config_commands.hpp
./config_commands.cpp
./tox_commands.hpp
./tox_commands.cpp
./fun_commands.hpp
./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
@ -28,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::TOX_EVENT_SELF_CONNECTION_STATUS);
_tc.subscribe(this, Tox_Event::TOX_EVENT_FRIEND_CONNECTION_STATUS);
_tc.subscribe(this, Tox_Event::TOX_EVENT_FRIEND_REQUEST);
_tc.subscribe(this, Tox_Event::TOX_EVENT_GROUP_INVITE);
_tc.subscribe(this, Tox_Event::TOX_EVENT_GROUP_SELF_JOIN);
_tc.subscribe(this, Tox_Event::TOX_EVENT_GROUP_PEER_JOIN);
_tc.subscribe(this, Tox_Event::TOX_EVENT_GROUP_PEER_EXIT);
_tc.subscribe(this, Tox_Event::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);

66
src/config_commands.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "./managment_commands.hpp"
#include <solanaceae/contact/contact_store_i.hpp>
#include <solanaceae/util/config_model.hpp>
#include <solanaceae/message3/message_command_dispatcher.hpp>
#include <solanaceae/message3/components.hpp>
#include <solanaceae/contact/components.hpp>
#include <iostream>
void registerConfigCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
ContactStore4I& cs,
RegistryMessageModelI& rmm
) {
mcd.registerCommand(
"Config", "conf",
"set-string",
[&](std::string_view params, Message3Handle m) -> bool {
// x x x
// 01234
if (params.size() < 5) {
return false;
}
const auto contact_from = m.get<Message::Components::ContactFrom>().c;
// split params into:
// - module
// - category
const auto first_space_pos = params.find_first_of(' ');
// x x x
// 01234
if (first_space_pos >= int64_t(params.size())-2) {
// TODO: error?
return false;
}
const auto second_space_pos = params.find_first_of(' ', first_space_pos+1);
if (second_space_pos >= params.size()-2) {
// TODO: error?
return false;
}
const auto fist_word = params.substr(0, first_space_pos);
const auto second_word = params.substr(first_space_pos+1, second_space_pos);
const auto rest_word = params.substr(second_space_pos+1);
//conf.set("MessageCommandDispatcher", group, params, true);
rmm.sendText(
contact_from,
"he"
);
return true;
},
"Set the config value for a category.",
MessageCommandDispatcher::Perms::ADMIN // yes, always
);
}

16
src/config_commands.hpp Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include <solanaceae/contact/fwd.hpp>
#include <solanaceae/message3/registry_message_model.hpp>
// fwd
class MessageCommandDispatcher;
struct ConfigModelI;
void registerConfigCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
ContactStore4I& cs,
RegistryMessageModelI& rmm
);

51
src/fun_commands.cpp Normal file
View File

@ -0,0 +1,51 @@
#include "./managment_commands.hpp"
//#include <solanaceae/util/config_model.hpp>
#include <solanaceae/contact/contact_store_i.hpp>
#include <solanaceae/message3/message_command_dispatcher.hpp>
//#include <solanaceae/contact/components.hpp>
#include <solanaceae/message3/components.hpp>
#include <random>
void registerFunCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
ContactStore4I& cs,
RegistryMessageModelI& rmm
) {
mcd.registerCommand(
"fun", "fun",
"dance",
[&](std::string_view params, Message3Handle m) -> bool {
static std::default_random_engine rng{std::random_device{}()};
const auto contact_from = m.get<Message::Components::ContactFrom>().c;
static const std::array<std::string_view, 12> dances {
"DANCE",
"(~^.^)~",
"♪┌|∵|┘♪",
"~( °٢° )~",
"m/...(>.<)…m/",
"(~‾⌣‾)~",
"└[∵┌]└[ ∵ ]┘[┐∵]┘",
"♨(⋆‿⋆)♨",
"ヾ(*´∀`*",
"ლ(o◡oლ)",
"┌(★o☆)┘",
"ヘ(^_^ヘ)",
};
rmm.sendText(
contact_from,
dances.at(rng()%dances.size())
);
return true;
},
"Make totato dance.",
MessageCommandDispatcher::Perms::EVERYONE
);
}

15
src/fun_commands.hpp Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <solanaceae/message3/registry_message_model.hpp>
// fwd
class MessageCommandDispatcher;
struct ConfigModelI;
void registerFunCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
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>
@ -13,10 +14,26 @@
#include "./tox_client.hpp"
#include "./auto_dirty.hpp"
#include "./message_cleanser.hpp"
#include "./message_command_dispatcher.hpp"
#include <solanaceae/message3/message_command_dispatcher.hpp>
#include "./managment_commands.hpp"
#include "./config_commands.hpp"
#include "./tox_commands.hpp"
#include "./fun_commands.hpp"
#include <solanaceae/message3/components.hpp> // TODO: move uptime
//#include <solanaceae/message3/components.hpp>
//#include <solanaceae/contact/components.hpp>
//#include <solanaceae/tox_contacts/components.hpp>
//#include <solanaceae/toxcore/utils.hpp>
#include <nlohmann/json.hpp>
#include <entt/entt.hpp>
#include <entt/fwd.hpp>
#include <cstdint>
#include <cmath>
#include <chrono>
#include <thread>
#include <atomic>
@ -31,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)
@ -111,7 +138,10 @@ int main(int argc, char** argv) {
signal(SIGINT, sigint_handler);
#endif
auto last_time = std::chrono::steady_clock::now();
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"};
@ -155,35 +185,35 @@ 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(
"host", "",
"info",
[](std::string_view) -> bool {
[](std::string_view, Message3Handle m) -> bool {
std::cout << "INFO got called :)\n";
return true;
},
"Get basic information about this bot"
);
}
PluginManager pm;
ToxEventLogger tel{std::cout}; // TODO: config
// TODO: password?
ToxClient tc{conf.get_string("tox", "save_file_path").value(), ""};
tel.subscribeAll(tc);
ToxClient tc{conf, conf.get_string("tox", "save_file_path").value(), ""};
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
@ -193,14 +223,19 @@ 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", "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);
g_provideInstance<ToxPrivateI>("ToxPrivateI", "host", &tpi);
@ -232,29 +267,92 @@ int main(int argc, char** argv) {
}
}
registerManagementCommands(mcd, conf, cs, rmm);
// TODO: finish impl
//registerConfigCommands(mcd, conf, cs, rmm);
registerToxCommands(mcd, conf, cs, rmm, tc, tpi);
registerFunCommands(mcd, conf, cs, rmm);
mcd.registerCommand(
"totato", "",
"uptime",
[&](std::string_view params, Message3Handle m) -> bool {
const auto contact_from = m.get<Message::Components::ContactFrom>().c;
const auto uptime = (std::chrono::steady_clock::now() - started_at);
const auto days = std::chrono::duration_cast<std::chrono::duration<int64_t, std::ratio<86400>>>(uptime);
const auto hours = std::chrono::duration_cast<std::chrono::hours>(uptime) - std::chrono::duration_cast<std::chrono::hours>(days);
const auto minutes = (std::chrono::duration_cast<std::chrono::minutes>(uptime) - std::chrono::duration_cast<std::chrono::minutes>(days)) - std::chrono::duration_cast<std::chrono::minutes>(hours);
const auto seconds = ((std::chrono::duration_cast<std::chrono::seconds>(uptime) - std::chrono::duration_cast<std::chrono::seconds>(days)) - std::chrono::duration_cast<std::chrono::seconds>(hours)) - std::chrono::duration_cast<std::chrono::seconds>(minutes);
std::string reply_text;
reply_text += "totato uptime: ";
reply_text += std::to_string(days.count());
reply_text += "d ";
reply_text += std::to_string(hours.count());
reply_text += "h ";
reply_text += std::to_string(minutes.count());
reply_text += "min ";
reply_text += std::to_string(seconds.count());
reply_text += "s (";
reply_text += std::to_string(std::chrono::duration_cast<std::chrono::seconds>(uptime).count());
reply_text += "s)";
rmm.sendText(
contact_from,
reply_text
);
return true;
},
"get current uptime.",
MessageCommandDispatcher::Perms::EVERYONE // mod?
);
conf.dump();
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // at startup, just to be safe
std::this_thread::sleep_for(std::chrono::milliseconds(25)); // at startup, just to be safe
float last_min_interval {0.1f};
while (!quit) {
//auto new_time = std::chrono::steady_clock::now();
auto new_time = std::chrono::steady_clock::now();
const float time_delta_tick = std::chrono::duration<float, std::chrono::seconds::period>(new_time - last_time_tick).count();
quit = !tc.iterate();
tcm.iterate(/*time_delta*/0.02f);
// TODO: implement the equivalent of screen->nextTick() for plugs
const bool tick = time_delta_tick >= last_min_interval;
if (tick) {
quit = !tc.iterate(time_delta_tick);
tcm.iterate(time_delta_tick);
ttm.iterate();
mts.iterate();
pm.tick(/*time_delta*/0.02f);
const float pm_interval = pm.tick(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;
mc.iterate(0.02f);
last_min_interval = min_var(
pm_interval,
mc_interval,
mcd_interval,
tox_interval
);
//std::this_thread::sleep_for( // time left to get to 60fps
//std::chrono::duration<float, std::chrono::seconds::period>(0.0166f) // 60fps frame duration
//- std::chrono::duration<float, std::chrono::seconds::period>(std::chrono::steady_clock::now() - new_time) // time used for rendering
//);
std::this_thread::sleep_for(std::chrono::milliseconds(20)); // HACK: until i figure out the best main loop
// dont sleep and do an extra check
last_time_tick = new_time;
//std::cout << "M: time_delta_tick: " << time_delta_tick << "\n";
//std::cout << "M: last_min_interval: " << last_min_interval << " (t:" << tox_interval << " p:" << pm_interval << ")\n";
} else {
// TODO: replace with something that works on windows
const float sleep_dur = std::max(last_min_interval-time_delta_tick, 0.001f);
std::this_thread::sleep_for(std::chrono::milliseconds(int64_t(sleep_dur*1000)));
}
}
conf.dump();
std::cout << "\nTOTATO shutting down...\n";

135
src/managment_commands.cpp Normal file
View File

@ -0,0 +1,135 @@
#include "./managment_commands.hpp"
#include <solanaceae/contact/contact_store_i.hpp>
#include <solanaceae/util/config_model.hpp>
#include <solanaceae/message3/message_command_dispatcher.hpp>
#include <solanaceae/message3/components.hpp>
#include <solanaceae/contact/components.hpp>
#include <solanaceae/util/utils.hpp>
#include <iostream>
#include <algorithm>
static std::optional<Contact4> getContactFromIDStr(
ContactStore4I& cs,
std::string_view id_str
) {
const std::vector<uint8_t> id = hex2bin(id_str);
const auto found_contact = cs.getOneContactByID(ByteSpan{id});
if (static_cast<bool>(found_contact)) {
return found_contact;
} else {
return std::nullopt;
}
}
static std::string getStatusFromContact(
ContactHandle4 c
) {
std::string status_str;
// name
if (c.all_of<Contact::Components::Name>()) {
status_str += " name: " + c.get<Contact::Components::Name>().name;
} else {
status_str += " name: not found";
}
// connection state
if (c.all_of<Contact::Components::ConnectionState>()) {
status_str += "\n connection: " + std::to_string(c.get<Contact::Components::ConnectionState>().state);
} else {
status_str += "\n connection: not found";
}
return status_str;
}
bool handleContactAddToGroup(
ConfigModelI& conf,
ContactStore4I& cs,
RegistryMessageModelI& rmm,
std::string_view params,
Message3Handle m,
const std::string_view group
) {
const auto contact_from = m.get<Message::Components::ContactFrom>().c;
// TODO: move parameter parsing to mcd
if (params.empty()) {
rmm.sendText(
contact_from,
std::string{"error: command requires the ID for the contact to promote to "} + std::string{group}
);
return true;
}
const auto target_opt = getContactFromIDStr(cs, params);
if (!target_opt.has_value()) {
rmm.sendText(
contact_from,
"error: unknown contact\n"
"For practicality reasons the contact has to be observed once, before it can be added."
);
return true;
}
std::string reply;
if (conf.get_bool("MessageCommandDispatcher", group, params).value_or(false)) {
reply += "warning: contact already ";
reply += group;
reply += "!\n";
}
conf.set("MessageCommandDispatcher", group, params, true);
reply += "Added '";
reply += params;
reply += "' to the ";
reply += group;
reply += "s.\n";
reply += getStatusFromContact(cs.contactHandle(target_opt.value()));
rmm.sendText(
contact_from,
reply
);
return true;
}
void registerManagementCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
ContactStore4I& cs,
RegistryMessageModelI& rmm
) {
mcd.registerCommand(
"Management", "manage",
"admin-add",
[&](std::string_view params, Message3Handle m) -> bool {
return handleContactAddToGroup(conf, cs, rmm, params, m, "admin");
},
"Add an admin by ID.",
MessageCommandDispatcher::Perms::ADMIN
);
mcd.registerCommand(
"Management", "manage",
"mod-add",
[&](std::string_view params, Message3Handle m) -> bool {
return handleContactAddToGroup(conf, cs, rmm, params, m, "moderator");
},
"Add a moderator by ID.",
MessageCommandDispatcher::Perms::ADMIN
);
}

View File

@ -0,0 +1,16 @@
#pragma once
#include <solanaceae/contact/fwd.hpp>
#include <solanaceae/message3/registry_message_model.hpp>
// fwd
class MessageCommandDispatcher;
struct ConfigModelI;
void registerManagementCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
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,28 +31,46 @@ 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);
}
});
reg->destroy(to_remove.cbegin(), to_remove.cend());
deleted_count += to_remove.size();
//reg->destroy(to_remove.cbegin(), to_remove.cend());
// we need to notify for every destruction, and give every listener a last chance
for (const auto c : to_remove) {
_rmm.throwEventDestroy(*reg, c);
reg->destroy(c);
}
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

@ -1,203 +0,0 @@
#include "./message_command_dispatcher.hpp"
#include <solanaceae/util/config_model.hpp>
#include <solanaceae/message3/components.hpp>
#include <utility>
#include <iostream>
//MessageCommandDispatcher::Command::Command(Command&& other) :
//m(std::move(other.m)),
//m_prefix(std::move(other.m_prefix)),
//command(std::move(other.command)),
//fn(std::move(other.fn)),
//help_text(std::move(other.help_text))
//{
//// is this really needed?
//}
MessageCommandDispatcher::MessageCommandDispatcher(
Contact3Registry& cr,
RegistryMessageModel& rmm,
ConfigModelI& conf
) :
_cr(cr), _rmm(rmm), _conf(conf)
{
_rmm.subscribe(this, RegistryMessageModel_Event::message_construct);
{ // setup basic commands for bot
registerCommand(
"host", "",
"help",
[this](std::string_view params) -> bool {
return helpCommand(params);
},
"Get help"
);
}
}
MessageCommandDispatcher::~MessageCommandDispatcher(void) {
}
void MessageCommandDispatcher::iterate(float time_delta) {
}
static std::string_view get_first_word(std::string_view text) {
if (text.empty()) {
return text;
}
// trim
const auto pos_first_non_space = text.find_first_not_of(' ');
if (pos_first_non_space == std::string_view::npos) {
// only contains spaces o.o
return ""; // should return text as is?
}
text = text.substr(pos_first_non_space);
const auto pos_first_space = text.find_first_of(' ');
if (pos_first_space == 0 || pos_first_space == std::string_view::npos) {
// does not contain spaces
// command is whole message
return text;
} else {
return text.substr(0, pos_first_space);
}
}
void MessageCommandDispatcher::registerCommand(
std::string_view m, // module
std::string_view m_prefix, // module prefix (if any)
std::string_view command, // command
std::function<bool(std::string_view params)>&& fn,
std::string_view help_text
) {
std::string full_command_string = std::string{m_prefix} + std::string{command};
if (_command_map.count(full_command_string)) {
std::cout << "MCD warning: overwriting existing '" << full_command_string << "'\n";
}
_command_map[full_command_string] = Command{
std::string{m},
std::string{m_prefix},
std::string{command},
std::move(fn),
std::string{help_text}
};
}
bool MessageCommandDispatcher::helpCommand(std::string_view params) {
std::cout << "MCD: help got called '" << params << "'\n";
return true;
}
bool MessageCommandDispatcher::onEvent(const Message::Events::MessageConstruct& e) {
if (!e.e.all_of<Message::Components::MessageText, Message::Components::TagUnread>()) {
std::cout << "MCD: got message that is not";
if (!e.e.all_of<Message::Components::MessageText>()) {
std::cout << " text";
}
if (!e.e.all_of<Message::Components::TagUnread>()) {
std::cout << " unread";
}
std::cout << "\n";
return false;
}
if (e.e.any_of<Message::Components::TagMessageIsAction>()) {
std::cout << "MCD: got message that is";
if (e.e.all_of<Message::Components::TagMessageIsAction>()) {
std::cout << " action";
}
std::cout << "\n";
return false;
}
std::string_view message_text = e.e.get<Message::Components::MessageText>().text;
if (message_text.empty()) {
// empty message?
return false;
}
// TODO: skip unrelyable synced
// TODO: is private
// TODO: has the permissions
if (false) { // is private
} else {
// check for command prefix
if (
message_text.at(0) != '!' &&
message_text.at(0) != '/'
) {
// does not start with command prefix, not for us
return false;
}
// remove c prefix
message_text = message_text.substr(1);
}
if (message_text.empty()) {
// empty message?
std::cout << "MCD: got empty command\n";
return false;
}
std::cout << "MCD: got command '" << message_text << "'\n";
std::string_view first_word;
std::string_view second_word;
first_word = get_first_word(message_text);
std::cout << "------- first_word:'" << first_word << "'\n";
if (first_word.size() != message_text.size()) {
second_word = get_first_word(
message_text.substr(
// TODO: optimize this
message_text.find(first_word)
+ first_word.size()
)
);
}
std::cout << "------- second_word:'" << second_word << "' empty:" << second_word.empty() << "\n";
// first search first + space + second word
if (!second_word.empty()) {
std::string query {first_word};
query += " ";
query += second_word;
const auto command_it = _command_map.find(query);
if (command_it != _command_map.cend()) {
command_it->second.fn(message_text);
return true;
}
}
// then seach first word only
const auto command_it = _command_map.find(std::string{first_word});
if (command_it != _command_map.cend()) {
command_it->second.fn(message_text);
return true;
}
return false;
}
bool MessageCommandDispatcher::onEvent(const Message::Events::MessageUpdated&) {
// do i need this?
return false;
}

View File

@ -1,54 +0,0 @@
#pragma once
#include <solanaceae/message3/registry_message_model.hpp>
// fwd
struct ConfigModelI;
class MessageCommandDispatcher : public RegistryMessageModelEventI {
Contact3Registry& _cr;
RegistryMessageModel& _rmm;
ConfigModelI& _conf;
struct Command {
std::string m; // module
std::string m_prefix; // module prefix (if any)
std::string command; // command
std::function<bool(std::string_view params)> fn;
std::string help_text;
//Command(const Command&) = delete;
};
std::unordered_map<std::string, Command> _command_map;
public:
MessageCommandDispatcher(Contact3Registry& cr, RegistryMessageModel& rmm, ConfigModelI& conf);
~MessageCommandDispatcher(void);
void iterate(float time_delta); // do we?
// module command/command <param type(s)>
// permissions?
// - user(s)
// - group(s)
// - everyone else?
// callable
// help text?
void registerCommand(
std::string_view m, // module
std::string_view m_prefix, // module prefix (if any)
std::string_view command, // command
std::function<bool(std::string_view params)>&& fn,
std::string_view help_text
);
// generates a help
bool helpCommand(std::string_view params);
protected: // mm
bool onEvent(const Message::Events::MessageConstruct& e) override;
bool onEvent(const Message::Events::MessageUpdated& e) override;
};

View File

@ -2,8 +2,9 @@
// meh, change this
#include <exception>
#include <stdexcept>
#include <system_error>
#include <toxencryptsave/toxencryptsave.h>
#include <tox/toxencryptsave.h>
#include <sodium.h>
@ -18,12 +19,13 @@ static void eee(std::string& mod) {
}
}
ToxClient::ToxClient(std::string_view save_path, std::string_view save_password) :
ToxClient::ToxClient(ConfigModelI& conf, std::string_view save_path, std::string_view save_password) :
_tox_profile_path(save_path), _tox_profile_password(save_password)
{
TOX_ERR_OPTIONS_NEW err_opt_new;
Tox_Options* options = tox_options_new(&err_opt_new);
assert(err_opt_new == TOX_ERR_OPTIONS_NEW::TOX_ERR_OPTIONS_NEW_OK);
std::string tmp_proxy_host; // the string needs to survive until options is freed
std::vector<uint8_t> profile_data{};
if (!_tox_profile_path.empty()) {
@ -67,6 +69,41 @@ ToxClient::ToxClient(std::string_view save_path, std::string_view save_password)
}
}
tox_options_set_ipv6_enabled(options, conf.get_bool("tox", "ipv6_enabled").value_or(true));
tox_options_set_udp_enabled(options, conf.get_bool("tox", "udp_enabled").value_or(true));
tox_options_set_local_discovery_enabled(options, conf.get_bool("tox", "local_discovery_enabled").value_or(true));
// TODO: should this be exposed?
tox_options_set_dht_announcements_enabled(options, conf.get_bool("tox", "dht_announcements_enabled").value_or(true));
const size_t proxy_conf_sum = conf.has_string("tox", "proxy_type")
+ conf.has_string("tox", "proxy_host")
+ conf.has_int("tox", "proxy_port")
;
// if all proxy parts defined
if (proxy_conf_sum == 3) {
const std::string_view proxy_type_str = conf.get_string("tox", "proxy_type").value();
if (proxy_type_str == "HTTP") {
tox_options_set_proxy_type(options, Tox_Proxy_Type::TOX_PROXY_TYPE_HTTP);
} else if (proxy_type_str == "SOCKS5") {
tox_options_set_proxy_type(options, Tox_Proxy_Type::TOX_PROXY_TYPE_SOCKS5);
} else {
throw std::runtime_error("invalid proxy type in config, terminating to be safe");
}
tmp_proxy_host = conf.get_string("tox", "proxy_host").value();
tox_options_set_proxy_host(options, tmp_proxy_host.c_str());
tox_options_set_proxy_port(options, conf.get_int("tox", "proxy_port").value());
} else if (proxy_conf_sum > 0) {
throw std::runtime_error("config only partly specified proxy, terminating to be safe");
}
tox_options_set_start_port(options, conf.get_int("tox", "start_port").value_or(0));
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);
tox_options_free(options);
@ -118,10 +155,13 @@ ToxClient::ToxClient(std::string_view save_path, std::string_view save_password)
}
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) {
@ -133,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();
}
@ -177,5 +218,6 @@ void ToxClient::saveToxProfile(void) {
}
_tox_profile_dirty = false;
_save_heat = 10.f;
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <solanaceae/util/config_model.hpp>
#include <solanaceae/toxcore/tox_default_impl.hpp>
#include <solanaceae/toxcore/tox_event_interface.hpp>
#include <solanaceae/toxcore/tox_event_provider_base.hpp>
@ -22,10 +24,10 @@ 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(/*const CommandLine& cl*/);
ToxClient(std::string_view save_path, std::string_view save_password);
ToxClient(ConfigModelI& conf, std::string_view save_path, std::string_view save_password);
~ToxClient(void);
public: // tox stuff
@ -34,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; }
@ -47,4 +49,3 @@ class ToxClient : public ToxDefaultImpl, public ToxEventProviderBase {
void saveToxProfile(void);
};

220
src/tox_commands.cpp Normal file
View File

@ -0,0 +1,220 @@
#include "./tox_commands.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>
#include <solanaceae/toxcore/tox_private_interface.hpp>
#include <solanaceae/message3/message_command_dispatcher.hpp>
#include <solanaceae/message3/components.hpp>
#include <solanaceae/contact/components.hpp>
#include <solanaceae/tox_contacts/components.hpp>
#include <iostream>
void registerToxCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
ContactStore4I& cs,
RegistryMessageModelI& rmm,
ToxI& t,
ToxPrivateI& tp
) {
mcd.registerCommand(
"tox", "tox",
"status",
[&](std::string_view, Message3Handle m) -> bool {
const auto tox_self_status = t.toxSelfGetConnectionStatus();
const auto contact_from = m.get<Message::Components::ContactFrom>().c;
std::string reply{"dht:"};
if (tox_self_status == Tox_Connection::TOX_CONNECTION_UDP) {
reply += "upd-direct";
} else if (tox_self_status == Tox_Connection::TOX_CONNECTION_TCP) {
reply += "tcp-relayed";
}
reply += "\ndht-closenum:";
reply += std::to_string(tp.toxDHTGetNumCloselist());
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) {
reply += "\nfriend:offline";
} else if (con_opt.value() == Tox_Connection::TOX_CONNECTION_UDP) {
reply += "\nfriend:udp-direct";
} else if (con_opt.value() == Tox_Connection::TOX_CONNECTION_TCP) {
reply += "\nfriend:tcp-relayed";
}
} else if (cr.all_of<Contact::Components::ToxGroupPeerEphemeral>(contact_from)) {
const auto [group_number, peer_number] = cr.get<Contact::Components::ToxGroupPeerEphemeral>(contact_from);
const auto [con_opt, _] = t.toxGroupPeerGetConnectionStatus(group_number, peer_number);
if (!con_opt.has_value() || con_opt.value() == Tox_Connection::TOX_CONNECTION_NONE) {
reply += "\ngroup-peer:offline";
} else if (con_opt.value() == Tox_Connection::TOX_CONNECTION_UDP) {
reply += "\ngroup-peer:udp-direct";
} else if (con_opt.value() == Tox_Connection::TOX_CONNECTION_TCP) {
reply += "\ngroup-peer:tcp-relayed";
}
} else if (cr.any_of<Contact::Components::ToxFriendPersistent, Contact::Components::ToxGroupPeerPersistent>(contact_from)) {
reply += "\noffline";
} else {
reply += "\nunk";
}
rmm.sendText(
contact_from,
reply
);
return true;
},
"Query the tox status of dht and to you.",
MessageCommandDispatcher::Perms::EVERYONE
);
mcd.registerCommand(
"tox", "tox",
"add",
[&](std::string_view params, Message3Handle m) -> bool {
const auto contact_from = m.get<Message::Components::ContactFrom>().c;
if (params.size() != 38*2) {
rmm.sendText(
contact_from,
"error adding friend, id is not the correct size!"
);
return true;
}
// TODO: add tcm interface
const auto [_, err] = t.toxFriendAdd(hex2bin(std::string{params}), "Add me, I am totato");
if (err == Tox_Err_Friend_Add::TOX_ERR_FRIEND_ADD_OK) {
rmm.sendText(
contact_from,
"freind request sent"
);
} else {
rmm.sendText(
contact_from,
"error adding friend, error code " + std::to_string(err)
);
}
return true;
},
"add a tox friend by id"
);
mcd.registerCommand(
"tox", "tox",
"join",
[&](std::string_view params, Message3Handle m) -> bool {
const auto contact_from = m.get<Message::Components::ContactFrom>().c;
if (params.size() != 32*2) {
rmm.sendText(
contact_from,
"error joining group, id is not the correct size!"
);
return true;
}
auto name_opt = conf.get_string("tox", "name");
// TODO: add tcm interface
const auto [_, err] = t.toxGroupJoin(hex2bin(std::string{params}), std::string{name_opt.value_or("no-name-found")}, "");
if (err == Tox_Err_Group_Join::TOX_ERR_GROUP_JOIN_OK) {
rmm.sendText(
contact_from,
"joining group..."
);
} else {
rmm.sendText(
contact_from,
"error joining group, error code " + std::to_string(err)
);
}
return true;
},
"join a tox group by id"
);
mcd.registerCommand(
"tox", "tox",
"invite",
[&](std::string_view params, Message3Handle m) -> bool {
const auto contact_from = m.get<Message::Components::ContactFrom>().c;
// trim friend extra stuff
if (params.size() < 32*2) {
rmm.sendText(
contact_from,
"error inviting, friend id is not the correct size!"
);
return true;
}
const auto friend_id = hex2bin(std::string{params.substr(0, 32*2)});
auto [friend_number_opt, err] = t.toxFriendByPublicKey(friend_id);
if (!friend_number_opt.has_value()) {
rmm.sendText(
contact_from,
"error inviting, friend not found!"
);
return true;
}
const auto& cr = cs.registry();
// get current group
if (!cr.all_of<Contact::Components::Parent>(contact_from)) {
rmm.sendText(
contact_from,
"error inviting, not sent from group!"
);
return true;
}
const auto contact_group = cr.get<Contact::Components::Parent>(contact_from).parent;
if (!cr.all_of<Contact::Components::ToxGroupEphemeral>(contact_from)) {
rmm.sendText(
contact_from,
"error inviting, group not connected (what?!?)!"
);
return true;
}
const auto inv_err = t.toxGroupInviteFriend(
cr.get<Contact::Components::ToxGroupEphemeral>(contact_group).group_number,
friend_number_opt.value()
);
if (inv_err == Tox_Err_Group_Invite_Friend::TOX_ERR_GROUP_INVITE_FRIEND_OK) {
rmm.sendText(
contact_from,
"invite sent..."
);
} else {
rmm.sendText(
contact_from,
"error inviting to group, error code " + std::to_string(inv_err)
);
}
return true;
},
"invite a tox friend to the same tox group"
);
}

20
src/tox_commands.hpp Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <solanaceae/contact/fwd.hpp>
#include <solanaceae/message3/registry_message_model.hpp>
// fwd
class MessageCommandDispatcher;
struct ConfigModelI;
struct ToxI;
struct ToxPrivateI;
void registerToxCommands(
MessageCommandDispatcher& mcd,
ConfigModelI& conf,
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};
}
}
};