From 55912eb0293ab8c539acbc97d85ee9cc73346091 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Thu, 13 Nov 2025 12:20:39 -0500 Subject: Bot now posts to Tumblr --- .gitmodules | 6 +-- CMakeLists.txt | 25 +++++++++---- director.cpp | 2 +- main.cpp | 1 - sap.cpp | 106 ++++++++++++++++++++++++++++++++--------------------- sap.h | 10 +++-- vendor/liboauthcpp | 1 + vendor/mastodonpp | 1 - vendor/rawr-ebooks | 2 +- 9 files changed, 94 insertions(+), 60 deletions(-) create mode 160000 vendor/liboauthcpp delete mode 160000 vendor/mastodonpp diff --git a/.gitmodules b/.gitmodules index 8187520..a534e8e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "vendor/rawr-ebooks"] path = vendor/rawr-ebooks url = https://git.fourisland.com/rawr-ebooks -[submodule "vendor/mastodonpp"] - path = vendor/mastodonpp - url = https://schlomp.space/tastytea/mastodonpp.git +[submodule "vendor/liboauthcpp"] + path = vendor/liboauthcpp + url = https://github.com/sirikata/liboauthcpp.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 13fb6d7..aa7e4b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,28 +1,36 @@ -cmake_minimum_required (VERSION 3.1) +cmake_minimum_required (VERSION 3.27) project (sap) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") -find_package(FFMPEG REQUIRED) +IF (NOT FFMPEG_FOUND) + find_package(FFMPEG) +ENDIF() find_package(PkgConfig) pkg_check_modules(yaml-cpp yaml-cpp REQUIRED) pkg_check_modules(GraphicsMagick GraphicsMagick++ REQUIRED) +set(RAWR_ONLY_LIBRARY TRUE) add_subdirectory(vendor/rawr-ebooks EXCLUDE_FROM_ALL) -add_subdirectory(vendor/mastodonpp) + +set(LIBOAUTHCPP_BUILD_DEMOS FALSE) +add_subdirectory(vendor/liboauthcpp/build EXCLUDE_FROM_ALL) +include_directories(vendor/liboauthcpp/include) include_directories( ${FFMPEG_INCLUDE_DIRS} vendor/rawr-ebooks - vendor/mastodonpp/include + vendor/liboauthcpp/include + ${CURL_INCLUDE_DIRS} vendor/json ${yaml-cpp_INCLUDE_DIRS} ${GraphicsMagick_INCLUDE_DIRS}) link_directories( ${yaml-cpp_LIBRARY_DIRS} - ${GraphicsMagick_LIBRARY_DIRS}) + ${GraphicsMagick_LIBRARY_DIRS} + ${FFMPEG_LIBRARY_DIRS}) add_executable(sap sap.cpp director.cpp designer.cpp main.cpp) set_property(TARGET sap PROPERTY CXX_STANDARD 17) @@ -30,7 +38,10 @@ set_property(TARGET sap PROPERTY CXX_STANDARD_REQUIRED ON) target_link_libraries(sap rawr - mastodonpp - ${FFMPEG_LIBRARIES} + curl + oauthcpp + avcodec + swscale + avformat ${GraphicsMagick_LIBRARIES} ${yaml-cpp_LIBRARIES}) diff --git a/director.cpp b/director.cpp index 24335da..a38377e 100644 --- a/director.cpp +++ b/director.cpp @@ -51,7 +51,7 @@ namespace ffmpeg { format& fmt, AVStream* st) { - AVCodec* dec = avcodec_find_decoder(st->codecpar->codec_id); + const AVCodec* dec = avcodec_find_decoder(st->codecpar->codec_id); if (!dec) { throw ffmpeg_error("Failed to find codec"); diff --git a/main.cpp b/main.cpp index 6ac8ee9..125bab3 100644 --- a/main.cpp +++ b/main.cpp @@ -10,7 +10,6 @@ extern "C" { int main(int argc, char** argv) { Magick::InitializeMagick(nullptr); - av_register_all(); std::random_device randomDevice; std::mt19937 rng(randomDevice()); diff --git a/sap.cpp b/sap.cpp index 093e1fd..8805d70 100644 --- a/sap.cpp +++ b/sap.cpp @@ -5,6 +5,7 @@ #include #include #include +#include /* - random frames from Spongebob (using ffmpeg) * with strange text overlaid, possibly rawr'd from @@ -21,12 +22,12 @@ sap::sap( // Load the config file. YAML::Node config = YAML::LoadFile(configFile); tempfile_ = config["tempfile"].as(); + blog_name_ = config["blog_name"].as(); - // Set up the Mastodon client. - instance_ = std::make_unique( - config["mastodon_instance"].as(), - config["mastodon_token"].as()); - connection_ = std::make_unique(*instance_); + // Set up the Tumblr client. + tumblr_consumer_ = std::make_unique(config["consumer_key"].as(), config["consumer_secret"].as()); + tumblr_token_ = std::make_unique(config["access_token"].as(), config["access_token_secret"].as()); + tumblr_client_ = std::make_unique(tumblr_consumer_.get(), tumblr_token_.get()); // Set up the text generator. for (const YAML::Node& corpusname : config["corpuses"]) @@ -72,22 +73,22 @@ void sap::run() const // Generate the text. std::uniform_int_distribution lenDist(5, 19); - std::string action = kgramstats_.randomSentence(lenDist(rng_)); + std::string action = kgramstats_.randomSentence(lenDist(rng_), rng_); // Lay the text on the video frame. Magick::Image textimage = layout_->generate(image.columns(), image.rows(), action, rng_); image.composite(textimage, 0, 0, Magick::OverCompositeOp); - // Send the tweet. - std::cout << "Sending tweet..." << std::endl; + // Post to tumblr. + std::cout << "Posting to Tumblr..." << std::endl; - sendTweet(std::move(image), action); + postToTumblr(action, std::move(image)); - std::cout << "Tweeted!" << std::endl; + std::cout << std::endl << "Posted!" << std::endl; // Wait. - std::this_thread::sleep_for(std::chrono::hours(12)); + std::this_thread::sleep_for(std::chrono::hours(11)); } catch (const Magick::ErrorImage& ex) { std::cout << "Image error: " << ex.what() << std::endl; @@ -97,42 +98,63 @@ void sap::run() const } } -void sap::sendTweet(Magick::Image image, const std::string& text) const +void sap::postToTumblr(const std::string& post_text, Magick::Image image) const { image.magick("jpeg"); image.write(tempfile_); - auto answer{connection_->post(mastodonpp::API::v1::media, - {{"file", std::string("@file:") + tempfile_}, {"description", text}})}; - if (!answer) - { - if (answer.curl_error_code == 0) - { - std::cout << "HTTP status: " << answer.http_status << std::endl; - } - else - { - std::cout << "libcurl error " << std::to_string(answer.curl_error_code) - << ": " << answer.error_message << std::endl; - } - return; - } + nlohmann::json jsonMedia; + jsonMedia["type"] = "image/jpeg"; + jsonMedia["identifier"] = "some-identifier"; + jsonMedia["width"] = image.columns(); + jsonMedia["height"] = image.rows(); + jsonMedia["alt_text"] = post_text; + jsonMedia["caption"] = post_text; - nlohmann::json response_json = nlohmann::json::parse(answer.body); - answer = connection_->post(mastodonpp::API::v1::statuses, - {{"status", ""}, {"media_ids", - std::vector{response_json["id"].get()}}}); + nlohmann::json contentBlock; + contentBlock["type"] = "image"; + contentBlock["media"] = jsonMedia; - if (!answer) - { - if (answer.curl_error_code == 0) - { - std::cout << "HTTP status: " << answer.http_status << std::endl; - } - else - { - std::cout << "libcurl error " << std::to_string(answer.curl_error_code) - << ": " << answer.error_message << std::endl; - } + nlohmann::json postBody; + postBody["content"].push_back(contentBlock); + postBody["tags"] = "spongebob,generated"; + + std::string postText = postBody.dump(); + std::cout << postText << std::endl; + + Magick::Blob blob; + image.write(&blob); + + std::string url = "https://api.tumblr.com/v2/blog/" + blog_name_ + "/posts"; + + std::string oauthHeader = tumblr_client_->getFormattedHttpHeader(OAuth::Http::Post, url, ""); + + struct curl_slist* header_list = NULL; + header_list = curl_slist_append(header_list, oauthHeader.c_str()); + + CURL* easy = curl_easy_init(); + curl_mime* mime = curl_mime_init(easy); + curl_mimepart* json_part = curl_mime_addpart(mime); + curl_mime_data(json_part, postText.c_str(), CURL_ZERO_TERMINATED); + curl_mime_name(json_part, "json"); + curl_mime_type(json_part, "application/json"); + + curl_mimepart* image_part = curl_mime_addpart(mime); + curl_mime_data(image_part, reinterpret_cast(blob.data()), blob.length()); + curl_mime_name(image_part, "some-identifier"); + curl_mime_type(image_part, "image/jpeg"); + curl_mime_filename(image_part, "image.jpg"); + + curl_easy_setopt(easy, CURLOPT_MIMEPOST, mime); + curl_easy_setopt(easy, CURLOPT_HTTPHEADER, header_list); + curl_easy_setopt(easy, CURLOPT_URL, url.c_str()); + + CURLcode error = curl_easy_perform(easy); + if (error != CURLE_OK) { + std::cout << curl_easy_strerror(error) << std::endl; } + + curl_easy_cleanup(easy); + curl_slist_free_all(header_list); + curl_mime_free(mime); } diff --git a/sap.h b/sap.h index 44ece23..c7f5909 100644 --- a/sap.h +++ b/sap.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include "designer.h" #include "director.h" @@ -21,15 +21,17 @@ public: private: - void sendTweet(Magick::Image image, const std::string& text) const; + void postToTumblr(const std::string& text, Magick::Image image) const; std::mt19937& rng_; - std::unique_ptr instance_; - std::unique_ptr connection_; + std::unique_ptr tumblr_consumer_; + std::unique_ptr tumblr_token_; + std::unique_ptr tumblr_client_; rawr kgramstats_; std::unique_ptr layout_; std::unique_ptr director_; std::string tempfile_; + std::string blog_name_; }; diff --git a/vendor/liboauthcpp b/vendor/liboauthcpp new file mode 160000 index 0000000..2893f1b --- /dev/null +++ b/vendor/liboauthcpp @@ -0,0 +1 @@ +Subproject commit 2893f1bf42e1dfd5acf0dd849519cf27ac9c7395 diff --git a/vendor/mastodonpp b/vendor/mastodonpp deleted file mode 160000 index c48f1dc..0000000 --- a/vendor/mastodonpp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c48f1dc3d0566cef2baf96df7b3a7c55490a3e91 diff --git a/vendor/rawr-ebooks b/vendor/rawr-ebooks index 247ee4d..f4fe2a7 160000 --- a/vendor/rawr-ebooks +++ b/vendor/rawr-ebooks @@ -1 +1 @@ -Subproject commit 247ee4de24eab5ecd030542724db9f69aaa1ed1a +Subproject commit f4fe2a79e3ac9590c83886d208561b9f6ee4ebba -- cgit 1.4.1