diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-08-06 22:51:15 -0400 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-08-06 22:51:15 -0400 |
commit | 26d75f744913a8856e46f5fccbfda8f8336924a0 (patch) | |
tree | c890aef9bb7033e8955abd069d92375ee97381c2 | |
parent | 0801fae5aa3b83380da403c86ba80a7de657a07a (diff) | |
download | rawr-ebooks-26d75f744913a8856e46f5fccbfda8f8336924a0.tar.gz rawr-ebooks-26d75f744913a8856e46f5fccbfda8f8336924a0.tar.bz2 rawr-ebooks-26d75f744913a8856e46f5fccbfda8f8336924a0.zip |
Switched from streaming API to timeline polling
This means the bot is also now single-threaded.
-rw-r--r-- | ebooks.cpp | 170 | ||||
m--------- | vendor/libtwittercpp | 0 |
2 files changed, 115 insertions, 55 deletions
diff --git a/ebooks.cpp b/ebooks.cpp index 8582919..0644132 100644 --- a/ebooks.cpp +++ b/ebooks.cpp | |||
@@ -12,20 +12,23 @@ | |||
12 | #include <chrono> | 12 | #include <chrono> |
13 | #include <algorithm> | 13 | #include <algorithm> |
14 | 14 | ||
15 | const auto QUEUE_TIMEOUT = std::chrono::minutes(1); | ||
16 | const auto POLL_TIMEOUT = std::chrono::minutes(5); | ||
17 | |||
15 | int main(int argc, char** args) | 18 | int main(int argc, char** args) |
16 | { | 19 | { |
17 | srand(time(NULL)); | 20 | srand(time(NULL)); |
18 | rand(); rand(); rand(); rand(); | 21 | rand(); rand(); rand(); rand(); |
19 | 22 | ||
20 | YAML::Node config = YAML::LoadFile("config.yml"); | 23 | YAML::Node config = YAML::LoadFile("config.yml"); |
21 | int delay = config["delay"].as<int>(); | 24 | int delay = config["delay"].as<int>(); |
22 | 25 | ||
23 | twitter::auth auth; | 26 | twitter::auth auth( |
24 | auth.setConsumerKey(config["consumer_key"].as<std::string>()); | 27 | config["consumer_key"].as<std::string>(), |
25 | auth.setConsumerSecret(config["consumer_secret"].as<std::string>()); | 28 | config["consumer_secret"].as<std::string>(), |
26 | auth.setAccessKey(config["access_key"].as<std::string>()); | 29 | config["access_key"].as<std::string>(), |
27 | auth.setAccessSecret(config["access_secret"].as<std::string>()); | 30 | config["access_secret"].as<std::string>()); |
28 | 31 | ||
29 | twitter::client client(auth); | 32 | twitter::client client(auth); |
30 | 33 | ||
31 | std::ifstream infile(config["corpus"].as<std::string>().c_str()); | 34 | std::ifstream infile(config["corpus"].as<std::string>().c_str()); |
@@ -37,10 +40,10 @@ int main(int argc, char** args) | |||
37 | { | 40 | { |
38 | line.pop_back(); | 41 | line.pop_back(); |
39 | } | 42 | } |
40 | 43 | ||
41 | corpus += line + "\n"; | 44 | corpus += line + "\n"; |
42 | } | 45 | } |
43 | 46 | ||
44 | // Replace old-style freevars while I can't be bothered to remake the corpus yet | 47 | // Replace old-style freevars while I can't be bothered to remake the corpus yet |
45 | std::vector<std::string> fv_names; | 48 | std::vector<std::string> fv_names; |
46 | std::ifstream namefile("names.txt"); | 49 | std::ifstream namefile("names.txt"); |
@@ -54,11 +57,11 @@ int main(int argc, char** args) | |||
54 | { | 57 | { |
55 | l.pop_back(); | 58 | l.pop_back(); |
56 | } | 59 | } |
57 | 60 | ||
58 | fv_names.push_back(l); | 61 | fv_names.push_back(l); |
59 | } | 62 | } |
60 | } | 63 | } |
61 | 64 | ||
62 | namefile.close(); | 65 | namefile.close(); |
63 | 66 | ||
64 | std::cout << "Preprocessing corpus..." << std::endl; | 67 | std::cout << "Preprocessing corpus..." << std::endl; |
@@ -71,60 +74,114 @@ int main(int argc, char** args) | |||
71 | { | 74 | { |
72 | form.replace(pos, 6, fv_names[rand() % fv_names.size()]); | 75 | form.replace(pos, 6, fv_names[rand() % fv_names.size()]); |
73 | } | 76 | } |
74 | 77 | ||
75 | return form; | 78 | return form; |
76 | }); | 79 | }); |
77 | 80 | ||
78 | twitter::stream user_stream(client, [&kgramstats, &client] (const twitter::notification& n) { | 81 | std::list<std::tuple<std::string, bool, twitter::tweet_id>> postQueue; |
79 | if (n.getType() == twitter::notification::type::tweet) | 82 | |
83 | auto startedTime = std::chrono::system_clock::now(); | ||
84 | |||
85 | auto queueTimer = std::chrono::system_clock::now(); | ||
86 | auto pollTimer = std::chrono::system_clock::now(); | ||
87 | auto genTimer = std::chrono::system_clock::now(); | ||
88 | |||
89 | for (;;) | ||
90 | { | ||
91 | auto currentTime = std::chrono::system_clock::now(); | ||
92 | |||
93 | if (currentTime >= genTimer) | ||
94 | { | ||
95 | std::string doc = kgramstats.randomSentence(140); | ||
96 | doc.resize(140); | ||
97 | |||
98 | postQueue.emplace_back(std::move(doc), false, 0); | ||
99 | |||
100 | int genwait = rand() % delay + 1; | ||
101 | |||
102 | genTimer = currentTime + std::chrono::seconds(genwait); | ||
103 | } | ||
104 | |||
105 | if (currentTime >= pollTimer) | ||
80 | { | 106 | { |
81 | if ((!n.getTweet().isRetweet()) && (n.getTweet().getAuthor() != client.getUser())) | 107 | pollTimer = currentTime; |
108 | |||
109 | try | ||
82 | { | 110 | { |
83 | std::string original = n.getTweet().getText(); | 111 | std::list<twitter::tweet> newTweets = |
84 | std::string canonical; | 112 | client.getMentionsTimeline().poll(); |
85 | std::transform(std::begin(original), std::end(original), std::back_inserter(canonical), [] (char ch) { | 113 | |
86 | return std::tolower(ch); | 114 | for (const twitter::tweet& tweet : newTweets) |
87 | }); | ||
88 | |||
89 | if (canonical.find("@rawr_ebooks") != std::string::npos) | ||
90 | { | 115 | { |
91 | std::string doc = n.getTweet().generateReplyPrefill(client.getUser()); | 116 | auto createdTime = |
92 | doc += kgramstats.randomSentence(140 - doc.length()); | 117 | std::chrono::system_clock::from_time_t(tweet.getCreatedAt()); |
93 | doc.resize(140); | ||
94 | 118 | ||
95 | try | 119 | if ( |
120 | // Ignore tweets from before the bot started up | ||
121 | createdTime > startedTime | ||
122 | // Ignore retweets | ||
123 | && !tweet.isRetweet() | ||
124 | // Ignore tweets from yourself | ||
125 | && tweet.getAuthor() != client.getUser()) | ||
96 | { | 126 | { |
97 | client.replyToTweet(doc, n.getTweet()); | 127 | std::string doc = tweet.generateReplyPrefill(client.getUser()); |
98 | } catch (const twitter::twitter_error& error) | 128 | doc += kgramstats.randomSentence(140 - doc.length()); |
99 | { | 129 | doc.resize(140); |
100 | std::cout << "Twitter error while tweeting: " << error.what() << std::endl; | 130 | |
131 | postQueue.emplace_back(std::move(doc), true, tweet.getID()); | ||
101 | } | 132 | } |
102 | } | 133 | } |
134 | } catch (const twitter::rate_limit_exceeded&) | ||
135 | { | ||
136 | // Wait out the rate limit (10 minutes here and 5 below = 15). | ||
137 | pollTimer += std::chrono::minutes(10); | ||
138 | } catch (const twitter::twitter_error& e) | ||
139 | { | ||
140 | std::cout << "Twitter error while polling: " << e.what() << std::endl; | ||
103 | } | 141 | } |
142 | |||
143 | pollTimer += std::chrono::minutes(POLL_TIMEOUT); | ||
104 | } | 144 | } |
105 | }); | ||
106 | |||
107 | std::this_thread::sleep_for(std::chrono::minutes(1)); | ||
108 | 145 | ||
109 | std::cout << "Generating..." << std::endl; | 146 | if ((currentTime >= queueTimer) && (!postQueue.empty())) |
110 | for (;;) | ||
111 | { | ||
112 | std::string doc = kgramstats.randomSentence(140); | ||
113 | doc.resize(140); | ||
114 | |||
115 | try | ||
116 | { | 147 | { |
117 | client.updateStatus(doc); | 148 | auto post = postQueue.front(); |
118 | } catch (const twitter::twitter_error& error) | 149 | postQueue.pop_front(); |
150 | |||
151 | try | ||
152 | { | ||
153 | if (std::get<1>(post)) | ||
154 | { | ||
155 | client.replyToTweet(std::get<0>(post), std::get<2>(post)); | ||
156 | } else { | ||
157 | client.updateStatus(std::get<0>(post)); | ||
158 | } | ||
159 | } catch (const twitter::twitter_error& error) | ||
160 | { | ||
161 | std::cout << "Twitter error while tweeting: " << error.what() | ||
162 | << std::endl; | ||
163 | } | ||
164 | |||
165 | queueTimer = currentTime + std::chrono::minutes(QUEUE_TIMEOUT); | ||
166 | } | ||
167 | |||
168 | auto soonestTimer = genTimer; | ||
169 | |||
170 | if (pollTimer < soonestTimer) | ||
119 | { | 171 | { |
120 | std::cout << "Twitter error while tweeting: " << error.what() << std::endl; | 172 | soonestTimer = pollTimer; |
121 | } | 173 | } |
122 | 174 | ||
123 | int waitlen = rand() % delay; | 175 | if ((queueTimer < soonestTimer) && (!postQueue.empty())) |
124 | if (waitlen == 0) | ||
125 | { | 176 | { |
126 | continue; | 177 | soonestTimer = queueTimer; |
127 | } else if (waitlen == 1) | 178 | } |
179 | |||
180 | int waitlen = | ||
181 | std::chrono::duration_cast<std::chrono::seconds>( | ||
182 | soonestTimer - currentTime).count(); | ||
183 | |||
184 | if (waitlen == 1) | ||
128 | { | 185 | { |
129 | std::cout << "Sleeping for 1 second..." << std::endl; | 186 | std::cout << "Sleeping for 1 second..." << std::endl; |
130 | } else if (waitlen < 60) | 187 | } else if (waitlen < 60) |
@@ -135,22 +192,25 @@ int main(int argc, char** args) | |||
135 | std::cout << "Sleeping for 1 minute..." << std::endl; | 192 | std::cout << "Sleeping for 1 minute..." << std::endl; |
136 | } else if (waitlen < 60*60) | 193 | } else if (waitlen < 60*60) |
137 | { | 194 | { |
138 | std::cout << "Sleeping for " << (waitlen/60) << " minutes..." << std::endl; | 195 | std::cout << "Sleeping for " << (waitlen/60) << " minutes..." |
196 | << std::endl; | ||
139 | } else if (waitlen == 60*60) | 197 | } else if (waitlen == 60*60) |
140 | { | 198 | { |
141 | std::cout << "Sleeping for 1 hour..." << std::endl; | 199 | std::cout << "Sleeping for 1 hour..." << std::endl; |
142 | } else if (waitlen < 60*60*24) | 200 | } else if (waitlen < 60*60*24) |
143 | { | 201 | { |
144 | std::cout << "Sleeping for " << (waitlen/60/60) << " hours..." << std::endl; | 202 | std::cout << "Sleeping for " << (waitlen/60/60) << " hours..." |
203 | << std::endl; | ||
145 | } else if (waitlen == 60*60*24) | 204 | } else if (waitlen == 60*60*24) |
146 | { | 205 | { |
147 | std::cout << "Sleeping for 1 day..." << std::endl; | 206 | std::cout << "Sleeping for 1 day..." << std::endl; |
148 | } else { | 207 | } else { |
149 | std::cout << "Sleeping for " << (waitlen/60/60/24) << " days..." << std::endl; | 208 | std::cout << "Sleeping for " << (waitlen/60/60/24) << " days..." |
209 | << std::endl; | ||
150 | } | 210 | } |
151 | 211 | ||
152 | std::this_thread::sleep_for(std::chrono::seconds(waitlen)); | 212 | std::this_thread::sleep_until(soonestTimer); |
153 | } | 213 | } |
154 | 214 | ||
155 | return 0; | 215 | return 0; |
156 | } | 216 | } |
diff --git a/vendor/libtwittercpp b/vendor/libtwittercpp | |||
Subproject d1d20515281e32b23657762e72bf37dcff14f0a | Subproject 4963c3dd55b765a33a16a77af432f2bfa12b835 | ||