about summary refs log tree commit diff stats
path: root/ebooks.cpp
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2018-08-06 22:51:15 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2018-08-06 22:51:15 -0400
commit26d75f744913a8856e46f5fccbfda8f8336924a0 (patch)
treec890aef9bb7033e8955abd069d92375ee97381c2 /ebooks.cpp
parent0801fae5aa3b83380da403c86ba80a7de657a07a (diff)
downloadrawr-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.
Diffstat (limited to 'ebooks.cpp')
-rw-r--r--ebooks.cpp170
1 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
15const auto QUEUE_TIMEOUT = std::chrono::minutes(1);
16const auto POLL_TIMEOUT = std::chrono::minutes(5);
17
15int main(int argc, char** args) 18int 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}