diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-08-10 15:39:42 -0400 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-08-10 15:39:42 -0400 |
commit | 1c93bfe61dbdbec297e1a0f7f489024265cb5f6e (patch) | |
tree | 48f2d1de0cf50f2f8c83a8746d36863f877d801f /insult.cpp | |
parent | c9edc3747f1a11c1f7debe7feb4b407117241c64 (diff) | |
download | insult-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.cpp | 185 |
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 | ||
13 | const auto QUEUE_TIMEOUT = std::chrono::minutes(1); | ||
14 | const auto POLL_TIMEOUT = std::chrono::minutes(5); | ||
15 | const auto GEN_TIMEOUT = std::chrono::hours(1); | ||
16 | |||
13 | int main(int argc, char** argv) | 17 | int 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 | { |