summary refs log tree commit diff stats
path: root/advice.cpp
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2017-02-03 20:24:38 -0500
committerKelly Rauchenberger <fefferburbia@gmail.com>2017-02-03 20:24:38 -0500
commit7ce570bc98a5ad813cd5ad3220a5275aa02622df (patch)
tree63f0aaf06a235b0f5feb9579d379879f1fa09f24 /advice.cpp
parent18742d79e1de863889521c492e938491489316fe (diff)
downloadadvice-7ce570bc98a5ad813cd5ad3220a5275aa02622df.tar.gz
advice-7ce570bc98a5ad813cd5ad3220a5275aa02622df.tar.bz2
advice-7ce570bc98a5ad813cd5ad3220a5275aa02622df.zip
Combined advice methods because of weird memory things?
Diffstat (limited to 'advice.cpp')
-rw-r--r--advice.cpp518
1 files changed, 243 insertions, 275 deletions
diff --git a/advice.cpp b/advice.cpp index 320f719..b9ab3f9 100644 --- a/advice.cpp +++ b/advice.cpp
@@ -35,315 +35,283 @@ advice::advice(
35 generator_ = std::unique_ptr<sentence>(new sentence(*database_, rng_)); 35 generator_ = std::unique_ptr<sentence>(new sentence(*database_, rng_));
36} 36}
37 37
38verbly::word advice::generateImageNoun() const 38void advice::run() const
39{
40 verbly::filter whitelist =
41 (verbly::notion::wnid == 109287968) // Geological formations
42 || (verbly::notion::wnid == 109208496) // Asterisms (collections of stars)
43 || (verbly::notion::wnid == 109239740) // Celestial bodies
44 || (verbly::notion::wnid == 109277686) // Exterrestrial objects (comets and meteroids)
45 || (verbly::notion::wnid == 109403211) // Radiators (supposedly natural radiators but actually these are just pictures of radiators)
46 || (verbly::notion::wnid == 109416076) // Rocks
47 || (verbly::notion::wnid == 105442131) // Chromosomes
48 || (verbly::notion::wnid == 100324978) // Tightrope walking
49 || (verbly::notion::wnid == 100326094) // Rock climbing
50 || (verbly::notion::wnid == 100433458) // Contact sports
51 || (verbly::notion::wnid == 100433802) // Gymnastics
52 || (verbly::notion::wnid == 100439826) // Track and field
53 || (verbly::notion::wnid == 100440747) // Skiing
54 || (verbly::notion::wnid == 100441824) // Water sport
55 || (verbly::notion::wnid == 100445351) // Rowing
56 || (verbly::notion::wnid == 100446980) // Archery
57 // TODO: add more sports
58 || (verbly::notion::wnid == 100021939) // Artifacts
59 || (verbly::notion::wnid == 101471682) // Vertebrates
60 ;
61
62 verbly::filter blacklist =
63 (verbly::notion::wnid == 106883725) // swastika
64 || (verbly::notion::wnid == 104416901) // tetraskele
65 || (verbly::notion::wnid == 102512053) // fish
66 || (verbly::notion::wnid == 103575691) // instrument of execution
67 ;
68
69 verbly::query<verbly::word> pictureQuery = database_->words(
70 (verbly::notion::fullHypernyms %= whitelist)
71 && !(verbly::notion::fullHypernyms %= blacklist)
72 && (verbly::notion::partOfSpeech == verbly::part_of_speech::noun)
73 && (verbly::notion::numOfImages >= 1));
74
75 return pictureQuery.first();
76}
77
78Magick::Image advice::getImageForNoun(verbly::word pictured) const
79{ 39{
80 // Accept string from Google Chrome 40 for (;;)
81 std::string accept = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8";
82 curl::curl_header headers;
83 headers.add(accept);
84
85 int backoff = 0;
86
87 std::cout << "Generating noun..." << std::endl;
88 std::cout << "Noun: " << pictured.getBaseForm() << std::endl;
89 std::cout << "Getting URLs..." << std::endl;
90
91 std::string lstdata;
92 while (lstdata.empty())
93 { 41 {
94 std::ostringstream lstbuf;
95 curl::curl_ios<std::ostringstream> lstios(lstbuf);
96 curl::curl_easy lsthandle(lstios);
97 std::string lsturl = pictured.getNotion().getImageNetUrl();
98 lsthandle.add<CURLOPT_URL>(lsturl.c_str());
99
100 try 42 try
101 { 43 {
102 lsthandle.perform(); 44 verbly::filter whitelist =
103 } catch (const curl::curl_easy_exception& e) 45 (verbly::notion::wnid == 109287968) // Geological formations
104 { 46 || (verbly::notion::wnid == 109208496) // Asterisms (collections of stars)
105 e.print_traceback(); 47 || (verbly::notion::wnid == 109239740) // Celestial bodies
106 48 || (verbly::notion::wnid == 109277686) // Exterrestrial objects (comets and meteroids)
107 backoff++; 49 || (verbly::notion::wnid == 109403211) // Radiators (supposedly natural radiators but actually these are just pictures of radiators)
108 std::cout << "Waiting for " << backoff << " seconds..." << std::endl; 50 || (verbly::notion::wnid == 109416076) // Rocks
109 51 || (verbly::notion::wnid == 105442131) // Chromosomes
110 std::this_thread::sleep_for(std::chrono::seconds(backoff)); 52 || (verbly::notion::wnid == 100324978) // Tightrope walking
111 53 || (verbly::notion::wnid == 100326094) // Rock climbing
112 continue; 54 || (verbly::notion::wnid == 100433458) // Contact sports
113 } 55 || (verbly::notion::wnid == 100433802) // Gymnastics
114 56 || (verbly::notion::wnid == 100439826) // Track and field
115 backoff = 0; 57 || (verbly::notion::wnid == 100440747) // Skiing
116 58 || (verbly::notion::wnid == 100441824) // Water sport
117 if (lsthandle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200) 59 || (verbly::notion::wnid == 100445351) // Rowing
118 { 60 || (verbly::notion::wnid == 100446980) // Archery
119 throw could_not_get_images(); 61 // TODO: add more sports
120 } 62 || (verbly::notion::wnid == 100021939) // Artifacts
63 || (verbly::notion::wnid == 101471682) // Vertebrates
64 ;
65
66 verbly::filter blacklist =
67 (verbly::notion::wnid == 106883725) // swastika
68 || (verbly::notion::wnid == 104416901) // tetraskele
69 || (verbly::notion::wnid == 102512053) // fish
70 || (verbly::notion::wnid == 103575691) // instrument of execution
71 ;
72
73 verbly::query<verbly::word> pictureQuery = database_->words(
74 (verbly::notion::fullHypernyms %= whitelist)
75 && !(verbly::notion::fullHypernyms %= blacklist)
76 && (verbly::notion::partOfSpeech == verbly::part_of_speech::noun)
77 && (verbly::notion::numOfImages >= 1));
78
79 verbly::word pictured = pictureQuery.first();
80
81 // Accept string from Google Chrome
82 std::string accept = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8";
83 curl::curl_header headers;
84 headers.add(accept);
85
86 int backoff = 0;
87
88 std::cout << "Generating noun..." << std::endl;
89 std::cout << "Noun: " << pictured.getBaseForm() << std::endl;
90 std::cout << "Getting URLs..." << std::endl;
91
92 std::string lstdata;
93 while (lstdata.empty())
94 {
95 std::ostringstream lstbuf;
96 curl::curl_ios<std::ostringstream> lstios(lstbuf);
97 curl::curl_easy lsthandle(lstios);
98 std::string lsturl = pictured.getNotion().getImageNetUrl();
99 lsthandle.add<CURLOPT_URL>(lsturl.c_str());
121 100
122 std::cout << "Got URLs." << std::endl; 101 try
123 lstdata = lstbuf.str(); 102 {
124 } 103 lsthandle.perform();
104 } catch (const curl::curl_easy_exception& e)
105 {
106 e.print_traceback();
125 107
126 std::vector<std::string> lstvec = verbly::split<std::vector<std::string>>(lstdata, "\r\n"); 108 backoff++;
127 if (lstvec.empty()) 109 std::cout << "Waiting for " << backoff << " seconds..." << std::endl;
128 {
129 throw could_not_get_images();
130 }
131 110
132 std::shuffle(std::begin(lstvec), std::end(lstvec), rng_); 111 std::this_thread::sleep_for(std::chrono::seconds(backoff));
133 112
134 std::deque<std::string> urls; 113 continue;
135 for (std::string& url : lstvec) 114 }
136 {
137 urls.push_back(url);
138 }
139 115
140 bool found = false; 116 backoff = 0;
141 Magick::Blob img;
142 Magick::Image pic;
143 117
144 while (!found && !urls.empty()) 118 if (lsthandle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200)
145 { 119 {
146 std::string url = urls.front(); 120 throw could_not_get_images();
147 urls.pop_front(); 121 }
148 122
149 std::ostringstream imgbuf; 123 std::cout << "Got URLs." << std::endl;
150 curl::curl_ios<std::ostringstream> imgios(imgbuf); 124 lstdata = lstbuf.str();
151 curl::curl_easy imghandle(imgios); 125 }
152 126
153 imghandle.add<CURLOPT_HTTPHEADER>(headers.get()); 127 std::vector<std::string> lstvec = verbly::split<std::vector<std::string>>(lstdata, "\r\n");
154 imghandle.add<CURLOPT_URL>(url.c_str()); 128 if (lstvec.empty())
155 imghandle.add<CURLOPT_CONNECTTIMEOUT>(30); 129 {
130 throw could_not_get_images();
131 }
156 132
157 try 133 std::shuffle(std::begin(lstvec), std::end(lstvec), rng_);
158 {
159 imghandle.perform();
160 } catch (curl::curl_easy_exception error) {
161 error.print_traceback();
162 134
163 continue; 135 std::deque<std::string> urls;
164 } 136 for (std::string& url : lstvec)
137 {
138 urls.push_back(url);
139 }
165 140
166 if (imghandle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200) 141 bool found = false;
167 { 142 Magick::Blob img;
168 continue; 143 Magick::Image pic;
169 }
170 144
171 std::string content_type = imghandle.get_info<CURLINFO_CONTENT_TYPE>().get(); 145 while (!found && !urls.empty())
172 if (content_type.substr(0, 6) != "image/") 146 {
173 { 147 std::string url = urls.front();
174 continue; 148 urls.pop_front();
175 } 149
150 std::ostringstream imgbuf;
151 curl::curl_ios<std::ostringstream> imgios(imgbuf);
152 curl::curl_easy imghandle(imgios);
153
154 imghandle.add<CURLOPT_HTTPHEADER>(headers.get());
155 imghandle.add<CURLOPT_URL>(url.c_str());
156 imghandle.add<CURLOPT_CONNECTTIMEOUT>(30);
157
158 try
159 {
160 imghandle.perform();
161 } catch (curl::curl_easy_exception error) {
162 error.print_traceback();
163
164 continue;
165 }
166
167 if (imghandle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200)
168 {
169 continue;
170 }
171
172 std::string content_type = imghandle.get_info<CURLINFO_CONTENT_TYPE>().get();
173 if (content_type.substr(0, 6) != "image/")
174 {
175 continue;
176 }
177
178 std::string imgstr = imgbuf.str();
179 img = Magick::Blob(imgstr.c_str(), imgstr.length());
180 pic.read(img);
181 if (pic.rows() == 0)
182 {
183 continue;
184 }
185
186 // Too small!
187 if (pic.columns() < 400)
188 {
189 continue;
190 }
191
192 std::cout << url << std::endl;
193 found = true;
194 }
176 195
177 std::string imgstr = imgbuf.str(); 196 if (!found)
178 img = Magick::Blob(imgstr.c_str(), imgstr.length()); 197 {
179 pic.read(img); 198 throw could_not_get_images();
180 if (pic.rows() == 0) 199 }
181 { 200
182 continue; 201 std::string title = generator_->generate();
183 }
184 202
185 // Too small! 203 // Want a 16:9 aspect
186 if (pic.columns() < 400) 204 int idealwidth = pic.rows()*(16.0/9.0);
187 { 205 if (idealwidth > pic.columns())
188 continue; 206 {
189 } 207 // If the image is narrower than the ideal width, use full width.
208 int newheight = pic.columns()*(9.0/16.0);
190 209
191 std::cout << url << std::endl; 210 // Just take a slice out of the middle of the image.
192 found = true; 211 int cropy = ((double)(pic.rows() - newheight))/2.0;
193 }
194 212
195 if (!found) 213 pic.crop(Magick::Geometry(pic.columns(), newheight, 0, cropy));
196 { 214 } else {
197 throw could_not_get_images(); 215 // If the image is wider than the ideal width, use full height.
198 } 216 // Just take a slice out of the middle of the image.
217 int cropx = ((double)(pic.columns() - idealwidth))/2.0;
199 218
200 return pic; 219 pic.crop(Magick::Geometry(idealwidth, pic.rows(), cropx, 0));
201} 220 }
202 221
203Magick::Image advice::layoutImage(Magick::Image pic, std::string title) const 222 pic.zoom(Magick::Geometry(400, 225));
204{
205 // Want a 16:9 aspect
206 int idealwidth = pic.rows()*(16.0/9.0);
207 if (idealwidth > pic.columns())
208 {
209 // If the image is narrower than the ideal width, use full width.
210 int newheight = pic.columns()*(9.0/16.0);
211 223
212 // Just take a slice out of the middle of the image. 224 // Layout the text.
213 int cropy = ((double)(pic.rows() - newheight))/2.0; 225 std::list<std::string> words = verbly::split<std::list<std::string>>(title, " ");
226 std::vector<std::string> lines;
227 std::list<std::string> cur;
228 Magick::TypeMetric metric;
229 pic.fontPointsize(20);
230 pic.font("@coolvetica.ttf");
214 231
215 pic.crop(Magick::Geometry(pic.columns(), newheight, 0, cropy)); 232 while (!words.empty())
216 } else { 233 {
217 // If the image is wider than the ideal width, use full height. 234 cur.push_back(words.front());
218 // Just take a slice out of the middle of the image. 235
219 int cropx = ((double)(pic.columns() - idealwidth))/2.0; 236 std::string prefixText = verbly::implode(std::begin(cur), std::end(cur), " ");
237 pic.fontTypeMetrics(prefixText, &metric);
238
239 if (metric.textWidth() > 380)
240 {
241 if (cur.size() == 1)
242 {
243 words.pop_front();
244 } else {
245 cur.pop_back();
246 }
247
248 prefixText = verbly::implode(std::begin(cur), std::end(cur), " ");
249 lines.push_back(prefixText);
250 cur.clear();
251 } else {
252 words.pop_front();
253 }
254 }
220 255
221 pic.crop(Magick::Geometry(idealwidth, pic.rows(), cropx, 0)); 256 if (!cur.empty())
222 } 257 {
258 std::string prefixText = verbly::implode(std::begin(cur), std::end(cur), " ");
259 lines.push_back(prefixText);
260 }
223 261
224 pic.zoom(Magick::Geometry(400, 225)); 262 int lineHeight = metric.textHeight()-2;
263 int blockHeight = lineHeight * lines.size() + 18;
264 std::cout << "line " << lineHeight << "; block " << blockHeight << std::endl;
265
266 std::list<Magick::Drawable> drawList;
267 drawList.push_back(Magick::DrawableFillColor("black"));
268 drawList.push_back(Magick::DrawableFillOpacity(0.5));
269 drawList.push_back(Magick::DrawableStrokeColor("transparent"));
270 drawList.push_back(Magick::DrawableRectangle(0, 225-blockHeight-20, 400, 255)); // 0, 225-60, 400, 255
271 pic.draw(drawList);
272
273 drawList.clear();
274 drawList.push_back(Magick::DrawableFont("@coolvetica.ttf"));
275 drawList.push_back(Magick::DrawableFillColor("white"));
276 drawList.push_back(Magick::DrawablePointSize(14));
277 drawList.push_back(Magick::DrawableText(10, 225-blockHeight+4, "How to")); // 10, 255-62-4
278 pic.draw(drawList);
279
280 for (int i=0; i<lines.size(); i++)
281 {
282 drawList.clear();
283 drawList.push_back(Magick::DrawableFont("@coolvetica.ttf"));
284 drawList.push_back(Magick::DrawableFillColor("white"));
285 drawList.push_back(Magick::DrawablePointSize(20));
286 drawList.push_back(Magick::DrawableText(10, 255-blockHeight+(i*lineHeight)-4, lines[i])); // 10, 255-20-25
287 pic.draw(drawList);
288 }
225 289
226 // Layout the text. 290 Magick::Blob outputimg;
227 std::list<std::string> words = verbly::split<std::list<std::string>>(title, " ");
228 std::vector<std::string> lines;
229 std::list<std::string> cur;
230 Magick::TypeMetric metric;
231 pic.fontPointsize(20);
232 pic.font("@coolvetica.ttf");
233 291
234 while (!words.empty()) 292 try
235 { 293 {
236 cur.push_back(words.front()); 294 pic.magick("png");
295 pic.write(&outputimg);
296 } catch (const Magick::WarningCoder& e)
297 {
298 // Ignore
299 }
237 300
238 std::string prefixText = verbly::implode(std::begin(cur), std::end(cur), " "); 301 std::cout << "Generated image!" << std::endl << "Tweeting..." << std::endl;
239 pic.fontTypeMetrics(prefixText, &metric);
240 302
241 if (metric.textWidth() > 380) 303 std::string tweetText;
242 { 304 size_t tweetLim = 140 - client_->getConfiguration().getCharactersReservedPerMedia();
243 if (cur.size() == 1) 305 if (title.length() > tweetLim)
244 { 306 {
245 words.pop_front(); 307 tweetText = title.substr(0, tweetLim - 1) + "…";
246 } else { 308 } else {
247 cur.pop_back(); 309 tweetText = title;
248 } 310 }
249 311
250 prefixText = verbly::implode(std::begin(cur), std::end(cur), " "); 312 long media_id = client_->uploadMedia("image/png", (const char*) outputimg.data(), outputimg.length());
251 lines.push_back(prefixText); 313 client_->updateStatus(tweetText, {media_id});
252 cur.clear(); 314
253 } else {
254 words.pop_front();
255 }
256 }
257
258 if (!cur.empty())
259 {
260 std::string prefixText = verbly::implode(std::begin(cur), std::end(cur), " ");
261 lines.push_back(prefixText);
262 }
263
264 int lineHeight = metric.textHeight()-2;
265 int blockHeight = lineHeight * lines.size() + 18;
266 std::cout << "line " << lineHeight << "; block " << blockHeight << std::endl;
267
268 std::list<Magick::Drawable> drawList;
269 drawList.push_back(Magick::DrawableFillColor("black"));
270 drawList.push_back(Magick::DrawableFillOpacity(0.5));
271 drawList.push_back(Magick::DrawableStrokeColor("transparent"));
272 drawList.push_back(Magick::DrawableRectangle(0, 225-blockHeight-20, 400, 255)); // 0, 225-60, 400, 255
273 pic.draw(drawList);
274
275 drawList.clear();
276 drawList.push_back(Magick::DrawableFont("@coolvetica.ttf"));
277 drawList.push_back(Magick::DrawableFillColor("white"));
278 drawList.push_back(Magick::DrawablePointSize(14));
279 drawList.push_back(Magick::DrawableText(10, 225-blockHeight+4, "How to")); // 10, 255-62-4
280 pic.draw(drawList);
281
282 for (int i=0; i<lines.size(); i++)
283 {
284 drawList.clear();
285 drawList.push_back(Magick::DrawableFont("@coolvetica.ttf"));
286 drawList.push_back(Magick::DrawableFillColor("white"));
287 drawList.push_back(Magick::DrawablePointSize(20));
288 drawList.push_back(Magick::DrawableText(10, 255-blockHeight+(i*lineHeight)-4, lines[i])); // 10, 255-20-25
289 pic.draw(drawList);
290 }
291
292 return pic;
293}
294
295void advice::sendTweet(Magick::Image pic, std::string title) const
296{
297 Magick::Blob outputimg;
298
299 try
300 {
301 pic.magick("png");
302 pic.write(&outputimg);
303 } catch (const Magick::WarningCoder& e)
304 {
305 // Ignore
306 }
307
308 std::cout << "Generated image!" << std::endl << "Tweeting..." << std::endl;
309
310 std::string tweetText;
311 size_t tweetLim = 140 - client_->getConfiguration().getCharactersReservedPerMedia();
312 if (title.length() > tweetLim)
313 {
314 tweetText = title.substr(0, tweetLim - 1) + "…";
315 } else {
316 tweetText = title;
317 }
318
319 long media_id = client_->uploadMedia("image/png", (const char*) outputimg.data(), outputimg.length());
320 client_->updateStatus(tweetText, {media_id});
321}
322
323void advice::run() const
324{
325 for (;;)
326 {
327 try
328 {
329 // Pick a noun to use for the picture.
330 verbly::word pictured = generateImageNoun();
331
332 // Find an image of the picked noun.
333 Magick::Image pic = getImageForNoun(pictured);
334
335 // Generate the image text.
336 std::string title = generator_->generate();
337
338 // Layout the image.
339 Magick::Image output = layoutImage(std::move(pic), title);
340
341 // Tweet the image.
342 sendTweet(std::move(output), title);
343
344 std::cout << "Done!" << std::endl << "Waiting..." << std::endl << std::endl;
345
346 // Wait.
347 std::this_thread::sleep_for(std::chrono::hours(1)); 315 std::this_thread::sleep_for(std::chrono::hours(1));
348 } catch (const could_not_get_images& ex) 316 } catch (const could_not_get_images& ex)
349 { 317 {