From a76e81ee8bcbad87c9eb58cdb25452cae65bd0c7 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Fri, 23 Dec 2022 11:09:58 +0000 Subject: Bot is now a Mastodon bot --- difference.cpp | 263 ++++++++++++++++----------------------------------------- 1 file changed, 73 insertions(+), 190 deletions(-) (limited to 'difference.cpp') diff --git a/difference.cpp b/difference.cpp index 9c060d4..86a0779 100644 --- a/difference.cpp +++ b/difference.cpp @@ -1,12 +1,14 @@ #include "difference.h" -#include -#include #include #include #include #include #include #include +#include +#include "imagenet.h" + +#define ENABLE_BOT difference::difference( std::string configFile, @@ -16,20 +18,22 @@ difference::difference( // Load the config file. YAML::Node config = YAML::LoadFile(configFile); - // Set up the Twitter client. - twitter::auth auth; - auth.setConsumerKey(config["consumer_key"].as()); - auth.setConsumerSecret(config["consumer_secret"].as()); - auth.setAccessKey(config["access_key"].as()); - auth.setAccessSecret(config["access_secret"].as()); - - client_ = std::unique_ptr(new twitter::client(auth)); - +#ifdef ENABLE_BOT + // Set up the Mastodon client. + instance_ = std::make_unique( + config["mastodon_instance"].as(), + config["mastodon_token"].as()); + connection_ = std::make_unique(*instance_); +#endif + // Set up the verbly database. database_ = std::unique_ptr( new verbly::database(config["verbly_datafile"].as())); + imagenet_ = std::make_unique(config["imagenet"].as()); + fontfile_ = "@" + config["font"].as(); + tempfile_ = config["tempfile"].as(); } void difference::run() const @@ -73,19 +77,19 @@ void difference::run() const // Generate the tweet text. std::cout << "Generating text..." << std::endl; - std::string text = generateTweetText(std::move(word1), std::move(word2)); + std::string text = generatePostText(std::move(word1), std::move(word2)); - std::cout << "Tweet text: " << text << std::endl; + std::cout << "Post text: " << text << std::endl; - // Send the tweet. - std::cout << "Sending tweet..." << std::endl; + // Send the post. + std::cout << "Sending post..." << std::endl; - sendTweet(std::move(text), std::move(image)); + sendPost(std::move(text), std::move(image)); - std::cout << "Tweeted!" << std::endl; + std::cout << "Posted!" << std::endl; // Wait. - std::this_thread::sleep_for(std::chrono::hours(1)); + std::this_thread::sleep_for(std::chrono::hours(9)); } catch (const could_not_get_images& ex) { std::cout << ex.what() << std::endl; @@ -95,9 +99,9 @@ void difference::run() const } catch (const Magick::ErrorCorruptImage& ex) { std::cout << "Corrupt image: " << ex.what() << std::endl; - } catch (const twitter::twitter_error& ex) + } catch (const std::exception& ex) { - std::cout << "Twitter error: " << ex.what() << std::endl; + std::cout << "Other error: " << ex.what() << std::endl; std::this_thread::sleep_for(std::chrono::hours(1)); } @@ -153,175 +157,22 @@ verbly::word difference::getPicturedNoun() const std::pair difference::getImagesForNoun(verbly::word pictured) const { - int backoff = 0; - std::cout << "Getting URLs..." << std::endl; - std::string lstdata; - while (lstdata.empty()) - { - std::ostringstream lstbuf; - curl::curl_ios lstios(lstbuf); - curl::curl_easy lsthandle(lstios); - std::string lsturl = pictured.getNotion().getImageNetUrl(); - lsthandle.add(lsturl.c_str()); - lsthandle.add(30); - lsthandle.add(300); - - try - { - lsthandle.perform(); - } catch (const curl::curl_easy_exception& e) - { - e.print_traceback(); - - backoff++; - std::cout << "Waiting for " << backoff << " seconds..." << std::endl; - - std::this_thread::sleep_for(std::chrono::seconds(backoff)); - - continue; - } - - backoff = 0; - - if (lsthandle.get_info().get() != 200) - { - throw could_not_get_images(); - } - - std::cout << "Got URLs." << std::endl; - lstdata = lstbuf.str(); - } - - std::vector lstvec = verbly::split>(lstdata, "\r\n"); - if (lstvec.size() < 2) - { - throw could_not_get_images(); - } - - std::shuffle(std::begin(lstvec), std::end(lstvec), rng_); - - std::deque urls; - for (std::string& url : lstvec) - { - urls.push_back(url); - } - - Magick::Image image1; - bool success = false; - while (!urls.empty()) - { - std::string url = urls.front(); - urls.pop_front(); - - try - { - image1 = getImageAtUrl(url); - - success = true; - break; - } catch (const could_not_get_images& ex) - { - // Just try the next one. - } - } - - if (!success) - { - throw could_not_get_images(); - } + std::vector> images = + imagenet_->getImagesForNotion(pictured.getNotion().getId(), rng_, 2); - Magick::Image image2; - success = false; - while (!urls.empty()) - { - std::string url = urls.front(); - urls.pop_front(); + const std::string& imgdata1 = std::get<0>(images[0]); + Magick::Blob image1(imgdata1.c_str(), imgdata1.length()); + Magick::Image pic1; + pic1.read(image1); - try - { - image2 = getImageAtUrl(url); + const std::string& imgdata2 = std::get<0>(images[1]); + Magick::Blob image2(imgdata2.c_str(), imgdata2.length()); + Magick::Image pic2; + pic2.read(image2); - success = true; - break; - } catch (const could_not_get_images& ex) - { - // Just try the next one. - } - } - - if (!success) - { - throw could_not_get_images(); - } - - return {std::move(image1), std::move(image2)}; -} - -Magick::Image difference::getImageAtUrl(std::string url) const -{ - // willyfogg.com is a thumbnail generator known to return 200 even if the target image no longer exists - if (url.find("willyfogg.com/thumb.php") != std::string::npos) - { - throw could_not_get_images(); - } - - // Accept string from Google Chrome - std::string accept = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"; - curl::curl_header headers; - headers.add(accept); - - std::ostringstream imgbuf; - curl::curl_ios imgios(imgbuf); - curl::curl_easy imghandle(imgios); - - imghandle.add(headers.get()); - imghandle.add(url.c_str()); - imghandle.add(30); - imghandle.add(300); - - try - { - imghandle.perform(); - } catch (const curl::curl_easy_exception& error) { - error.print_traceback(); - - throw could_not_get_images(); - } - - if (imghandle.get_info().get() != 200) - { - throw could_not_get_images(); - } - - std::string content_type = imghandle.get_info().get(); - if (content_type.substr(0, 6) != "image/") - { - throw could_not_get_images(); - } - - std::string imgstr = imgbuf.str(); - Magick::Blob img(imgstr.c_str(), imgstr.length()); - Magick::Image pic; - - try - { - pic.read(img); - - if ((pic.rows() > 0) && (pic.columns() >= 800)) - { - std::cout << url << std::endl; - } - } catch (const Magick::ErrorOption& e) - { - // Occurs when the the data downloaded from the server is malformed - std::cout << "Magick: " << e.what() << std::endl; - - throw could_not_get_images(); - } - - return pic; + return {std::move(pic1), std::move(pic2)}; } std::pair difference::getOppositeIdentifiers() const @@ -419,7 +270,7 @@ Magick::Image difference::composeImage( return composite; } -std::string difference::generateTweetText( +std::string difference::generatePostText( verbly::word word1, verbly::word word2) const { @@ -432,12 +283,44 @@ std::string difference::generateTweetText( return result.compile(); } -void difference::sendTweet(std::string text, Magick::Image image) const +void difference::sendPost(std::string text, Magick::Image image) const { - Magick::Blob outputBlob; image.magick("png"); - image.write(&outputBlob); + image.write(tempfile_); - long media_id = client_->uploadMedia("image/png", (const char*) outputBlob.data(), outputBlob.length()); - client_->updateStatus(std::move(text), {media_id}); +#ifdef ENABLE_BOT + 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 response_json = nlohmann::json::parse(answer.body); + answer = connection_->post(mastodonpp::API::v1::statuses, + {{"status", ""}, {"media_ids", + std::vector{response_json["id"].get()}}}); + + 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; + } + } +#endif } -- cgit 1.4.1