about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2016-04-15 17:52:35 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2016-04-15 17:52:35 -0400
commit9d021d0ef0e24595cd35e42d2847a6e679c386e0 (patch)
tree0cedf25e9ac9f95399d72e27aff75c625ae83b0e
downloaddifference-9d021d0ef0e24595cd35e42d2847a6e679c386e0.tar.gz
difference-9d021d0ef0e24595cd35e42d2847a6e679c386e0.tar.bz2
difference-9d021d0ef0e24595cd35e42d2847a6e679c386e0.zip
Initial commit
-rw-r--r--.gitmodules9
-rw-r--r--CMakeLists.txt18
-rwxr-xr-xcoolvetica.ttfbin0 -> 135916 bytes
-rw-r--r--difference.cpp354
m---------vendor/curlcpp0
m---------vendor/libtwittercpp0
m---------vendor/verbly0
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 @@
1cmake_minimum_required (VERSION 3.1)
2project (difference)
3
4set(CMAKE_BUILD_TYPE Debug)
5
6add_subdirectory(vendor/libtwittercpp)
7add_subdirectory(vendor/verbly)
8
9find_package(PkgConfig)
10pkg_check_modules(YamlCpp yaml-cpp REQUIRED)
11pkg_check_modules(sqlite3 sqlite3 REQUIRED)
12pkg_check_modules(GraphicsMagick GraphicsMagick++ REQUIRED)
13
14include_directories(vendor/libtwittercpp/src ${sqlite3_INCLUDE_DIR} vendor/verbly/lib vendor/curlcpp/include ${GraphicsMagick_INCLUDE_DIRS} vendor/libtwittercpp/vendor/json/src)
15add_executable(difference difference.cpp)
16set_property(TARGET difference PROPERTY CXX_STANDARD 11)
17set_property(TARGET difference PROPERTY CXX_STANDARD_REQUIRED ON)
18target_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
15std::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
39int 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