Compare commits

..

No commits in common. "master" and "0.1" have entirely different histories.
master ... 0.1

46 changed files with 351 additions and 1523 deletions

16
.gitattributes vendored
View file

@ -1,16 +0,0 @@
* text=auto eol=lf
# Configuration files
*.json text eol=crlf
# Development files
CMakeLists.txt text eol=lf
*.cmake text eol=lf
*.cpp text eol=lf
*.h text eol=lf
# BSD and Linux development files
*.pc.in text eol=lf
# Windows development files
*.rc.in text encoding=cp1252 eol=crlf

View file

@ -1,45 +0,0 @@
name: Linux
on: push
jobs:
Release:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- name: Enterprise Linux 7
version: el7
- name: Enterprise Linux 8
version: el8
- name: Enterprise Linux 9
version: el9
- name: openSUSE Leap 15.5
version: lp155
steps:
- name: Cloning
uses: actions/checkout@v4
- name: Preparing
run: mkdir -m 777 ${{github.workspace}}/rpms
- name: Build RPM
uses: addnab/docker-run-action@v3
with:
image: docker.io/syping/dtranslatebot-build:${{matrix.version}}
options: -v ${{github.workspace}}:/home/rpmbuild/dtranslatebot -v ${{github.workspace}}/rpms:/home/rpmbuild/rpmbuild/RPMS
run: |
VERSION=$(cat dtranslatebot/CMakeLists.txt | grep -oP "project\(dtranslatebot VERSION \K(\S*)(?= LANGUAGES CXX\))")
mkdir -p dtranslatebot-$VERSION
shopt -s extglob
cp -R dtranslatebot/!(rpms|rpmsrc) \
dtranslatebot-$VERSION
tar cfz dtranslatebot-$VERSION.tar.gz dtranslatebot-$VERSION
cp dtranslatebot-$VERSION.tar.gz \
dtranslatebot/rpmsrc/!(*.spec) \
rpmbuild/SOURCES
cp dtranslatebot/rpmsrc/*.spec \
rpmbuild/SPECS
rpmbuild -ba rpmbuild/SPECS/dtranslatebot.spec
- name: Upload
uses: actions/upload-artifact@v4
with:
name: ${{matrix.name}}
path: |
${{github.workspace}}/rpms/x86_64/*.rpm

View file

@ -1,43 +0,0 @@
name: Windows
on: push
jobs:
Release:
runs-on: windows-latest
env:
BUILD_TYPE: Release
defaults:
run:
shell: msys2 {0}
steps:
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: clang64
update: true
install: >-
git
make
mingw-w64-clang-x86_64-clang
mingw-w64-clang-x86_64-cmake
mingw-w64-clang-x86_64-ninja
perl
- name: Cloning
uses: actions/checkout@v4
- name: Configure CMake
run: cmake -B dtranslatebot-build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_EXE_LINKER_FLAGS="-static -lc++" -DWITH_DPP_STATIC_BUNDLE=TRUE -GNinja
- name: Download and build OpenSSL
run: cmake --build dtranslatebot-build --config ${{env.BUILD_TYPE}} --target OpenSSL
- name: Download and build zlib
run: cmake --build dtranslatebot-build --config ${{env.BUILD_TYPE}} --target ZLIB
- name: Download and build DPP
run: cmake --build dtranslatebot-build --config ${{env.BUILD_TYPE}} --target DPP
- name: Build dtranslatebot
run: cmake --build dtranslatebot-build --config ${{env.BUILD_TYPE}}
- name: Install
run: cmake --install dtranslatebot-build --config ${{env.BUILD_TYPE}} --prefix dtranslatebot-install --strip
- name: Upload
uses: actions/upload-artifact@v4
with:
name: Windows
path: |
dtranslatebot-install/bin/dtranslatebot.exe

View file

@ -17,43 +17,34 @@
****************************************************************************]]
cmake_minimum_required(VERSION 3.16)
cmake_policy(VERSION 3.16...3.27)
project(dtranslatebot VERSION 0.2.0 LANGUAGES CXX)
project(dtranslatebot VERSION 0.1 LANGUAGES CXX)
include(GNUInstallDirs)
# dtranslatebot Source files
set(DTRANSLATEBOT_HEADERS
src/core/database.h
src/core/message_queue.h
src/core/regex.h
src/core/settings.h
src/core/settings_types.h
src/core/slashcommands.h
src/core/submit_queue.h
src/core/translator.h
src/core/webhook_push.h
src/database/file/file.h
src/translator/deepl/deepl.h
src/translator/mozhi/mozhi.h
src/translator/libretranslate/libretranslate.h
src/translator/lingvatranslate/lingvatranslate.h
src/translator/stub/stub.h
src/database_core.h
src/database_file.h
src/message_queue.h
src/regex.h
src/settings.h
src/settings_types.h
src/slashcommands.h
src/submit_queue.h
src/translator_core.h
src/translator_libretranslate.h
src/webhook_push.h
)
set(DTRANSLATEBOT_SOURCES
src/core/database.cpp
src/core/main.cpp
src/core/message_queue.cpp
src/core/settings.cpp
src/core/slashcommands.cpp
src/core/submit_queue.cpp
src/core/translator.cpp
src/core/webhook_push.cpp
src/database/file/file.cpp
src/translator/deepl/deepl.cpp
src/translator/mozhi/mozhi.cpp
src/translator/libretranslate/libretranslate.cpp
src/translator/lingvatranslate/lingvatranslate.cpp
src/translator/stub/stub.cpp
src/database_core.cpp
src/database_file.cpp
src/main.cpp
src/message_queue.cpp
src/settings.cpp
src/slashcommands.cpp
src/submit_queue.cpp
src/translator_core.cpp
src/translator_libretranslate.cpp
src/webhook_push.cpp
)
# dtranslatebot Module Path
@ -72,59 +63,21 @@ if (WITH_BOOST)
endif()
# D++ Discord API Library for Bots
option(WITH_DPP_STATIC_BUNDLE "Build with DPP Static Bundle" OFF)
if (WITH_DPP_STATIC_BUNDLE)
include(DPPStaticBundle)
else()
find_package(DPP REQUIRED)
endif()
find_package(DPP REQUIRED)
# pthread Support
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
# dtranslatebot Win32 Shared Resources
if (WIN32)
configure_file(src/resources/win32/dtranslatebot.rc.in "${dtranslatebot_BINARY_DIR}/resources/win32/dtranslatebot.rc" @ONLY)
list(APPEND DTRANSLATEBOT_RESOURCES
"${dtranslatebot_BINARY_DIR}/resources/win32/dtranslatebot.rc"
)
endif()
# dtranslatebot systemd Service
if (UNIX AND NOT APPLE)
option(WITH_SYSTEMD "Build with systemd Support" OFF)
if (WITH_SYSTEMD)
find_program(SYSTEMD_ESCAPE_EXECUTABLE NAMES systemd-escape)
if (DEFINED SYSTEMD_ESCAPE_EXECUTABLE)
execute_process(
COMMAND "${SYSTEMD_ESCAPE_EXECUTABLE}" "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/dtranslatebot"
OUTPUT_VARIABLE dtranslatebot_SERVICE_WORKDIR
)
string(STRIP "${dtranslatebot_SERVICE_WORKDIR}" dtranslatebot_SERVICE_WORKDIR)
else()
set(dtranslatebot_SERVICE_WORKDIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/dtranslatebot")
endif()
configure_file(src/systemd/dtranslatebot.service.in "${dtranslatebot_BINARY_DIR}/systemd/service/dtranslatebot.service" @ONLY)
configure_file(src/systemd/dtranslatebot.sysusersd.in "${dtranslatebot_BINARY_DIR}/systemd/sysusers.d/dtranslatebot.conf" @ONLY)
install(FILES "${dtranslatebot_BINARY_DIR}/systemd/service/dtranslatebot.service" DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/systemd/system")
install(FILES "${dtranslatebot_BINARY_DIR}/systemd/sysusers.d/dtranslatebot.conf" DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/sysusers.d")
endif()
endif()
# dtranslatebot Target + Installs
add_executable(dtranslatebot ${DTRANSLATEBOT_HEADERS} ${DTRANSLATEBOT_SOURCES} ${DTRANSLATEBOT_RESOURCES})
if (WITH_DPP_STATIC_BUNDLE)
add_dependencies(dtranslatebot DPP)
endif()
add_executable(dtranslatebot ${DTRANSLATEBOT_HEADERS} ${DTRANSLATEBOT_SOURCES})
target_compile_definitions(dtranslatebot PRIVATE
${DPP_DEFINITIONS}
$<$<BOOL:${DTRANSLATEBOT_USE_BOOST_REGEX}>:DTRANSLATEBOT_USE_BOOST_REGEX>
)
if (MSVC AND MSVC_VERSION GREATER_EQUAL 1914)
target_compile_options(dtranslatebot PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>)
endif()
target_link_libraries(dtranslatebot PRIVATE ${DTRANSLATEBOT_LIBRARIES} ${DPP_LIBRARIES} Threads::Threads)
target_link_libraries(dtranslatebot PRIVATE Threads::Threads ${DPP_LIBRARIES} ${DTRANSLATEBOT_LIBRARIES})
target_include_directories(dtranslatebot PRIVATE ${DPP_INCLUDE_DIR})
set_target_properties(dtranslatebot PROPERTIES
CXX_STANDARD 17

View file

@ -2,7 +2,7 @@
Open Source Discord Translation Bot
- Translate incoming channel messages to a Webhook
- Support configuration through slash commands and JSON
- Support configuration through slashcommands and JSON
- Cross-Platform
#### Build Dependencies
@ -10,11 +10,8 @@ Open Source Discord Translation Bot
- Compiler with C++17 Support
- [D++: A C++ Discord API Library for Bots](https://dpp.dev/)
#### Supported Translation Engines
- [LibreTranslate](https://libretranslate.com/) (Default)
- [Lingva Translate](https://lingva.ml/)
- [Mozhi](https://codeberg.org/aryak/mozhi)
- [DeepL](https://deepl.com/) (Experimental)
#### Runtime Dependencies
- [LibreTranslate](https://libretranslate.com/)
#### Build dtranslatebot
@ -26,6 +23,4 @@ sudo cmake --install dtranslatebot-build
```
##### Optional CMake flags
`-DWITH_BOOST=TRUE`
`-DWITH_DPP_STATIC_BUNDLE=TRUE`
`-DWITH_SYSTEMD=TRUE`
`-DWITH_BOOST=TRUE`

View file

@ -1,65 +0,0 @@
#[[**************************************************************************
* dtranslatebot Discord Translate Bot
* Copyright (C) 2024 Syping
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* This software is provided as-is, no warranties are given to you, we are not
* responsible for anything with use of the software, you are self responsible.
****************************************************************************]]
if (DEFINED CMAKE_BUILD_TYPE)
list(APPEND CMAKE_PASSTHROUGH_ARGS "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
endif()
if (DEFINED CMAKE_C_COMPILER)
list(APPEND CMAKE_PASSTHROUGH_ARGS "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}")
list(APPEND CMAKE_PASSTHROUGH_ENV "CC=${CMAKE_C_COMPILER}")
elseif ("$ENV{MSYSTEM}" STREQUAL "CLANG64")
list(APPEND CMAKE_PASSTHROUGH_ENV "CC=clang")
endif()
if (DEFINED CMAKE_C_COMPILER_TARGET)
list(APPEND CMAKE_PASSTHROUGH_ARGS "-DCMAKE_C_COMPILER_TARGET=${CMAKE_C_COMPILER_TARGET}")
endif()
if (DEFINED CMAKE_CXX_COMPILER)
list(APPEND CMAKE_PASSTHROUGH_ARGS "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}")
list(APPEND CMAKE_PASSTHROUGH_ENV "CXX=${CMAKE_CXX_COMPILER}")
elseif ("$ENV{MSYSTEM}" STREQUAL "CLANG64")
list(APPEND CMAKE_PASSTHROUGH_ENV "CXX=clang++")
endif()
if (DEFINED CMAKE_CXX_COMPILER_TARGET)
list(APPEND CMAKE_PASSTHROUGH_ARGS "-DCMAKE_CXX_COMPILER_TARGET=${CMAKE_CXX_COMPILER_TARGET}")
endif()
if (DEFINED CMAKE_RC_COMPILER)
list(APPEND CMAKE_PASSTHROUGH_ARGS "-DCMAKE_RC_COMPILER=${CMAKE_RC_COMPILER}")
endif()
if (DEFINED CMAKE_STRIP)
list(APPEND CMAKE_PASSTHROUGH_ARGS "-DCMAKE_STRIP=${CMAKE_STRIP}")
endif()
if (DEFINED CMAKE_SYSROOT)
list(APPEND CMAKE_PASSTHROUGH_ARGS "-DCMAKE_SYSROOT=${CMAKE_SYSROOT}")
endif()
if (DEFINED CMAKE_SYSTEM_NAME AND NOT CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME)
list(APPEND CMAKE_PASSTHROUGH_ARGS "-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}")
endif()
if (DEFINED CMAKE_SYSTEM_PROCESSOR AND NOT CMAKE_SYSTEM_PROCESSOR STREQUAL CMAKE_HOST_SYSTEM_PROCESSOR)
list(APPEND CMAKE_PASSTHROUGH_ARGS "-DCMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}")
endif()
if (DEFINED CMAKE_SYSTEM_VERSION AND NOT CMAKE_SYSTEM_VERSION STREQUAL CMAKE_HOST_SYSTEM_VERSION)
list(APPEND CMAKE_PASSTHROUGH_ARGS "-DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}")
endif()
if (DEFINED CMAKE_TOOLCHAIN_FILE)
list(APPEND CMAKE_PASSTHROUGH_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}")
endif()
if (DEFINED CMAKE_PASSTHROUGH_ENV)
set(CMAKE_PASSTHROUGH_ENV
"${CMAKE_COMMAND}" -E env ${CMAKE_PASSTHROUGH_ENV}
)
endif()

View file

@ -1,119 +0,0 @@
#[[**************************************************************************
* dtranslatebot Discord Translate Bot
* Copyright (C) 2024 Syping
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* This software is provided as-is, no warranties are given to you, we are not
* responsible for anything with use of the software, you are self responsible.
****************************************************************************]]
include(ArgumentPassthrough)
# OpenSSL needs to be configured with perl and build with make
find_program(MAKE_EXECUTABLE NAMES make gmake)
if (NOT DEFINED MAKE_EXECUTABLE)
message(SEND_ERROR "make not found")
endif()
find_program(NPROC_EXECUTABLE nproc)
if (DEFINED NPROC_EXECUTABLE)
execute_process(
COMMAND "${NPROC_EXECUTABLE}"
OUTPUT_VARIABLE NPROC
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(MAKE_JOBS_ARG "-j${NPROC}")
endif()
find_program(PERL_EXECUTABLE NAMES perl)
if (NOT DEFINED PERL_EXECUTABLE)
message(SEND_ERROR "perl not found")
endif()
include(ExternalProject)
ExternalProject_Add(ZLIB
URL https://www.zlib.net/zlib-1.3.1.tar.xz
URL_HASH SHA256=38ef96b8dfe510d42707d9c781877914792541133e1870841463bfa73f883e32
CMAKE_ARGS
-DBUILD_SHARED_LIBS=OFF
${CMAKE_PASSTHROUGH_ARGS}
"-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>"
-DZLIB_BUILD_EXAMPLES=OFF
${ZLIB_CONFIGURE_ARGS}
)
ExternalProject_Get_Property(ZLIB INSTALL_DIR)
set(ZLIB_INSTALL_DIR "${INSTALL_DIR}")
set(OPENSSL_PLATFORM_ARG $<$<BOOL:$ENV{MSYSTEM}>:mingw64>)
ExternalProject_Add(OpenSSL
URL https://github.com/openssl/openssl/releases/download/openssl-3.0.17/openssl-3.0.17.tar.gz
URL_HASH SHA256=dfdd77e4ea1b57ff3a6dbde6b0bdc3f31db5ac99e7fdd4eaf9e1fbb6ec2db8ce
CONFIGURE_COMMAND
${CMAKE_PASSTHROUGH_ENV}
"${PERL_EXECUTABLE}"
"<SOURCE_DIR>/Configure"
"--prefix=<INSTALL_DIR>"
$<$<CONFIG:Debug>:-d>
no-dso
no-dtls
no-engine
no-shared
no-zlib
${OPENSSL_PLATFORM_ARG}
${OPENSSL_CONFIGURE_ARGS}
BUILD_COMMAND "${MAKE_EXECUTABLE}" ${MAKE_JOBS_ARG} build_libs
INSTALL_COMMAND "${MAKE_EXECUTABLE}" ${MAKE_JOBS_ARG} install_dev
)
ExternalProject_Get_Property(OpenSSL INSTALL_DIR)
set(OpenSSL_INSTALL_DIR "${INSTALL_DIR}")
ExternalProject_Add(DPP
URL https://github.com/brainboxdotcc/DPP/archive/refs/tags/v10.1.3.tar.gz
URL_HASH SHA256=a32d94dcd6b23430afff82918234e4e28e0616bd2ddf743c5ab2f1778c5a600b
CMAKE_ARGS
-DAVX_TYPE=AVX0
-DBUILD_SHARED_LIBS=OFF
-DBUILD_VOICE_SUPPORT=OFF
${CMAKE_PASSTHROUGH_ARGS}
"-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>"
-DDPP_BUILD_TEST=OFF
-DDPP_NO_CORO=ON
-DDPP_NO_VCPKG=ON
-DRUN_LDCONFIG=OFF
"-DOpenSSL_ROOT=${OpenSSL_INSTALL_DIR}"
"-DZLIB_ROOT=${ZLIB_INSTALL_DIR}"
${DPP_CONFIGURE_ARGS}
DEPENDS OpenSSL ZLIB
)
ExternalProject_Get_Property(DPP INSTALL_DIR)
set(DPP_INSTALL_DIR "${INSTALL_DIR}")
set(DPP_INCLUDE_DIR "${DPP_INSTALL_DIR}/include")
set(DPP_LIBRARIES
-Wl,-Bstatic
"-L${DPP_INSTALL_DIR}/lib"
"-L${DPP_INSTALL_DIR}/lib64"
dpp
"-L${OpenSSL_INSTALL_DIR}/lib"
"-L${OpenSSL_INSTALL_DIR}/lib64"
ssl
crypto
"-L${ZLIB_INSTALL_DIR}/lib"
"-L${ZLIB_INSTALL_DIR}/lib64"
$<IF:$<BOOL:${WIN32}>,zlibstatic,z>
-Wl,-Bdynamic
)
if (WIN32)
set(DPP_DEFINITIONS DPP_STATIC)
list(APPEND DPP_LIBRARIES
ws2_32
)
endif()

View file

@ -1,4 +1,4 @@
find_path(DPP_INCLUDE_DIR NAMES "dpp/dpp.h" HINTS "${DPP_ROOT_DIR}")
find_library(DPP_LIBRARIES NAMES "dpp" "libdpp.a" HINTS "${DPP_ROOT_DIR}")
find_path(DPP_INCLUDE_DIR NAMES dpp/dpp.h HINTS ${DPP_ROOT_DIR})
find_library(DPP_LIBRARIES NAMES dpp "libdpp.a" HINTS ${DPP_ROOT_DIR})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(DPP DEFAULT_MSG DPP_LIBRARIES DPP_INCLUDE_DIR)

View file

@ -0,0 +1,37 @@
{
"guilds": {
"$guild1_id": {
"$channel1_id": {
"source": "en",
"target": "de",
"webhook": "https://discord.com/api/webhooks/$guild1_de_webhook_id/$guild1_de_webhook_token"
},
"$channel2_id": {
"source": "de",
"target": "en",
"webhook": "https://discord.com/api/webhooks/$guild1_en_webhook_id/$guild1_en_webhook_token"
}
},
"My Discord Guild": {
"id": $guild2_id,
"General English": {
"id": $channel3_id,
"source": "en",
"target": {
"de": "https://discord.com/api/webhooks/$guild2_de_webhook_id/$guild2_de_webhook_token",
"fr": "https://discord.com/api/webhooks/$guild2_fr_webhook_id/$guild2_fr_webhook_token"
}
}
}
},
"preferred_lang": ["en", "de", "fr", ...],
"storage": "$working_directory",
"user": {
"avatar_size": 256
},
"token": "$bot_token",
"translator": {
"url": "http://127.0.0.1:5000/",
"apiKey": ""
}
}

7
etc/dtranslatebot.json Normal file
View file

@ -0,0 +1,7 @@
{
"token": "",
"translator": {
"url": "http://127.0.0.1:5000/",
"apiKey": ""
}
}

View file

@ -1,14 +0,0 @@
{
/*
A DISCORD BOT TOKEN IS NECESSARY TO USE THIS APPLICATION!
You can get a Bot Token from the Discord Developer Portal
by creating a New Application:
https://discord.com/developers/applications
*/
"token": "",
"translator": {
"type": "stub"
}
}

