diff options
| -rw-r--r-- | difference.cpp | 313 | ||||
| m--------- | vendor/libtwittercpp | 0 | 
2 files changed, 152 insertions, 161 deletions
| diff --git a/difference.cpp b/difference.cpp index 3d3bb18..c2e3b37 100644 --- a/difference.cpp +++ b/difference.cpp | |||
| @@ -1,16 +1,17 @@ | |||
| 1 | #include <verbly.h> | 1 | #include <verbly.h> | 
| 2 | #include <twitter.h> | 2 | #include <twitter.h> | 
| 3 | #include <curl_easy.h> | 3 | #include <curl_easy.h> | 
| 4 | #include <curl_header.h> | ||
| 4 | #include <list> | 5 | #include <list> | 
| 5 | #include <set> | 6 | #include <set> | 
| 6 | #include <vector> | 7 | #include <vector> | 
| 7 | #include <algorithm> | 8 | #include <algorithm> | 
| 8 | #include <cstdlib> | ||
| 9 | #include <ctime> | ||
| 10 | #include <Magick++.h> | 9 | #include <Magick++.h> | 
| 11 | #include <iostream> | 10 | #include <iostream> | 
| 12 | #include <yaml-cpp/yaml.h> | 11 | #include <yaml-cpp/yaml.h> | 
| 13 | #include <unistd.h> | 12 | #include <chrono> | 
| 13 | #include <thread> | ||
| 14 | #include <random> | ||
| 14 | 15 | ||
| 15 | std::string capitalize(std::string input) | 16 | std::string capitalize(std::string input) | 
| 16 | { | 17 | { | 
| @@ -65,7 +66,8 @@ bool downloadImage(std::string url, curl::curl_header headers, Magick::Blob& img | |||
| 65 | return false; | 66 | return false; | 
| 66 | } | 67 | } | 
| 67 | 68 | ||
| 68 | if (std::string(imghandle.get_info<CURLINFO_CONTENT_TYPE>().get()).substr(0, 6) != "image/") | 69 | std::string content_type = imghandle.get_info<CURLINFO_CONTENT_TYPE>().get(); | 
| 70 | if (content_type.substr(0, 6) != "image/") | ||
| 69 | { | 71 | { | 
| 70 | return false; | 72 | return false; | 
| 71 | } | 73 | } | 
| @@ -85,7 +87,6 @@ bool downloadImage(std::string url, curl::curl_header headers, Magick::Blob& img | |||
| 85 | 87 | ||
| 86 | int main(int argc, char** argv) | 88 | int main(int argc, char** argv) | 
| 87 | { | 89 | { | 
| 88 | srand(time(NULL)); | ||
| 89 | Magick::InitializeMagick(nullptr); | 90 | Magick::InitializeMagick(nullptr); | 
| 90 | 91 | ||
| 91 | int delay = 60 * 60; | 92 | int delay = 60 * 60; | 
| @@ -99,6 +100,9 @@ int main(int argc, char** argv) | |||
| 99 | 100 | ||
| 100 | twitter::client client(auth); | 101 | twitter::client client(auth); | 
| 101 | 102 | ||
| 103 | std::random_device random_device; | ||
| 104 | std::mt19937 random_engine{random_device()}; | ||
| 105 | |||
| 102 | verbly::data database("data.sqlite3"); | 106 | verbly::data database("data.sqlite3"); | 
| 103 | 107 | ||
| 104 | auto whitelist = database.nouns(); | 108 | auto whitelist = database.nouns(); | 
| @@ -149,194 +153,181 @@ int main(int argc, char** argv) | |||
| 149 | std::cout << "Started!" << std::endl; | 153 | std::cout << "Started!" << std::endl; | 
| 150 | for (;;) | 154 | for (;;) | 
| 151 | { | 155 | { | 
| 152 | std::cout << "Generating noun..." << std::endl; | 156 | try | 
| 153 | verbly::noun pictured = database.nouns().full_hyponym_of(whitefilter).at_least_n_images(2).random().limit(1).run().front(); | 157 | { | 
| 154 | std::cout << "Noun: " << pictured.singular_form() << std::endl; | 158 | std::cout << "Generating noun..." << std::endl; | 
| 155 | std::cout << "Getting URLs..." << std::endl; | 159 | verbly::noun pictured = database.nouns().full_hyponym_of(whitefilter).at_least_n_images(2).random().limit(1).run().front(); | 
| 156 | std::ostringstream lstbuf; | 160 | std::cout << "Noun: " << pictured.singular_form() << std::endl; | 
| 157 | curl::curl_ios<std::ostringstream> lstios(lstbuf); | 161 | std::cout << "Getting URLs..." << std::endl; | 
| 158 | curl::curl_easy lsthandle(lstios); | 162 | std::ostringstream lstbuf; | 
| 159 | std::string lsturl = pictured.imagenet_url(); | 163 | curl::curl_ios<std::ostringstream> lstios(lstbuf); | 
| 160 | lsthandle.add<CURLOPT_URL>(lsturl.c_str()); | 164 | curl::curl_easy lsthandle(lstios); | 
| 161 | try { | 165 | std::string lsturl = pictured.imagenet_url(); | 
| 166 | lsthandle.add<CURLOPT_URL>(lsturl.c_str()); | ||
| 162 | lsthandle.perform(); | 167 | lsthandle.perform(); | 
| 163 | } catch (curl::curl_easy_exception error) { | ||
| 164 | error.print_traceback(); | ||
| 165 | sleep(delay); | ||
| 166 | |||
| 167 | continue; | ||
| 168 | } | ||
| 169 | 168 | ||
| 170 | if (lsthandle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200) | 169 | if (lsthandle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200) | 
| 171 | { | 170 | { | 
| 172 | std::cout << "Could not get URLs" << std::endl; | 171 | std::cout << "Could not get URLs" << std::endl; | 
| 173 | continue; | 172 | continue; | 
| 174 | } | 173 | } | 
| 175 | 174 | ||
| 176 | std::cout << "Got URLs." << std::endl; | 175 | std::cout << "Got URLs." << std::endl; | 
| 177 | std::string lstdata = lstbuf.str(); | 176 | std::string lstdata = lstbuf.str(); | 
| 178 | auto lstlist = verbly::split<std::list<std::string>>(lstdata, "\r\n"); | 177 | auto lstlist = verbly::split<std::list<std::string>>(lstdata, "\r\n"); | 
| 179 | std::set<std::string> lstset; | 178 | std::set<std::string> lstset; | 
| 180 | for (auto url : lstlist) | 179 | for (auto url : lstlist) | 
| 181 | { | 180 | { | 
| 182 | lstset.insert(url); | 181 | lstset.insert(url); | 
| 183 | } | 182 | } | 
| 184 | 183 | ||
| 185 | if (lstset.size() < 2) | 184 | if (lstset.size() < 2) | 
| 186 | { | 185 | { | 
| 187 | continue; | 186 | continue; | 
| 188 | } | 187 | } | 
| 189 | 188 | ||
| 190 | std::vector<std::string> lstvec; | 189 | std::vector<std::string> lstvec; | 
| 191 | for (auto url : lstset) | 190 | for (auto url : lstset) | 
| 192 | { | 191 | { | 
| 193 | lstvec.push_back(url); | 192 | lstvec.push_back(url); | 
| 194 | } | 193 | } | 
| 195 | 194 | ||
| 196 | std::random_shuffle(std::begin(lstvec), std::end(lstvec)); | 195 | std::shuffle(std::begin(lstvec), std::end(lstvec), random_engine); | 
| 197 | 196 | ||
| 198 | Magick::Blob img1; | 197 | Magick::Blob img1; | 
| 199 | Magick::Image pic1; | 198 | Magick::Image pic1; | 
| 200 | bool success = false; | 199 | bool success = false; | 
| 201 | int curind = 0; | 200 | int curind = 0; | 
| 202 | for (; curind < lstvec.size(); curind++) | 201 | for (; curind < lstvec.size(); curind++) | 
| 203 | { | ||
| 204 | if (downloadImage(lstvec[curind], headers, img1, pic1)) | ||
| 205 | { | 202 | { | 
| 206 | success = true; | 203 | if (downloadImage(lstvec[curind], headers, img1, pic1)) | 
| 207 | break; | 204 | { | 
| 205 | success = true; | ||
| 206 | break; | ||
| 207 | } | ||
| 208 | } | 208 | } | 
| 209 | } | ||
| 210 | 209 | ||
| 211 | if (!success) | 210 | if (!success) | 
| 212 | { | 211 | { | 
| 213 | continue; | 212 | continue; | 
| 214 | } | 213 | } | 
| 215 | 214 | ||
| 216 | success = false; | 215 | success = false; | 
| 217 | Magick::Blob img2; | 216 | Magick::Blob img2; | 
| 218 | Magick::Image pic2; | 217 | Magick::Image pic2; | 
| 219 | for (curind++; curind < lstvec.size(); curind++) | 218 | for (curind++; curind < lstvec.size(); curind++) | 
| 220 | { | ||
| 221 | if (downloadImage(lstvec[curind], headers, img2, pic2)) | ||
| 222 | { | 219 | { | 
| 223 | success = true; | 220 | if (downloadImage(lstvec[curind], headers, img2, pic2)) | 
| 224 | break; | 221 | { | 
| 222 | success = true; | ||
| 223 | break; | ||
| 224 | } | ||
| 225 | } | 225 | } | 
| 226 | } | ||
| 227 | 226 | ||
| 228 | if (!success) | 227 | if (!success) | 
| 229 | { | 228 | { | 
| 230 | continue; | 229 | continue; | 
| 231 | } | 230 | } | 
| 232 | 231 | ||
| 233 | std::string ant1, ant2; | 232 | std::string ant1, ant2; | 
| 234 | if (rand()%2==0) | 233 | std::uniform_int_distribution<int> coinflip(0, 1); | 
| 235 | { | 234 | if (coinflip(random_engine)==0) | 
| 236 | verbly::noun n1 = database.nouns().has_antonyms().random().limit(1).run().front(); | 235 | { | 
| 237 | verbly::noun n2 = n1.antonyms().random().limit(1).run().front(); | 236 | verbly::noun n1 = database.nouns().has_antonyms().random().limit(1).run().front(); | 
| 238 | ant1 = n1.singular_form(); | 237 | verbly::noun n2 = n1.antonyms().random().limit(1).run().front(); | 
| 239 | ant2 = n2.singular_form(); | 238 | ant1 = n1.singular_form(); | 
| 240 | } else { | 239 | ant2 = n2.singular_form(); | 
| 241 | verbly::adjective a1 = database.adjectives().has_antonyms().random().limit(1).run().front(); | 240 | } else { | 
| 242 | verbly::adjective a2 = a1.antonyms().random().limit(1).run().front(); | 241 | verbly::adjective a1 = database.adjectives().has_antonyms().random().limit(1).run().front(); | 
| 243 | ant1 = a1.base_form(); | 242 | verbly::adjective a2 = a1.antonyms().random().limit(1).run().front(); | 
| 244 | ant2 = a2.base_form(); | 243 | ant1 = a1.base_form(); | 
| 245 | } | 244 | ant2 = a2.base_form(); | 
| 245 | } | ||
| 246 | 246 | ||
| 247 | if (pic1.columns() < 320) | 247 | if (pic1.columns() < 320) | 
| 248 | { | 248 | { | 
| 249 | pic1.zoom(Magick::Geometry(320, pic1.rows() * 320 / pic1.columns(), 0, 0)); | 249 | pic1.zoom(Magick::Geometry(320, pic1.rows() * 320 / pic1.columns(), 0, 0)); | 
| 250 | } | 250 | } | 
| 251 | 251 | ||
| 252 | if (pic2.columns() < 320) | 252 | if (pic2.columns() < 320) | 
| 253 | { | 253 | { | 
| 254 | pic2.zoom(Magick::Geometry(320, pic2.rows() * 320 / pic2.columns(), 0, 0)); | 254 | pic2.zoom(Magick::Geometry(320, pic2.rows() * 320 / pic2.columns(), 0, 0)); | 
| 255 | } | 255 | } | 
| 256 | 256 | ||
| 257 | int width = std::min(pic1.columns(), pic2.columns()); | 257 | int width = std::min(pic1.columns(), pic2.columns()); | 
| 258 | int height = std::min(pic1.rows(), pic2.rows()); | 258 | int height = std::min(pic1.rows(), pic2.rows()); | 
| 259 | Magick::Geometry geo1(width, height, pic1.columns()/2 - width/2, pic1.rows()/2 - height/2); | 259 | Magick::Geometry geo1(width, height, pic1.columns()/2 - width/2, pic1.rows()/2 - height/2); | 
| 260 | Magick::Geometry geo2(width, height, pic2.columns()/2 - width/2, pic2.rows()/2 - height/2); | 260 | Magick::Geometry geo2(width, height, pic2.columns()/2 - width/2, pic2.rows()/2 - height/2); | 
| 261 | pic1.crop(geo1); | 261 | pic1.crop(geo1); | 
| 262 | pic2.crop(geo2); | 262 | pic2.crop(geo2); | 
| 263 | 263 | ||
| 264 | Magick::Image composite(Magick::Geometry(width*2, height, 0, 0), "white"); | 264 | Magick::Image composite(Magick::Geometry(width*2, height, 0, 0), "white"); | 
| 265 | composite.draw(Magick::DrawableCompositeImage(0, 0, pic1)); | 265 | composite.draw(Magick::DrawableCompositeImage(0, 0, pic1)); | 
| 266 | composite.draw(Magick::DrawableCompositeImage(width, 0, pic2)); | 266 | composite.draw(Magick::DrawableCompositeImage(width, 0, pic2)); | 
| 267 | composite.font("@coolvetica.ttf"); | 267 | composite.font("@coolvetica.ttf"); | 
| 268 | 268 | ||
| 269 | double fontsize = 72; | 269 | double fontsize = 72; | 
| 270 | for (;;) | 270 | for (;;) | 
| 271 | { | ||
| 272 | composite.fontPointsize(fontsize); | ||
| 273 | |||
| 274 | Magick::TypeMetric metric; | ||
| 275 | composite.fontTypeMetrics(ant1, &metric); | ||
| 276 | if (metric.textWidth() > 300) | ||
| 277 | { | 271 | { | 
| 278 | fontsize -= 0.5; | 272 | composite.fontPointsize(fontsize); | 
| 279 | continue; | ||
| 280 | } | ||
| 281 | 273 | ||
| 282 | composite.fontTypeMetrics(ant2, &metric); | 274 | Magick::TypeMetric metric; | 
| 283 | if (metric.textWidth() > 300) | 275 | composite.fontTypeMetrics(ant1, &metric); | 
| 284 | { | 276 | if (metric.textWidth() > 300) | 
| 285 | fontsize -= 0.5; | 277 | { | 
| 286 | continue; | 278 | fontsize -= 0.5; | 
| 287 | } | 279 | continue; | 
| 280 | } | ||
| 288 | 281 | ||
| 289 | break; | 282 | composite.fontTypeMetrics(ant2, &metric); | 
| 290 | } | 283 | if (metric.textWidth() > 300) | 
| 291 | 284 | { | |
| 292 | Magick::TypeMetric metric; | 285 | fontsize -= 0.5; | 
| 293 | composite.fontTypeMetrics(ant1, &metric); | 286 | continue; | 
| 294 | int y = rand() % ((int)(composite.rows() - 40 - metric.textHeight())) + 20; | 287 | } | 
| 295 | y = composite.rows() - y; | 288 | |
| 296 | int x1 = (width - 40 - metric.textWidth())/2+20; | 289 | break; | 
| 297 | composite.fontTypeMetrics(ant2, &metric); | 290 | } | 
| 298 | int x2 = (width - 40 - metric.textWidth())/2+20+width; | ||
| 299 | composite.strokeColor("white"); | ||
| 300 | composite.strokeWidth(2); | ||
| 301 | composite.antiAlias(false); | ||
| 302 | composite.draw(Magick::DrawableText(x1, y, ant1)); | ||
| 303 | composite.draw(Magick::DrawableText(x2, y, ant2)); | ||
| 304 | 291 | ||
| 305 | composite.magick("png"); | 292 | Magick::TypeMetric metric; | 
| 293 | composite.fontTypeMetrics(ant1, &metric); | ||
| 294 | std::uniform_int_distribution<int> rowdist(20, (int)(composite.rows() - 19 - metric.textHeight())); | ||
| 295 | int y = rowdist(random_engine); | ||
| 296 | y = composite.rows() - y; | ||
| 297 | int x1 = (width - 40 - metric.textWidth())/2+20; | ||
| 298 | composite.fontTypeMetrics(ant2, &metric); | ||
| 299 | int x2 = (width - 40 - metric.textWidth())/2+20+width; | ||
| 300 | composite.strokeColor("white"); | ||
| 301 | composite.strokeWidth(2); | ||
| 302 | composite.antiAlias(false); | ||
| 303 | composite.draw(Magick::DrawableText(x1, y, ant1)); | ||
| 304 | composite.draw(Magick::DrawableText(x2, y, ant2)); | ||
| 306 | 305 | ||
| 307 | Magick::Blob outputimg; | 306 | composite.magick("png"); | 
| 308 | composite.write(&outputimg); | ||
| 309 | 307 | ||
| 310 | std::cout << "Generated image!" << std::endl << "Tweeting..." << std::endl; | 308 | Magick::Blob outputimg; | 
| 309 | composite.write(&outputimg); | ||
| 311 | 310 | ||
| 312 | std::stringstream msg; | 311 | std::cout << "Generated image!" << std::endl << "Tweeting..." << std::endl; | 
| 313 | msg << capitalize(ant1); | ||
| 314 | msg << " vs. "; | ||
| 315 | msg << capitalize(ant2); | ||
| 316 | 312 | ||
| 317 | long media_id; | 313 | std::stringstream msg; | 
| 318 | twitter::response resp = client.uploadMedia("image/png", (const char*) outputimg.data(), outputimg.length(), media_id); | 314 | msg << capitalize(ant1); | 
| 319 | if (resp != twitter::response::ok) | 315 | msg << " vs. "; | 
| 316 | msg << capitalize(ant2); | ||
| 317 | |||
| 318 | long media_id = client.uploadMedia("image/png", (const char*) outputimg.data(), outputimg.length()); | ||
| 319 | client.updateStatus(msg.str(), {media_id}); | ||
| 320 | |||
| 321 | std::cout << "Done!" << std::endl << "Waiting..." << std::endl << std::endl; | ||
| 322 | } catch (const curl::curl_easy_exception& error) | ||
| 320 | { | 323 | { | 
| 321 | std::cout << "Twitter error while uploading image: " << resp << std::endl; | 324 | error.print_traceback(); | 
| 322 | sleep(delay); | 325 | } catch (const twitter::twitter_error& e) | 
| 323 | |||
| 324 | continue; | ||
| 325 | } | ||
| 326 | |||
| 327 | twitter::tweet tw; | ||
| 328 | resp = client.updateStatus(msg.str(), tw, {media_id}); | ||
| 329 | if (resp != twitter::response::ok) | ||
| 330 | { | 326 | { | 
| 331 | std::cout << "Twitter error while tweeting: " << resp << std::endl; | 327 | std::cout << "Twitter error: " << e.what() << std::endl; | 
| 332 | sleep(delay); | ||
| 333 | |||
| 334 | continue; | ||
| 335 | } | 328 | } | 
| 336 | 329 | ||
| 337 | std::cout << "Done!" << std::endl << "Waiting..." << std::endl << std::endl; | 330 | std::this_thread::sleep_for(std::chrono::seconds(delay)); | 
| 338 | |||
| 339 | sleep(delay); | ||
| 340 | } | 331 | } | 
| 341 | 332 | ||
| 342 | return 0; | 333 | return 0; | 
| diff --git a/vendor/libtwittercpp b/vendor/libtwittercpp | |||
| Subproject a3d1ed08683c75c22d13f25e33f4333debf4f71 | Subproject a2d28cfbd7ab8ea71a802f06c2ae76c62decb02 | ||
