summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2018-03-25 17:45:10 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2018-03-25 17:45:10 -0400
commit2d8c8b0d8827159edcabd3a8665f45ef65cb3153 (patch)
tree8afc85bab2a65685ae31dcbbce4db63e84b526f6
parent99fe6c21ad757c41e4c082fadaa47c79a93c10c5 (diff)
downloadtoldya-2d8c8b0d8827159edcabd3a8665f45ef65cb3153.tar.gz
toldya-2d8c8b0d8827159edcabd3a8665f45ef65cb3153.tar.bz2
toldya-2d8c8b0d8827159edcabd3a8665f45ef65cb3153.zip
Bot now uniformly picks a tweet author, then a tweet
This changes the distribution so that people who tweet more often aren't more likely to be picked.

Also modernized the bot, added a gitignore, and made whitespace changes.
-rw-r--r--.gitignore6
-rw-r--r--toldya.cpp182
2 files changed, 125 insertions, 63 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..23fe4d0 --- /dev/null +++ b/.gitignore
@@ -0,0 +1,6 @@
1.DS_Store
2CMakeFiles
3CMakeCache.txt
4cmake_install.cmake
5Makefile
6*.swp
diff --git a/toldya.cpp b/toldya.cpp index d880c81..a5e7172 100644 --- a/toldya.cpp +++ b/toldya.cpp
@@ -6,6 +6,7 @@
6#include <chrono> 6#include <chrono>
7#include <iostream> 7#include <iostream>
8#include <algorithm> 8#include <algorithm>
9#include <random>
9 10
10int main(int argc, char** argv) 11int main(int argc, char** argv)
11{ 12{
@@ -15,53 +16,61 @@ int main(int argc, char** argv)
15 return -1; 16 return -1;
16 } 17 }
17 18
19 std::random_device randomDevice;
20 std::mt19937 rng(randomDevice());
21
18 std::string configfile(argv[1]); 22 std::string configfile(argv[1]);
19 YAML::Node config = YAML::LoadFile(configfile); 23 YAML::Node config = YAML::LoadFile(configfile);
20 24
21 twitter::auth auth; 25 twitter::auth auth;
22 auth.setConsumerKey(config["consumer_key"].as<std::string>()); 26 auth.setConsumerKey(config["consumer_key"].as<std::string>());
23 auth.setConsumerSecret(config["consumer_secret"].as<std::string>()); 27 auth.setConsumerSecret(config["consumer_secret"].as<std::string>());
24 auth.setAccessKey(config["access_key"].as<std::string>()); 28 auth.setAccessKey(config["access_key"].as<std::string>());
25 auth.setAccessSecret(config["access_secret"].as<std::string>()); 29 auth.setAccessSecret(config["access_secret"].as<std::string>());
26 30
27 std::vector<std::string> captions { 31 std::vector<std::string> captions {
28 "It begins.", 32 "It begins.",
29 "Yikes.", 33 "Yikes.",
30 "Frightening.", 34 "Frightening.",
31 "This is how it starts..." 35 "This is how it starts..."
32 }; 36 };
33 37
34 std::vector<twitter::tweet> potential; 38 std::map<twitter::user_id, std::vector<twitter::tweet>> potential;
35 std::set<twitter::tweet_id> deletions; 39 std::set<twitter::tweet_id> deletions;
36 std::mutex potential_mutex; 40 std::mutex potentialMutex;
37 41
38 twitter::client client(auth); 42 twitter::client client(auth);
39 std::set<twitter::user_id> streamed_friends; 43 std::set<twitter::user_id> streamedFriends;
40 44
41 std::cout << "Starting streaming" << std::endl; 45 std::cout << "Starting streaming" << std::endl;
42 46
43 twitter::stream user_stream(client, [&] (twitter::notification n) { 47 twitter::stream userStream(client, [&] (twitter::notification n) {
44 if (n.getType() == twitter::notification::type::friends) 48 if (n.getType() == twitter::notification::type::friends)
45 { 49 {
46 streamed_friends = n.getFriends(); 50 streamedFriends = n.getFriends();
47 } else if (n.getType() == twitter::notification::type::follow) 51 } else if (n.getType() == twitter::notification::type::follow)
48 { 52 {
49 streamed_friends.insert(n.getUser().getID()); 53 streamedFriends.insert(n.getUser().getID());
50 } else if (n.getType() == twitter::notification::type::unfollow) 54 } else if (n.getType() == twitter::notification::type::unfollow)
51 { 55 {
52 streamed_friends.erase(n.getUser().getID()); 56 streamedFriends.erase(n.getUser().getID());
53 } else if (n.getType() == twitter::notification::type::tweet) 57 } else if (n.getType() == twitter::notification::type::tweet)
54 { 58 {
59 // Only monitor people you are following
60 // Ignore retweets
61 // Ignore messages
55 if ( 62 if (
56 (streamed_friends.count(n.getTweet().getAuthor().getID()) == 1) // Only monitor people you are following 63 (streamedFriends.count(n.getTweet().getAuthor().getID()) == 1)
57 && (!n.getTweet().isRetweet()) // Ignore retweets 64 && (!n.getTweet().isRetweet())
58 && (n.getTweet().getText().front() != '@') // Ignore messages 65 && (n.getTweet().getText().front() != '@')
59 ) 66 )
60 { 67 {
61 std::lock_guard<std::mutex> potential_guard(potential_mutex); 68 std::lock_guard<std::mutex> potentialGuard(potentialMutex);
62 std::cout << n.getTweet().getID() << ": " << n.getTweet().getText() << std::endl; 69 std::cout << n.getTweet().getID() << ": " << n.getTweet().getText()
63 70 << std::endl;
64 potential.push_back(std::move(n.getTweet())); 71
72 potential[n.getTweet().getAuthor().getID()].
73 push_back(std::move(n.getTweet()));
65 } 74 }
66 } else if (n.getType() == twitter::notification::type::followed) 75 } else if (n.getType() == twitter::notification::type::followed)
67 { 76 {
@@ -70,19 +79,20 @@ int main(int argc, char** argv)
70 client.follow(n.getUser()); 79 client.follow(n.getUser());
71 } catch (const twitter::twitter_error& error) 80 } catch (const twitter::twitter_error& error)
72 { 81 {
73 std::cout << "Twitter error while following @" << n.getUser().getScreenName() << ": " << error.what() << std::endl; 82 std::cout << "Twitter error while following @"
83 << n.getUser().getScreenName() << ": " << error.what() << std::endl;
74 } 84 }
75 } else if (n.getType() == twitter::notification::type::deletion) 85 } else if (n.getType() == twitter::notification::type::deletion)
76 { 86 {
77 std::lock_guard<std::mutex> potential_guard(potential_mutex); 87 std::lock_guard<std::mutex> potentialGuard(potentialMutex);
78 std::cout << "Tweet " << n.getTweetID() << " was deleted." << std::endl; 88 std::cout << "Tweet " << n.getTweetID() << " was deleted." << std::endl;
79 89
80 deletions.insert(n.getTweetID()); 90 deletions.insert(n.getTweetID());
81 } 91 }
82 }); 92 });
83 93
84 std::this_thread::sleep_for(std::chrono::minutes(1)); 94 std::this_thread::sleep_for(std::chrono::minutes(1));
85 95
86 for (;;) 96 for (;;)
87 { 97 {
88 // Wait until 9am 98 // Wait until 9am
@@ -92,7 +102,10 @@ int main(int argc, char** argv)
92 midtm->tm_min = 0; 102 midtm->tm_min = 0;
93 midtm->tm_sec = 0; 103 midtm->tm_sec = 0;
94 auto to_until = std::chrono::system_clock::from_time_t(std::mktime(midtm)); 104 auto to_until = std::chrono::system_clock::from_time_t(std::mktime(midtm));
95 auto to_wait = std::chrono::duration_cast<std::chrono::seconds>((to_until + std::chrono::hours(24 + 9)) - std::chrono::system_clock::now()); 105 auto to_wait = std::chrono::duration_cast<std::chrono::seconds>(
106 (to_until + std::chrono::hours(24 + 9))
107 - std::chrono::system_clock::now());
108
96 int waitlen = to_wait.count(); 109 int waitlen = to_wait.count();
97 if (waitlen == 0) 110 if (waitlen == 0)
98 { 111 {
@@ -108,95 +121,138 @@ int main(int argc, char** argv)
108 std::cout << "Sleeping for 1 minute..." << std::endl; 121 std::cout << "Sleeping for 1 minute..." << std::endl;
109 } else if (waitlen < 60*60) 122 } else if (waitlen < 60*60)
110 { 123 {
111 std::cout << "Sleeping for " << (waitlen/60) << " minutes..." << std::endl; 124 std::cout << "Sleeping for " << (waitlen/60) << " minutes..."
125 << std::endl;
112 } else if (waitlen == 60*60) 126 } else if (waitlen == 60*60)
113 { 127 {
114 std::cout << "Sleeping for 1 hour..." << std::endl; 128 std::cout << "Sleeping for 1 hour..." << std::endl;
115 } else if (waitlen < 60*60*24) 129 } else if (waitlen < 60*60*24)
116 { 130 {
117 std::cout << "Sleeping for " << (waitlen/60/60) << " hours..." << std::endl; 131 std::cout << "Sleeping for " << (waitlen/60/60) << " hours..."
132 << std::endl;
118 } else if (waitlen == 60*60*24) 133 } else if (waitlen == 60*60*24)
119 { 134 {
120 std::cout << "Sleeping for 1 day..." << std::endl; 135 std::cout << "Sleeping for 1 day..." << std::endl;
121 } else { 136 } else {
122 std::cout << "Sleeping for " << (waitlen/60/60/24) << " days..." << std::endl; 137 std::cout << "Sleeping for " << (waitlen/60/60/24) << " days..."
138 << std::endl;
123 } 139 }
124 140
125 std::this_thread::sleep_for(to_wait); 141 std::this_thread::sleep_for(to_wait);
126 142
143 // The rest of the loop deals with the potential tweets
144 std::lock_guard<std::mutex> potentialGuard(potentialMutex);
145
127 // Unfollow people who have unfollowed us 146 // Unfollow people who have unfollowed us
128 try 147 try
129 { 148 {
130 std::set<twitter::user_id> friends = client.getFriends(); 149 std::set<twitter::user_id> friends = client.getFriends();
131 std::set<twitter::user_id> followers = client.getFollowers(); 150 std::set<twitter::user_id> followers = client.getFollowers();
132 151
133 std::list<twitter::user_id> old_friends; 152 std::list<twitter::user_id> oldFriends;
134 std::list<twitter::user_id> new_followers; 153 std::set_difference(
135 std::set_difference(std::begin(friends), std::end(friends), std::begin(followers), std::end(followers), std::back_inserter(old_friends)); 154 std::begin(friends),
136 std::set_difference(std::begin(followers), std::end(followers), std::begin(friends), std::end(friends), std::back_inserter(new_followers)); 155 std::end(friends),
137 156 std::begin(followers),
138 std::set<twitter::user_id> old_friends_set; 157 std::end(followers),
139 for (auto f : old_friends) 158 std::back_inserter(oldFriends));
159
160 std::list<twitter::user_id> newFollowers;
161 std::set_difference(
162 std::begin(followers),
163 std::end(followers),
164 std::begin(friends),
165 std::end(friends),
166 std::back_inserter(newFollowers));
167
168 std::set<twitter::user_id> oldFriendsSet;
169 for (twitter::user_id f : oldFriends)
140 { 170 {
141 old_friends_set.insert(f); 171 oldFriendsSet.insert(f);
142 172
143 try 173 try
144 { 174 {
145 client.unfollow(f); 175 client.unfollow(f);
146 } catch (const twitter::twitter_error& error) 176 } catch (const twitter::twitter_error& error)
147 { 177 {
148 std::cout << "Twitter error while unfollowing: " << error.what() << std::endl; 178 std::cout << "Twitter error while unfollowing: " << error.what()
179 << std::endl;
149 } 180 }
150 } 181 }
151 182
152 for (auto f : new_followers) 183 for (twitter::user_id f : newFollowers)
153 { 184 {
154 try 185 try
155 { 186 {
156 client.follow(f); 187 client.follow(f);
157 } catch (const twitter::twitter_error& error) 188 } catch (const twitter::twitter_error& error)
158 { 189 {
159 std::cout << "Twitter error while following: " << error.what() << std::endl; 190 std::cout << "Twitter error while following: " << error.what()
191 << std::endl;
160 } 192 }
161 } 193 }
162 194
163 std::lock_guard<std::mutex> potential_guard(potential_mutex); 195 // Filter the potential tweets for users that are still following us, and
164 std::vector<twitter::tweet> to_keep; 196 // and for tweets that haven't been deleted.
165 for (auto& pt : potential) 197 std::map<twitter::user_id, std::vector<twitter::tweet>> toKeep;
198
199 for (auto& p : potential)
166 { 200 {
167 if ( 201 // The author has not unfollowed
168 (old_friends_set.count(pt.getAuthor().getID()) == 0) && // The author has not unfollowed 202 if (!oldFriendsSet.count(p.first))
169 (deletions.count(pt.getID()) == 0)) // The tweet was not deleted
170 { 203 {
171 to_keep.push_back(std::move(pt)); 204 std::vector<twitter::tweet> userTweets;
205
206 for (twitter::tweet& pt : p.second)
207 {
208 // The tweet was not deleted
209 if (!deletions.count(pt.getID()))
210 {
211 userTweets.push_back(std::move(pt));
212 }
213 }
214
215 if (!userTweets.empty())
216 {
217 toKeep[p.first] = std::move(userTweets);
218 }
172 } 219 }
173 } 220 }
174 221
175 potential = std::move(to_keep); 222 potential = std::move(toKeep);
176 deletions.clear(); 223 deletions.clear();
177 } catch (const twitter::twitter_error& error) 224 } catch (const twitter::twitter_error& error)
178 { 225 {
179 std::cout << "Twitter error while getting friends/followers: " << error.what() << std::endl; 226 std::cout << "Twitter error while getting friends/followers: "
227 << error.what() << std::endl;
180 } 228 }
181 229
182 // Tweet! 230 // Tweet!
183 if (!potential.empty()) 231 if (!potential.empty())
184 { 232 {
185 auto to_quote = std::move(potential[rand() % potential.size()]); 233 std::uniform_int_distribution<size_t> userDist(0, potential.size() - 1);
186 potential.clear(); 234 const std::vector<twitter::tweet>& toQuoteUser =
187 235 std::next(std::begin(potential), userDist(rng))->second;
188 std::string caption = captions[rand() % captions.size()]; 236
189 std::string doc = caption + " " + to_quote.getURL(); 237 std::uniform_int_distribution<size_t> postDist(0, toQuoteUser.size() - 1);
190 238 const twitter::tweet& toQuote = toQuoteUser.at(postDist(rng));
239
240 std::uniform_int_distribution<size_t> captionDist(0, captions.size() - 1);
241 const std::string& caption = captions.at(captionDist(rng));
242
243 std::string doc = caption + " " + toQuote.getURL();
244
191 try 245 try
192 { 246 {
193 client.updateStatus(doc); 247 client.updateStatus(doc);
194 248
195 std::cout << "Tweeted!" << std::endl; 249 std::cout << "Tweeted!" << std::endl;
196 } catch (const twitter::twitter_error& error) 250 } catch (const twitter::twitter_error& error)
197 { 251 {
198 std::cout << "Error tweeting: " << error.what() << std::endl; 252 std::cout << "Error tweeting: " << error.what() << std::endl;
199 } 253 }
254
255 potential.clear();
200 } 256 }
201 } 257 }
202} 258}