Compare commits
12
Commits
91bce904ac
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a21c7f54f | ||
|
|
7c2cc5e8d3 | ||
|
|
a8be017843 | ||
|
|
9d95c98424 | ||
|
|
607f26e4c1 | ||
|
|
a6936c2f30 | ||
|
|
7ed3f0193f | ||
|
|
3333013fd1 | ||
|
|
1c01f21fed | ||
|
|
aaa68d5c17 | ||
|
|
11122502cf | ||
|
|
db561e0c90 |
+23
-2
@@ -1,6 +1,27 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(mmcs-quotes-bridge LANGUAGES CXX)
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries by default" NO)
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
|
||||
endif()
|
||||
|
||||
set(CURL_DISABLE_AWS ON)
|
||||
set(CURL_DISABLE_DICT ON)
|
||||
set(CURL_DISABLE_FILE ON)
|
||||
set(CURL_DISABLE_FTP ON)
|
||||
set(CURL_DISABLE_GOPHER ON)
|
||||
set(CURL_DISABLE_IMAP ON)
|
||||
set(CURL_DISABLE_LDAP ON)
|
||||
set(CURL_DISABLE_LDAPS ON)
|
||||
set(CURL_DISABLE_MQTT ON)
|
||||
set(CURL_DISABLE_NTLM ON)
|
||||
set(CURL_DISABLE_POP3 ON)
|
||||
set(CURL_DISABLE_RTSP ON)
|
||||
set(CURL_DISABLE_SMB ON)
|
||||
set(CURL_DISABLE_TFTP ON)
|
||||
set(CURL_DISABLE_WEBSOCKETS ON)
|
||||
|
||||
add_subdirectory(curl)
|
||||
add_subdirectory(json)
|
||||
add_subdirectory(libuv)
|
||||
@@ -9,6 +30,6 @@ add_subdirectory(td)
|
||||
|
||||
add_executable(${PROJECT_NAME} main.cpp config.cpp http.cpp manager.cpp posts.cpp state.cpp tg.cpp vk.cpp)
|
||||
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE -std=c++2b)
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE -std=c++2a)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl nlohmann_json::nlohmann_json uv spdlog::spdlog Td::TdStatic $<$<BOOL:${MINGW}>:ws2_32>)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl nlohmann_json::nlohmann_json uv_a spdlog::spdlog Td::TdStatic $<$<BOOL:${MINGW}>:ws2_32>)
|
||||
@@ -0,0 +1,23 @@
|
||||
# MMCS Quotes Bridge
|
||||
> Текущее название является рабочим вариантом, имеет мало общего с сутью проекта, все совпадения аббревиатур с реальными случайны и не несут никакой смысловой нагрузки.
|
||||
>
|
||||
> *P.S. кто знает тот знает короче :DDD*
|
||||
|
||||
Этот проект - реализация бота, предназначенного для репоста текстовых постов из источников в VK и Telegram в один выходной Telegram-канал. Изначальная цель - репостить цитаты из разных цитатников в один канал, но в принципе вы можете придумать своё применение.
|
||||
|
||||
## Платформа
|
||||
**Операционная система.** Предполагается работа на Linux, но теоретически можно заставить бота работать и на других системах.
|
||||
|
||||
**Компилятор.** Проект написан на C++20 и соответственно требует более-менее современный компилятор.
|
||||
|
||||
## Зависимости
|
||||
Большая часть необходимых библиотек присутствует в репозитории, однако для того, чтобы скачать их вместе с проектом, при клонировании требуется указать --recursive.
|
||||
CMakeLists настроен на максимально портативный билд, однако всё же не все библиотеки встраиваются в исполняемый файл.
|
||||
Для работы бота достаточно установленной библиотеки curl.
|
||||
Для сборки (помимо CMake) нужны следующие дополнительные пакеты:
|
||||
- gperf
|
||||
- заголовочные файлы для OpenSSL, zlib
|
||||
По вопросам сборки можно писать в issues.
|
||||
|
||||
## Конфигурация
|
||||
Для настройки используется файл `bridge_config.json`. В качестве примера настройки оставлен `bridge_config.example.json`, который нужно отредактировать под себя.
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"vk_service_key": "service_key_service_key_service_key_service_key_service_key_service_key",
|
||||
|
||||
"tg_api_id": 1234567,
|
||||
"tg_api_hash": "53a513d108f2c7d9edc67dadd3ce4185",
|
||||
"tg_phone_number": "+79528125252",
|
||||
|
||||
"vk_sources": [{"id": "source1"}, {"id":123451234, "link": "source2"}],
|
||||
"tg_sources": [{"id": -1002282282280, "link": "tgsource"}],
|
||||
"tg_destination_id": -1005252525252
|
||||
}
|
||||
+54
-43
@@ -60,6 +60,12 @@ void RepostManager::load_more_telegram_chats() {
|
||||
|
||||
void RepostManager::start() {
|
||||
m_nRequiredChats = 100500;//m_appConfig->tgSources.size() + 1;
|
||||
for (auto &appState : m_appState->vkRepostState) {
|
||||
appState.lastLoadedPostDate = appState.lastForwardedPostDate;
|
||||
}
|
||||
for (auto &appState : m_appState->tgRepostState) {
|
||||
appState.lastLoadedPostDate = appState.lastForwardedPostDate;
|
||||
}
|
||||
m_tg.add_update_handler([this](void*, td_api::Object &obj){
|
||||
if (obj.get_id() == td_api::updateAuthorizationState::ID) {
|
||||
auto &authState = (td_api::updateAuthorizationState&)obj;
|
||||
@@ -69,7 +75,6 @@ void RepostManager::start() {
|
||||
}
|
||||
} else if (obj.get_id() == td_api::updateNewChat::ID) {
|
||||
auto &update = (td_api::updateNewChat&)obj;
|
||||
spdlog::debug("chat {}", update.chat_->id_);
|
||||
if (update.chat_->id_ == m_appConfig->tgDestinationId) {
|
||||
++m_nLoadedRequiredChats;
|
||||
spdlog::info("destination chat {} loaded (loaded {}/{} chats)", m_appConfig->tgDestinationId, m_nLoadedRequiredChats, m_nRequiredChats);
|
||||
@@ -184,8 +189,10 @@ void NewPostFetcher::continue_fetch() {
|
||||
for (auto &p : posts) {
|
||||
spdlog::debug("[tg:{}] got post dated {}", i, p->date_);
|
||||
}
|
||||
state.offset = posts[posts.size() - 1]->id_;
|
||||
spdlog::info("[tg:{}] setting from to id {}", i, posts[posts.size() - 1]->id_);
|
||||
if (posts.size() > 0) {
|
||||
state.offset = posts[posts.size() - 1]->id_;
|
||||
spdlog::info("[tg:{}] setting from to id {}", i, posts[posts.size() - 1]->id_);
|
||||
}
|
||||
check_tg_posts(i, std::move(posts));
|
||||
});
|
||||
state.count = state.count * 3 / 2;
|
||||
@@ -280,6 +287,7 @@ void NewPostFetcher::continue_fetch() {
|
||||
}
|
||||
mgr->m_unprocessedTgPosts.pop();
|
||||
}
|
||||
working = false;
|
||||
onDone(std::move(merged));
|
||||
}
|
||||
}
|
||||
@@ -291,22 +299,25 @@ void NewPostFetcher::check_vk_posts(int index, std::vector<vk::Post> posts) {
|
||||
long oldLastPostDate = appState.lastLoadedPostDate;
|
||||
if (posts.size() > 0) {
|
||||
spdlog::info("[vk:{}] last post date is now {}", index, posts[0].date);
|
||||
}
|
||||
std::vector<AbstractPost> aposts = mgr->to_abstract_posts(posts, state.sourceIndex);
|
||||
spdlog::info("[vk:{}] looking for date {}, have {} - {}", index, oldLastPostDate, aposts[0].date, aposts[aposts.size() - 1].date);
|
||||
if (mgr->drop_posts_older_than(aposts, oldLastPostDate)) {
|
||||
spdlog::info("[vk:{}] found last remembered post", index);
|
||||
std::vector<AbstractPost> aposts = mgr->to_abstract_posts(posts, state.sourceIndex);
|
||||
spdlog::info("[vk:{}] looking for date {}, have {} - {}", index, oldLastPostDate, aposts[0].date, aposts[aposts.size() - 1].date);
|
||||
if (mgr->drop_posts_older_than(aposts, oldLastPostDate)) {
|
||||
spdlog::info("[vk:{}] found last remembered post", index);
|
||||
state.ready = true;
|
||||
}
|
||||
state.posts.reserve(state.posts.size() + aposts.size());
|
||||
for (auto i = aposts.begin(), e = aposts.end(); i != e; ++i) {
|
||||
state.posts.emplace_back(std::move(*i));
|
||||
}
|
||||
state.needRequest = true;
|
||||
if (state.ready && !state.posts.empty()) {
|
||||
spdlog::debug("[vk:{}] last loaded post date is now {}", index, state.posts[0].date);
|
||||
appState.lastLoadedPostDate = state.posts[0].date;
|
||||
}
|
||||
} else {
|
||||
state.ready = true;
|
||||
}
|
||||
state.posts.reserve(state.posts.size() + aposts.size());
|
||||
for (auto i = aposts.begin(), e = aposts.end(); i != e; ++i) {
|
||||
state.posts.emplace_back(std::move(*i));
|
||||
}
|
||||
state.needRequest = true;
|
||||
if (state.ready && !state.posts.empty()) {
|
||||
spdlog::debug("[vk:{}] last loaded post date is now {}", index, state.posts[0].date);
|
||||
appState.lastLoadedPostDate = state.posts[0].date;
|
||||
}
|
||||
|
||||
continue_fetch();
|
||||
}
|
||||
|
||||
@@ -317,31 +328,28 @@ void NewPostFetcher::check_tg_posts(int index, std::vector<td::tl::unique_ptr<td
|
||||
long oldLastPostDate = appState.lastLoadedPostDate;
|
||||
if (posts.size() > 0) {
|
||||
spdlog::info("[tg:{}] last post date is now {}", index, posts[0]->date_);
|
||||
}
|
||||
std::vector<AbstractPost> aposts = mgr->to_abstract_posts(posts, state.sourceIndex);
|
||||
if (mgr->drop_posts_older_than(aposts, oldLastPostDate)) {
|
||||
spdlog::info("[tg:{}] found last remembered post", index);
|
||||
std::vector<AbstractPost> aposts = mgr->to_abstract_posts(posts, state.sourceIndex);
|
||||
if (mgr->drop_posts_older_than(aposts, oldLastPostDate)) {
|
||||
spdlog::info("[tg:{}] found last remembered post", index);
|
||||
state.ready = true;
|
||||
}
|
||||
state.posts.reserve(state.posts.size() + aposts.size());
|
||||
for (auto i = aposts.begin(), e = aposts.end(); i != e; ++i) {
|
||||
state.posts.emplace_back(std::move(*i));
|
||||
}
|
||||
state.needRequest = true;
|
||||
if (state.ready && !state.posts.empty()) {
|
||||
spdlog::debug("[tg:{}] last loaded post date is now {}", index, state.posts[0].date);
|
||||
appState.lastLoadedPostDate = state.posts[0].id;
|
||||
}
|
||||
} else {
|
||||
state.ready = true;
|
||||
}
|
||||
state.posts.reserve(state.posts.size() + aposts.size());
|
||||
for (auto i = aposts.begin(), e = aposts.end(); i != e; ++i) {
|
||||
state.posts.emplace_back(std::move(*i));
|
||||
}
|
||||
state.needRequest = true;
|
||||
if (state.ready && !state.posts.empty()) {
|
||||
spdlog::debug("[tg:{}] last loaded post date is now {}", index, state.posts[0].date);
|
||||
appState.lastLoadedPostDate = state.posts[0].id;
|
||||
}
|
||||
|
||||
continue_fetch();
|
||||
}
|
||||
|
||||
void RepostManager::on_clients_ready() {
|
||||
for (auto &appState : m_appState->vkRepostState) {
|
||||
appState.lastLoadedPostDate = appState.lastForwardedPostDate;
|
||||
}
|
||||
for (auto &appState : m_appState->tgRepostState) {
|
||||
appState.lastLoadedPostDate = appState.lastForwardedPostDate;
|
||||
}
|
||||
|
||||
spdlog::info("checking all sources");
|
||||
m_fetcher.fetch(
|
||||
@@ -461,11 +469,12 @@ bool RepostManager::drop_posts_older_than(std::vector<AbstractPost> &posts, long
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<AbstractPost> to_abstract_post(const vk::Post &post, int sourceIndex) {
|
||||
return { AbstractPost(posts::SRC_VK, sourceIndex, post.id, post.date, post.text) };
|
||||
std::optional<AbstractPost> RepostManager::to_abstract_post(const vk::Post &post, int sourceIndex) {
|
||||
if (post.text.empty()) return {};
|
||||
else return { AbstractPost(posts::SRC_VK, sourceIndex, post.id, post.date, post.text) };
|
||||
}
|
||||
|
||||
std::optional<AbstractPost> to_abstract_post(const td_api::message &post, int sourceIndex) {
|
||||
std::optional<AbstractPost> RepostManager::to_abstract_post(const td_api::message &post, int sourceIndex) {
|
||||
if (post.content_->get_id() == td_api::messageText::ID) {
|
||||
auto &content = (td_api::messageText&) *post.content_;
|
||||
return { AbstractPost(posts::SRC_TELEGRAM, sourceIndex, post.id_, post.date_, content.text_->text_) };
|
||||
@@ -478,6 +487,7 @@ std::vector<AbstractPost> RepostManager::to_abstract_posts(std::vector<vk::Post>
|
||||
std::vector<AbstractPost> result;
|
||||
result.reserve(posts.size());
|
||||
for (auto &post : posts) {
|
||||
if (post.text.empty()) continue;
|
||||
result.emplace_back(posts::SRC_VK, sourceIndex, post.id, post.date, post.text);
|
||||
}
|
||||
return result;
|
||||
@@ -547,6 +557,10 @@ bool RepostManager::recheck_vk_posts(std::function<void()> onDone) {
|
||||
}
|
||||
|
||||
void RepostManager::repost(AbstractPost &post) {
|
||||
if (post.text.length() == 0) {
|
||||
spdlog::error("repost(): zero post length (post id {} date {})", post.id, post.date);
|
||||
return;
|
||||
}
|
||||
spdlog::debug("reposting (post length {})", post.text.length());
|
||||
std::string_view signature = posts::add_signature(post, m_appConfig);
|
||||
int signatureStart = post.text.length() - signature.length();
|
||||
@@ -601,9 +615,6 @@ void RepostManager::on_tg_message(td_api::updateNewMessage &update) {
|
||||
}
|
||||
std::vector<AbstractPost> posts = { *post };
|
||||
spdlog::debug("rechecking vk posts before processing the new tg post");
|
||||
recheck_vk_posts([this, posts](){
|
||||
spdlog::debug("processing the new tg post");
|
||||
on_new_posts(posts);
|
||||
});
|
||||
recheck_vk_posts({});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user