View file

@ -1,13 +0,0 @@
[Unit]
Description=Discord Translation Bot
After=network.target
[Service]
User=dtranslatebot
Group=dtranslatebot
WorkingDirectory=/var/lib/dtranslatebot
ExecStart=/usr/bin/dtranslatebot /etc/dtranslatebot.json
Restart=on-abnormal
[Install]
WantedBy=multi-user.target

View file

@ -1,96 +0,0 @@
%global _lto_cflags %{?_lto_cflags} -ffat-lto-objects
%if 0%{?rhel} && 0%{?rhel} < 8
%global cmake %{?cmake3}
%global cmake_build %{?cmake3_build}
%global cmake_install %{?cmake3_install}
%global cmake_suffix 3
%global toolset_prefix devtoolset-9-
%endif
%if 0%{?rhel} && 0%{?rhel} == 8
%global toolset_prefix gcc-toolset-9-
%endif
%if 0%{?suse_version} && 0%{?suse_version} < 1600
%global toolset_version 9
%endif
Name: dtranslatebot
Version: 0.2.0
Release: 1%{?dist}
Summary: Discord Translation Bot
License: BSD-2-Clause
URL: https://github.com/Syping/%{name}
Source0: %{name}-%{version}.tar.gz
Source1: %{name}.json
Source2: %{name}.service
Source3: %{name}.sysusersd
%if 0%{?fedora} || 0%{?rhel}
BuildRequires: %{?toolset_prefix}annobin
%if 0%{?rhel} && 0%{?rhel} < 9
BuildRequires: epel-rpm-macros-systemd
%endif
%endif
BuildRequires: cmake%{?cmake_suffix}
BuildRequires: %{?toolset_prefix}gcc%{?toolset_version}-c++
BuildRequires: make
BuildRequires: perl(IPC::Cmd)
BuildRequires: systemd-rpm-macros
%systemd_requires
%if 0%{?fedora} || 0%{?rhel}
%sysusers_requires_compat
%endif
%description
dtranslatebot is a Discord Bot which translate incoming Discord messages to Discord webhooks.
%prep
%setup -q
%build
%if 0%{?rhel} && 0%{?rhel} < 8
source /opt/rh/devtoolset-9/enable
%endif
%if 0%{?rhel} && 0%{?rhel} == 8
source /opt/rh/gcc-toolset-9/enable
%endif
%cmake \
-DCMAKE_BUILD_TYPE=Release \
%if 0%{?toolset_version}
-DCMAKE_C_COMPILER=gcc-%{toolset_version} \
-DCMAKE_CXX_COMPILER=g++-%{toolset_version} \
%endif
-DWITH_DPP_STATIC_BUNDLE=TRUE \
-DWITH_SYSTEMD=TRUE
%cmake_build
%install
%cmake_install
mkdir -p %{buildroot}%{_localstatedir}/lib/%{name}
install -p -D -m 0644 %{SOURCE1} %{buildroot}%{_sysconfdir}/%{name}.json
install -p -D -m 0644 %{SOURCE2} %{buildroot}%{_unitdir}/%{name}.service
install -p -D -m 0644 %{SOURCE3} %{buildroot}%{_sysusersdir}/%{name}.conf
%pre
%if 0%{?fedora} || 0%{?rhel}
%sysusers_create_compat %{SOURCE3}
%endif
%if 0%{?suse_version}
%sysusers_create_package %{name} %{SOURCE3}
%endif
%post
%systemd_post %{name}.service
%preun
%systemd_preun %{name}.service
%postun
%systemd_postun_with_restart %{name}.service
%files
%{_bindir}/%{name}
%{_unitdir}/%{name}.service
%{_sysusersdir}/%{name}.conf
%config(noreplace) %attr(0640,root,%{name}) %{_sysconfdir}/%{name}.json
%dir %attr(0750,%{name},%{name}) %{_localstatedir}/lib/%{name}

