diff options
| author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2016-04-15 17:52:35 -0400 |
|---|---|---|
| committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2016-04-15 17:52:35 -0400 |
| commit | 9d021d0ef0e24595cd35e42d2847a6e679c386e0 (patch) | |
| tree | 0cedf25e9ac9f95399d72e27aff75c625ae83b0e | |
| download | difference-9d021d0ef0e24595cd35e42d2847a6e679c386e0.tar.gz difference-9d021d0ef0e24595cd35e42d2847a6e679c386e0.tar.bz2 difference-9d021d0ef0e24595cd35e42d2847a6e679c386e0.zip | |
Initial commit
| -rw-r--r-- | .gitmodules | 9 | ||||
| -rw-r--r-- | CMakeLists.txt | 18 | ||||
| -rwxr-xr-x | coolvetica.ttf | bin | 0 -> 135916 bytes | |||
| -rw-r--r-- | difference.cpp | 354 | ||||
| m--------- | vendor/curlcpp | 0 | ||||
| m--------- | vendor/libtwittercpp | 0 | ||||
| m--------- | vendor/verbly | 0 |
7 files changed, 381 insertions, 0 deletions
| diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6c14770 --- /dev/null +++ b/.gitmodules | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | [submodule "vendor/verbly"] | ||
| 2 | path = vendor/verbly | ||
| 3 | url = https://github.com/hatkirby/verbly | ||
| 4 | [submodule "vendor/libtwittercpp"] | ||
| 5 | path = vendor/libtwittercpp | ||
| 6 | url = https://github.com/hatkirby/libtwittercpp | ||
| 7 | [submodule "vendor/curlcpp"] | ||
| 8 | path = vendor/curlcpp | ||
| 9 | url = https://github.com/JosephP91/curlcpp | ||
| diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..da4306f --- /dev/null +++ b/CMakeLists.txt | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | cmake_minimum_required (VERSION 3.1) | ||
| 2 | project (difference) | ||
| 3 | |||
| 4 | set(CMAKE_BUILD_TYPE Debug) | ||
| 5 | |||
| 6 | add_subdirectory(vendor/libtwittercpp) | ||
| 7 | add_subdirectory(vendor/verbly) | ||
| 8 | |||
| 9 | find_package(PkgConfig) | ||
| 10 | pkg_check_modules(YamlCpp yaml-cpp REQUIRED) | ||
| 11 | pkg_check_modules(sqlite3 sqlite3 REQUIRED) | ||
| 12 | pkg_check_modules(GraphicsMagick GraphicsMagick++ REQUIRED) | ||
| 13 | |||
| 14 | include_directories(vendor/libtwittercpp/src ${sqlite3_INCLUDE_DIR} vendor/verbly/lib vendor/curlcpp/include ${GraphicsMagick_INCLUDE_DIRS} vendor/libtwittercpp/vendor/json/src) | ||
| 15 | add_executable(difference difference.cpp) | ||
| 16 | set_property(TARGET difference PROPERTY CXX_STANDARD 11) | ||
| 17 | set_property(TARGET difference PROPERTY CXX_STANDARD_REQUIRED ON) | ||
| 18 | target_link_libraries(difference verbly ${sqlite3_LIBRARIES} ${YamlCpp_LIBRARIES} twitter++ curlcpp ${GraphicsMagick_LIBRARIES}) | ||
| diff --git a/coolvetica.ttf b/coolvetica.ttf new file mode 100755 index 0000000..410ca31 --- /dev/null +++ b/coolvetica.ttf | |||
| Binary files differ | |||
| diff --git a/difference.cpp b/difference.cpp new file mode 100644 index 0000000..66e4550 --- /dev/null +++ b/difference.cpp | |||
| @@ -0,0 +1,354 @@ | |||
| 1 | #include <verbly.h> | ||
| 2 | #include <twitter.h> | ||
| 3 | #include <curl_easy.h> | ||
| 4 | #include <list> | ||
| 5 | #include <set> | ||
| 6 | #include <vector> | ||
| 7 | #include <algorithm> | ||
| 8 | #include <cstdlib> | ||
| 9 | #include <ctime> | ||
| 10 | #include <Magick++.h> | ||
| 11 | #include <iostream> | ||
| 12 | #include <yaml-cpp/yaml.h> | ||
| 13 | #include <unistd.h> | ||
| 14 | |||
| 15 | std::string capitalize(std::string input) | ||
| 16 | { | ||
| 17 | std::string result; | ||
| 18 | bool capnext = true; | ||
| 19 | |||
| 20 | for (auto ch : input) | ||
| 21 | { | ||
| 22 | if (capnext) | ||
| 23 | { | ||
| 24 | result += toupper(ch); | ||
| 25 | capnext = false; | ||
| 26 | } else { | ||
| 27 | result += ch; | ||
| 28 | } | ||
| 29 | |||
| 30 | if ((ch == ' ') || (ch == '-')) | ||
| 31 | { | ||
| 32 | capnext = true; | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | return result; | ||
| 37 | } | ||
| 38 | |||
| 39 | int main(int argc, char** argv) | ||
| 40 | { | ||
| 41 | srand(time(NULL)); | ||
| 42 | Magick::InitializeMagick(nullptr); | ||
| 43 | |||
| 44 | int delay = 60 * 60; | ||
| 45 | |||
| 46 | YAML::Node config = YAML::LoadFile("config.yml"); | ||
| 47 | twitter::auth auth; | ||
| 48 | auth.setConsumerKey(config["consumer_key"].as<std::string>()); | ||
| 49 | auth.setConsumerSecret(config["consumer_secret"].as<std::string>()); | ||
| 50 | auth.setAccessKey(config["access_key"].as<std::string>()); | ||
| 51 | auth.setAccessSecret(config["access_secret"].as<std::string>()); | ||
| 52 | |||
| 53 | twitter::client client(auth); | ||
| 54 | |||
| 55 | verbly::data database("data.sqlite3"); | ||
| 56 | |||
| 57 | auto whitelist = database.nouns(); | ||
| 58 | whitelist.with_wnid(111530512); // Crops (plants) | ||
| 59 | whitelist.with_wnid(111536087); // Ornamental (plants) | ||
| 60 | whitelist.with_wnid(111536230); // Pot plants | ||
| 61 | whitelist.with_wnid(113083023); // Houseplants | ||
| 62 | whitelist.with_wnid(113083306); // Garden plants | ||
| 63 | whitelist.with_wnid(113083586); // Vascular plants (includes flowers) | ||
| 64 | whitelist.with_wnid(109287968); // Geological formations | ||
| 65 | whitelist.with_wnid(109208496); // Asterisms (collections of stars) | ||
| 66 | whitelist.with_wnid(109239740); // Celestial bodies | ||
| 67 | whitelist.with_wnid(109277686); // Exterrestrial objects (comets and meteroids) | ||
| 68 | whitelist.with_wnid(109403211); // Radiators (supposedly natural radiators but actually these are just pictures of radiators) | ||
| 69 | whitelist.with_wnid(109416076); // Rocks | ||
| 70 | whitelist.with_wnid(105442131); // Chromosomes | ||
| 71 | whitelist.with_wnid(100324978); // Tightrope walking | ||
| 72 | whitelist.with_wnid(100326094); // Rock climbing | ||
| 73 | whitelist.with_wnid(100433458); // Contact sports | ||
| 74 | whitelist.with_wnid(100433802); // Gymnastics | ||
| 75 | whitelist.with_wnid(100439826); // Track and field | ||
| 76 | whitelist.with_wnid(100440747); // Skiing | ||
| 77 | whitelist.with_wnid(100441824); // Water sport | ||
| 78 | whitelist.with_wnid(100445351); // Rowing | ||
| 79 | whitelist.with_wnid(100446980); // Archery | ||
| 80 | // TODO: add more sports | ||
| 81 | whitelist.with_wnid(100021939); // Artifacts | ||
| 82 | whitelist.with_wnid(101471682); // Vertebrates | ||
| 83 | |||
| 84 | auto whitedata = whitelist.run(); | ||
| 85 | std::set<int> wnids; | ||
| 86 | verbly::filter<verbly::noun> whitefilter; | ||
| 87 | whitefilter.set_orlogic(true); | ||
| 88 | for (auto wd : whitedata) | ||
| 89 | { | ||
| 90 | if (wnids.count(wd.wnid()) == 0) | ||
| 91 | { | ||
| 92 | whitefilter << wd; | ||
| 93 | wnids.insert(wd.wnid()); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | std::cout << "Started!" << std::endl; | ||
| 98 | for (;;) | ||
| 99 | { | ||
| 100 | std::cout << "Generating noun..." << std::endl; | ||
| 101 | verbly::noun pictured = database.nouns().full_hyponym_of(whitefilter).at_least_n_images(2).random().limit(1).run().front(); | ||
| 102 | std::cout << "Noun: " << pictured.singular_form() << std::endl; | ||
| 103 | std::cout << "Getting URLs..." << std::endl; | ||
| 104 | std::ostringstream lstbuf; | ||
| 105 | curl::curl_ios<std::ostringstream> lstios(lstbuf); | ||
| 106 | curl::curl_easy lsthandle(lstios); | ||
| 107 | std::string lsturl = pictured.imagenet_url(); | ||
| 108 | lsthandle.add<CURLOPT_URL>(lsturl.c_str()); | ||
| 109 | try { | ||
| 110 | lsthandle.perform(); | ||
| 111 | } catch (curl::curl_easy_exception error) { | ||
| 112 | error.print_traceback(); | ||
| 113 | sleep(delay); | ||
| 114 | |||
| 115 | continue; | ||
| 116 | } | ||
| 117 | |||
| 118 | if (lsthandle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200) | ||
| 119 | { | ||
| 120 | std::cout << "Could not get URLs" << std::endl; | ||
| 121 | continue; | ||
| 122 | } | ||
| 123 | |||
| 124 | std::cout << "Got URLs." << std::endl; | ||
| 125 | std::string lstdata = lstbuf.str(); | ||
| 126 | auto lstlist = verbly::split<std::list<std::string>>(lstdata, "\r\n"); | ||
| 127 | std::set<std::string> lstset; | ||
| 128 | for (auto url : lstlist) | ||
| 129 | { | ||
| 130 | lstset.insert(url); | ||
| 131 | } | ||
| 132 | |||
| 133 | if (lstset.size() < 2) | ||
| 134 | { | ||
| 135 | continue; | ||
| 136 | } | ||
| 137 | |||
| 138 | std::vector<std::string> lstvec; | ||
| 139 | for (auto url : lstset) | ||
| 140 | { | ||
| 141 | lstvec.push_back(url); | ||
| 142 | } | ||
| 143 | |||
| 144 | std::random_shuffle(std::begin(lstvec), std::end(lstvec)); | ||
| 145 | |||
| 146 | Magick::Blob img1; | ||
| 147 | Magick::Image pic1; | ||
| 148 | bool success = false; | ||
| 149 | int curind = 0; | ||
| 150 | for (; curind < lstvec.size(); curind++) | ||
| 151 | { | ||
| 152 | std::ostringstream img1buf; | ||
| 153 | curl::curl_ios<std::ostringstream> img1ios(img1buf); | ||
| 154 | curl::curl_easy img1handle(img1ios); | ||
| 155 | std::string img1url = lstvec[curind]; | ||
| 156 | img1handle.add<CURLOPT_URL>(img1url.c_str()); | ||
| 157 | img1handle.add<CURLOPT_CONNECTTIMEOUT>(30); | ||
| 158 | |||
| 159 | try { | ||
| 160 | img1handle.perform(); | ||
| 161 | } catch (curl::curl_easy_exception error) { | ||
| 162 | error.print_traceback(); | ||
| 163 | |||
| 164 | continue; | ||
| 165 | } | ||
| 166 | |||
| 167 | if (img1handle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200) | ||
| 168 | { | ||
| 169 | continue; | ||
| 170 | } | ||
| 171 | |||
| 172 | if (std::string(img1handle.get_info<CURLINFO_CONTENT_TYPE>().get()).substr(0, 6) != "image/") | ||
| 173 | { | ||
| 174 | continue; | ||
| 175 | } | ||
| 176 | |||
| 177 | std::string img1str = img1buf.str(); | ||
| 178 | img1 = Magick::Blob(img1str.c_str(), img1str.length()); | ||
| 179 | pic1.read(img1); | ||
| 180 | if (pic1.rows() == 0) | ||
| 181 | { | ||
| 182 | continue; | ||
| 183 | } | ||
| 184 | |||
| 185 | std::cout << img1url << std::endl; | ||
| 186 | success = true; | ||
| 187 | |||
| 188 | break; | ||
| 189 | } | ||
| 190 | |||
| 191 | if (!success) | ||
| 192 | { | ||
| 193 | continue; | ||
| 194 | } | ||
| 195 | |||
| 196 | success = false; | ||
| 197 | Magick::Blob img2; | ||
| 198 | Magick::Image pic2; | ||
| 199 | for (curind++; curind < lstvec.size(); curind++) | ||
| 200 | { | ||
| 201 | std::ostringstream img2buf; | ||
| 202 | curl::curl_ios<std::ostringstream> img2ios(img2buf); | ||
| 203 | curl::curl_easy img2handle(img2ios); | ||
| 204 | std::string img2url = lstvec[curind]; | ||
| 205 | img2handle.add<CURLOPT_URL>(img2url.c_str()); | ||
| 206 | img2handle.add<CURLOPT_CONNECTTIMEOUT>(30); | ||
| 207 | |||
| 208 | try { | ||
| 209 | img2handle.perform(); | ||
| 210 | } catch (curl::curl_easy_exception error) { | ||
| 211 | error.print_traceback(); | ||
| 212 | |||
| 213 | continue; | ||
| 214 | } | ||
| 215 | |||
| 216 | if (img2handle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200) | ||
| 217 | { | ||
| 218 | continue; | ||
| 219 | } | ||
| 220 | |||
| 221 | if (std::string(img2handle.get_info<CURLINFO_CONTENT_TYPE>().get()).substr(0, 6) != "image/") | ||
| 222 | { | ||
| 223 | continue; | ||
| 224 | } | ||
| 225 | |||
| 226 | std::string img2str = img2buf.str(); | ||
| 227 | img2 = Magick::Blob(img2str.c_str(), img2str.length()); | ||
| 228 | pic2.read(img2); | ||
| 229 | if (pic2.rows() == 0) | ||
| 230 | { | ||
| 231 | continue; | ||
| 232 | } | ||
| 233 | |||
| 234 | std::cout << img2url << std::endl; | ||
| 235 | success = true; | ||
| 236 | |||
| 237 | break; | ||
| 238 | } | ||
| 239 | |||
| 240 | if (!success) | ||
| 241 | { | ||
| 242 | continue; | ||
| 243 | } | ||
| 244 | |||
| 245 | std::string ant1, ant2; | ||
| 246 | if (rand()%2==0) | ||
| 247 | { | ||
| 248 | verbly::noun n1 = database.nouns().has_antonyms().random().limit(1).run().front(); | ||
| 249 | verbly::noun n2 = n1.antonyms().random().limit(1).run().front(); | ||
| 250 | ant1 = n1.singular_form(); | ||
| 251 | ant2 = n2.singular_form(); | ||
| 252 | } else { | ||
| 253 | verbly::adjective a1 = database.adjectives().has_antonyms().random().limit(1).run().front(); | ||
| 254 | verbly::adjective a2 = a1.antonyms().random().limit(1).run().front(); | ||
| 255 | ant1 = a1.base_form(); | ||
| 256 | ant2 = a2.base_form(); | ||
| 257 | } | ||
| 258 | |||
| 259 | if (pic1.columns() < 320) | ||
| 260 | { | ||
| 261 | pic1.zoom(Magick::Geometry(320, pic1.rows() * 320 / pic1.columns(), 0, 0)); | ||
| 262 | } | ||
| 263 | |||
| 264 | if (pic2.columns() < 320) | ||
| 265 | { | ||
| 266 | pic2.zoom(Magick::Geometry(320, pic2.rows() * 320 / pic2.columns(), 0, 0)); | ||
| 267 | } | ||
| 268 | |||
| 269 | int width = std::min(pic1.columns(), pic2.columns()); | ||
| 270 | int height = std::min(pic1.rows(), pic2.rows()); | ||
| 271 | Magick::Geometry geo1(width, height, pic1.columns()/2 - width/2, pic1.rows()/2 - height/2); | ||
| 272 | Magick::Geometry geo2(width, height, pic2.columns()/2 - width/2, pic2.rows()/2 - height/2); | ||
| 273 | pic1.crop(geo1); | ||
| 274 | pic2.crop(geo2); | ||
| 275 | |||
| 276 | Magick::Image composite(Magick::Geometry(width*2, height, 0, 0), "white"); | ||
| 277 | composite.draw(Magick::DrawableCompositeImage(0, 0, pic1)); | ||
| 278 | composite.draw(Magick::DrawableCompositeImage(width, 0, pic2)); | ||
| 279 | composite.font("@coolvetica.ttf"); | ||
| 280 | |||
| 281 | double fontsize = rand() % 72 + 16; | ||
| 282 | for (;;) | ||
| 283 | { | ||
| 284 | composite.fontPointsize(fontsize); | ||
| 285 | |||
| 286 | Magick::TypeMetric metric; | ||
| 287 | composite.fontTypeMetrics(ant1, &metric); | ||
| 288 | if (metric.textWidth() > 300) | ||
| 289 | { | ||
| 290 | fontsize -= 0.5; | ||
| 291 | continue; | ||
| 292 | } | ||
| 293 | |||
| 294 | composite.fontTypeMetrics(ant2, &metric); | ||
| 295 | if (metric.textWidth() > 300) | ||
| 296 | { | ||
| 297 | fontsize -= 0.5; | ||
| 298 | continue; | ||
| 299 | } | ||
| 300 | |||
| 301 | break; | ||
| 302 | } | ||
| 303 | |||
| 304 | Magick::TypeMetric metric; | ||
| 305 | composite.fontTypeMetrics(ant1, &metric); | ||
| 306 | int y = rand() % ((int)(composite.rows() - 40 - metric.textHeight())) + 20; | ||
| 307 | y = composite.rows() - y; | ||
| 308 | int x1 = (width - 40 - metric.textWidth())/2+20; | ||
| 309 | composite.fontTypeMetrics(ant2, &metric); | ||
| 310 | int x2 = (width - 40 - metric.textWidth())/2+20+width; | ||
| 311 | composite.strokeColor("white"); | ||
| 312 | composite.strokeWidth(1); | ||
| 313 | composite.draw(Magick::DrawableText(x1, y, ant1)); | ||
| 314 | composite.draw(Magick::DrawableText(x2, y, ant2)); | ||
| 315 | |||
| 316 | composite.magick("png"); | ||
| 317 | |||
| 318 | Magick::Blob outputimg; | ||
| 319 | composite.write(&outputimg); | ||
| 320 | |||
| 321 | std::cout << "Generated image!" << std::endl << "Tweeting..." << std::endl; | ||
| 322 | |||
| 323 | std::stringstream msg; | ||
| 324 | msg << capitalize(ant1); | ||
| 325 | msg << " vs. "; | ||
| 326 | msg << capitalize(ant2); | ||
| 327 | |||
| 328 | long media_id; | ||
| 329 | twitter::response resp = client.uploadMedia("image/png", (const char*) outputimg.data(), outputimg.length(), media_id); | ||
| 330 | if (resp != twitter::response::ok) | ||
| 331 | { | ||
| 332 | std::cout << "Twitter error while uploading image: " << resp << std::endl; | ||
| 333 | sleep(delay); | ||
| 334 | |||
| 335 | continue; | ||
| 336 | } | ||
| 337 | |||
| 338 | twitter::tweet tw; | ||
| 339 | resp = client.updateStatus(msg.str(), tw, {media_id}); | ||
| 340 | if (resp != twitter::response::ok) | ||
| 341 | { | ||
| 342 | std::cout << "Twitter error while tweeting: " << resp << std::endl; | ||
| 343 | sleep(delay); | ||
| 344 | |||
| 345 | continue; | ||
| 346 | } | ||
| 347 | |||
| 348 | std::cout << "Done!" << std::endl << "Waiting..." << std::endl << std::endl; | ||
| 349 | |||
| 350 | sleep(delay); | ||
| 351 | } | ||
| 352 | |||
| 353 | return 0; | ||
| 354 | } | ||
| diff --git a/vendor/curlcpp b/vendor/curlcpp new file mode 160000 | |||
| Subproject 0cdfb670d726252bdce4fb2a194e11de38a42e0 | |||
| diff --git a/vendor/libtwittercpp b/vendor/libtwittercpp new file mode 160000 | |||
| Subproject 7e85b35d7d1714e3f85434b891a1050ad584e33 | |||
| diff --git a/vendor/verbly b/vendor/verbly new file mode 160000 | |||
| Subproject 040ee58fecdc9c478004bc2e554e1ae126ec460 | |||
