summary refs log tree commit diff stats
path: root/capital.cpp
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2017-10-29 09:55:03 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2017-10-29 09:55:03 -0400
commit0bb6d64c929b237278abf2565b6eb9f5f85fcd37 (patch)
treecff1e8508faffa1b3ec928d582b5a3e1a3785334 /capital.cpp
downloadcapital-0bb6d64c929b237278abf2565b6eb9f5f85fcd37.tar.gz
capital-0bb6d64c929b237278abf2565b6eb9f5f85fcd37.tar.bz2
capital-0bb6d64c929b237278abf2565b6eb9f5f85fcd37.zip
Created bot
Diffstat (limited to 'capital.cpp')
-rw-r--r--capital.cpp292
1 files changed, 292 insertions, 0 deletions
diff --git a/capital.cpp b/capital.cpp new file mode 100644 index 0000000..526e655 --- /dev/null +++ b/capital.cpp
@@ -0,0 +1,292 @@
1#include "capital.h"
2#include <vector>
3#include <yaml-cpp/yaml.h>
4#include <curl_easy.h>
5#include <curl_header.h>
6#include <iostream>
7#include <deque>
8
9capital::capital(
10 std::string configFile,
11 std::mt19937& rng) :
12 rng_(rng)
13{
14 // Load the config file.
15 YAML::Node config = YAML::LoadFile(configFile);
16
17 // Set up the Twitter client.
18 twitter::auth auth;
19 auth.setConsumerKey(config["consumer_key"].as<std::string>());
20 auth.setConsumerSecret(config["consumer_secret"].as<std::string>());
21 auth.setAccessKey(config["access_key"].as<std::string>());
22 auth.setAccessSecret(config["access_secret"].as<std::string>());
23
24 client_ = std::unique_ptr<twitter::client>(new twitter::client(auth));
25
26 // Set up the verbly database.
27 database_ = std::unique_ptr<verbly::database>(
28 new verbly::database(config["verbly_datafile"].as<std::string>()));
29}
30
31void capital::run() const
32{
33 for (;;)
34 {
35 std::cout << "Generating tweet..." << std::endl;
36
37 try
38 {
39 // Find a noun to use as the pictured item.
40 std::cout << "Choosing pictured noun..." << std::endl;
41
42 verbly::word pictured = getPicturedNoun();
43
44 std::cout << "Noun: " << pictured.getBaseForm().getText() << std::endl;
45
46 // Choose a picture of that noun.
47 std::cout << "Finding an image..." << std::endl;
48
49 Magick::Image image = getImageForNoun(pictured);
50
51 // Generate the tweet text.
52 std::cout << "Generating text..." << std::endl;
53
54 std::string text = generateTweetText(pictured);
55
56 std::cout << "Tweet text: " << text << std::endl;
57
58 // Send the tweet.
59 std::cout << "Sending tweet..." << std::endl;
60
61 sendTweet(std::move(text), std::move(image));
62
63 std::cout << "Tweeted!" << std::endl;
64
65 // Wait.
66 std::this_thread::sleep_for(std::chrono::hours(1));
67 } catch (const could_not_get_image& ex)
68 {
69 std::cout << ex.what() << std::endl;
70 } catch (const Magick::ErrorImage& ex)
71 {
72 std::cout << "Image error: " << ex.what() << std::endl;
73 } catch (const Magick::ErrorCorruptImage& ex)
74 {
75 std::cout << "Corrupt image: " << ex.what() << std::endl;
76 } catch (const twitter::twitter_error& ex)
77 {
78 std::cout << "Twitter error: " << ex.what() << std::endl;
79
80 std::this_thread::sleep_for(std::chrono::hours(1));
81 }
82
83 std::cout << std::endl;
84 }
85}
86
87verbly::word capital::getPicturedNoun() const
88{
89 verbly::filter whitelist = (verbly::notion::wnid == 100021939); // Artifacts
90
91 verbly::filter blacklist =
92 (verbly::notion::wnid == 106883725) // swastika
93 || (verbly::notion::wnid == 104416901) // tetraskele
94 || (verbly::notion::wnid == 103575691) // instrument of execution
95 || (verbly::notion::wnid == 103829563) // noose
96 ;
97
98 verbly::query<verbly::word> pictureQuery = database_->words(
99 (verbly::notion::fullHypernyms %= whitelist)
100 && !(verbly::notion::fullHypernyms %= blacklist)
101 && (verbly::notion::partOfSpeech == verbly::part_of_speech::noun)
102 && (verbly::notion::numOfImages >= 1)
103 // Blacklist ethnic slurs
104 && !(verbly::word::usageDomains %= (verbly::notion::wnid == 106718862))
105 );
106
107 verbly::word pictured = pictureQuery.first();
108
109 return pictured;
110}
111
112Magick::Image capital::getImageForNoun(verbly::word pictured) const
113{
114 // Accept string from Google Chrome
115 std::string accept = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8";
116 curl::curl_header headers;
117 headers.add(accept);
118
119 int backoff = 0;
120
121 std::cout << "Getting URLs..." << std::endl;
122
123 std::string lstdata;
124 while (lstdata.empty())
125 {
126 std::ostringstream lstbuf;
127 curl::curl_ios<std::ostringstream> lstios(lstbuf);
128 curl::curl_easy lsthandle(lstios);
129 std::string lsturl = pictured.getNotion().getImageNetUrl();
130 lsthandle.add<CURLOPT_URL>(lsturl.c_str());
131
132 try
133 {
134 lsthandle.perform();
135 } catch (const curl::curl_easy_exception& e)
136 {
137 e.print_traceback();
138
139 backoff++;
140 std::cout << "Waiting for " << backoff << " seconds..." << std::endl;
141
142 std::this_thread::sleep_for(std::chrono::seconds(backoff));
143
144 continue;
145 }
146
147 backoff = 0;
148
149 if (lsthandle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200)
150 {
151 throw could_not_get_image();
152 }
153
154 std::cout << "Got URLs." << std::endl;
155 lstdata = lstbuf.str();
156 }
157
158 std::vector<std::string> lstvec =
159 verbly::split<std::vector<std::string>>(lstdata, "\r\n");
160 if (lstvec.empty())
161 {
162 throw could_not_get_image();
163 }
164
165 std::shuffle(std::begin(lstvec), std::end(lstvec), rng_);
166
167 std::deque<std::string> urls;
168 for (std::string& url : lstvec)
169 {
170 urls.push_back(url);
171 }
172
173 bool found = false;
174 Magick::Blob img;
175 Magick::Image pic;
176
177 while (!found && !urls.empty())
178 {
179 std::string url = urls.front();
180 urls.pop_front();
181
182 std::ostringstream imgbuf;
183 curl::curl_ios<std::ostringstream> imgios(imgbuf);
184 curl::curl_easy imghandle(imgios);
185
186 imghandle.add<CURLOPT_HTTPHEADER>(headers.get());
187 imghandle.add<CURLOPT_URL>(url.c_str());
188 imghandle.add<CURLOPT_CONNECTTIMEOUT>(30);
189
190 try
191 {
192 imghandle.perform();
193 } catch (curl::curl_easy_exception error) {
194 error.print_traceback();
195
196 continue;
197 }
198
199 if (imghandle.get_info<CURLINFO_RESPONSE_CODE>().get() != 200)
200 {
201 continue;
202 }
203
204 std::string content_type =
205 imghandle.get_info<CURLINFO_CONTENT_TYPE>().get();
206 if (content_type.substr(0, 6) != "image/")
207 {
208 continue;
209 }
210
211 std::string imgstr = imgbuf.str();
212 img = Magick::Blob(imgstr.c_str(), imgstr.length());
213
214 try
215 {
216 pic.read(img);
217
218 if (pic.rows() > 0)
219 {
220 std::cout << url << std::endl;
221 found = true;
222 }
223 } catch (const Magick::ErrorOption& e)
224 {
225 // Occurs when the the data downloaded from the server is malformed
226 std::cout << "Magick: " << e.what() << std::endl;
227 }
228 }
229
230 if (!found)
231 {
232 throw could_not_get_image();
233 }
234
235 return pic;
236}
237
238std::string capital::generateTweetText(verbly::word pictured) const
239{
240 int msd = std::uniform_int_distribution<int>(1, 9)(rng_);
241 int mag = std::uniform_int_distribution<int>(2, 9)(rng_);
242
243 std::string money;
244 for (int i=0; i<mag; i++)
245 {
246 money.insert(0, "0");
247
248 if ((i % 3 == 2) && (mag > 3))
249 {
250 money.insert(0, ",");
251 }
252 }
253
254 money.insert(0, std::to_string(msd));
255 money.insert(0, "$");
256
257 verbly::token nounTok = verbly::token::capitalize(
258 verbly::token::casing::title_case,
259 pictured);
260
261 int aci = std::uniform_int_distribution<int>(0, 7)(rng_);
262 verbly::token action;
263
264 switch (aci)
265 {
266 case 0: action = { "No One Will Buy This", money, nounTok }; break;
267 case 1: action = { "This", nounTok, "Is Not Worth", money }; break;
268 case 2: action = { "We Can't Get Rid Of This", money, nounTok }; break;
269 case 3: action = { "Millenials Will No Longer Spend", money, "For",
270 verbly::token::definiteArticle(nounTok) }; break;
271 case 4: action = { "Why Does This", money, nounTok, "Exist?" }; break;
272 case 5: action = { "Someone Spent", money, "Making This", nounTok,
273 "That No One Will Buy" }; break;
274 case 6: action = { "What A Waste: This", nounTok, "Will Rot Because",
275 "No One Can Afford Its", money, "Price Tag" }; break;
276 case 7: action = { "This", money, nounTok, "Was A Mistake" }; break;
277 }
278
279 return action.compile();
280}
281
282void capital::sendTweet(std::string text, Magick::Image image) const
283{
284 Magick::Blob outputBlob;
285 image.magick("jpg");
286 image.write(&outputBlob);
287
288 long media_id = client_->uploadMedia("image/jpeg",
289 (const char*) outputBlob.data(), outputBlob.length());
290
291 client_->updateStatus(std::move(text), {media_id});
292}