summary refs log tree commit diff stats
path: root/insult.cpp
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2018-08-10 15:39:42 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2018-08-10 15:39:42 -0400
commit1c93bfe61dbdbec297e1a0f7f489024265cb5f6e (patch)
tree48f2d1de0cf50f2f8c83a8746d36863f877d801f /insult.cpp
parentc9edc3747f1a11c1f7debe7feb4b407117241c64 (diff)
downloadinsult-1c93bfe61dbdbec297e1a0f7f489024265cb5f6e.tar.gz
insult-1c93bfe61dbdbec297e1a0f7f489024265cb5f6e.tar.bz2
insult-1c93bfe61dbdbec297e1a0f7f489024265cb5f6e.zip
Switched from streaming API to timeline polling
Diffstat (limited to 'insult.cpp')
-rw-r--r--insult.cpp185
1 files changed, 137 insertions, 48 deletions
diff --git a/insult.cpp b/insult.cpp index db4f920..53231c5 100644 --- a/insult.cpp +++ b/insult.cpp
@@ -10,6 +10,10 @@
10#include <algorithm> 10#include <algorithm>
11#include "patterner.h" 11#include "patterner.h"
12 12
13const auto QUEUE_TIMEOUT = std::chrono::minutes(1);
14const auto POLL_TIMEOUT = std::chrono::minutes(5);
15const auto GEN_TIMEOUT = std::chrono::hours(1);
16
13int main(int argc, char** argv) 17int main(int argc, char** argv)
14{ 18{
15 if (argc != 2) 19 if (argc != 2)
@@ -21,11 +25,11 @@ int main(int argc, char** argv)
21 std::string configfile(argv[1]); 25 std::string configfile(argv[1]);
22 YAML::Node config = YAML::LoadFile(configfile); 26 YAML::Node config = YAML::LoadFile(configfile);
23 27
24 twitter::auth auth; 28 twitter::auth auth(
25 auth.setConsumerKey(config["consumer_key"].as<std::string>()); 29 config["consumer_key"].as<std::string>(),
26 auth.setConsumerSecret(config["consumer_secret"].as<std::string>()); 30 config["consumer_secret"].as<std::string>(),
27 auth.setAccessKey(config["access_key"].as<std::string>()); 31 config["access_key"].as<std::string>(),
28 auth.setAccessSecret(config["access_secret"].as<std::string>()); 32 config["access_secret"].as<std::string>());
29 33
30 twitter::client client(auth); 34 twitter::client client(auth);
31 35
@@ -39,70 +43,155 @@ int main(int argc, char** argv)
39 verbly::database database(config["verbly_datafile"].as<std::string>()); 43 verbly::database database(config["verbly_datafile"].as<std::string>());
40 patterner pgen(config["forms_file"].as<std::string>(), database, rng); 44 patterner pgen(config["forms_file"].as<std::string>(), database, rng);
41 45
42 std::cout << "Starting streaming..." << std::endl; 46 std::list<std::tuple<std::string, bool, twitter::tweet_id>> postQueue;
43 47
44 twitter::stream userStream(client, [&pgen, &client, &blocks] 48 auto startedTime = std::chrono::system_clock::now();
45 (const twitter::notification& n) {
46 if (n.getType() == twitter::notification::type::tweet)
47 {
48 if ((!n.getTweet().isRetweet())
49 && (n.getTweet().getAuthor() != client.getUser())
50 && (!blocks.count(n.getTweet().getAuthor().getID())))
51 {
52 std::string original = n.getTweet().getText();
53 std::string canonical;
54 49
55 std::transform(std::begin(original), std::end(original), 50 auto queueTimer = std::chrono::system_clock::now();
56 std::back_inserter(canonical), [] (char ch) 51 auto pollTimer = std::chrono::system_clock::now();
57 { 52 auto genTimer = std::chrono::system_clock::now();
58 return std::tolower(ch);
59 });
60 53
61 if (canonical.find("@teammeanies") != std::string::npos) 54 for (;;)
62 { 55 {
63 std::string doc = 56 auto currentTime = std::chrono::system_clock::now();
64 n.getTweet().generateReplyPrefill(client.getUser());
65 57
66 doc += pgen.generate(); 58 if (currentTime >= genTimer)
67 doc.resize(140); 59 {
60 std::string doc = pgen.generate();
61 doc.resize(140);
68 62
69 try 63 postQueue.emplace_back(std::move(doc), false, 0);
70 { 64
71 client.replyToTweet(doc, n.getTweet()); 65 genTimer = currentTime + GEN_TIMEOUT;
72 } catch (const twitter::twitter_error& error) 66 }
67
68 if (currentTime >= pollTimer)
69 {
70 pollTimer = currentTime;
71
72 try
73 {
74 std::list<twitter::tweet> newTweets =
75 client.getMentionsTimeline().poll();
76
77 for (const twitter::tweet& tweet : newTweets)
78 {
79 auto createdTime =
80 std::chrono::system_clock::from_time_t(tweet.getCreatedAt());
81
82 if (
83 // Ignore tweets from before the bot started up
84 createdTime > startedTime
85 // Ignore retweets
86 && !tweet.isRetweet()
87 // Ignore tweets from yourself
88 && tweet.getAuthor() != client.getUser()
89 // Ignore tweets from blocked users
90 && !blocks.count(tweet.getAuthor().getID()))
91 {
92 std::string original = tweet.getText();
93 std::string canonical;
94
95 std::transform(
96 std::begin(original),
97 std::end(original),
98 std::back_inserter(canonical),
99 [] (char ch)
100 {
101 return std::tolower(ch);
102 });
103
104 if (canonical.find("@teammeanies") != std::string::npos)
73 { 105 {
74 std::cout << "Twitter error while tweeting: " 106 std::string doc = tweet.generateReplyPrefill(client.getUser());
75 << error.what() << std::endl; 107 doc += pgen.generate();
108 doc.resize(140);
109
110 postQueue.emplace_back(std::move(doc), true, tweet.getID());
76 } 111 }
77 } 112 }
78 } 113 }
114 } catch (const twitter::rate_limit_exceeded&)
115 {
116 // Wait out the rate limit (10 minutes here and 5 below = 15).
117 pollTimer += std::chrono::minutes(10);
118 } catch (const twitter::twitter_error& e)
119 {
120 std::cout << "Twitter error while polling: " << e.what() << std::endl;
79 } 121 }
80 });
81 122
82 std::this_thread::sleep_for(std::chrono::minutes(1)); 123 pollTimer += std::chrono::minutes(POLL_TIMEOUT);
124 }
83 125
84 for (;;) 126 if ((currentTime >= queueTimer) && (!postQueue.empty()))
85 { 127 {
86 std::cout << "Generating tweet..." << std::endl; 128 auto post = postQueue.front();
129 postQueue.pop_front();
87 130
88 std::string action = pgen.generate(); 131 std::cout << std::get<0>(post) << std::endl;
89 action.resize(140); 132
133 try
134 {
135 if (std::get<1>(post))
136 {
137 client.replyToTweet(std::get<0>(post), std::get<2>(post));
138 } else {
139 client.updateStatus(std::get<0>(post));
140 }
141 } catch (const twitter::twitter_error& error)
142 {
143 std::cout << "Twitter error while tweeting: " << error.what()
144 << std::endl;
145 }
146
147 queueTimer = currentTime + std::chrono::minutes(QUEUE_TIMEOUT);
148 }
90 149
91 std::cout << action << std::endl; 150 auto soonestTimer = genTimer;
92 151
93 try 152 if (pollTimer < soonestTimer)
94 { 153 {
95 client.updateStatus(action); 154 soonestTimer = pollTimer;
155 }
96 156
97 std::cout << "Tweeted!" << std::endl; 157 if ((queueTimer < soonestTimer) && (!postQueue.empty()))
98 } catch (const twitter::twitter_error& e)
99 { 158 {
100 std::cout << "Twitter error: " << e.what() << std::endl; 159 soonestTimer = queueTimer;
101 } 160 }
102 161
103 std::cout << "Waiting..." << std::endl; 162 int waitlen =
163 std::chrono::duration_cast<std::chrono::seconds>(
164 soonestTimer - currentTime).count();
165
166 if (waitlen == 1)
167 {
168 std::cout << "Sleeping for 1 second..." << std::endl;
169 } else if (waitlen < 60)
170 {
171 std::cout << "Sleeping for " << waitlen << " seconds..." << std::endl;
172 } else if (waitlen == 60)
173 {
174 std::cout << "Sleeping for 1 minute..." << std::endl;
175 } else if (waitlen < 60*60)
176 {
177 std::cout << "Sleeping for " << (waitlen/60) << " minutes..."
178 << std::endl;
179 } else if (waitlen == 60*60)
180 {
181 std::cout << "Sleeping for 1 hour..." << std::endl;
182 } else if (waitlen < 60*60*24)
183 {
184 std::cout << "Sleeping for " << (waitlen/60/60) << " hours..."
185 << std::endl;
186 } else if (waitlen == 60*60*24)
187 {
188 std::cout << "Sleeping for 1 day..." << std::endl;
189 } else {
190 std::cout << "Sleeping for " << (waitlen/60/60/24) << " days..."
191 << std::endl;
192 }
104 193
105 std::this_thread::sleep_for(std::chrono::hours(1)); 194 std::this_thread::sleep_until(soonestTimer);
106 } 195 }
107 } catch (std::invalid_argument& e) 196 } catch (std::invalid_argument& e)
108 { 197 {