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 --- sap.cpp | 106 +++++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 64 insertions(+), 42 deletions(-) (limited to 'sap.cpp') 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); } -- cgit 1.4.1