View file

@ -1,2 +0,0 @@
#Type Name ID GECOS Home directory Shell
u dtranslatebot - "Discord Translation Bot" /var/lib/translatebot -

View file

@ -19,9 +19,17 @@
#ifndef NDEBUG
#include <iostream>
#endif
#include "database.h"
#include "database_core.h"
using namespace bot::database;
database::database()
{
}
database::~database()
{
}
void database::add_channel_target(dpp::snowflake guild_id, dpp::snowflake channel_id, const bot::settings::target &target)
{
#ifndef NDEBUG

View file

@ -16,8 +16,8 @@
* responsible for anything with use of the software, you are self responsible.
*****************************************************************************/
#ifndef DATABASE_H
#define DATABASE_H
#ifndef DATABASE_CORE_H
#define DATABASE_CORE_H
#include "settings_types.h"
@ -30,10 +30,8 @@ namespace bot {
class database {
public:
explicit database() = default;
database(const database&) = delete;
database& operator=(const database&) = delete;
virtual ~database() = default;
explicit database();
virtual ~database();
virtual void add_channel_target(dpp::snowflake guild_id, dpp::snowflake channel_id, const bot::settings::target &target);
virtual void delete_channel(dpp::snowflake guild_id, dpp::snowflake channel_id);
virtual void delete_channel_target(dpp::snowflake guild_id, dpp::snowflake channel_id, const std::string &target);
@ -54,4 +52,4 @@ namespace bot {
}
}
#endif // DATABASE_H
#endif // DATABASE_CORE_H

View file

@ -27,7 +27,7 @@
#include <fstream>
#include <iostream>
#include <thread>
#include "file.h"
#include "database_file.h"
using namespace bot::database;
using namespace std::string_literals;
@ -98,7 +98,7 @@ void file::add_channel_target(dpp::snowflake guild_id, dpp::snowflake channel_id
}
bot::settings::channel channel;
cache_get_channel(channel_id, channel);
cache_get_channel(channel_id, &channel);
channel.targets.push_back(target);
cache_add_channel(guild_id, channel_id);
guild->channel.push_back(std::move(channel));
@ -107,7 +107,7 @@ void file::add_channel_target(dpp::snowflake guild_id, dpp::snowflake channel_id
}
bot::settings::channel channel;
cache_get_channel(channel_id, channel);
cache_get_channel(channel_id, &channel);
channel.targets.push_back(target);
cache_add_channel(guild_id, channel_id);
m_dataCache.push_back({ guild_id, { std::move(channel) } });
@ -141,7 +141,7 @@ void file::delete_channel(dpp::snowflake guild_id, dpp::snowflake channel_id)
}
std::vector<dpp::snowflake> channels;
cache_guild(guild_id, channels);
cache_guild(guild_id, &channels);
for (auto channel = channels.begin(); channel != channels.end(); channel++) {
if (*channel == channel_id) {
channels.erase(channel);
@ -175,7 +175,7 @@ void file::delete_channel_target(dpp::snowflake guild_id, dpp::snowflake channel
}
bot::settings::channel channel;
cache_get_channel(channel_id, channel);
cache_get_channel(channel_id, &channel);
for (auto _target = channel.targets.begin(); _target != channel.targets.end(); _target++) {
if (_target->target == target) {
channel.targets.erase(_target);
@ -188,7 +188,7 @@ void file::delete_channel_target(dpp::snowflake guild_id, dpp::snowflake channel
}
bot::settings::channel channel;
cache_get_channel(channel_id, channel);
cache_get_channel(channel_id, &channel);
for (auto _target = channel.targets.begin(); _target != channel.targets.end(); _target++) {
if (_target->target == target) {
channel.targets.erase(_target);
@ -266,7 +266,7 @@ bot::settings::channel file::get_channel(dpp::snowflake guild_id, dpp::snowflake
}
bot::settings::channel channel;
cache_get_channel(channel_id, channel);
cache_get_channel(channel_id, &channel);
return channel;
}
@ -281,7 +281,7 @@ std::vector<dpp::snowflake> file::get_channels(dpp::snowflake guild_id)
}
std::vector<dpp::snowflake> channels;
cache_guild(guild_id, channels);
cache_guild(guild_id, &channels);
return channels;
}
@ -330,7 +330,7 @@ std::vector<dpp::snowflake> file::get_guilds()
{
const std::lock_guard<std::mutex> guard(m_mutex);
std::vector<dpp::snowflake> guilds;
list_guilds(guilds);
list_guilds(&guilds);
return guilds;
}
@ -348,7 +348,7 @@ void file::set_channel_source(dpp::snowflake guild_id, dpp::snowflake channel_id
}
bot::settings::channel channel;
cache_get_channel(channel_id, channel);
cache_get_channel(channel_id, &channel);
channel.source = source;
cache_add_channel(guild_id, channel_id);
guild->channel.push_back(std::move(channel));
@ -357,7 +357,7 @@ void file::set_channel_source(dpp::snowflake guild_id, dpp::snowflake channel_id
}
bot::settings::channel channel;
cache_get_channel(channel_id, channel);
cache_get_channel(channel_id, &channel);
channel.source = source;
cache_add_channel(guild_id, channel_id);
m_dataCache.push_back({ guild_id, { std::move(channel) } });
@ -390,16 +390,16 @@ void file::cache_add_channel(dpp::snowflake guild_id, dpp::snowflake channel_id)
}
std::vector<dpp::snowflake> channels;
cache_guild(guild_id, channels);
cache_guild(guild_id, &channels);
if (std::find(channels.begin(), channels.end(), channel_id) == channels.end())
channels.push_back(channel_id);
m_channelCache.push_back({ guild_id, std::move(channels) });
}
void file::cache_get_channel(dpp::snowflake channel_id, settings::channel &channel)
void file::cache_get_channel(dpp::snowflake channel_id, bot::settings::channel *channel)
{
channel.id = channel_id;
channel->id = channel_id;
const std::filesystem::path channel_file = m_storagePath / "channel" / (std::to_string(channel_id) + ".json");
@ -418,7 +418,7 @@ void file::cache_get_channel(dpp::snowflake channel_id, settings::channel &chann
if (json.is_object()) {
auto json_channel_source = json.find("source");
if (json_channel_source != json.end())
channel.source = *json_channel_source;
channel->source = *json_channel_source;
auto json_channel_target = json.find("target");
if (json_channel_target != json.end()) {
@ -433,7 +433,7 @@ void file::cache_get_channel(dpp::snowflake channel_id, settings::channel &chann
else if (json_target->is_string()) {
target.webhook = dpp::webhook(*json_target);
}
channel.targets.push_back(std::move(target));
channel->targets.push_back(std::move(target));
}
}
}
@ -444,7 +444,7 @@ void file::cache_get_channel(dpp::snowflake channel_id, settings::channel &chann
}
}
void file::cache_guild(dpp::snowflake guild_id, std::vector<dpp::snowflake> &channels)
void file::cache_guild(dpp::snowflake guild_id, std::vector<dpp::snowflake> *channels)
{
const std::filesystem::path guild_file = m_storagePath / "guild" / (std::to_string(guild_id) + ".json");
@ -463,9 +463,9 @@ void file::cache_guild(dpp::snowflake guild_id, std::vector<dpp::snowflake> &cha
if (json.is_array()) {
for (auto channel = json.begin(); channel != json.end(); channel++) {
if (channel->is_number())
channels.push_back(*channel);
channels->push_back(*channel);
else if (channel->is_string())
channels.push_back(std::stoull(std::string(*channel)));
channels->push_back(std::stoull(std::string(*channel)));
}
}
}
@ -474,7 +474,7 @@ void file::cache_guild(dpp::snowflake guild_id, std::vector<dpp::snowflake> &cha
}
}
void file::list_guilds(std::vector<dpp::snowflake> &guilds)
void file::list_guilds(std::vector<dpp::snowflake> *guilds)
{
const std::filesystem::path guild_dir = m_storagePath / "guild";
@ -488,7 +488,7 @@ void file::list_guilds(std::vector<dpp::snowflake> &guilds)
if (std::all_of(guild_filename.begin(), guild_filename.end(), ::isdigit)) {
try {
dpp::snowflake guild_id = std::stoull(guild_filename);
guilds.push_back(guild_id);
guilds->push_back(guild_id);
}
catch (const std::exception &exception) {
std::cerr << "[Exception] " << exception.what() << std::endl;

View file

@ -26,7 +26,7 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "../../core/database.h"
#include "database_core.h"
namespace bot {
namespace database {
@ -53,9 +53,9 @@ namespace bot {
private:
void cache_add_channel(dpp::snowflake guild_id, dpp::snowflake channel_id);
void cache_get_channel(dpp::snowflake channel_id, bot::settings::channel &channel);
void cache_guild(dpp::snowflake guild_id, std::vector<dpp::snowflake> &channels);
void list_guilds(std::vector<dpp::snowflake> &guilds);
void cache_get_channel(dpp::snowflake channel_id, bot::settings::channel *channel);
void cache_guild(dpp::snowflake guild_id, std::vector<dpp::snowflake> *channels);
void list_guilds(std::vector<dpp::snowflake> *guilds);
void sync_cache();
#if defined(__unix__)
int fd;

View file

@ -24,39 +24,22 @@
#include "message_queue.h"
#include "settings.h"
#include "slashcommands.h"
using namespace std::chrono_literals;
int main(int argc, char* argv[]) {
bool flag_wait_for_translator = false;
std::vector<std::string> args;
for (size_t i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--wait-for-translator"))
flag_wait_for_translator = true;
else
args.push_back(argv[i]);
}
if (args.size() != 1) {
std::cout << "Usage: " << argv[0] << " [--wait-for-translator] [json]" << std::endl;
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " [json]" << std::endl;
return 0;
}
std::cout << "[Launch] Processing configuration..." << std::endl;
bot::settings::settings settings;
if (!settings.parse_file(args.at(0)))
if (!settings.parse_file(argv[1]))
return 1;
for (;;) {
std::cout << "[Launch] Requesting supported languages..." << std::endl;
if (!settings.get_translator()->get_languages().empty()) {
break;
}
else if (flag_wait_for_translator) {
std::this_thread::sleep_for(5000ms);
}
else {
std::cerr << "[Error] Failed to initialise translateable languages" << std::endl;
return 1;
}
std::cout << "[Launch] Requesting supported languages..." << std::endl;
if (settings.get_translator()->get_languages().empty()) {
std::cerr << "[Error] Failed to initialise translateable languages" << std::endl;
return 2;
}
dpp::cluster bot(settings.token(), dpp::i_default_intents | dpp::i_message_content);
@ -72,7 +55,7 @@ int main(int argc, char* argv[]) {
bot.on_message_create(std::bind(&bot::message_queue::process_message_event, &message_queue, &bot, &settings, std::placeholders::_1));
bot.on_slashcommand(std::bind(&bot::slashcommands::process_command_event, &bot, &settings, std::placeholders::_1));
bot.on_ready([&bot, &settings]([[maybe_unused]] const dpp::ready_t &event) {
bot.on_ready([&bot, &settings](const dpp::ready_t &event) {
if (dpp::run_once<struct register_bot_commands>()) {
bot::slashcommands::register_commands(&bot, &settings);
}

View file

@ -19,7 +19,7 @@
#include <thread>
#include "message_queue.h"
#include "settings.h"
using bot::message_queue;
using namespace bot;
using namespace std::chrono_literals;
void message_queue::add(const message &message)
@ -36,10 +36,6 @@ void message_queue::add(message &&message)
void message_queue::process_message_event(dpp::cluster *bot, bot::settings::settings *settings, const dpp::message_create_t &event)
{
// We check for conditions we want to skip translation for
if (event.msg.author.id == bot->me.id || event.msg.content.empty() || event.msg.has_thread())
return;
if (event.msg.webhook_id) {
const std::lock_guard<bot::settings::settings> guard(*settings);
@ -48,6 +44,10 @@ void message_queue::process_message_event(dpp::cluster *bot, bot::settings::sett
return;
}
// Same as before, just without the involvement of webhooks
if (event.msg.author.id == bot->me.id)
return;
const std::lock_guard<bot::settings::settings> guard(*settings);
if (const bot::settings::channel *channel = settings->get_channel(event.msg.guild_id, event.msg.channel_id)) {
bot::message message;

View file

@ -1,29 +0,0 @@
#include <windows.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION @dtranslatebot_VERSION_MAJOR@, @dtranslatebot_VERSION_MINOR@, @dtranslatebot_VERSION_PATCH@, 0
PRODUCTVERSION @dtranslatebot_VERSION_MAJOR@, @dtranslatebot_VERSION_MINOR@, @dtranslatebot_VERSION_PATCH@, 0
FILEFLAGSMASK 0x3fL
FILEFLAGS 0
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0, 1200
END
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Syping"
VALUE "FileDescription", "Discord Translation Bot"
VALUE "FileVersion", "@dtranslatebot_VERSION@"
VALUE "InternalName", "dtranslatebot"
VALUE "LegalCopyright", "Copyright © 2023-2024 Syping"
VALUE "OriginalFilename", "dtranslatebot.exe"
VALUE "ProductName", "dtranslatebot"
VALUE "ProductVersion", "@dtranslatebot_VERSION@"
END
END
END

View file

@ -19,23 +19,19 @@
#include <dpp/json.h>
#include <fstream>
#include <iostream>
#include "database_file.h"
#include "settings.h"
#include "../database/file/file.h"
#include "../translator/deepl/deepl.h"
#include "../translator/mozhi/mozhi.h"
#include "../translator/libretranslate/libretranslate.h"
#include "../translator/lingvatranslate/lingvatranslate.h"
#include "../translator/stub/stub.h"
#include "translator_libretranslate.h"
using namespace bot::settings;
void process_database_channels(std::shared_ptr<bot::database::database> database, bot::settings::guild &guild, std::vector<dpp::snowflake> &webhookIds)
void process_database_channels(std::shared_ptr<bot::database::database> database, bot::settings::guild *guild, std::vector<dpp::snowflake> *webhookIds)
{
const std::vector<dpp::snowflake> db_channels = database->get_channels(guild.id);
const std::vector<dpp::snowflake> db_channels = database->get_channels(guild->id);
for (auto db_channel_id = db_channels.begin(); db_channel_id != db_channels.end(); db_channel_id++) {
bool channel_found = false;
for (auto channel = guild.channel.begin(); channel != guild.channel.end(); channel++) {
for (auto channel = guild->channel.begin(); channel != guild->channel.end(); channel++) {
if (channel->id == *db_channel_id) {
const bot::settings::channel db_channel = database->get_channel(guild.id, channel->id);
const bot::settings::channel db_channel = database->get_channel(guild->id, channel->id);
if (!db_channel.source.empty())
channel->source = db_channel.source;
for (auto db_target = db_channel.targets.begin(); db_target != db_channel.targets.end(); db_target++) {
@ -43,14 +39,14 @@ void process_database_channels(std::shared_ptr<bot::database::database> database
for (auto target = channel->targets.begin(); target != channel->targets.end(); target++) {
if (target->target == db_target->target) {
target->webhook = db_target->webhook;
webhookIds.push_back(db_target->webhook.id);
webhookIds->push_back(db_target->webhook.id);
target_found = true;
break;
}
}
if (!target_found) {
channel->targets.push_back(*db_target);
webhookIds.push_back(db_target->webhook.id);
webhookIds->push_back(db_target->webhook.id);
}
}
channel_found = true;
@ -58,23 +54,23 @@ void process_database_channels(std::shared_ptr<bot::database::database> database
}
}
if (!channel_found) {
const bot::settings::channel db_channel = database->get_channel(guild.id, *db_channel_id);
guild.channel.push_back(db_channel);
const bot::settings::channel db_channel = database->get_channel(guild->id, *db_channel_id);
guild->channel.push_back(db_channel);
for (auto db_target = db_channel.targets.begin(); db_target != db_channel.targets.end(); db_target++)
webhookIds.push_back(db_target->webhook.id);
webhookIds->push_back(db_target->webhook.id);
}
}
}
void process_database(std::shared_ptr<bot::database::database> database, std::vector<guild> &guilds, std::vector<dpp::snowflake> &webhookIds)
void process_database(std::shared_ptr<bot::database::database> database, std::vector<guild> *guilds, std::vector<dpp::snowflake> *webhookIds)
{
std::cout << "[Launch] Loading database..." << std::endl;
const std::vector<dpp::snowflake> db_guilds = database->get_guilds();
for (auto db_guild_id = db_guilds.begin(); db_guild_id != db_guilds.end(); db_guild_id++) {
bool guild_found = false;
for (auto guild = guilds.begin(); guild != guilds.end(); guild++) {
for (auto guild = guilds->begin(); guild != guilds->end(); guild++) {
if (guild->id == *db_guild_id) {
process_database_channels(database, *guild, webhookIds);
process_database_channels(database, &*guild, webhookIds);
guild_found = true;
break;
}
@ -82,13 +78,13 @@ void process_database(std::shared_ptr<bot::database::database> database, std::ve
if (!guild_found) {
bot::settings::guild guild;
guild.id = *db_guild_id;
process_database_channels(database, guild, webhookIds);
guilds.push_back(std::move(guild));
process_database_channels(database, &guild, webhookIds);
guilds->push_back(std::move(guild));
}
}
}
void process_guild_settings(const dpp::json &json, std::vector<guild> &guilds, std::vector<dpp::snowflake> &webhookIds)
void process_guild_settings(const dpp::json &json, std::vector<guild> *guilds, std::vector<dpp::snowflake> *webhookIds)
{
for (auto json_guild = json.begin(); json_guild != json.end(); json_guild++) {
if (json_guild->is_object()) {
@ -132,7 +128,7 @@ void process_guild_settings(const dpp::json &json, std::vector<guild> &guilds, s
target target;
target.target = *json_channel_target;
target.webhook = dpp::webhook(json_channel->at("webhook"));
webhookIds.push_back(target.webhook.id);
webhookIds->push_back(target.webhook.id);
channel.targets.push_back(std::move(target));
}
else if (json_channel_target->is_object()) {
@ -140,7 +136,7 @@ void process_guild_settings(const dpp::json &json, std::vector<guild> &guilds, s
target target;
target.target = json_target.key();
target.webhook = dpp::webhook(*json_target);
webhookIds.push_back(target.webhook.id);
webhookIds->push_back(target.webhook.id);
channel.targets.push_back(std::move(target));
}
}
@ -150,7 +146,7 @@ void process_guild_settings(const dpp::json &json, std::vector<guild> &guilds, s
guild.channel.push_back(std::move(channel));
}
}
guilds.push_back(std::move(guild));
guilds->push_back(std::move(guild));
}
}
}
@ -166,162 +162,103 @@ void process_preflang_settings(const dpp::json &json, std::vector<std::string> *
}
}
void process_server_url(const std::string &url, translator &translator)
void process_user_settings(const dpp::json &json, uint16_t *avatar_size)
{
auto json_avatar_size = json.find("avatar_size");
if (json_avatar_size != json.end()) {
*avatar_size = *json_avatar_size;
if (*avatar_size < 16)
*avatar_size = 16;
else if (*avatar_size > 4096)
*avatar_size = 4096;
}
}
void process_url(const std::string &url, translator *translator)
{
std::string_view url_v = url;
if (url_v.substr(0, 7) == "http://") {
translator.tls = false;
if (!translator.port)
translator.port = 80;
translator->tls = false;
if (!translator->port)
translator->port = 80;
url_v = url_v.substr(7);
}
else if (url_v.substr(0, 8) == "https://") {
translator.tls = true;
if (!translator.port)
translator.port = 443;
translator->tls = true;
if (!translator->port)
translator->port = 443;
url_v = url_v.substr(8);
}
else {
translator.tls = false;
if (!translator.port)
translator.port = 80;
translator->tls = false;
if (!translator->port)
translator->port = 80;
}
auto slash_pos = url_v.find_first_of('/');
if (slash_pos != std::string_view::npos) {
translator.url = url_v.substr(slash_pos);
translator->url = url_v.substr(slash_pos);
url_v = url_v.substr(0, slash_pos);
}
else {
translator.url = "/";
translator->url = "/";
url_v = url_v.substr(0, slash_pos);
}
// We don't have IPv6 support here yet
auto colon_pos = url_v.find_last_of(':');
auto colon_pos = url_v.find_first_of(':');
if (colon_pos != std::string_view::npos) {
translator.hostname = url_v.substr(0, colon_pos);
translator->hostname = url_v.substr(0, colon_pos);
const int port = std::stoi(std::string(url_v.substr(colon_pos + 1)));
if (port > 0 && port < 65536)
translator.port = static_cast<uint16_t>(port);
translator->port = static_cast<uint16_t>(port);
else
throw std::invalid_argument("Port is out of range");
}
else {
translator.hostname = url_v;
translator->hostname = url_v;
}
}
bool process_server(const dpp::json &json, translator &translator)
{
auto json_hostname = json.find("hostname");
if (json_hostname != json.end())
translator.hostname = *json_hostname;
auto json_tls = json.find("tls");
if (json_tls != json.end())
translator.tls = *json_tls;
else
translator.tls = false;
auto json_port = json.find("port");
if (json_port != json.end())
translator.port = *json_port;
else
translator.port = 0;
auto json_url = json.find("url");
if (json_url == json.end()) {
std::cerr << "[Error] Value url not found in translator object" << std::endl;
return false;
}
if (translator.hostname.empty())
process_server_url(*json_url, translator);
else
translator.url = *json_url;
auto json_apiKey = json.find("apiKey");
if (json_apiKey != json.end())
translator.apiKey = *json_apiKey;
return true;
}
void process_user_settings(const dpp::json &json, uint16_t &avatar_size)
{
auto json_avatar_size = json.find("avatar_size");
if (json_avatar_size != json.end()) {
avatar_size = *json_avatar_size;
if (avatar_size < 16)
avatar_size = 16;
else if (avatar_size > 4096)
avatar_size = 4096;
}
}
bool process_translator_settings(const dpp::json &json, std::shared_ptr<bot::translator::translator> &translator_instance)
bool process_translator_settings(const dpp::json &json, translator *translator)
{
if (!json.is_object()) {
std::cerr << "[Error] Value translator needs to be a object" << std::endl;
return false;
}
bot::settings::translator translator;
auto json_translator_type = json.find("type");
if (json_translator_type != json.end()) {
translator.type = *json_translator_type;
std::transform(translator.type.begin(), translator.type.end(), translator.type.begin(), ::tolower);
}
else {
translator.type = "libretranslate";
}
auto json_translate_hostname = json.find("hostname");
if (json_translate_hostname != json.end())
translator->hostname = *json_translate_hostname;
else
translator->hostname = {};
if (translator.type == "deepl") {
auto json_deepl_hostname = json.find("hostname");
if (json_deepl_hostname != json.end())
translator.hostname = *json_deepl_hostname;
else
translator.hostname = "api-free.deepl.com";
auto json_translate_tls = json.find("tls");
if (json_translate_tls != json.end())
translator->tls = *json_translate_tls;
else
translator->tls = false;
auto json_deepl_apiKey = json.find("apiKey");
if (json_deepl_apiKey == json.end()) {
std::cerr << "[Error] DeepL requires API key for authorization" << std::endl;
return false;
}
translator.apiKey = *json_deepl_apiKey;
auto json_translate_port = json.find("port");
if (json_translate_port != json.end())
translator->port = *json_translate_port;
else
translator->port = 0;
translator_instance = std::make_shared<bot::translator::deepl>(translator.hostname, translator.apiKey);
}
else if (translator.type == "mozhi") {
if (!process_server(json, translator))
return false;
std::string mozhi_engine;
auto json_mozhi_engine = json.find("engine");
if (json_mozhi_engine != json.end())
mozhi_engine = *json_mozhi_engine;
else
mozhi_engine = "google";
translator_instance = std::make_shared<bot::translator::mozhi>(translator.hostname, translator.port, translator.url, translator.tls, mozhi_engine);
}
else if (translator.type == "libretranslate") {
if (!process_server(json, translator))
return false;
translator_instance = std::make_shared<bot::translator::libretranslate>(translator.hostname, translator.port, translator.url, translator.tls, translator.apiKey);
}
else if (translator.type == "lingvatranslate") {
if (!process_server(json, translator))
return false;
translator_instance = std::make_shared<bot::translator::lingvatranslate>(translator.hostname, translator.port, translator.url, translator.tls);
}
else if (translator.type == "stub") {
translator_instance = std::make_shared<bot::translator::stub>();
}
else {
std::cerr << "[Error] Translator " << translator.type << " is unknown" << std::endl;
auto json_translate_url = json.find("url");
if (json_translate_url == json.end()) {
std::cerr << "[Error] Value url not found in translator object" << std::endl;
return false;
}
if (translator->hostname.empty()) {
process_url(*json_translate_url, translator);
}
else {
translator->url = *json_translate_url;
}
auto json_translate_apiKey = json.find("apiKey");
if (json_translate_apiKey != json.end())
translator->apiKey = *json_translate_apiKey;
else
translator->apiKey.clear();
return true;
}
@ -363,27 +300,6 @@ void settings::add_translatebot_webhook(dpp::snowflake webhook_id)
m_webhookIds.push_back(webhook_id);
}
void settings::erase_channel(guild &guild, dpp::snowflake channel_id)
{
for (auto channel = guild.channel.begin(); channel != guild.channel.end(); channel++) {
if (channel->id == channel_id) {
guild.channel.erase(channel);
return;
}
}
}
void settings::erase_guild(dpp::snowflake guild_id)
{
const std::lock_guard<std::recursive_mutex> guard(m_mutex);
for (auto guild = m_guilds.begin(); guild != m_guilds.end(); guild++) {
if (guild->id == guild_id) {
m_guilds.erase(guild);
return;
}
}
}
void settings::erase_translatebot_webhook(dpp::snowflake webhook_id)
{
const std::lock_guard<std::recursive_mutex> guard(m_mutex);
@ -398,9 +314,9 @@ uint16_t settings::avatar_size()
return m_avatarSize;
}
channel* settings::get_channel(guild &guild, dpp::snowflake channel_id)
channel* settings::get_channel(guild *guild, dpp::snowflake channel_id)
{
for (auto channel = guild.channel.begin(); channel != guild.channel.end(); channel++) {
for (auto channel = guild->channel.begin(); channel != guild->channel.end(); channel++) {
if (channel->id == channel_id)
return &*channel;
}
@ -497,10 +413,10 @@ std::shared_ptr<bot::database::database> settings::get_database() const
return m_database;
}
std::shared_ptr<bot::translator::translator> settings::get_translator() const
std::unique_ptr<bot::translator::translator> settings::get_translator() const
{
const std::lock_guard<std::recursive_mutex> guard(m_mutex);
return m_translator;
return std::make_unique<bot::translator::libretranslate>(m_translator.hostname, m_translator.port, m_translator.url, m_translator.tls, m_translator.apiKey);
}
const std::string settings::token() const
@ -530,7 +446,7 @@ bool settings::parse(const std::string &data, bool initialize)
try {
dpp::json json;
try {
json = dpp::json::parse(data, nullptr, true, true);
json = dpp::json::parse(data);
}
catch (const std::exception &exception) {
std::cerr << "[Exception] " << exception.what() << std::endl;
@ -538,24 +454,21 @@ bool settings::parse(const std::string &data, bool initialize)
return false;
}
const std::lock_guard<std::recursive_mutex> guard(m_mutex);
auto json_token = json.find("token");
if (json_token != json.end())
m_token = *json_token;
else if (char *token = getenv("DTRANSLATEBOT_TOKEN"))
m_token = token;
if (m_token.empty()) {
std::cerr << "[Error] Discord Bot Token is not configured" << std::endl;
if (json_token == json.end()) {
std::cerr << "[Error] Value token not found" << std::endl;
return false;
}
const std::lock_guard<std::recursive_mutex> guard(m_mutex);
m_token = *json_token;
std::filesystem::path storage_path;
auto json_storage = json.find("storage");
if (json_storage != json.end())
storage_path = std::string(*json_storage);
else if (char *storagepath = getenv("DTRANSLATEBOT_STORAGE"))
storage_path = storagepath;
storage_path = storagepath;
if (storage_path.empty())
storage_path = std::filesystem::current_path();
@ -567,12 +480,14 @@ bool settings::parse(const std::string &data, bool initialize)
std::cerr << "[Error] Value translator not found" << std::endl;
return false;
}
if (!process_translator_settings(*json_translator, m_translator))
if (!process_translator_settings(*json_translator, &m_translator))
return false;
m_avatarSize = 256;
auto json_guilds = json.find("guilds");
if (json_guilds != json.end() && json_guilds->is_object())
process_guild_settings(*json_guilds, m_guilds, m_webhookIds);
process_guild_settings(*json_guilds, &m_guilds, &m_webhookIds);
auto json_preflangs = json.find("preferred_lang");
if (json_preflangs != json.end() && json_preflangs->is_array())
@ -580,9 +495,9 @@ bool settings::parse(const std::string &data, bool initialize)
auto json_user = json.find("user");
if (json_user != json.end() && json_user->is_object())
process_user_settings(*json_user, m_avatarSize);
process_user_settings(*json_user, &m_avatarSize);
process_database(m_database, m_guilds, m_webhookIds);
process_database(m_database, &m_guilds, &m_webhookIds);
return true;
}

View file

@ -19,9 +19,9 @@
#ifndef SETTINGS_H
#define SETTINGS_H
#include <mutex>
#include "database.h"
#include "database_core.h"
#include "settings_types.h"
#include "translator.h"
#include "translator_core.h"
namespace bot {
namespace settings {
@ -33,13 +33,11 @@ namespace bot {
void add_translatebot_webhook(dpp::snowflake webhook_id);
/* erase functions */
static void erase_channel(guild &guild, dpp::snowflake channel_id);
void erase_guild(dpp::snowflake guild_id);
void erase_translatebot_webhook(dpp::snowflake webhook_id);
/* get functions */
uint16_t avatar_size();
static channel* get_channel(guild &guild, dpp::snowflake channel_id);
static channel* get_channel(guild *guild, dpp::snowflake channel_id);
channel* get_channel(dpp::snowflake guild_id, dpp::snowflake channel_id);
guild* get_guild(dpp::snowflake guild_id);
target* get_target(dpp::snowflake guild_id, dpp::snowflake channel_id, const std::string &target);
@ -47,7 +45,7 @@ namespace bot {
static const target* get_target(const channel *channel, const std::string &target);
const std::vector<std::string> preferred_languages() const;
std::shared_ptr<bot::database::database> get_database() const;
std::shared_ptr<bot::translator::translator> get_translator() const;
std::unique_ptr<bot::translator::translator> get_translator() const;
const std::string token() const;
/* is functions */
@ -61,19 +59,14 @@ namespace bot {
bool parse(const std::string &data, bool initialize = true);
bool parse_file(const std::string &filename, bool initialize = true);
/* prevent copies */
settings() = default;
settings(const settings&) = delete;
settings& operator=(const settings&) = delete;
private:
mutable std::recursive_mutex m_mutex;
size_t m_externallyLockedCount = 0;
uint16_t m_avatarSize = 256;
size_t m_externallyLockedCount;
uint16_t m_avatarSize;
std::shared_ptr<bot::database::database> m_database;
std::vector<guild> m_guilds;
std::vector<std::string> m_prefLangs;
std::shared_ptr<bot::translator::translator> m_translator;
bot::settings::translator m_translator;
std::string m_token;
std::vector<dpp::snowflake> m_webhookIds;
};

View file

@ -41,7 +41,6 @@ namespace bot {
std::vector<bot::settings::channel> channel;
};
struct translator {
std::string type;
std::string hostname;
uint16_t port;
std::string url;

View file

@ -18,20 +18,19 @@
#include <sstream>
#include "slashcommands.h"
using bot::slashcommands;
using namespace std::string_literals;
void slashcommands::process_command_event(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event)
void bot::slashcommands::process_command_event(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event)
{
if (event.command.get_command_name() == "edit")
slashcommands::process_edit_command(bot, settings, event);
bot::slashcommands::process_edit_command(bot, settings, event);
else if (event.command.get_command_name() == "list")
slashcommands::process_list_command(bot, settings, event);
bot::slashcommands::process_list_command(bot, settings, event);
else if (event.command.get_command_name() == "translate" || event.command.get_command_name() == "translate_pref")
slashcommands::process_translate_command(bot, settings, event);
bot::slashcommands::process_translate_command(bot, settings, event);
}
void slashcommands::process_edit_command(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event)
void bot::slashcommands::process_edit_command(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event)
{
try {
dpp::permission user_permissions = event.command.get_resolved_permission(event.command.usr.id);
@ -41,83 +40,64 @@ void slashcommands::process_edit_command(dpp::cluster *bot, bot::settings::setti
dpp::command_interaction interaction = event.command.get_command_interaction();
if (interaction.options[0].name == "delete") {
const std::lock_guard<bot::settings::settings> guard(*settings);
if (bot::settings::guild *guild = settings->get_guild(event.command.guild_id)) {
if (bot::settings::channel *channel = settings->get_channel(*guild, event.command.channel_id)) {
const std::string target = std::get<std::string>(event.get_parameter("target"));
if (bot::settings::channel *channel = settings->get_channel(event.command.guild_id, event.command.channel_id)) {
const std::string target = std::get<std::string>(event.get_parameter("target"));
auto database = settings->get_database();
const bot::settings::channel db_channel = database->get_channel(event.command.guild_id, event.command.channel_id);
std::shared_ptr<bot::database::database> database = settings->get_database();
const bot::settings::channel db_channel = database->get_channel(event.command.guild_id, event.command.channel_id);
if (db_channel.targets.empty()) {
event.reply(dpp::message("The current channel has no deleteable targets!").set_flags(dpp::m_ephemeral));
if (db_channel.targets.empty()) {
event.reply(dpp::message("The current channel has no deleteable targets!").set_flags(dpp::m_ephemeral));
}
else if (target == "**") {
std::vector<std::string> targets;
for (auto db_target = db_channel.targets.begin(); db_target != db_channel.targets.end(); db_target++) {
targets.push_back(db_target->target);
}
else if (target == "**") {
std::vector<std::string> targets;
for (auto db_target = db_channel.targets.begin(); db_target != db_channel.targets.end(); db_target++) {
targets.push_back(db_target->target);
for (auto target = channel->targets.begin(); target != channel->targets.end();) {
if (std::find(targets.begin(), targets.end(), target->target) != targets.end()) {
bot->delete_webhook(target->webhook.id, std::bind(&bot::slashcommands::process_deleted_webhook, settings, target->webhook.id, std::placeholders::_1));
target = channel->targets.erase(target);
}
for (auto target = channel->targets.begin(); target != channel->targets.end();) {
if (std::find(targets.begin(), targets.end(), target->target) != targets.end()) {
bot->delete_webhook(target->webhook.id, std::bind(&slashcommands::process_deleted_webhook, settings, target->webhook.id, std::placeholders::_1));
target = channel->targets.erase(target);
}
else {
target++;
}
else {
target++;
}
database->delete_channel(event.command.guild_id, event.command.channel_id);
database->sync();
if (channel->targets.empty()) {
settings->erase_channel(*guild, event.command.channel_id);
if (guild->channel.empty()) {
settings->erase_guild(event.command.guild_id);
}
}
event.reply(dpp::message("Deleteable targets have being deleted!").set_flags(dpp::m_ephemeral));
}
else {
bool target_found = false;
for (auto db_target = db_channel.targets.begin(); db_target != db_channel.targets.end(); db_target++) {
if (db_target->target == target) {
target_found = true;
database->delete_channel(event.command.guild_id, event.command.channel_id);
database->sync();
event.reply(dpp::message("Deleteable targets have being deleted!").set_flags(dpp::m_ephemeral));
}
else {
bool target_found = false;
for (auto db_target = db_channel.targets.begin(); db_target != db_channel.targets.end(); db_target++) {
if (db_target->target == target) {
target_found = true;
break;
}
}
if (target_found) {
for (auto _target = channel->targets.begin(); _target != channel->targets.end(); _target++) {
if (_target->target == target) {
bot->delete_webhook(_target->webhook.id, std::bind(&bot::slashcommands::process_deleted_webhook, settings, _target->webhook.id, std::placeholders::_1));
channel->targets.erase(_target);
break;
}
}
if (target_found) {
for (auto _target = channel->targets.begin(); _target != channel->targets.end(); _target++) {
if (_target->target == target) {
bot->delete_webhook(_target->webhook.id, std::bind(&slashcommands::process_deleted_webhook, settings, _target->webhook.id, std::placeholders::_1));
channel->targets.erase(_target);
break;
}
}
if (db_channel.targets.size() == 1)
database->delete_channel(event.command.guild_id, event.command.channel_id);
else
database->delete_channel_target(event.command.guild_id, event.command.channel_id, target);
database->sync();
if (db_channel.targets.size() == 1)
database->delete_channel(event.command.guild_id, event.command.channel_id);
else
database->delete_channel_target(event.command.guild_id, event.command.channel_id, target);
database->sync();
if (channel->targets.empty()) {
settings->erase_channel(*guild, event.command.channel_id);
if (guild->channel.empty()) {
settings->erase_guild(event.command.guild_id);
}
}
event.reply(dpp::message("Target have being deleted!").set_flags(dpp::m_ephemeral));
}
else {
event.reply(dpp::message("Target language is not being found or deleteable!").set_flags(dpp::m_ephemeral));
}
event.reply(dpp::message("Target have being deleted!").set_flags(dpp::m_ephemeral));
}
else {
event.reply(dpp::message("Target language is not being found or deleteable!").set_flags(dpp::m_ephemeral));
}
}
else {
event.reply(dpp::message("The current channel is not being translated!").set_flags(dpp::m_ephemeral));
}
}
else {
@ -137,13 +117,13 @@ void slashcommands::process_edit_command(dpp::cluster *bot, bot::settings::setti
source_valid = true;
break;
}
language_codes << ' ' << language.code;
language_codes << " " << language.code;
}
if (source_valid) {
channel->source = source;
auto database = settings->get_database();
std::shared_ptr<bot::database::database> database = settings->get_database();
database->set_channel_source(event.command.guild_id, event.command.channel_id, source);
database->sync();
@ -167,7 +147,7 @@ void slashcommands::process_edit_command(dpp::cluster *bot, bot::settings::setti
}
}
void slashcommands::process_deleted_webhook(bot::settings::settings *settings, dpp::snowflake webhook_id, const dpp::confirmation_callback_t &callback)
void bot::slashcommands::process_deleted_webhook(bot::settings::settings *settings, dpp::snowflake webhook_id, const dpp::confirmation_callback_t &callback)
{
if (callback.is_error()) {
std::cerr << "[Error] Failed to delete Webhook " << webhook_id << std::endl;
@ -176,7 +156,7 @@ void slashcommands::process_deleted_webhook(bot::settings::settings *settings, d
settings->erase_translatebot_webhook(webhook_id);
}
void slashcommands::process_list_command(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event)
void bot::slashcommands::process_list_command(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event)
{
try {
dpp::command_interaction interaction = event.command.get_command_interaction();
@ -191,7 +171,7 @@ void slashcommands::process_list_command(dpp::cluster *bot, bot::settings::setti
// We want give more information to users who can Manage Webhooks
dpp::permission user_permissions = event.command.get_resolved_permission(event.command.usr.id);
if (user_permissions.has(dpp::p_manage_webhooks)) {
auto database = settings->get_database();
std::shared_ptr<bot::database::database> database = settings->get_database();
const bot::settings::channel db_channel = database->get_channel(event.command.guild_id, event.command.channel_id);
for (auto target = channel->targets.begin(); target != channel->targets.end(); target++) {
@ -237,44 +217,17 @@ void slashcommands::process_list_command(dpp::cluster *bot, bot::settings::setti
event.reply(dpp::message("The current guild have no translated channel!").set_flags(dpp::m_ephemeral));
}
}
else if (interaction.options[0].name == "languages") {
dpp::permission user_permissions = event.command.get_resolved_permission(event.command.usr.id);
if (user_permissions.has(dpp::p_manage_webhooks)) {
const std::vector<bot::translator::language> languages = settings->get_translator()->get_languages();
std::ostringstream reply_languages;
reply_languages << "**Available Languages**\n";
for (auto language = languages.begin(); language != languages.end();) {
reply_languages << language->name << ": " << language->code;
if (++language != languages.end())
reply_languages << '\n';
}
if (reply_languages.str().length() <= 2000) {
event.reply(dpp::message(reply_languages.str()).set_flags(dpp::m_ephemeral));
}
else {
reply_languages.str({});
reply_languages << "Available Languages:";
for (auto language = languages.begin(); language != languages.end(); language++) {
reply_languages << ' ' << language->code;
}
event.reply(dpp::message(reply_languages.str()).set_flags(dpp::m_ephemeral));
}
}
else {
event.reply(dpp::message("Unauthorized to list available languages!").set_flags(dpp::m_ephemeral));
}
}
else {
throw std::invalid_argument("Option " + interaction.options[0].name + " is not known");
}
}
catch (const std::exception &exception) {
catch (const std::exception& exception) {
std::cerr << "[Exception] " << exception.what() << std::endl;
event.reply(dpp::message("Exception while processing command:\n"s + exception.what()).set_flags(dpp::m_ephemeral));
}
}
void slashcommands::process_translate_command(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event)
void bot::slashcommands::process_translate_command(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event)
{
try {
dpp::permission user_permissions = event.command.get_resolved_permission(event.command.usr.id);
@ -288,13 +241,13 @@ void slashcommands::process_translate_command(dpp::cluster *bot, bot::settings::
dpp::command_interaction interaction = event.command.get_command_interaction();
if (interaction.options[0].name == "channel") {
v_target = event.command.get_resolved_channel(
std::get<dpp::snowflake>(event.get_parameter("channel")));
std::get<dpp::snowflake>(event.get_parameter("channel")));
}
else if (interaction.options[0].name == "webhook") {
v_target = dpp::webhook(std::get<std::string>(event.get_parameter("webhook")));
}
const auto languages = settings->get_translator()->get_languages();
const std::vector<bot::translator::language> languages = settings->get_translator()->get_languages();
std::ostringstream language_codes;
bool source_valid = false, target_valid = false;
@ -305,7 +258,7 @@ void slashcommands::process_translate_command(dpp::cluster *bot, bot::settings::
target_valid = true;
if (source_valid && target_valid)
break;
language_codes << ' ' << language.code;
language_codes << " " << language.code;
}
if (source_valid && target_valid) {
@ -318,7 +271,7 @@ void slashcommands::process_translate_command(dpp::cluster *bot, bot::settings::
webhook.guild_id = channel->guild_id;
webhook.name = "Translate Bot Webhook <" + std::to_string(event.command.channel_id) + ":" + source + ":" + target + ">";
bot->create_webhook(webhook, std::bind(&slashcommands::process_translate_webhook_new_channel, settings, event, source, target, std::placeholders::_1));
bot->create_webhook(webhook, std::bind(&bot::slashcommands::process_translate_webhook_new_channel, settings, event, source, target, std::placeholders::_1));
}
else if (dpp::webhook *webhook = std::get_if<dpp::webhook>(&v_target)) {
const bot::settings::target s_target = { target, *webhook };
@ -327,7 +280,7 @@ void slashcommands::process_translate_command(dpp::cluster *bot, bot::settings::
settings->add_channel(s_channel, event.command.guild_id);
settings->add_translatebot_webhook(webhook->id);
auto database = settings->get_database();
std::shared_ptr<bot::database::database> database = settings->get_database();
database->set_channel_source(event.command.guild_id, event.command.channel_id, source);
database->add_channel_target(event.command.guild_id, event.command.channel_id, s_target);
database->sync();
@ -345,7 +298,7 @@ void slashcommands::process_translate_command(dpp::cluster *bot, bot::settings::
webhook.guild_id = channel->guild_id;
webhook.name = "Translate Bot Webhook <" + std::to_string(event.command.channel_id) + ":" + source + ":" + target + ">";
bot->create_webhook(webhook, std::bind(&slashcommands::process_translate_webhook_add_target, settings, event, target, std::placeholders::_1));
bot->create_webhook(webhook, std::bind(&bot::slashcommands::process_translate_webhook_add_target, settings, event, target, std::placeholders::_1));
}
else if (dpp::webhook *webhook = std::get_if<dpp::webhook>(&v_target)) {
const bot::settings::target s_target = { target, *webhook };
@ -353,7 +306,7 @@ void slashcommands::process_translate_command(dpp::cluster *bot, bot::settings::
settings->add_target(s_target, event.command.guild_id, event.command.channel_id);
settings->add_translatebot_webhook(webhook->id);
auto database = settings->get_database();
std::shared_ptr<bot::database::database> database = settings->get_database();
database->add_channel_target(event.command.guild_id, event.command.channel_id, s_target);
database->sync();
@ -380,7 +333,7 @@ void slashcommands::process_translate_command(dpp::cluster *bot, bot::settings::
}
}
void slashcommands::process_translate_webhook_add_target(bot::settings::settings *settings, const dpp::slashcommand_t &event, const std::string &target, const dpp::confirmation_callback_t &callback)
void bot::slashcommands::process_translate_webhook_add_target(bot::settings::settings *settings, const dpp::slashcommand_t &event, const std::string &target, const dpp::confirmation_callback_t &callback)
{
if (callback.is_error()) {
event.reply(dpp::message("Failed to generate webhook!").set_flags(dpp::m_ephemeral));
@ -394,14 +347,14 @@ void slashcommands::process_translate_webhook_add_target(bot::settings::settings
settings->add_target(s_target, event.command.guild_id, event.command.channel_id);
settings->add_translatebot_webhook(webhook.id);
auto database = settings->get_database();
std::shared_ptr<bot::database::database> database = settings->get_database();
database->add_channel_target(event.command.guild_id, event.command.channel_id, s_target);
database->sync();
event.reply(dpp::message("Channel will be now translated!").set_flags(dpp::m_ephemeral));
}
void slashcommands::process_translate_webhook_new_channel(bot::settings::settings *settings, const dpp::slashcommand_t &event, const std::string &source, const std::string &target, const dpp::confirmation_callback_t &callback)
void bot::slashcommands::process_translate_webhook_new_channel(bot::settings::settings *settings, const dpp::slashcommand_t &event, const std::string &source, const std::string &target, const dpp::confirmation_callback_t &callback)
{
if (callback.is_error()) {
event.reply(dpp::message("Failed to generate webhook!").set_flags(dpp::m_ephemeral));
@ -416,7 +369,7 @@ void slashcommands::process_translate_webhook_new_channel(bot::settings::setting
settings->add_channel(s_channel, event.command.guild_id);
settings->add_translatebot_webhook(webhook.id);
auto database = settings->get_database();
std::shared_ptr<bot::database::database> database = settings->get_database();
database->set_channel_source(event.command.guild_id, event.command.channel_id, source);
database->add_channel_target(event.command.guild_id, event.command.channel_id, s_target);
database->sync();
@ -424,7 +377,7 @@ void slashcommands::process_translate_webhook_new_channel(bot::settings::setting
event.reply(dpp::message("Channel will be now translated!").set_flags(dpp::m_ephemeral));
}
void slashcommands::register_commands(dpp::cluster *bot, bot::settings::settings *settings)
void bot::slashcommands::register_commands(dpp::cluster *bot, bot::settings::settings *settings)
{
settings->lock();
const std::vector<bot::translator::language> languages = settings->get_translator()->get_languages();
@ -434,9 +387,9 @@ void slashcommands::register_commands(dpp::cluster *bot, bot::settings::settings
std::vector<dpp::slashcommand> commands;
dpp::command_option source_option(dpp::co_string, "source", "Source language (ISO 639-1)", true);
source_option.set_max_length(static_cast<int64_t>(5)).set_min_length(static_cast<int64_t>(2));
source_option.set_max_length(static_cast<int64_t>(2)).set_min_length(static_cast<int64_t>(2));
dpp::command_option target_option(dpp::co_string, "target", "Target language (ISO 639-1)", true);
target_option.set_max_length(static_cast<int64_t>(5)).set_min_length(static_cast<int64_t>(2));
target_option.set_max_length(static_cast<int64_t>(2)).set_min_length(static_cast<int64_t>(2));
dpp::slashcommand command_edit("edit", "Edit current channel settings", bot->me.id);
command_edit.set_default_permissions(dpp::p_manage_webhooks);
@ -451,10 +404,8 @@ void slashcommands::register_commands(dpp::cluster *bot, bot::settings::settings
dpp::slashcommand command_list("list", "List translation settings", bot->me.id);
dpp::command_option channel_list_subcommand(dpp::co_sub_command, "channel", "List current channel translation settings");
dpp::command_option guild_list_subcommand(dpp::co_sub_command, "guild", "List current guild translation settings");
dpp::command_option languages_list_subcommand(dpp::co_sub_command, "languages", "List available languages to translate");
command_list.add_option(channel_list_subcommand);
command_list.add_option(guild_list_subcommand);
command_list.add_option(languages_list_subcommand);
commands.push_back(command_list);
dpp::slashcommand command_translate("translate", "Translate current channel", bot->me.id);

View file

@ -23,20 +23,16 @@
#include "settings.h"
namespace bot {
class slashcommands {
public:
slashcommands() = delete;
static void process_command_event(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event);
static void register_commands(dpp::cluster *bot, bot::settings::settings *settings);
private:
static void process_edit_command(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event);
static void process_deleted_webhook(bot::settings::settings *settings, dpp::snowflake webhook_id, const dpp::confirmation_callback_t &callback);
static void process_list_command(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event);
static void process_translate_command(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event);
static void process_translate_webhook_add_target(bot::settings::settings *settings, const dpp::slashcommand_t &event, const std::string &target, const dpp::confirmation_callback_t &callback);
static void process_translate_webhook_new_channel(bot::settings::settings *settings, const dpp::slashcommand_t &event, const std::string &source, const std::string &target, const dpp::confirmation_callback_t &callback);
};
namespace slashcommands {
extern void process_command_event(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event);
extern void process_edit_command(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event);
extern void process_deleted_webhook(bot::settings::settings *settings, dpp::snowflake webhook_id, const dpp::confirmation_callback_t &callback);
extern void process_list_command(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event);
extern void process_translate_command(dpp::cluster *bot, bot::settings::settings *settings, const dpp::slashcommand_t &event);
extern void process_translate_webhook_add_target(bot::settings::settings *settings, const dpp::slashcommand_t &event, const std::string &target, const dpp::confirmation_callback_t &callback);
extern void process_translate_webhook_new_channel(bot::settings::settings *settings, const dpp::slashcommand_t &event, const std::string &source, const std::string &target, const dpp::confirmation_callback_t &callback);
extern void register_commands(dpp::cluster *bot, bot::settings::settings *settings);
}
}
#endif // SLASHCOMMANDS_H

View file

@ -19,7 +19,7 @@
#include <thread>
#include "submit_queue.h"
#include "webhook_push.h"
using bot::submit_queue;
using namespace bot;
using namespace std::chrono_literals;
void submit_queue::add(const translated_message &message)

View file

@ -1,13 +0,0 @@
[Unit]
Description=Discord Translation Bot
After=network.target
[Service]
User=dtranslatebot
Group=dtranslatebot
WorkingDirectory=@dtranslatebot_SERVICE_WORKDIR@
ExecStart="@CMAKE_INSTALL_FULL_BINDIR@/dtranslatebot" "@CMAKE_INSTALL_FULL_SYSCONFDIR@/dtranslatebot.json"
Restart=on-abnormal
[Install]
WantedBy=multi-user.target

View file

@ -1 +0,0 @@
u dtranslatebot - "Discord Translation Bot" "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/lib/dtranslatebot" -

View file

@ -1,116 +0,0 @@
/*****************************************************************************
* dtranslatebot Discord Translate Bot
* Copyright (C) 2024 Syping
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* This software is provided as-is, no warranties are given to you, we are not
* responsible for anything with use of the software, you are self responsible.
*****************************************************************************/
#include <dpp/json.h>
#include <dpp/httpsclient.h>
#include "deepl.h"
using namespace bot::translator;
using namespace std::chrono_literals;
using namespace std::string_literals;
deepl::deepl(const std::string &hostname, const std::string apiKey) :
m_hostname(hostname), m_apiKey(apiKey)
{
}
deepl::~deepl()
{
}
const std::vector<language> deepl::get_languages()
{
if (!m_languages.languages.empty()) {
auto current_time = std::chrono::system_clock::now();
auto threshold_time = m_languages.query_time + 24h;
if (current_time <= threshold_time)
return m_languages.languages;
}
try {
dpp::https_client http_request(&m_cluster, m_hostname, 443, "/v2/languages?type=target", "GET", {}, { {"Authorization"s, "DeepL-Auth-Key " + m_apiKey} }, false);
if (http_request.get_status() == 200) {
const dpp::json response = dpp::json::parse(http_request.get_content());
if (response.is_array()) {
m_languages.languages.clear();
for (auto json_language = response.begin(); json_language != response.end(); json_language++) {
if (json_language->is_object()) {
language language;
auto json_lang_code = json_language->find("language");
if (json_lang_code != json_language->end())
language.code = *json_lang_code;
if (language.code.size() > 2)
std::transform(language.code.begin(), language.code.begin() + 2, language.code.begin(), ::tolower);
else
std::transform(language.code.begin(), language.code.end(), language.code.begin(), ::tolower);
auto json_lang_name = json_language->find("name");
if (json_lang_name != json_language->end())
language.name = *json_lang_name;
if (!language.code.empty() && !language.name.empty())
m_languages.languages.push_back(std::move(language));
}
}
m_languages.query_time = std::chrono::system_clock::now();
}
}
}
catch (const std::exception &exception) {
std::cerr << "[Exception] " << exception.what() << std::endl;
}
return m_languages.languages;
}
const std::string deepl::translate(const std::string &text, const std::string &source, const std::string &target)
{
const dpp::http_headers http_headers = {
{"Authorization"s, "DeepL-Auth-Key " + m_apiKey},
{"Content-Type"s, "application/json"s}
};
dpp::json json_body = {
{"text"s, { text } },
{"source_lang"s, source},
{"target_lang"s, target},
};
try {
dpp::https_client http_request(&m_cluster, m_hostname, 443, "/v2/translate", "POST", json_body.dump(), http_headers, false);
if (http_request.get_status() == 200) {
const dpp::json response = dpp::json::parse(http_request.get_content());
if (response.is_object()) {
auto translations = response.find("translations");
if (translations != response.end() && translations->is_array()) {
for (auto translation = translations->begin(); translation != translations->end(); translation++) {
auto tr_text = translation->find("text");
if (tr_text != translation->end())
return *tr_text;
}
}
}
}
}
catch (const std::exception &exception) {
std::cerr << "[Exception] " << exception.what() << std::endl;
}
return text;
}

View file

@ -1,43 +0,0 @@
/*****************************************************************************
* dtranslatebot Discord Translate Bot
* Copyright (C) 2024 Syping
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* This software is provided as-is, no warranties are given to you, we are not
* responsible for anything with use of the software, you are self responsible.
*****************************************************************************/
#ifndef TRANSLATOR_DEEPL_H
#define TRANSLATOR_DEEPL_H
#include <dpp/cluster.h>
#include "../../core/translator.h"
namespace bot {
namespace translator {
class deepl : public translator {
public:
explicit deepl(const std::string &hostname, const std::string apiKey = {});
~deepl() override;
const std::vector<language> get_languages() override;
const std::string translate(const std::string &text, const std::string &source, const std::string &target) override;
private:
dpp::cluster m_cluster;
std::string m_apiKey;
std::string m_hostname;
supported_languages m_languages;
};
}
}
#endif // TRANSLATOR_DEEPL_H

View file

@ -1,99 +0,0 @@
/*****************************************************************************
* dtranslatebot Discord Translate Bot
* Copyright (C) 2024 Syping
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* This software is provided as-is, no warranties are given to you, we are not
* responsible for anything with use of the software, you are self responsible.
*****************************************************************************/
#include <dpp/json.h>
#include <dpp/httpsclient.h>
#include <dpp/utility.h>
#include "lingvatranslate.h"
using namespace bot::translator;
using namespace std::chrono_literals;
using namespace std::string_literals;
lingvatranslate::lingvatranslate(const std::string &hostname, uint16_t port, const std::string &url, bool tls) :
m_hostname(hostname), m_port(port), m_url(url), m_tls(tls)
{
}
lingvatranslate::~lingvatranslate()
{
}
const std::vector<language> lingvatranslate::get_languages()
{
if (!m_languages.languages.empty()) {
auto current_time = std::chrono::system_clock::now();
auto threshold_time = m_languages.query_time + 24h;
if (current_time <= threshold_time)
return m_languages.languages;
}
try {
dpp::https_client http_request(&m_cluster, m_hostname, m_port, m_url + "api/v1/languages/target", "GET", {}, {}, !m_tls);
if (http_request.get_status() == 200) {
const dpp::json response = dpp::json::parse(http_request.get_content());
if (response.is_object()) {
auto languages = response.find("languages");
if (languages != response.end()) {
m_languages.languages.clear();
for (auto json_language = languages->begin(); json_language != languages->end(); json_language++) {
if (json_language->is_object()) {
language language;
auto json_lang_code = json_language->find("code");
if (json_lang_code != json_language->end())
language.code = *json_lang_code;
auto json_lang_name = json_language->find("name");
if (json_lang_name != json_language->end())
language.name = *json_lang_name;
if (!language.code.empty() && !language.name.empty())
m_languages.languages.push_back(std::move(language));
}
}
m_languages.query_time = std::chrono::system_clock::now();
}
}
}
}
catch (const std::exception &exception) {
std::cerr << "[Exception] " << exception.what() << std::endl;
}
return m_languages.languages;
}
const std::string lingvatranslate::translate(const std::string &text, const std::string &source, const std::string &target)
{
try {
dpp::https_client http_request(&m_cluster, m_hostname, m_port, m_url + "api/v1/" + source + "/" + target + "/" + dpp::utility::url_encode(text), "GET", {}, {}, !m_tls);
if (http_request.get_status() == 200) {
const dpp::json response = dpp::json::parse(http_request.get_content());
if (response.is_object()) {
auto tr_text = response.find("translation");
if (tr_text != response.end())
return *tr_text;
}
}
}
catch (const std::exception &exception) {
std::cerr << "[Exception] " << exception.what() << std::endl;
}
return text;
}

View file

@ -1,47 +0,0 @@
/*****************************************************************************
* dtranslatebot Discord Translate Bot
* Copyright (C) 2024 Syping
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* This software is provided as-is, no warranties are given to you, we are not
* responsible for anything with use of the software, you are self responsible.
*****************************************************************************/
#ifndef TRANSLATOR_LINGVATRANSLATE_H
#define TRANSLATOR_LINGVATRANSLATE_H
#include <cstdint>
#include <dpp/cluster.h>
#include "../../core/translator.h"
namespace bot {
namespace translator {
class lingvatranslate : public translator {
public:
explicit lingvatranslate(const std::string &hostname, uint16_t port, const std::string &url, bool tls);
~lingvatranslate() override;
const std::vector<language> get_languages() override;
const std::string translate(const std::string &text, const std::string &source, const std::string &target) override;
private:
dpp::cluster m_cluster;
std::string m_hostname;
supported_languages m_languages;
uint16_t m_port;
std::string m_url;
bool m_tls;
};
}
}
#endif // TRANSLATOR_LINGVATRANSLATE_H

View file

@ -1,105 +0,0 @@
/*****************************************************************************
* dtranslatebot Discord Translate Bot
* Copyright (C) 2024 Syping
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* This software is provided as-is, no warranties are given to you, we are not
* responsible for anything with use of the software, you are self responsible.
*****************************************************************************/
#include <dpp/json.h>
#include <dpp/httpsclient.h>
#include <dpp/utility.h>
#include "mozhi.h"
using namespace bot::translator;
using namespace std::chrono_literals;
using namespace std::string_literals;
mozhi::mozhi(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const std::string &engine) :
m_hostname(hostname), m_port(port), m_url(url), m_tls(tls), m_engine(engine)
{
}
mozhi::~mozhi()
{
}
const std::vector<language> mozhi::get_languages()
{
if (!m_languages.languages.empty()) {
auto current_time = std::chrono::system_clock::now();
auto threshold_time = m_languages.query_time + 24h;
if (current_time <= threshold_time)
return m_languages.languages;
}
try {
const std::string parameters = dpp::utility::make_url_parameters({
{"engine"s, m_engine}
});
dpp::https_client http_request(&m_cluster, m_hostname, m_port, m_url + "api/target_languages" + parameters, "GET", {}, {}, !m_tls);
if (http_request.get_status() == 200) {
const dpp::json response = dpp::json::parse(http_request.get_content());
if (response.is_array()) {
m_languages.languages.clear();
for (auto json_language = response.begin(); json_language != response.end(); json_language++) {
if (json_language->is_object()) {
language language;
auto json_lang_code = json_language->find("Id");
if (json_lang_code != json_language->end())
language.code = *json_lang_code;
auto json_lang_name = json_language->find("Name");
if (json_lang_name != json_language->end())
language.name = *json_lang_name;
if (!language.code.empty() && !language.name.empty())
m_languages.languages.push_back(std::move(language));
}
}
m_languages.query_time = std::chrono::system_clock::now();
}
}
}
catch (const std::exception &exception) {
std::cerr << "[Exception] " << exception.what() << std::endl;
}
return m_languages.languages;
}
const std::string mozhi::translate(const std::string &text, const std::string &source, const std::string &target)
{
try {
const std::string parameters = dpp::utility::make_url_parameters({
{"engine"s, m_engine},
{"from"s, source},
{"to"s, target},
{"text"s, text},
});
dpp::https_client http_request(&m_cluster, m_hostname, m_port, m_url + "api/translate" + parameters, "GET", {}, {}, !m_tls);
if (http_request.get_status() == 200) {
const dpp::json response = dpp::json::parse(http_request.get_content());
if (response.is_object()) {
auto tr_text = response.find("translated-text");
if (tr_text != response.end())
return *tr_text;
}
}
}
catch (const std::exception &exception) {
std::cerr << "[Exception] " << exception.what() << std::endl;
}
return text;
}

View file

@ -1,48 +0,0 @@
/*****************************************************************************
* dtranslatebot Discord Translate Bot
* Copyright (C) 2024 Syping
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* This software is provided as-is, no warranties are given to you, we are not
* responsible for anything with use of the software, you are self responsible.
*****************************************************************************/
#ifndef TRANSLATOR_MOZHI_H
#define TRANSLATOR_MOZHI_H
#include <cstdint>
#include <dpp/cluster.h>
#include "../../core/translator.h"
namespace bot {
namespace translator {
class mozhi : public translator {
public:
explicit mozhi(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const std::string &engine);
~mozhi() override;
const std::vector<language> get_languages() override;
const std::string translate(const std::string &text, const std::string &source, const std::string &target) override;
private:
dpp::cluster m_cluster;
std::string m_engine;
std::string m_hostname;
supported_languages m_languages;
uint16_t m_port;
std::string m_url;
bool m_tls;
};
}
}
#endif // TRANSLATOR_MOZHI_H

View file

@ -1,39 +0,0 @@
/*****************************************************************************
* dtranslatebot Discord Translate Bot
* Copyright (C) 2024 Syping
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* This software is provided as-is, no warranties are given to you, we are not
* responsible for anything with use of the software, you are self responsible.
*****************************************************************************/
#include "stub.h"
using namespace bot::translator;
using namespace std::string_literals;
stub::stub()
{
}
stub::~stub()
{
}
const std::vector<language> stub::get_languages()
{
return { {"a*", "Any Language"} };
}
const std::string stub::translate(const std::string &text, [[maybe_unused]] const std::string &source, [[maybe_unused]] const std::string &target)
{
return text;
}

View file

@ -1,36 +0,0 @@
/*****************************************************************************
* dtranslatebot Discord Translate Bot
* Copyright (C) 2024 Syping
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* This software is provided as-is, no warranties are given to you, we are not
* responsible for anything with use of the software, you are self responsible.
*****************************************************************************/
#ifndef TRANSLATOR_STUB_H
#define TRANSLATOR_STUB_H
#include "../../core/translator.h"
namespace bot {
namespace translator {
class stub : public translator {
public:
explicit stub();
~stub() override;
const std::vector<language> get_languages() override;
const std::string translate(const std::string &text, const std::string &source, const std::string &target) override;
};
}
}
#endif // TRANSLATOR_STUB_H

View file

@ -19,9 +19,17 @@
#ifndef NDEBUG
#include <iostream>
#endif
#include "translator.h"
#include "translator_core.h"
using namespace bot::translator;
translator::translator()
{
}
translator::~translator()
{
}
const std::vector<language> translator::get_languages()
{
#ifndef NDEBUG

View file

@ -16,10 +16,9 @@
* responsible for anything with use of the software, you are self responsible.
*****************************************************************************/
#ifndef TRANSLATOR_H
#define TRANSLATOR_H
#ifndef TRANSLATOR_CORE_H
#define TRANSLATOR_CORE_H
#include <chrono>
#include <string>
#include <vector>
@ -30,21 +29,14 @@ namespace bot {
std::string name;
};
struct supported_languages {
std::vector<language> languages;
std::chrono::system_clock::time_point query_time;
};
class translator {
public:
explicit translator() = default;
virtual ~translator() = default;
translator(const translator&) = delete;
translator& operator=(const translator&) = delete;
explicit translator();
virtual ~translator();
virtual const std::vector<language> get_languages();
virtual const std::string translate(const std::string &text, const std::string &source, const std::string &target);
};
}
}
#endif // TRANSLATOR_H
#endif // TRANSLATOR_CORE_H

View file

@ -18,9 +18,8 @@
#include <dpp/json.h>
#include <dpp/httpsclient.h>
#include "libretranslate.h"
#include "translator_libretranslate.h"
using namespace bot::translator;
using namespace std::chrono_literals;
using namespace std::string_literals;
libretranslate::libretranslate(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const std::string apiKey) :
@ -34,36 +33,29 @@ libretranslate::~libretranslate()
const std::vector<language> libretranslate::get_languages()
{
if (!m_languages.languages.empty()) {
auto current_time = std::chrono::system_clock::now();
auto threshold_time = m_languages.query_time + 24h;
if (current_time <= threshold_time)
return m_languages.languages;
}
std::vector<language> languages;
try {
dpp::https_client http_request(&m_cluster, m_hostname, m_port, m_url + "languages", "GET", {}, {}, !m_tls);
dpp::https_client http_request(m_hostname, m_port, m_url + "languages", "GET", {}, {}, !m_tls);
if (http_request.get_status() == 200) {
const dpp::json response = dpp::json::parse(http_request.get_content());
if (response.is_array()) {
m_languages.languages.clear();
for (auto json_language = response.begin(); json_language != response.end(); json_language++) {
if (json_language->is_object()) {
for (const auto &json_language : response) {
if (json_language.is_object()) {
language language;
auto json_lang_code = json_language->find("code");
if (json_lang_code != json_language->end())
auto json_lang_code = json_language.find("code");
if (json_lang_code != json_language.end())
language.code = *json_lang_code;
auto json_lang_name = json_language->find("name");
if (json_lang_name != json_language->end())
auto json_lang_name = json_language.find("name");
if (json_lang_name != json_language.end())
language.name = *json_lang_name;
if (!language.code.empty() && !language.name.empty())
m_languages.languages.push_back(std::move(language));
languages.push_back(std::move(language));
}
}
m_languages.query_time = std::chrono::system_clock::now();
}
}
}
@ -71,7 +63,7 @@ const std::vector<language> libretranslate::get_languages()
std::cerr << "[Exception] " << exception.what() << std::endl;
}
return m_languages.languages;
return languages;
}
const std::string libretranslate::translate(const std::string &text, const std::string &source, const std::string &target)
@ -91,7 +83,7 @@ const std::string libretranslate::translate(const std::string &text, const std::
json_body["apiKey"] = m_apiKey;
try {
dpp::https_client http_request(&m_cluster, m_hostname, m_port, m_url + "translate", "POST", json_body.dump(), http_headers, !m_tls);
dpp::https_client http_request(m_hostname, m_port, m_url + "translate", "POST", json_body.dump(), http_headers, !m_tls);
if (http_request.get_status() == 200) {
const dpp::json response = dpp::json::parse(http_request.get_content());
if (response.is_object()) {

View file

@ -20,13 +20,12 @@
#define TRANSLATOR_LIBRETRANSLATE_H
#include <cstdint>
#include <dpp/cluster.h>
#include "../../core/translator.h"
#include <string>
#include "translator_core.h"
namespace bot {
namespace translator {
class libretranslate : public translator {
public:
explicit libretranslate(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const std::string apiKey = {});
~libretranslate() override;
@ -34,10 +33,8 @@ namespace bot {
const std::string translate(const std::string &text, const std::string &source, const std::string &target) override;
private:
dpp::cluster m_cluster;
std::string m_apiKey;
std::string m_hostname;
supported_languages m_languages;
uint16_t m_port;
std::string m_url;
bool m_tls;

View file

@ -25,12 +25,10 @@ using namespace std::string_view_literals;
void bot::webhook_push::run(const bot::translated_message &message, dpp::cluster *bot)
{
dpp::json json_body = {
{"username"s, message.author}
{"username"s, message.author},
{"avatar_url"s, message.avatar}
};
if (!message.avatar.empty())
json_body["avatar_url"] = message.avatar;
// We will split too long messages into multiple messages
if (message.message.length() > 2000) {
std::string_view message_v = message.message;
@ -75,18 +73,14 @@ void bot::webhook_push::run(const bot::translated_message &message, dpp::cluster
}
}
void webhook_request_completed(std::promise<dpp::http_request_completion_t> &promise, dpp::json &json, const dpp::http_request_completion_t &event)
{
if (event.status != 204)
std::cerr << "[Warning] Webhook push returned unexpected code " << event.status << std::endl;
promise.set_value(event);
}
void bot::webhook_push::push_request(dpp::snowflake webhook_id, const std::string &webhook_token, const std::string &json, dpp::cluster *bot)
{
std::promise<dpp::http_request_completion_t> promise;
std::future<dpp::http_request_completion_t> future = promise.get_future();
bot->post_rest(API_PATH "/webhooks", std::to_string(webhook_id), dpp::utility::url_encode(webhook_token), dpp::m_post, json,
std::bind(&webhook_request_completed, std::ref(promise), std::placeholders::_1, std::placeholders::_2));
future.wait();
std::promise<dpp::http_request_completion_t> _p;
std::future<dpp::http_request_completion_t> _f = _p.get_future();
bot->post_rest(API_PATH "/webhooks", std::to_string(webhook_id), dpp::utility::url_encode(webhook_token), dpp::m_post, json, [&bot, &_p](dpp::json &json, const dpp::http_request_completion_t &event) {
if (event.status != 204)
std::cerr << "[Warning] Webhook push returned unexpected code " << event.status << std::endl;
_p.set_value(event);
});
_f.wait();
}

View file

@ -26,7 +26,6 @@
namespace bot {
class webhook_push {
public:
webhook_push() = delete;
static void run(const bot::translated_message &message, dpp::cluster *bot);
private: