mvp irc client for solanaceae with plugin
non exhausitve list of missing stuff: - notices (only channel partly implemented) - invites - initiating private chat - channel membership status and other flags
This commit is contained in:
commit
53453872f2
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
.vs/
|
||||||
|
*.o
|
||||||
|
*.swp
|
||||||
|
~*
|
||||||
|
*~
|
||||||
|
.idea/
|
||||||
|
cmake-build-debug/
|
||||||
|
cmake-build-debugandtest/
|
||||||
|
cmake-build-release/
|
||||||
|
*.stackdump
|
||||||
|
*.coredump
|
||||||
|
compile_commands.json
|
||||||
|
/build*
|
||||||
|
/result*
|
||||||
|
.clangd
|
||||||
|
.cache
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
CMakeCache.txt
|
||||||
|
|
||||||
|
*.tox
|
||||||
|
imgui.ini
|
75
CMakeLists.txt
Normal file
75
CMakeLists.txt
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.24 FATAL_ERROR)
|
||||||
|
|
||||||
|
# cmake setup begin
|
||||||
|
project(solanaceae_ircclient)
|
||||||
|
|
||||||
|
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||||
|
set(SOLANACEAE_IRCCLIENT_STANDALONE ON)
|
||||||
|
# why the f do i need this >:(
|
||||||
|
set(NOT_SOLANACEAE_IRCCLIENT_STANDALONE OFF)
|
||||||
|
else()
|
||||||
|
set(SOLANACEAE_IRCCLIENT_STANDALONE OFF)
|
||||||
|
set(NOT_SOLANACEAE_IRCCLIENT_STANDALONE ON)
|
||||||
|
endif()
|
||||||
|
message("II SOLANACEAE_IRCCLIENT_STANDALONE " ${SOLANACEAE_IRCCLIENT_STANDALONE})
|
||||||
|
|
||||||
|
option(SOLANACEAE_IRCCLIENT_BUILD_PLUGINS "Build the ircclient plugins" ${SOLANACEAE_IRCCLIENT_STANDALONE})
|
||||||
|
|
||||||
|
if (SOLANACEAE_IRCCLIENT_STANDALONE)
|
||||||
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
|
# defaulting to debug mode, if not specified
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE "Debug")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# setup my vim ycm :D
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
# more paths
|
||||||
|
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 EXCLUDE_FROM_ALL) # before increasing warn levels, sad :(
|
||||||
|
|
||||||
|
if (SOLANACEAE_IRCCLIENT_STANDALONE)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
# bump up warning levels appropriately for clang, gcc & msvc
|
||||||
|
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
||||||
|
add_compile_options(
|
||||||
|
-Wall -Wextra # Reasonable and standard
|
||||||
|
-Wpedantic # Warn if non-standard C++ is used
|
||||||
|
-Wunused # Warn on anything being unused
|
||||||
|
#-Wconversion # Warn on type conversions that may lose data
|
||||||
|
#-Wsign-conversion # Warn on sign conversions
|
||||||
|
-Wshadow # Warn if a variable declaration shadows one from a parent context
|
||||||
|
)
|
||||||
|
|
||||||
|
if (NOT WIN32)
|
||||||
|
#link_libraries(-fsanitize=address)
|
||||||
|
#link_libraries(-fsanitize=address,undefined)
|
||||||
|
#link_libraries(-fsanitize-address-use-after-scope)
|
||||||
|
#link_libraries(-fsanitize=undefined)
|
||||||
|
endif()
|
||||||
|
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
|
||||||
|
if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
||||||
|
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||||
|
else()
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# cmake setup end
|
||||||
|
|
||||||
|
add_subdirectory(./src)
|
||||||
|
|
||||||
|
if (SOLANACEAE_IRCCLIENT_BUILD_PLUGINS)
|
||||||
|
add_subdirectory(./plugins)
|
||||||
|
endif()
|
||||||
|
|
97
external/CMakeLists.txt
vendored
Normal file
97
external/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
cmake_minimum_required(VERSION 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
|
||||||
|
GIT_TAG master
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(solanaceae_util)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT TARGET solanaceae_contact)
|
||||||
|
FetchContent_Declare(solanaceae_contact
|
||||||
|
GIT_REPOSITORY https://github.com/Green-Sky/solanaceae_contact.git
|
||||||
|
GIT_TAG master
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(solanaceae_contact)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT TARGET solanaceae_message3)
|
||||||
|
FetchContent_Declare(solanaceae_message3
|
||||||
|
GIT_REPOSITORY https://github.com/Green-Sky/solanaceae_message3.git
|
||||||
|
GIT_TAG master
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(solanaceae_message3)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT TARGET solanaceae_plugin)
|
||||||
|
FetchContent_Declare(solanaceae_plugin
|
||||||
|
GIT_REPOSITORY https://github.com/Green-Sky/solanaceae_plugin.git
|
||||||
|
GIT_TAG master
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(solanaceae_plugin)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT TARGET libircclient)
|
||||||
|
#add_subdirectory(./libircclient)
|
||||||
|
FetchContent_Declare(libircclient
|
||||||
|
GIT_REPOSITORY https://github.com/Green-Sky/libircclient.git
|
||||||
|
GIT_TAG master
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(libircclient)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (
|
||||||
|
NOT TARGET libsodium AND
|
||||||
|
NOT TARGET unofficial-sodium::sodium AND
|
||||||
|
NOT TARGET unofficial-sodium::sodium_config_public AND
|
||||||
|
NOT TARGET sodium
|
||||||
|
)
|
||||||
|
# for find sodium
|
||||||
|
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||||
|
|
||||||
|
find_package(unofficial-sodium CONFIG QUIET)
|
||||||
|
find_package(sodium QUIET)
|
||||||
|
if(unofficial-sodium_FOUND) # vcpkg
|
||||||
|
if(TARGET unofficial-sodium::sodium)
|
||||||
|
#TODO: alias can not target another alias
|
||||||
|
#target_link_libraries(toxcore unofficial-sodium::sodium)
|
||||||
|
#add_library(libsodium ALIAS unofficial-sodium::sodium)
|
||||||
|
|
||||||
|
add_library(libsodium INTERFACE)
|
||||||
|
target_link_libraries(libsodium INTERFACE unofficial-sodium::sodium)
|
||||||
|
endif()
|
||||||
|
if(TARGET unofficial-sodium::sodium_config_public)
|
||||||
|
#TODO: alias can not target another alias
|
||||||
|
#target_link_libraries(toxcore unofficial-sodium::sodium_config_public)
|
||||||
|
#add_library(libsodium ALIAS unofficial-sodium::sodium_config_public)
|
||||||
|
|
||||||
|
add_library(libsodium INTERFACE)
|
||||||
|
target_link_libraries(libsodium INTERFACE unofficial-sodium::sodium_config_public)
|
||||||
|
endif()
|
||||||
|
elseif(sodium_FOUND)
|
||||||
|
#add_library(libsodium ALIAS sodium)
|
||||||
|
add_library(libsodium INTERFACE)
|
||||||
|
target_link_libraries(libsodium INTERFACE sodium)
|
||||||
|
else()
|
||||||
|
message(SEND_ERROR "missing libsodium")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
297
external/cmake/Findsodium.cmake
vendored
Normal file
297
external/cmake/Findsodium.cmake
vendored
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
# Written in 2016 by Henrik Steffen Gaßmann <henrik@gassmann.onl>
|
||||||
|
#
|
||||||
|
# To the extent possible under law, the author(s) have dedicated all
|
||||||
|
# copyright and related and neighboring rights to this software to the
|
||||||
|
# public domain worldwide. This software is distributed without any warranty.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the CC0 Public Domain Dedication
|
||||||
|
# along with this software. If not, see
|
||||||
|
#
|
||||||
|
# http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
# Tries to find the local libsodium installation.
|
||||||
|
#
|
||||||
|
# On Windows the sodium_DIR environment variable is used as a default
|
||||||
|
# hint which can be overridden by setting the corresponding cmake variable.
|
||||||
|
#
|
||||||
|
# Once done the following variables will be defined:
|
||||||
|
#
|
||||||
|
# sodium_FOUND
|
||||||
|
# sodium_INCLUDE_DIR
|
||||||
|
# sodium_LIBRARY_DEBUG
|
||||||
|
# sodium_LIBRARY_RELEASE
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Furthermore an imported "sodium" target is created.
|
||||||
|
#
|
||||||
|
|
||||||
|
if (CMAKE_C_COMPILER_ID STREQUAL "GNU"
|
||||||
|
OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||||
|
set(_GCC_COMPATIBLE 1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# static library option
|
||||||
|
if (NOT DEFINED sodium_USE_STATIC_LIBS)
|
||||||
|
option(sodium_USE_STATIC_LIBS "enable to statically link against sodium" OFF)
|
||||||
|
endif()
|
||||||
|
if(NOT (sodium_USE_STATIC_LIBS EQUAL sodium_USE_STATIC_LIBS_LAST))
|
||||||
|
unset(sodium_LIBRARY CACHE)
|
||||||
|
unset(sodium_LIBRARY_DEBUG CACHE)
|
||||||
|
unset(sodium_LIBRARY_RELEASE CACHE)
|
||||||
|
unset(sodium_DLL_DEBUG CACHE)
|
||||||
|
unset(sodium_DLL_RELEASE CACHE)
|
||||||
|
set(sodium_USE_STATIC_LIBS_LAST ${sodium_USE_STATIC_LIBS} CACHE INTERNAL "internal change tracking variable")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# UNIX
|
||||||
|
if (UNIX)
|
||||||
|
# import pkg-config
|
||||||
|
find_package(PkgConfig QUIET)
|
||||||
|
if (PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules(sodium_PKG QUIET libsodium)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(sodium_USE_STATIC_LIBS)
|
||||||
|
foreach(_libname ${sodium_PKG_STATIC_LIBRARIES})
|
||||||
|
if (NOT _libname MATCHES "^lib.*\\.a$") # ignore strings already ending with .a
|
||||||
|
list(INSERT sodium_PKG_STATIC_LIBRARIES 0 "lib${_libname}.a")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
list(REMOVE_DUPLICATES sodium_PKG_STATIC_LIBRARIES)
|
||||||
|
|
||||||
|
# if pkgconfig for libsodium doesn't provide
|
||||||
|
# static lib info, then override PKG_STATIC here..
|
||||||
|
if (NOT sodium_PKG_STATIC_FOUND)
|
||||||
|
set(sodium_PKG_STATIC_LIBRARIES libsodium.a)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(XPREFIX sodium_PKG_STATIC)
|
||||||
|
else()
|
||||||
|
if (NOT sodium_PKG_FOUND)
|
||||||
|
set(sodium_PKG_LIBRARIES sodium)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(XPREFIX sodium_PKG)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_path(sodium_INCLUDE_DIR sodium.h
|
||||||
|
HINTS ${${XPREFIX}_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
find_library(sodium_LIBRARY_DEBUG NAMES ${${XPREFIX}_LIBRARIES}
|
||||||
|
HINTS ${${XPREFIX}_LIBRARY_DIRS}
|
||||||
|
)
|
||||||
|
find_library(sodium_LIBRARY_RELEASE NAMES ${${XPREFIX}_LIBRARIES}
|
||||||
|
HINTS ${${XPREFIX}_LIBRARY_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# Windows
|
||||||
|
elseif (WIN32)
|
||||||
|
set(sodium_DIR "$ENV{sodium_DIR}" CACHE FILEPATH "sodium install directory")
|
||||||
|
mark_as_advanced(sodium_DIR)
|
||||||
|
|
||||||
|
find_path(sodium_INCLUDE_DIR sodium.h
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES include
|
||||||
|
)
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# detect target architecture
|
||||||
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp" [=[
|
||||||
|
#if defined _M_IX86
|
||||||
|
#error ARCH_VALUE x86_32
|
||||||
|
#elif defined _M_X64
|
||||||
|
#error ARCH_VALUE x86_64
|
||||||
|
#endif
|
||||||
|
#error ARCH_VALUE unknown
|
||||||
|
]=])
|
||||||
|
try_compile(_UNUSED_VAR "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp"
|
||||||
|
OUTPUT_VARIABLE _COMPILATION_LOG
|
||||||
|
)
|
||||||
|
string(REGEX REPLACE ".*ARCH_VALUE ([a-zA-Z0-9_]+).*" "\\1" _TARGET_ARCH "${_COMPILATION_LOG}")
|
||||||
|
|
||||||
|
# construct library path
|
||||||
|
if (_TARGET_ARCH STREQUAL "x86_32")
|
||||||
|
string(APPEND _PLATFORM_PATH "Win32")
|
||||||
|
elseif(_TARGET_ARCH STREQUAL "x86_64")
|
||||||
|
string(APPEND _PLATFORM_PATH "x64")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "the ${_TARGET_ARCH} architecture is not supported by Findsodium.cmake.")
|
||||||
|
endif()
|
||||||
|
string(APPEND _PLATFORM_PATH "/$$CONFIG$$")
|
||||||
|
|
||||||
|
if (MSVC_VERSION LESS 1900)
|
||||||
|
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 60")
|
||||||
|
else()
|
||||||
|
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 50")
|
||||||
|
endif()
|
||||||
|
string(APPEND _PLATFORM_PATH "/v${_VS_VERSION}")
|
||||||
|
|
||||||
|
if (sodium_USE_STATIC_LIBS)
|
||||||
|
string(APPEND _PLATFORM_PATH "/static")
|
||||||
|
else()
|
||||||
|
string(APPEND _PLATFORM_PATH "/dynamic")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
string(REPLACE "$$CONFIG$$" "Debug" _DEBUG_PATH_SUFFIX "${_PLATFORM_PATH}")
|
||||||
|
string(REPLACE "$$CONFIG$$" "Release" _RELEASE_PATH_SUFFIX "${_PLATFORM_PATH}")
|
||||||
|
|
||||||
|
find_library(sodium_LIBRARY_DEBUG libsodium.lib
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}
|
||||||
|
)
|
||||||
|
find_library(sodium_LIBRARY_RELEASE libsodium.lib
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}
|
||||||
|
)
|
||||||
|
if (NOT sodium_USE_STATIC_LIBS)
|
||||||
|
set(CMAKE_FIND_LIBRARY_SUFFIXES_BCK ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||||
|
set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll")
|
||||||
|
find_library(sodium_DLL_DEBUG libsodium
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}
|
||||||
|
)
|
||||||
|
find_library(sodium_DLL_RELEASE libsodium
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}
|
||||||
|
)
|
||||||
|
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_BCK})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
elseif(_GCC_COMPATIBLE)
|
||||||
|
if (sodium_USE_STATIC_LIBS)
|
||||||
|
find_library(sodium_LIBRARY_DEBUG libsodium.a
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES lib
|
||||||
|
)
|
||||||
|
find_library(sodium_LIBRARY_RELEASE libsodium.a
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES lib
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
find_library(sodium_LIBRARY_DEBUG libsodium.dll.a
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES lib
|
||||||
|
)
|
||||||
|
find_library(sodium_LIBRARY_RELEASE libsodium.dll.a
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES lib
|
||||||
|
)
|
||||||
|
|
||||||
|
file(GLOB _DLL
|
||||||
|
LIST_DIRECTORIES false
|
||||||
|
RELATIVE "${sodium_DIR}/bin"
|
||||||
|
"${sodium_DIR}/bin/libsodium*.dll"
|
||||||
|
)
|
||||||
|
find_library(sodium_DLL_DEBUG ${_DLL} libsodium
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES bin
|
||||||
|
)
|
||||||
|
find_library(sodium_DLL_RELEASE ${_DLL} libsodium
|
||||||
|
HINTS ${sodium_DIR}
|
||||||
|
PATH_SUFFIXES bin
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# unsupported
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# common stuff
|
||||||
|
|
||||||
|
# extract sodium version
|
||||||
|
if (sodium_INCLUDE_DIR)
|
||||||
|
set(_VERSION_HEADER "${_INCLUDE_DIR}/sodium/version.h")
|
||||||
|
if (EXISTS _VERSION_HEADER)
|
||||||
|
file(READ "${_VERSION_HEADER}" _VERSION_HEADER_CONTENT)
|
||||||
|
string(REGEX REPLACE ".*#[ \t]*define[ \t]*SODIUM_VERSION_STRING[ \t]*\"([^\n]*)\".*" "\\1"
|
||||||
|
sodium_VERSION "${_VERSION_HEADER_CONTENT}")
|
||||||
|
set(sodium_VERSION "${sodium_VERSION}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# communicate results
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(
|
||||||
|
sodium # The name must be either uppercase or match the filename case.
|
||||||
|
REQUIRED_VARS
|
||||||
|
sodium_LIBRARY_RELEASE
|
||||||
|
sodium_LIBRARY_DEBUG
|
||||||
|
sodium_INCLUDE_DIR
|
||||||
|
VERSION_VAR
|
||||||
|
sodium_VERSION
|
||||||
|
)
|
||||||
|
|
||||||
|
if(Sodium_FOUND)
|
||||||
|
set(sodium_LIBRARIES
|
||||||
|
optimized ${sodium_LIBRARY_RELEASE} debug ${sodium_LIBRARY_DEBUG})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# mark file paths as advanced
|
||||||
|
mark_as_advanced(sodium_INCLUDE_DIR)
|
||||||
|
mark_as_advanced(sodium_LIBRARY_DEBUG)
|
||||||
|
mark_as_advanced(sodium_LIBRARY_RELEASE)
|
||||||
|
if (WIN32)
|
||||||
|
mark_as_advanced(sodium_DLL_DEBUG)
|
||||||
|
mark_as_advanced(sodium_DLL_RELEASE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# create imported target
|
||||||
|
if(sodium_USE_STATIC_LIBS)
|
||||||
|
set(_LIB_TYPE STATIC)
|
||||||
|
else()
|
||||||
|
set(_LIB_TYPE SHARED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT TARGET sodium)
|
||||||
|
add_library(sodium ${_LIB_TYPE} IMPORTED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_target_properties(sodium PROPERTIES
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${sodium_INCLUDE_DIR}"
|
||||||
|
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (sodium_USE_STATIC_LIBS)
|
||||||
|
set_target_properties(sodium PROPERTIES
|
||||||
|
INTERFACE_COMPILE_DEFINITIONS "SODIUM_STATIC"
|
||||||
|
IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}"
|
||||||
|
IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
if (UNIX)
|
||||||
|
set_target_properties(sodium PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}"
|
||||||
|
IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}"
|
||||||
|
)
|
||||||
|
elseif (WIN32)
|
||||||
|
set_target_properties(sodium PROPERTIES
|
||||||
|
IMPORTED_IMPLIB "${sodium_LIBRARY_RELEASE}"
|
||||||
|
IMPORTED_IMPLIB_DEBUG "${sodium_LIBRARY_DEBUG}"
|
||||||
|
)
|
||||||
|
if (NOT (sodium_DLL_DEBUG MATCHES ".*-NOTFOUND"))
|
||||||
|
set_target_properties(sodium PROPERTIES
|
||||||
|
IMPORTED_LOCATION_DEBUG "${sodium_DLL_DEBUG}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
if (NOT (sodium_DLL_RELEASE MATCHES ".*-NOTFOUND"))
|
||||||
|
set_target_properties(sodium PROPERTIES
|
||||||
|
IMPORTED_LOCATION_RELWITHDEBINFO "${sodium_DLL_RELEASE}"
|
||||||
|
IMPORTED_LOCATION_MINSIZEREL "${sodium_DLL_RELEASE}"
|
||||||
|
IMPORTED_LOCATION_RELEASE "${sodium_DLL_RELEASE}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
12
plugins/CMakeLists.txt
Normal file
12
plugins/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.14...3.24 FATAL_ERROR)
|
||||||
|
|
||||||
|
add_library(plugin_ircclient SHARED
|
||||||
|
./plugin_ircclient.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(plugin_ircclient PUBLIC
|
||||||
|
solanaceae_plugin
|
||||||
|
solanaceae_ircclient_contacts
|
||||||
|
solanaceae_ircclient_messages
|
||||||
|
)
|
||||||
|
|
92
plugins/plugin_ircclient.cpp
Normal file
92
plugins/plugin_ircclient.cpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#include <solanaceae/plugin/solana_plugin_v1.h>
|
||||||
|
|
||||||
|
#include <solanaceae/ircclient/ircclient.hpp>
|
||||||
|
#include <solanaceae/ircclient_contacts/ircclient_contact_model.hpp>
|
||||||
|
#include <solanaceae/ircclient_messages/ircclient_message_manager.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#define RESOLVE_INSTANCE(x) static_cast<x*>(solana_api->resolveInstance(#x))
|
||||||
|
#define PROVIDE_INSTANCE(x, p, v) solana_api->provideInstance(#x, p, static_cast<x*>(v))
|
||||||
|
|
||||||
|
static std::unique_ptr<IRCClient1> g_ircc = nullptr;
|
||||||
|
static std::unique_ptr<IRCClientContactModel> g_ircccm = nullptr;
|
||||||
|
static std::unique_ptr<IRCClientMessageManager> g_irccmm = nullptr;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
SOLANA_PLUGIN_EXPORT const char* solana_plugin_get_name(void) {
|
||||||
|
return "IRCClient";
|
||||||
|
}
|
||||||
|
|
||||||
|
SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_get_version(void) {
|
||||||
|
return SOLANA_PLUGIN_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api) {
|
||||||
|
std::cout << "PLUGIN IRCC START()\n";
|
||||||
|
|
||||||
|
if (solana_api == nullptr) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Contact3Registry* cr;
|
||||||
|
RegistryMessageModel* rmm = nullptr;
|
||||||
|
ConfigModelI* conf = nullptr;
|
||||||
|
|
||||||
|
{ // make sure required types are loaded
|
||||||
|
cr = RESOLVE_INSTANCE(Contact3Registry);
|
||||||
|
rmm = RESOLVE_INSTANCE(RegistryMessageModel);
|
||||||
|
conf = RESOLVE_INSTANCE(ConfigModelI);
|
||||||
|
|
||||||
|
if (cr == nullptr) {
|
||||||
|
std::cerr << "PLUGIN IRCC missing Contact3Registry\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rmm == nullptr) {
|
||||||
|
std::cerr << "PLUGIN IRCC missing RegistryMessageModel\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conf == nullptr) {
|
||||||
|
std::cerr << "PLUGIN IRCC missing ConfigModelI\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static store, could be anywhere tho
|
||||||
|
// construct with fetched dependencies
|
||||||
|
g_ircc = std::make_unique<IRCClient1>(*conf);
|
||||||
|
|
||||||
|
// register types
|
||||||
|
PROVIDE_INSTANCE(IRCClient1, "IRCClient", g_ircc.get());
|
||||||
|
|
||||||
|
g_ircccm = std::make_unique<IRCClientContactModel>(*cr, *conf, *g_ircc);
|
||||||
|
|
||||||
|
// register types
|
||||||
|
PROVIDE_INSTANCE(IRCClientContactModel, "IRCClient", g_ircccm.get());
|
||||||
|
|
||||||
|
g_irccmm = std::make_unique<IRCClientMessageManager>(*rmm, *cr, *conf, *g_ircc, *g_ircccm);
|
||||||
|
|
||||||
|
// register types
|
||||||
|
PROVIDE_INSTANCE(IRCClientMessageManager, "IRCClient", g_irccmm.get());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SOLANA_PLUGIN_EXPORT void solana_plugin_stop(void) {
|
||||||
|
std::cout << "PLUGIN IRCC STOP()\n";
|
||||||
|
|
||||||
|
g_ircc.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
SOLANA_PLUGIN_EXPORT void solana_plugin_tick(float delta) {
|
||||||
|
(void)delta;
|
||||||
|
//std::cout << "PLUGIN IRCC TICK()\n";
|
||||||
|
g_ircc->iterate();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern C
|
||||||
|
|
63
src/CMakeLists.txt
Normal file
63
src/CMakeLists.txt
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.9...3.24 FATAL_ERROR)
|
||||||
|
|
||||||
|
project(solanaceae)
|
||||||
|
|
||||||
|
add_library(solanaceae_ircclient
|
||||||
|
./solanaceae/ircclient/ircclient.hpp
|
||||||
|
./solanaceae/ircclient/ircclient.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(solanaceae_ircclient PUBLIC .)
|
||||||
|
target_compile_features(solanaceae_ircclient PRIVATE cxx_std_20)
|
||||||
|
target_compile_features(solanaceae_ircclient INTERFACE cxx_std_17)
|
||||||
|
target_link_libraries(solanaceae_ircclient PUBLIC
|
||||||
|
solanaceae_util
|
||||||
|
libircclient
|
||||||
|
libsodium
|
||||||
|
)
|
||||||
|
|
||||||
|
########################################
|
||||||
|
|
||||||
|
add_library(solanaceae_ircclient_contacts
|
||||||
|
./solanaceae/ircclient_contacts/components.hpp
|
||||||
|
./solanaceae/ircclient_contacts/components_id.inl
|
||||||
|
|
||||||
|
./solanaceae/ircclient_contacts/ircclient_contact_model.hpp
|
||||||
|
./solanaceae/ircclient_contacts/ircclient_contact_model.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(solanaceae_ircclient_contacts PUBLIC .)
|
||||||
|
target_compile_features(solanaceae_ircclient_contacts PRIVATE cxx_std_20)
|
||||||
|
target_compile_features(solanaceae_ircclient_contacts INTERFACE cxx_std_17)
|
||||||
|
target_link_libraries(solanaceae_ircclient_contacts PUBLIC
|
||||||
|
solanaceae_util
|
||||||
|
solanaceae_contact
|
||||||
|
solanaceae_ircclient
|
||||||
|
)
|
||||||
|
|
||||||
|
########################################
|
||||||
|
|
||||||
|
add_library(solanaceae_ircclient_messages
|
||||||
|
./solanaceae/ircclient_messages/ircclient_message_manager.hpp
|
||||||
|
./solanaceae/ircclient_messages/ircclient_message_manager.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(solanaceae_ircclient_messages PUBLIC .)
|
||||||
|
target_compile_features(solanaceae_ircclient_messages PRIVATE cxx_std_20)
|
||||||
|
target_compile_features(solanaceae_ircclient_messages INTERFACE cxx_std_17)
|
||||||
|
target_link_libraries(solanaceae_ircclient_messages PUBLIC
|
||||||
|
solanaceae_ircclient_contacts
|
||||||
|
solanaceae_message3
|
||||||
|
)
|
||||||
|
|
||||||
|
########################################
|
||||||
|
|
||||||
|
add_executable(test2
|
||||||
|
test2.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(test2 PUBLIC
|
||||||
|
solanaceae_ircclient
|
||||||
|
solanaceae_ircclient_contacts
|
||||||
|
solanaceae_ircclient_messages
|
||||||
|
)
|
181
src/solanaceae/ircclient/ircclient.cpp
Normal file
181
src/solanaceae/ircclient/ircclient.cpp
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
#include "./ircclient.hpp"
|
||||||
|
|
||||||
|
#include <bits/iterator_concepts.h>
|
||||||
|
#include <libircclient.h>
|
||||||
|
#include <libirc_rfcnumeric.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <random>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
void IRCClient1::on_event_numeric(irc_session_t* session, unsigned int event, const char* origin, const char** params, unsigned int count) {
|
||||||
|
std::vector<std::string_view> params_view;
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
params_view.push_back(params[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
std::cout << "IRC: event_numeric " << event << " " << origin << "\n";
|
||||||
|
|
||||||
|
for (const auto it : params_view) {
|
||||||
|
std::cout << " " << it << "\n";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto ircc = static_cast<IRCClient1*>(irc_get_ctx(session));
|
||||||
|
ircc->dispatch(IRCClient_Event::NUMERIC, IRCClient::Events::Numeric{event, origin, params_view});
|
||||||
|
}
|
||||||
|
|
||||||
|
IRCClient1::IRCClient1(
|
||||||
|
ConfigModelI& conf
|
||||||
|
) : _conf(conf) {
|
||||||
|
|
||||||
|
static irc_callbacks_t cb{};
|
||||||
|
|
||||||
|
cb.event_numeric = on_event_numeric;
|
||||||
|
|
||||||
|
#define IRC_CB_G(x0, x1, x2) cb.x0 = on_event_generic_new<IRCClient_Event::x1, IRCClient::Events::x2>;
|
||||||
|
|
||||||
|
//cb.event_connect = on_event_generic_new<IRCClientContactModel_Event::CONNECT, IRCClient::Events::Connect>;
|
||||||
|
|
||||||
|
IRC_CB_G(event_connect, CONNECT, Connect);
|
||||||
|
IRC_CB_G(event_nick, NICK, Nick);
|
||||||
|
IRC_CB_G(event_quit, QUIT, Quit);
|
||||||
|
IRC_CB_G(event_join, JOIN, Join);
|
||||||
|
IRC_CB_G(event_part, PART, Part);
|
||||||
|
IRC_CB_G(event_mode, MODE, Mode);
|
||||||
|
IRC_CB_G(event_umode, UMODE, UMode);
|
||||||
|
IRC_CB_G(event_topic, TOPIC, Topic);
|
||||||
|
IRC_CB_G(event_kick, KICK, Kick);
|
||||||
|
|
||||||
|
IRC_CB_G(event_channel, CHANNEL, Channel);
|
||||||
|
|
||||||
|
IRC_CB_G(event_privmsg, PRIVMSG, PrivMSG);
|
||||||
|
IRC_CB_G(event_notice, NOTICE, Notice);
|
||||||
|
IRC_CB_G(event_channel_notice, CHANNELNOTICE, ChannelNotice);
|
||||||
|
IRC_CB_G(event_invite, INVITE, Invite);
|
||||||
|
|
||||||
|
IRC_CB_G(event_ctcp_req, CTCP_REQ, CTCP_Req);
|
||||||
|
IRC_CB_G(event_ctcp_rep, CTCP_REP, CTCP_Rep);
|
||||||
|
IRC_CB_G(event_ctcp_action, CTCP_ACTION, CTCP_Action);
|
||||||
|
|
||||||
|
IRC_CB_G(event_unknown, UNKNOWN, Unknown);
|
||||||
|
|
||||||
|
#undef IRC_CB_G
|
||||||
|
|
||||||
|
// TODO: dcc
|
||||||
|
//irc_event_dcc_chat_t event_dcc_chat_req;
|
||||||
|
//irc_event_dcc_send_t event_dcc_send_req;
|
||||||
|
|
||||||
|
|
||||||
|
_irc_session = irc_create_session(&cb);
|
||||||
|
irc_set_ctx(_irc_session, this);
|
||||||
|
|
||||||
|
irc_option_set(_irc_session, LIBIRC_OPTION_DEBUG);
|
||||||
|
irc_option_set(_irc_session, LIBIRC_OPTION_STRIPNICKS);
|
||||||
|
irc_option_set(_irc_session, LIBIRC_OPTION_SSL_NO_VERIFY); // why
|
||||||
|
|
||||||
|
|
||||||
|
if (!_conf.has_string("IRCClient", "server")) {
|
||||||
|
std::cerr << "IRCC error: no irc server in config!!\n";
|
||||||
|
throw std::runtime_error("missing server in config");
|
||||||
|
}
|
||||||
|
|
||||||
|
// if server is prefixed with '#', its ssl
|
||||||
|
std::string server = _conf.get_string("IRCClient", "server").value();
|
||||||
|
_server_name = server; // TODO: find a better solution
|
||||||
|
int64_t port = _conf.get_int("IRCClient", "port").value_or(6660);
|
||||||
|
// TODO: password
|
||||||
|
|
||||||
|
std::string nick;
|
||||||
|
if (_conf.has_string("IRCClient", "nick")) {
|
||||||
|
nick = _conf.get_string("IRCClient", "nick").value();
|
||||||
|
} else {
|
||||||
|
nick = "solanaceae_guest_" + std::to_string(std::random_device{}() % 10'000);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string username;
|
||||||
|
if (_conf.has_string("IRCClient", "username")) {
|
||||||
|
username = _conf.get_string("IRCClient", "username").value();
|
||||||
|
} else {
|
||||||
|
username = nick + "_";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string realname;
|
||||||
|
if (_conf.has_string("IRCClient", "realname")) {
|
||||||
|
realname = _conf.get_string("IRCClient", "realname").value();
|
||||||
|
} else {
|
||||||
|
realname = username + "_";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irc_connect(_irc_session, server.c_str(), port, nullptr, nick.c_str(), username.c_str(), realname.c_str()) != 0) {
|
||||||
|
std::cerr << "error failed to connect: (" << irc_errno(_irc_session) << ") " << irc_strerror(irc_errno(_irc_session)) << "\n";
|
||||||
|
throw std::runtime_error("failed to connect to irc");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IRCClient1::~IRCClient1(void) {
|
||||||
|
irc_destroy_session(_irc_session);
|
||||||
|
}
|
||||||
|
|
||||||
|
// tmp
|
||||||
|
void IRCClient1::run(void) {
|
||||||
|
if (irc_run(_irc_session) != 0) {
|
||||||
|
std::cerr << "error failed to run: " << irc_strerror(irc_errno(_irc_session)) << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRCClient1::iterate(void) {
|
||||||
|
//if ( session->state != LIBIRC_STATE_CONNECTING )
|
||||||
|
//{
|
||||||
|
//session->lasterror = LIBIRC_ERR_STATE;
|
||||||
|
//return 1;
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (!irc_is_connected(_irc_session)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval tv;
|
||||||
|
fd_set in_set, out_set;
|
||||||
|
int maxfd = 0;
|
||||||
|
|
||||||
|
//tv.tv_usec = 20000; // 20ms
|
||||||
|
tv.tv_usec = 1000; // 1ms
|
||||||
|
tv.tv_sec = 0;
|
||||||
|
|
||||||
|
// Init sets
|
||||||
|
FD_ZERO (&in_set);
|
||||||
|
FD_ZERO (&out_set);
|
||||||
|
|
||||||
|
irc_add_select_descriptors(_irc_session, &in_set, &out_set, &maxfd);
|
||||||
|
|
||||||
|
if (select(maxfd + 1, &in_set, &out_set, 0, &tv) < 0)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
if (socket_error() == EINTR) {
|
||||||
|
//continue;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//session->lasterror = LIBIRC_ERR_TERMINATED;
|
||||||
|
//return 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irc_process_select_descriptors(_irc_session, &in_set, &out_set)) {
|
||||||
|
//return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
irc_session_t* IRCClient1::getSession(void) {
|
||||||
|
return _irc_session;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string_view IRCClient1::getServerName(void) const {
|
||||||
|
return _server_name;
|
||||||
|
}
|
225
src/solanaceae/ircclient/ircclient.hpp
Normal file
225
src/solanaceae/ircclient/ircclient.hpp
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <solanaceae/util/config_model.hpp>
|
||||||
|
#include <solanaceae/util/event_provider.hpp>
|
||||||
|
|
||||||
|
#include <iostream> // tmp
|
||||||
|
|
||||||
|
// fwd
|
||||||
|
struct irc_session_s;
|
||||||
|
using irc_session_t = irc_session_s;
|
||||||
|
extern "C" void* irc_get_ctx(irc_session_t* session);
|
||||||
|
|
||||||
|
namespace IRCClient::Events {
|
||||||
|
|
||||||
|
// TODO: proper param seperation
|
||||||
|
|
||||||
|
struct Numeric {
|
||||||
|
unsigned int event;
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Connect {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Nick {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Quit {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Join {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Part {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Mode {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UMode {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Topic {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Kick {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Channel {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PrivMSG {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Notice {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChannelNotice {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Invite {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CTCP_Req {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CTCP_Rep {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CTCP_Action {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Unknown {
|
||||||
|
std::string_view origin;
|
||||||
|
std::vector<std::string_view> params;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // Events
|
||||||
|
|
||||||
|
enum class IRCClient_Event : uint32_t {
|
||||||
|
NUMERIC,
|
||||||
|
CONNECT,
|
||||||
|
NICK,
|
||||||
|
QUIT,
|
||||||
|
JOIN,
|
||||||
|
PART,
|
||||||
|
MODE,
|
||||||
|
UMODE,
|
||||||
|
TOPIC,
|
||||||
|
KICK,
|
||||||
|
CHANNEL,
|
||||||
|
PRIVMSG,
|
||||||
|
NOTICE,
|
||||||
|
CHANNELNOTICE,
|
||||||
|
INVITE,
|
||||||
|
|
||||||
|
CTCP_REQ,
|
||||||
|
CTCP_REP,
|
||||||
|
CTCP_ACTION,
|
||||||
|
|
||||||
|
UNKNOWN,
|
||||||
|
|
||||||
|
MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IRCClientEventI {
|
||||||
|
using enumType = IRCClient_Event;
|
||||||
|
|
||||||
|
virtual ~IRCClientEventI(void) {}
|
||||||
|
|
||||||
|
virtual bool onEvent(const IRCClient::Events::Numeric&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::Connect&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::Nick&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::Quit&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::Join&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::Part&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::Mode&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::UMode&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::Topic&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::Kick&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::Channel&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::PrivMSG&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::Notice&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::ChannelNotice&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::Invite&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::CTCP_Req&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::CTCP_Rep&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::CTCP_Action&) { return false; }
|
||||||
|
virtual bool onEvent(const IRCClient::Events::Unknown&) { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
using IRCClientEventProviderI = EventProviderI<IRCClientEventI>;
|
||||||
|
|
||||||
|
// one network per instance only
|
||||||
|
class IRCClient1 : public IRCClientEventProviderI {
|
||||||
|
ConfigModelI& _conf;
|
||||||
|
|
||||||
|
irc_session_t* _irc_session = nullptr;
|
||||||
|
|
||||||
|
std::string _server_name; // name of the irc network this iirc is connected to
|
||||||
|
|
||||||
|
public:
|
||||||
|
IRCClient1(
|
||||||
|
ConfigModelI& conf
|
||||||
|
);
|
||||||
|
|
||||||
|
~IRCClient1(void);
|
||||||
|
|
||||||
|
// tmp
|
||||||
|
void run(void);
|
||||||
|
void iterate(void);
|
||||||
|
|
||||||
|
// raw access
|
||||||
|
irc_session_t* getSession(void);
|
||||||
|
|
||||||
|
const std::string_view getServerName(void) const;
|
||||||
|
|
||||||
|
// join
|
||||||
|
void join(std::string_view channel);
|
||||||
|
|
||||||
|
private: // callbacks for libircclient
|
||||||
|
static void on_event_numeric(irc_session_t* session, unsigned int event, const char* origin, const char** params, unsigned int count);
|
||||||
|
|
||||||
|
template<IRCClient_Event event_type_enum, typename EventType>
|
||||||
|
static void on_event_generic_new(irc_session_t* session, const char* event, const char* origin, const char** params, unsigned int count) {
|
||||||
|
std::vector<std::string_view> params_view;
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
params_view.push_back(params[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "IRC: event " << event << " " << origin << "\n";
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (std::string_view{event} == "ACTION") {
|
||||||
|
std::cout << " -action is " << typeid(EventType).name() << "\n";
|
||||||
|
std::cout << " -enum is " << (int)event_type_enum << "\n";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (const auto it : params_view) {
|
||||||
|
std::cout << " " << it << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* ircc = static_cast<IRCClient1*>(irc_get_ctx(session));
|
||||||
|
assert(ircc != nullptr);
|
||||||
|
|
||||||
|
ircc->dispatch(event_type_enum, EventType{origin, params_view});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
27
src/solanaceae/ircclient_contacts/components.hpp
Normal file
27
src/solanaceae/ircclient_contacts/components.hpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Contact::Components::IRC {
|
||||||
|
|
||||||
|
struct ServerName {
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChannelName {
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UserName {
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// - membership level in channels
|
||||||
|
// - dcc stuff
|
||||||
|
// - tags for server channel user?
|
||||||
|
|
||||||
|
} // Contact::Components::IRC
|
||||||
|
|
||||||
|
#include "./components_id.inl"
|
||||||
|
|
22
src/solanaceae/ircclient_contacts/components_id.inl
Normal file
22
src/solanaceae/ircclient_contacts/components_id.inl
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./components.hpp"
|
||||||
|
|
||||||
|
#include <entt/core/type_info.hpp>
|
||||||
|
|
||||||
|
// TODO: move more central
|
||||||
|
#define DEFINE_COMP_ID(x) \
|
||||||
|
template<> \
|
||||||
|
constexpr entt::id_type entt::type_hash<x>::value() noexcept { \
|
||||||
|
using namespace entt::literals; \
|
||||||
|
return #x##_hs; \
|
||||||
|
}
|
||||||
|
|
||||||
|
// cross compiler stable ids
|
||||||
|
|
||||||
|
DEFINE_COMP_ID(Contact::Components::IRC::ServerName)
|
||||||
|
DEFINE_COMP_ID(Contact::Components::IRC::ChannelName)
|
||||||
|
DEFINE_COMP_ID(Contact::Components::IRC::UserName)
|
||||||
|
|
||||||
|
#undef DEFINE_COMP_ID
|
||||||
|
|
412
src/solanaceae/ircclient_contacts/ircclient_contact_model.cpp
Normal file
412
src/solanaceae/ircclient_contacts/ircclient_contact_model.cpp
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
#include "./ircclient_contact_model.hpp"
|
||||||
|
|
||||||
|
#include "./components.hpp"
|
||||||
|
|
||||||
|
#include <sodium/crypto_hash_sha256.h>
|
||||||
|
#include <solanaceae/contact/components.hpp>
|
||||||
|
|
||||||
|
#include <libirc_rfcnumeric.h>
|
||||||
|
#include <libircclient.h>
|
||||||
|
|
||||||
|
#include <sodium.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
IRCClientContactModel::IRCClientContactModel(
|
||||||
|
Contact3Registry& cr,
|
||||||
|
ConfigModelI& conf,
|
||||||
|
IRCClient1& ircc
|
||||||
|
) : _cr(cr), _conf(conf), _ircc(ircc) {
|
||||||
|
_ircc.subscribe(this, IRCClient_Event::CONNECT);
|
||||||
|
|
||||||
|
_ircc.subscribe(this, IRCClient_Event::NUMERIC);
|
||||||
|
|
||||||
|
_ircc.subscribe(this, IRCClient_Event::JOIN);
|
||||||
|
_ircc.subscribe(this, IRCClient_Event::PART);
|
||||||
|
_ircc.subscribe(this, IRCClient_Event::QUIT);
|
||||||
|
|
||||||
|
_ircc.subscribe(this, IRCClient_Event::CTCP_REQ);
|
||||||
|
|
||||||
|
// dont create server self etc until connect event comes
|
||||||
|
|
||||||
|
for (const auto& [channel, should_join] : _conf.entries_bool("IRCClient", "autojoin")) {
|
||||||
|
if (should_join) {
|
||||||
|
std::cout << "IRCCCM: autojoining " << channel << "\n";
|
||||||
|
join(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IRCClientContactModel::~IRCClientContactModel(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRCClientContactModel::join(const std::string& channel) {
|
||||||
|
if (_connected) {
|
||||||
|
irc_cmd_join(
|
||||||
|
_ircc.getSession(),
|
||||||
|
channel.c_str(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
std::cout << "IRCCCM: connected joining channel...\n";
|
||||||
|
} else {
|
||||||
|
_join_queue.push(channel);
|
||||||
|
std::cout << "IRCCCM: not connected yet, queued join...\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> IRCClientContactModel::getHash(std::string_view value) {
|
||||||
|
assert(!value.empty());
|
||||||
|
|
||||||
|
std::vector<uint8_t> hash(crypto_hash_sha256_bytes(), 0x00);
|
||||||
|
crypto_hash_sha256(hash.data(), reinterpret_cast<const uint8_t*>(value.data()), value.size());
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> IRCClientContactModel::getIDHash(std::string_view name) {
|
||||||
|
assert(!_server_hash.empty());
|
||||||
|
assert(!name.empty());
|
||||||
|
|
||||||
|
std::vector<uint8_t> data = _server_hash;
|
||||||
|
data.insert(data.end(), name.begin(), name.end());
|
||||||
|
return getHash(std::string_view{reinterpret_cast<const char*>(data.data()), data.size()});
|
||||||
|
}
|
||||||
|
|
||||||
|
Contact3Handle IRCClientContactModel::getC(std::string_view channel) {
|
||||||
|
const auto server_name = _ircc.getServerName();
|
||||||
|
// TODO: this needs a better way
|
||||||
|
for (const auto e : _cr.view<Contact::Components::IRC::ServerName, Contact::Components::IRC::ChannelName>()) {
|
||||||
|
if (_cr.get<Contact::Components::IRC::ServerName>(e).name == server_name && _cr.get<Contact::Components::IRC::ChannelName>(e).name == channel) {
|
||||||
|
return {_cr, e};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {_cr, entt::null};
|
||||||
|
}
|
||||||
|
|
||||||
|
Contact3Handle IRCClientContactModel::getU(std::string_view nick) {
|
||||||
|
const auto server_name = _ircc.getServerName();
|
||||||
|
// TODO: this needs a better way
|
||||||
|
for (const auto e : _cr.view<Contact::Components::IRC::ServerName, Contact::Components::IRC::UserName>()) {
|
||||||
|
if (_cr.get<Contact::Components::IRC::ServerName>(e).name == server_name && _cr.get<Contact::Components::IRC::UserName>(e).name == nick) {
|
||||||
|
return {_cr, e};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {_cr, entt::null};
|
||||||
|
}
|
||||||
|
|
||||||
|
Contact3Handle IRCClientContactModel::getCU(std::string_view name) {
|
||||||
|
if (name.empty()) {
|
||||||
|
return {_cr, entt::null};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::string_view channel_prefixes{
|
||||||
|
// rfc 1459 1.3
|
||||||
|
"&" // local
|
||||||
|
"#" // regular
|
||||||
|
|
||||||
|
// rfc 2812 1.3
|
||||||
|
"+"
|
||||||
|
"!"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (channel_prefixes.find(name.front()) != std::string_view::npos) {
|
||||||
|
return getC(name);
|
||||||
|
} else {
|
||||||
|
return getU(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRCClientContactModel::onEvent(const IRCClient::Events::Connect& e) {
|
||||||
|
_server_hash = getHash(_ircc.getServerName());
|
||||||
|
_connected = true;
|
||||||
|
|
||||||
|
{ // server
|
||||||
|
if (!_cr.valid(_server)) {
|
||||||
|
_server = _cr.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
_cr.emplace_or_replace<Contact::Components::ContactModel>(_server, this);
|
||||||
|
_cr.emplace_or_replace<Contact::Components::IRC::ServerName>(_server, std::string{_ircc.getServerName()}); // really?
|
||||||
|
_cr.emplace_or_replace<Contact::Components::Name>(_server, std::string{_ircc.getServerName()}); // TODO: add special string?
|
||||||
|
_cr.emplace_or_replace<Contact::Components::ID>(_server, _server_hash);
|
||||||
|
|
||||||
|
// does this make sense ?
|
||||||
|
_cr.emplace_or_replace<Contact::Components::ConnectionState>(_server, Contact::Components::ConnectionState::State::direct);
|
||||||
|
|
||||||
|
_cr.emplace_or_replace<Contact::Components::TagBig>(_server);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // self
|
||||||
|
if (!_cr.valid(_self)) {
|
||||||
|
_self = _cr.create();
|
||||||
|
}
|
||||||
|
_cr.emplace_or_replace<Contact::Components::ContactModel>(_self, this);
|
||||||
|
_cr.emplace_or_replace<Contact::Components::TagSelfStrong>(_self);
|
||||||
|
_cr.emplace_or_replace<Contact::Components::IRC::ServerName>(_self, std::string{_ircc.getServerName()}); // really?
|
||||||
|
if (!e.params.empty()) {
|
||||||
|
_cr.emplace_or_replace<Contact::Components::IRC::UserName>(_self, std::string{e.params.front()});
|
||||||
|
_cr.emplace_or_replace<Contact::Components::Name>(_self, std::string{e.params.front()});
|
||||||
|
// make id hash(hash(ServerName)+UserName)
|
||||||
|
// or irc name format, but those might cause collisions
|
||||||
|
_cr.emplace_or_replace<Contact::Components::ID>(_self, getIDHash(e.params.front()));
|
||||||
|
}
|
||||||
|
|
||||||
|
_cr.emplace_or_replace<Contact::Components::ConnectionState>(_self, Contact::Components::ConnectionState::State::cloud);
|
||||||
|
|
||||||
|
// add self to server
|
||||||
|
_cr.emplace_or_replace<Contact::Components::Self>(_server, _self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// join queued
|
||||||
|
while (!_join_queue.empty()) {
|
||||||
|
irc_cmd_join(
|
||||||
|
_ircc.getSession(),
|
||||||
|
_join_queue.front().c_str(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
_join_queue.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRCClientContactModel::onEvent(const IRCClient::Events::Numeric& e) {
|
||||||
|
if (e.event == LIBIRC_RFC_RPL_NAMREPLY) {
|
||||||
|
// user list
|
||||||
|
// e.origin is the server
|
||||||
|
// e.params.at(0) user (self)
|
||||||
|
// e.params.at(1) =
|
||||||
|
// e.params.at(2) channel
|
||||||
|
// e.params.at(3) list of users space seperated with power prefixed
|
||||||
|
if (e.params.size() != 4) {
|
||||||
|
// error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.params.at(1) != "=") {
|
||||||
|
// error, unexpected
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& channel_name = e.params.at(2);
|
||||||
|
auto channel = getC(channel_name);
|
||||||
|
if (!channel.valid()) {
|
||||||
|
std::cerr << "IRCCM error: name list for unknown channel\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view user_list = e.params.at(3);
|
||||||
|
|
||||||
|
std::string_view::size_type space_pos;
|
||||||
|
do {
|
||||||
|
space_pos = user_list.find_first_of(' ');
|
||||||
|
auto user_str = user_list.substr(0, space_pos);
|
||||||
|
{ // handle user
|
||||||
|
// rfc 2812 5.1
|
||||||
|
// The '@' and '+' characters next to the channel name
|
||||||
|
// indicate whether a client is a channel operator or
|
||||||
|
// has been granted permission to speak on a moderated
|
||||||
|
// channel.
|
||||||
|
|
||||||
|
if (user_str.empty()) {
|
||||||
|
std::cerr << "IRCCCM error: empty user\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://modern.ircdocs.horse/#channel-membership-prefixes
|
||||||
|
static constexpr std::string_view membership_prefixes{
|
||||||
|
"~" // founder
|
||||||
|
"&" // protected
|
||||||
|
"@" // operator
|
||||||
|
"%" // half operator
|
||||||
|
"+" // voice
|
||||||
|
};
|
||||||
|
if (membership_prefixes.find(user_str.front()) != std::string_view::npos) {
|
||||||
|
switch (user_str.front()) {
|
||||||
|
// TODO: use this info
|
||||||
|
case '~': break;
|
||||||
|
case '&': break;
|
||||||
|
case '@': break;
|
||||||
|
case '%': break;
|
||||||
|
case '+': break;
|
||||||
|
}
|
||||||
|
user_str = user_str.substr(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_str.empty()) {
|
||||||
|
std::cerr << "IRCCCM error: empty user after removing membership prefix\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::cout << "u: " << user_str << "\n";
|
||||||
|
|
||||||
|
auto user = getU(user_str);
|
||||||
|
if (!user.valid()) {
|
||||||
|
user = {_cr, _cr.create()};
|
||||||
|
|
||||||
|
user.emplace<Contact::Components::ContactModel>(this);
|
||||||
|
user.emplace<Contact::Components::IRC::ServerName>(std::string{_ircc.getServerName()});
|
||||||
|
// channel list?
|
||||||
|
// add to channel?
|
||||||
|
user.emplace<Contact::Components::IRC::UserName>(std::string{user_str});
|
||||||
|
user.emplace<Contact::Components::Name>(std::string{user_str});
|
||||||
|
user.emplace<Contact::Components::ID>(getIDHash(user_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.entity() != _self) {
|
||||||
|
user.emplace_or_replace<Contact::Components::ConnectionState>(Contact::Components::ConnectionState::State::cloud);
|
||||||
|
user.emplace_or_replace<Contact::Components::Self>(_self);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // add user to channel
|
||||||
|
auto& channel_user_list = channel.get_or_emplace<Contact::Components::ParentOf>().subs;
|
||||||
|
if (std::find(channel_user_list.begin(), channel_user_list.end(), user) == channel_user_list.end()) {
|
||||||
|
//std::cout << "!!!!!!!! new user in channel!\n";
|
||||||
|
channel_user_list.push_back(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (space_pos == std::string_view::npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim user
|
||||||
|
user_list = user_list.substr(space_pos);
|
||||||
|
const auto next_non_space = user_list.find_first_not_of(' ');
|
||||||
|
if (next_non_space == std::string_view::npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
user_list = user_list.substr(next_non_space);
|
||||||
|
} while (space_pos != std::string_view::npos);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRCClientContactModel::onEvent(const IRCClient::Events::Join& e) {
|
||||||
|
if (e.params.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& joined_channel_name = e.params.front();
|
||||||
|
|
||||||
|
//std::cout << "JOIN!!!! " << e.origin << " in " << joined_channel_name << "\n";
|
||||||
|
|
||||||
|
auto channel = getC(e.params.front());
|
||||||
|
if (!channel.valid()) {
|
||||||
|
channel = {_cr, _cr.create()};
|
||||||
|
channel.emplace_or_replace<Contact::Components::ContactModel>(this);
|
||||||
|
channel.emplace_or_replace<Contact::Components::IRC::ServerName>(std::string{_ircc.getServerName()});
|
||||||
|
channel.emplace_or_replace<Contact::Components::IRC::ChannelName>(std::string{joined_channel_name});
|
||||||
|
channel.emplace_or_replace<Contact::Components::Name>(std::string{joined_channel_name});
|
||||||
|
|
||||||
|
channel.emplace_or_replace<Contact::Components::ID>(getIDHash(joined_channel_name));
|
||||||
|
|
||||||
|
channel.emplace_or_replace<Contact::Components::ConnectionState>(Contact::Components::ConnectionState::State::cloud);
|
||||||
|
|
||||||
|
channel.emplace<Contact::Components::TagBig>();
|
||||||
|
|
||||||
|
// add self to channel
|
||||||
|
channel.emplace_or_replace<Contact::Components::Self>(_self);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto user = getU(e.origin);
|
||||||
|
if (!user.valid()) {
|
||||||
|
user = {_cr, _cr.create()};
|
||||||
|
|
||||||
|
user.emplace<Contact::Components::ContactModel>(this);
|
||||||
|
user.emplace<Contact::Components::IRC::ServerName>(std::string{_ircc.getServerName()});
|
||||||
|
// channel list?
|
||||||
|
// add to channel?
|
||||||
|
user.emplace<Contact::Components::IRC::UserName>(std::string{e.origin});
|
||||||
|
user.emplace<Contact::Components::Name>(std::string{e.origin});
|
||||||
|
user.emplace<Contact::Components::ID>(getIDHash(e.origin));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.entity() != _self) {
|
||||||
|
user.emplace_or_replace<Contact::Components::ConnectionState>(Contact::Components::ConnectionState::State::cloud);
|
||||||
|
user.emplace_or_replace<Contact::Components::Self>(_self);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // add user to channel
|
||||||
|
auto& channel_user_list = channel.get_or_emplace<Contact::Components::ParentOf>().subs;
|
||||||
|
if (std::find(channel_user_list.begin(), channel_user_list.end(), user) == channel_user_list.end()) {
|
||||||
|
//std::cout << "!!!!!!!! new user in channel!\n";
|
||||||
|
channel_user_list.push_back(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRCClientContactModel::onEvent(const IRCClient::Events::Part& e) {
|
||||||
|
if (e.params.size() < 1) {
|
||||||
|
// error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.origin // is the parting user
|
||||||
|
auto user = getU(e.origin);
|
||||||
|
if (!user.valid()) {
|
||||||
|
// ignoring unknown users, might be caused by a bug
|
||||||
|
std::cerr << "ignoring unknown users, might be caused by a bug\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.params.front() is the channel
|
||||||
|
auto channel = getC(e.params.front());
|
||||||
|
if (!channel.valid()) {
|
||||||
|
// ignoring unknown channel, might be caused by a bug
|
||||||
|
std::cerr << "ignoring unknown channel, might be caused by a bug\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // remove user from channel
|
||||||
|
auto& channel_user_list = channel.get_or_emplace<Contact::Components::ParentOf>().subs;
|
||||||
|
if (auto it = std::find(channel_user_list.begin(), channel_user_list.end(), user); it != channel_user_list.end()) {
|
||||||
|
//std::cout << "!!!!!!!! removing user from channel!\n";
|
||||||
|
channel_user_list.erase(it);
|
||||||
|
} else {
|
||||||
|
//std::cout << "!!!!!!!! unknown user leaving channel!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRCClientContactModel::onEvent(const IRCClient::Events::Quit& e) {
|
||||||
|
// e.origin // is the quitting user
|
||||||
|
|
||||||
|
// e.params.front() is the quit reason
|
||||||
|
|
||||||
|
auto user = getU(e.origin);
|
||||||
|
if (!user.valid()) {
|
||||||
|
// ignoring unknown users, might be caused by a bug
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.entity() != _self) {
|
||||||
|
user.emplace_or_replace<Contact::Components::ConnectionState>(Contact::Components::ConnectionState::State::disconnected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// should we remove the user from the channel?
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRCClientContactModel::onEvent(const IRCClient::Events::CTCP_Req& e) {
|
||||||
|
if (e.params.size() < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.params.front() == "VERSION") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <solanaceae/contact/contact_model3.hpp>
|
||||||
|
#include <solanaceae/util/config_model.hpp>
|
||||||
|
|
||||||
|
#include <solanaceae/ircclient/ircclient.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <queue>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <iostream> // tmp
|
||||||
|
|
||||||
|
class IRCClientContactModel : public IRCClientEventI, public ContactModel3I {
|
||||||
|
Contact3Registry& _cr;
|
||||||
|
ConfigModelI& _conf;
|
||||||
|
IRCClient1& _ircc;
|
||||||
|
|
||||||
|
// cm needs the connect event to happen
|
||||||
|
bool _connected {false};
|
||||||
|
|
||||||
|
std::vector<uint8_t> _server_hash; // cached for id gen
|
||||||
|
Contact3 _server = entt::null;
|
||||||
|
Contact3 _self = entt::null;
|
||||||
|
|
||||||
|
// used if not connected
|
||||||
|
std::queue<std::string> _join_queue;
|
||||||
|
|
||||||
|
public:
|
||||||
|
IRCClientContactModel(
|
||||||
|
Contact3Registry& cr,
|
||||||
|
ConfigModelI& conf,
|
||||||
|
IRCClient1& ircc
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual ~IRCClientContactModel(void);
|
||||||
|
|
||||||
|
void join(const std::string& channel);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// just the hash algo
|
||||||
|
std::vector<uint8_t> getHash(std::string_view value);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// the actually ID is a chain containing the server+channel or server+name
|
||||||
|
// eg: hash(hash(ServerName)+ChannelName)
|
||||||
|
std::vector<uint8_t> getIDHash(std::string_view name);
|
||||||
|
|
||||||
|
Contact3Handle getC(std::string_view channel);
|
||||||
|
Contact3Handle getU(std::string_view nick);
|
||||||
|
// user or channel using channel prefix
|
||||||
|
Contact3Handle getCU(std::string_view name);
|
||||||
|
|
||||||
|
private: // ircclient
|
||||||
|
bool onEvent(const IRCClient::Events::Connect& e) override;
|
||||||
|
bool onEvent(const IRCClient::Events::Numeric& e) override;
|
||||||
|
bool onEvent(const IRCClient::Events::Join& e) override;
|
||||||
|
bool onEvent(const IRCClient::Events::Part& e) override;
|
||||||
|
bool onEvent(const IRCClient::Events::Quit& e) override;
|
||||||
|
bool onEvent(const IRCClient::Events::CTCP_Req&) override;
|
||||||
|
};
|
296
src/solanaceae/ircclient_messages/ircclient_message_manager.cpp
Normal file
296
src/solanaceae/ircclient_messages/ircclient_message_manager.cpp
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
#include "./ircclient_message_manager.hpp"
|
||||||
|
|
||||||
|
#include <solanaceae/ircclient_contacts/components.hpp>
|
||||||
|
|
||||||
|
#include <solanaceae/contact/components.hpp>
|
||||||
|
#include <solanaceae/message3/components.hpp>
|
||||||
|
#include <solanaceae/message3/registry_message_model.hpp>
|
||||||
|
|
||||||
|
#include <libirc_rfcnumeric.h>
|
||||||
|
#include <libircclient.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
//namespace Components {
|
||||||
|
//struct ServerName {
|
||||||
|
//std::string name;
|
||||||
|
//};
|
||||||
|
//} // Components
|
||||||
|
|
||||||
|
IRCClientMessageManager::IRCClientMessageManager(
|
||||||
|
RegistryMessageModel& rmm,
|
||||||
|
Contact3Registry& cr,
|
||||||
|
ConfigModelI& conf,
|
||||||
|
IRCClient1& ircc,
|
||||||
|
IRCClientContactModel& ircccm
|
||||||
|
) : _rmm(rmm), _cr(cr), _conf(conf), _ircc(ircc), _ircccm(ircccm) {
|
||||||
|
_ircc.subscribe(this, IRCClient_Event::CHANNEL);
|
||||||
|
_ircc.subscribe(this, IRCClient_Event::PRIVMSG);
|
||||||
|
_ircc.subscribe(this, IRCClient_Event::NOTICE);
|
||||||
|
_ircc.subscribe(this, IRCClient_Event::CHANNELNOTICE);
|
||||||
|
_ircc.subscribe(this, IRCClient_Event::CTCP_ACTION);
|
||||||
|
|
||||||
|
_rmm.subscribe(this, RegistryMessageModel_Event::send_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
IRCClientMessageManager::~IRCClientMessageManager(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRCClientMessageManager::processMessage(Contact3Handle from, Contact3Handle to, std::string_view message_text, bool action) {
|
||||||
|
const uint64_t ts = Message::getTimeMS();
|
||||||
|
|
||||||
|
Message3Registry* reg_ptr = nullptr;
|
||||||
|
if (to.all_of<Contact::Components::TagSelfStrong>()) {
|
||||||
|
reg_ptr = _rmm.get(from);
|
||||||
|
} else {
|
||||||
|
reg_ptr = _rmm.get(to);
|
||||||
|
}
|
||||||
|
if (reg_ptr == nullptr) {
|
||||||
|
std::cerr << "IRCCMM error: cant find reg\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check for existence, hs or other syncing mechanics might have sent it already (or like, it arrived 2x or whatever)
|
||||||
|
auto new_msg = Message3Handle{*reg_ptr, reg_ptr->create()};
|
||||||
|
|
||||||
|
{ // contact
|
||||||
|
// from
|
||||||
|
new_msg.emplace<Message::Components::ContactFrom>(from);
|
||||||
|
|
||||||
|
// to
|
||||||
|
new_msg.emplace<Message::Components::ContactTo>(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
// no message id :(
|
||||||
|
|
||||||
|
new_msg.emplace<Message::Components::MessageText>(message_text);
|
||||||
|
|
||||||
|
if (action) {
|
||||||
|
new_msg.emplace<Message::Components::TagMessageIsAction>();
|
||||||
|
}
|
||||||
|
|
||||||
|
new_msg.emplace<Message::Components::TimestampProcessed>(ts);
|
||||||
|
new_msg.emplace<Message::Components::Timestamp>(ts); // reactive?
|
||||||
|
|
||||||
|
new_msg.emplace<Message::Components::TagUnread>();
|
||||||
|
|
||||||
|
_rmm.throwEventConstruct(new_msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRCClientMessageManager::sendText(const Contact3 c, std::string_view message, bool action) {
|
||||||
|
if (!_cr.valid(c)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.empty()) {
|
||||||
|
return false; // TODO: empty messages allowed?
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t ts = Message::getTimeMS();
|
||||||
|
|
||||||
|
if (_cr.all_of<Contact::Components::TagSelfStrong>(c)) {
|
||||||
|
return false; // message to self? not with irc
|
||||||
|
}
|
||||||
|
|
||||||
|
// test for contact irc specific components
|
||||||
|
// TODO: what about commands and server messages?
|
||||||
|
if (!_cr.any_of<Contact::Components::IRC::UserName, Contact::Components::IRC::ChannelName>(c)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_str;
|
||||||
|
if (_cr.all_of<Contact::Components::IRC::UserName>(c)) {
|
||||||
|
to_str = _cr.get<Contact::Components::IRC::UserName>(c).name;
|
||||||
|
} else {
|
||||||
|
to_str = _cr.get<Contact::Components::IRC::ChannelName>(c).name;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* reg_ptr = _rmm.get(c);
|
||||||
|
if (reg_ptr == nullptr) {
|
||||||
|
return false; // nope
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_cr.all_of<Contact::Components::Self>(c)) {
|
||||||
|
std::cerr << "IRCCMM error: cant get self\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // actually send
|
||||||
|
std::string tmp_message{message}; // string_view might not be nul terminated
|
||||||
|
if (action) {
|
||||||
|
if (irc_cmd_me(_ircc.getSession(), to_str.c_str(), tmp_message.c_str()) != 0) {
|
||||||
|
std::cerr << "IRCCMM error: failed to send action\n";
|
||||||
|
|
||||||
|
// we dont have offline messaging in irc
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (irc_cmd_msg(_ircc.getSession(), to_str.c_str(), tmp_message.c_str()) != 0) {
|
||||||
|
std::cerr << "IRCCMM error: failed to send message\n";
|
||||||
|
|
||||||
|
// we dont have offline messaging in irc
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Contact3 c_self = _cr.get<Contact::Components::Self>(c).self;
|
||||||
|
|
||||||
|
auto new_msg = Message3Handle{*reg_ptr, reg_ptr->create()};
|
||||||
|
|
||||||
|
new_msg.emplace<Message::Components::ContactFrom>(c_self);
|
||||||
|
new_msg.emplace<Message::Components::ContactTo>(c);
|
||||||
|
|
||||||
|
new_msg.emplace<Message::Components::MessageText>(message);
|
||||||
|
|
||||||
|
if (action) {
|
||||||
|
new_msg.emplace<Message::Components::TagMessageIsAction>();
|
||||||
|
}
|
||||||
|
|
||||||
|
new_msg.emplace<Message::Components::TimestampWritten>(ts);
|
||||||
|
new_msg.emplace<Message::Components::Timestamp>(ts); // reactive?
|
||||||
|
new_msg.emplace<Message::Components::TimestampProcessed>(ts);
|
||||||
|
|
||||||
|
// mark as read
|
||||||
|
new_msg.emplace<Message::Components::Read>(ts); // reactive?
|
||||||
|
|
||||||
|
_rmm.throwEventConstruct(new_msg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRCClientMessageManager::onEvent(const IRCClient::Events::Channel& e) {
|
||||||
|
if (e.params.size() < 2) {
|
||||||
|
std::cerr << "IRCCMM error: channel event too few params\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// e.origin is sender
|
||||||
|
auto sender = _ircccm.getU(e.origin); // assuming its always a user // aka ContactFrom
|
||||||
|
if (!sender.valid()) {
|
||||||
|
std::cerr << "IRCCMM error: channel event unknown sender\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.params.at(0) is channel
|
||||||
|
auto channel = _ircccm.getC(e.params.at(0)); // aka ContactTo
|
||||||
|
if (!channel.valid()) {
|
||||||
|
std::cerr << "IRCCMM error: channel event unknown channel\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.params.at(1) is message
|
||||||
|
const auto& message_text = e.params.at(1);
|
||||||
|
|
||||||
|
return processMessage(sender, channel, message_text, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRCClientMessageManager::onEvent(const IRCClient::Events::PrivMSG& e) {
|
||||||
|
if (e.params.size() < 2) {
|
||||||
|
std::cerr << "IRCCMM error: privmsg event too few params\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.origin is sender
|
||||||
|
auto from = _ircccm.getU(e.origin); // assuming its always a user // aka ContactFrom
|
||||||
|
if (!from.valid()) {
|
||||||
|
std::cerr << "IRCCMM error: channel event unknown sender\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.params.at(0) is receiver (us?)
|
||||||
|
auto to = _ircccm.getU(e.params.at(0)); // aka ContactTo
|
||||||
|
if (!to.valid()) {
|
||||||
|
std::cerr << "IRCCMM error: channel event unknown channel\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// upgrade contact to big
|
||||||
|
from.emplace_or_replace<Contact::Components::TagBig>(); // could be like an invite?
|
||||||
|
|
||||||
|
return processMessage(from, to, e.params.at(1), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRCClientMessageManager::onEvent(const IRCClient::Events::Notice& e) {
|
||||||
|
if (e.params.size() < 2) {
|
||||||
|
std::cerr << "IRCCMM error: notice event too few params\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// server message type 1
|
||||||
|
// e.origin is server host (not network name)
|
||||||
|
// e.params.at(0) is '*'
|
||||||
|
// server message type 2
|
||||||
|
// e.origin is server host (not network name)
|
||||||
|
// e.params.at(0) is user (us)
|
||||||
|
// server message type 3
|
||||||
|
// e.origin is "Global"
|
||||||
|
// e.params.at(0) is user (us)
|
||||||
|
// user message (private)
|
||||||
|
// e.origin is sending user
|
||||||
|
// e.params.at(0) is user (us)
|
||||||
|
|
||||||
|
// e.params.at(1) is message
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRCClientMessageManager::onEvent(const IRCClient::Events::ChannelNotice& e) {
|
||||||
|
if (e.params.size() < 2) {
|
||||||
|
std::cerr << "IRCCMM error: channel notice event too few params\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.origin is sending user (probably)
|
||||||
|
auto from = _ircccm.getU(e.origin);
|
||||||
|
if (!from.valid()) {
|
||||||
|
std::cerr << "IRCCMM error: channel notice event unknown sender\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.params.at(0) is channel
|
||||||
|
auto to = _ircccm.getC(e.params.at(0));
|
||||||
|
if (!to.valid()) {
|
||||||
|
std::cerr << "IRCCMM error: unknown receiver\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add notice tag
|
||||||
|
|
||||||
|
// e.params.at(1) is message
|
||||||
|
return processMessage(from, to, e.params.at(1), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRCClientMessageManager::onEvent(const IRCClient::Events::CTCP_Action& e) {
|
||||||
|
if (e.params.size() < 2) {
|
||||||
|
std::cerr << "IRCCMM error: action event too few params\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.origin is sender
|
||||||
|
auto from = _ircccm.getU(e.origin); // assuming its always a user // aka ContactFrom
|
||||||
|
if (!from.valid()) {
|
||||||
|
std::cerr << "IRCCMM error: channel event unknown sender\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.params.at(0) is receiver (self if pm or channel if channel)
|
||||||
|
auto receiver = _ircccm.getCU(e.params.at(0));
|
||||||
|
if (!receiver.valid()) {
|
||||||
|
std::cerr << "IRCCMM error: unknown receiver\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// e.params.at(1) is message
|
||||||
|
|
||||||
|
// upgrade contact to big
|
||||||
|
if (receiver.all_of<Contact::Components::IRC::UserName>()) {
|
||||||
|
from.emplace_or_replace<Contact::Components::TagBig>(); // could be like an invite?
|
||||||
|
}
|
||||||
|
|
||||||
|
return processMessage(from, receiver, e.params.at(1), true);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "solanaceae/contact/contact_model3.hpp"
|
||||||
|
#include <solanaceae/ircclient/ircclient.hpp>
|
||||||
|
#include <solanaceae/ircclient_contacts/ircclient_contact_model.hpp>
|
||||||
|
#include <solanaceae/message3/registry_message_model.hpp>
|
||||||
|
|
||||||
|
class IRCClientMessageManager : public IRCClientEventI, public RegistryMessageModelEventI {
|
||||||
|
protected:
|
||||||
|
RegistryMessageModel& _rmm;
|
||||||
|
Contact3Registry& _cr;
|
||||||
|
ConfigModelI& _conf;
|
||||||
|
IRCClient1& _ircc;
|
||||||
|
IRCClientContactModel& _ircccm;
|
||||||
|
|
||||||
|
public:
|
||||||
|
IRCClientMessageManager(
|
||||||
|
RegistryMessageModel& rmm,
|
||||||
|
Contact3Registry& cr,
|
||||||
|
ConfigModelI& conf,
|
||||||
|
IRCClient1& ircc,
|
||||||
|
IRCClientContactModel& ircccm
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual ~IRCClientMessageManager(void);
|
||||||
|
|
||||||
|
// bring event overloads into scope
|
||||||
|
using IRCClientEventI::onEvent;
|
||||||
|
using RegistryMessageModelEventI::onEvent;
|
||||||
|
private:
|
||||||
|
bool processMessage(Contact3Handle from, Contact3Handle to, std::string_view message_text, bool action);
|
||||||
|
|
||||||
|
private: // mm3
|
||||||
|
bool sendText(const Contact3 c, std::string_view message, bool action = false) override;
|
||||||
|
|
||||||
|
private: // ircclient
|
||||||
|
bool onEvent(const IRCClient::Events::Channel& e) override;
|
||||||
|
bool onEvent(const IRCClient::Events::PrivMSG& e) override;
|
||||||
|
bool onEvent(const IRCClient::Events::Notice& e) override;
|
||||||
|
bool onEvent(const IRCClient::Events::ChannelNotice& e) override;
|
||||||
|
bool onEvent(const IRCClient::Events::CTCP_Action& e) override;
|
||||||
|
};
|
36
src/test2.cpp
Normal file
36
src/test2.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include <solanaceae/util/simple_config_model.hpp>
|
||||||
|
#include <solanaceae/contact/contact_model3.hpp>
|
||||||
|
#include <solanaceae/message3/registry_message_model.hpp>
|
||||||
|
#include <solanaceae/ircclient/ircclient.hpp>
|
||||||
|
#include <solanaceae/ircclient_contacts/ircclient_contact_model.hpp>
|
||||||
|
#include <solanaceae/ircclient_messages/ircclient_message_manager.hpp>
|
||||||
|
|
||||||
|
#include <libircclient.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
SimpleConfigModel conf;
|
||||||
|
|
||||||
|
conf.set("IRCClient", "server", std::string_view{"#irc.rizon.net"});
|
||||||
|
conf.set("IRCClient", "port", int64_t(6697));
|
||||||
|
conf.set("IRCClient", "autojoin", "#HorribleSubs", true);
|
||||||
|
conf.set("IRCClient", "autojoin", "#green_testing", true);
|
||||||
|
|
||||||
|
Contact3Registry cr;
|
||||||
|
RegistryMessageModel rmm{cr};
|
||||||
|
|
||||||
|
IRCClient1 ircc{conf};
|
||||||
|
|
||||||
|
IRCClientContactModel ircccm{cr, conf, ircc};
|
||||||
|
IRCClientMessageManager irccmm{rmm, cr, conf, ircc, ircccm};
|
||||||
|
|
||||||
|
//ircccm.join("#green_testing");
|
||||||
|
|
||||||
|
while (irc_is_connected(ircc.getSession())) {
|
||||||
|
ircc.iterate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user