summary refs log tree commit diff stats
path: root/sap.cpp
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2025-11-13 12:20:39 -0500
committerStar Rauchenberger <fefferburbia@gmail.com>2025-11-13 12:20:39 -0500
commit55912eb0293ab8c539acbc97d85ee9cc73346091 (patch)
tree5fc90cbf7fda6d2fedc0c55cf33e5372f58db167 /sap.cpp
parentc728c44d896425bbe39f0ce3803e028d119bd63c (diff)
downloadsap-55912eb0293ab8c539acbc97d85ee9cc73346091.tar.gz
sap-55912eb0293ab8c539acbc97d85ee9cc73346091.tar.bz2
sap-55912eb0293ab8c539acbc97d85ee9cc73346091.zip
Bot now posts to Tumblr
Diffstat (limited to 'sap.cpp')
-rw-r--r--sap.cpp106
1 files changed, 64 insertions, 42 deletions
diff --git a/sap.cpp b/sap.cpp index 093e1fd..8805d70 100644 --- a/sap.cpp +++ b/sap.cpp
@@ -5,6 +5,7 @@
5#include <fstream> 5#include <fstream>
6#include <iostream> 6#include <iostream>
7#include <json.hpp> 7#include <json.hpp>
8#include <curl/curl.h>
8 9
9/* - random frames from Spongebob (using ffmpeg) 10/* - random frames from Spongebob (using ffmpeg)
10 * with strange text overlaid, possibly rawr'd from 11 * with strange text overlaid, possibly rawr'd from
@@ -21,12 +22,12 @@ sap::sap(
21 // Load the config file. 22 // Load the config file.
22 YAML::Node config = YAML::LoadFile(configFile); 23 YAML::Node config = YAML::LoadFile(configFile);
23 tempfile_ = config["tempfile"].as<std::string>(); 24 tempfile_ = config["tempfile"].as<std::string>();
25 blog_name_ = config["blog_name"].as<std::string>();
24 26
25 // Set up the Mastodon client. 27 // Set up the Tumblr client.
26 instance_ = std::make_unique<mastodonpp::Instance>( 28 tumblr_consumer_ = std::make_unique<OAuth::Consumer>(config["consumer_key"].as<std::string>(), config["consumer_secret"].as<std::string>());
27 config["mastodon_instance"].as<std::string>(), 29 tumblr_token_ = std::make_unique<OAuth::Token>(config["access_token"].as<std::string>(), config["access_token_secret"].as<std::string>());
28 config["mastodon_token"].as<std::string>()); 30 tumblr_client_ = std::make_unique<OAuth::Client>(tumblr_consumer_.get(), tumblr_token_.get());
29 connection_ = std::make_unique<mastodonpp::Connection>(*instance_);
30 31
31 // Set up the text generator. 32 // Set up the text generator.
32 for (const YAML::Node& corpusname : config["corpuses"]) 33 for (const YAML::Node& corpusname : config["corpuses"])
@@ -72,22 +73,22 @@ void sap::run() const
72 73
73 // Generate the text. 74 // Generate the text.
74 std::uniform_int_distribution<size_t> lenDist(5, 19); 75 std::uniform_int_distribution<size_t> lenDist(5, 19);
75 std::string action = kgramstats_.randomSentence(lenDist(rng_)); 76 std::string action = kgramstats_.randomSentence(lenDist(rng_), rng_);
76 77
77 // Lay the text on the video frame. 78 // Lay the text on the video frame.
78 Magick::Image textimage = 79 Magick::Image textimage =
79 layout_->generate(image.columns(), image.rows(), action, rng_); 80 layout_->generate(image.columns(), image.rows(), action, rng_);
80 image.composite(textimage, 0, 0, Magick::OverCompositeOp); 81 image.composite(textimage, 0, 0, Magick::OverCompositeOp);
81 82
82 // Send the tweet. 83 // Post to tumblr.
83 std::cout << "Sending tweet..." << std::endl; 84 std::cout << "Posting to Tumblr..." << std::endl;
84 85
85 sendTweet(std::move(image), action); 86 postToTumblr(action, std::move(image));
86 87
87 std::cout << "Tweeted!" << std::endl; 88 std::cout << std::endl << "Posted!" << std::endl;
88 89
89 // Wait. 90 // Wait.
90 std::this_thread::sleep_for(std::chrono::hours(12)); 91 std::this_thread::sleep_for(std::chrono::hours(11));
91 } catch (const Magick::ErrorImage& ex) 92 } catch (const Magick::ErrorImage& ex)
92 { 93 {
93 std::cout << "Image error: " << ex.what() << std::endl; 94 std::cout << "Image error: " << ex.what() << std::endl;
@@ -97,42 +98,63 @@ void sap::run() const
97 } 98 }
98} 99}
99 100
100void sap::sendTweet(Magick::Image image, const std::string& text) const 101void sap::postToTumblr(const std::string& post_text, Magick::Image image) const
101{ 102{
102 image.magick("jpeg"); 103 image.magick("jpeg");
103 image.write(tempfile_); 104 image.write(tempfile_);
104 105
105 auto answer{connection_->post(mastodonpp::API::v1::media, 106 nlohmann::json jsonMedia;
106 {{"file", std::string("@file:") + tempfile_}, {"description", text}})}; 107 jsonMedia["type"] = "image/jpeg";
107 if (!answer) 108 jsonMedia["identifier"] = "some-identifier";
108 { 109 jsonMedia["width"] = image.columns();
109 if (answer.curl_error_code == 0) 110 jsonMedia["height"] = image.rows();
110 { 111 jsonMedia["alt_text"] = post_text;
111 std::cout << "HTTP status: " << answer.http_status << std::endl; 112 jsonMedia["caption"] = post_text;
112 }
113 else
114 {
115 std::cout << "libcurl error " << std::to_string(answer.curl_error_code)
116 << ": " << answer.error_message << std::endl;
117 }
118 return;
119 }
120 113
121 nlohmann::json response_json = nlohmann::json::parse(answer.body); 114 nlohmann::json contentBlock;
122 answer = connection_->post(mastodonpp::API::v1::statuses, 115 contentBlock["type"] = "image";
123 {{"status", ""}, {"media_ids", 116 contentBlock["media"] = jsonMedia;
124 std::vector<std::string_view>{response_json["id"].get<std::string>()}}});
125 117
126 if (!answer) 118 nlohmann::json postBody;
127 { 119 postBody["content"].push_back(contentBlock);
128 if (answer.curl_error_code == 0) 120 postBody["tags"] = "spongebob,generated";
129 { 121
130 std::cout << "HTTP status: " << answer.http_status << std::endl; 122 std::string postText = postBody.dump();
131 } 123 std::cout << postText << std::endl;
132 else 124
133 { 125 Magick::Blob blob;
134 std::cout << "libcurl error " << std::to_string(answer.curl_error_code) 126 image.write(&blob);
135 << ": " << answer.error_message << std::endl; 127
136 } 128 std::string url = "https://api.tumblr.com/v2/blog/" + blog_name_ + "/posts";
129
130 std::string oauthHeader = tumblr_client_->getFormattedHttpHeader(OAuth::Http::Post, url, "");
131
132 struct curl_slist* header_list = NULL;
133 header_list = curl_slist_append(header_list, oauthHeader.c_str());
134
135 CURL* easy = curl_easy_init();
136 curl_mime* mime = curl_mime_init(easy);
137 curl_mimepart* json_part = curl_mime_addpart(mime);
138 curl_mime_data(json_part, postText.c_str(), CURL_ZERO_TERMINATED);
139 curl_mime_name(json_part, "json");
140 curl_mime_type(json_part, "application/json");
141
142 curl_mimepart* image_part = curl_mime_addpart(mime);
143 curl_mime_data(image_part, reinterpret_cast<const char*>(blob.data()), blob.length());
144 curl_mime_name(image_part, "some-identifier");
145 curl_mime_type(image_part, "image/jpeg");
146 curl_mime_filename(image_part, "image.jpg");
147
148 curl_easy_setopt(easy, CURLOPT_MIMEPOST, mime);
149 curl_easy_setopt(easy, CURLOPT_HTTPHEADER, header_list);
150 curl_easy_setopt(easy, CURLOPT_URL, url.c_str());
151
152 CURLcode error = curl_easy_perform(easy);
153 if (error != CURLE_OK) {
154 std::cout << curl_easy_strerror(error) << std::endl;
137 } 155 }
156
157 curl_easy_cleanup(easy);
158 curl_slist_free_all(header_list);
159 curl_mime_free(mime);
138} 160}