From 2d8c8b0d8827159edcabd3a8665f45ef65cb3153 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sun, 25 Mar 2018 17:45:10 -0400 Subject: 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. --- .gitignore | 6 ++ toldya.cpp | 182 ++++++++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 125 insertions(+), 63 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..23fe4d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +CMakeFiles +CMakeCache.txt +cmake_install.cmake +Makefile +*.swp diff --git a/toldya.cpp b/toldya.cpp index d880c81..a5e7172 100644 --- a/toldya.cpp +++ b/toldya.cpp @@ -6,6 +6,7 @@ #include #include #include +#include int main(int argc, char** argv) { @@ -15,53 +16,61 @@ int main(int argc, char** argv) return -1; } + std::random_device randomDevice; + std::mt19937 rng(randomDevice()); + std::string configfile(argv[1]); YAML::Node config = YAML::LoadFile(configfile); - + twitter::auth auth; auth.setConsumerKey(config["consumer_key"].as()); auth.setConsumerSecret(config["consumer_secret"].as()); auth.setAccessKey(config["access_key"].as()); auth.setAccessSecret(config["access_secret"].as()); - + std::vector captions { "It begins.", "Yikes.", "Frightening.", "This is how it starts..." }; - - std::vector potential; + + std::map> potential; std::set deletions; - std::mutex potential_mutex; - + std::mutex potentialMutex; + twitter::client client(auth); - std::set streamed_friends; - + std::set streamedFriends; + std::cout << "Starting streaming" << std::endl; - - twitter::stream user_stream(client, [&] (twitter::notification n) { + + twitter::stream userStream(client, [&] (twitter::notification n) { if (n.getType() == twitter::notification::type::friends) { - streamed_friends = n.getFriends(); + streamedFriends = n.getFriends(); } else if (n.getType() == twitter::notification::type::follow) { - streamed_friends.insert(n.getUser().getID()); + streamedFriends.insert(n.getUser().getID()); } else if (n.getType() == twitter::notification::type::unfollow) { - streamed_friends.erase(n.getUser().getID()); + streamedFriends.erase(n.getUser().getID()); } else if (n.getType() == twitter::notification::type::tweet) { + // Only monitor people you are following + // Ignore retweets + // Ignore messages if ( - (streamed_friends.count(n.getTweet().getAuthor().getID()) == 1) // Only monitor people you are following - && (!n.getTweet().isRetweet()) // Ignore retweets - && (n.getTweet().getText().front() != '@') // Ignore messages + (streamedFriends.count(n.getTweet().getAuthor().getID()) == 1) + && (!n.getTweet().isRetweet()) + && (n.getTweet().getText().front() != '@') ) { - std::lock_guard potential_guard(potential_mutex); - std::cout << n.getTweet().getID() << ": " << n.getTweet().getText() << std::endl; - - potential.push_back(std::move(n.getTweet())); + std::lock_guard potentialGuard(potentialMutex); + std::cout << n.getTweet().getID() << ": " << n.getTweet().getText() + << std::endl; + + potential[n.getTweet().getAuthor().getID()]. + push_back(std::move(n.getTweet())); } } else if (n.getType() == twitter::notification::type::followed) { @@ -70,19 +79,20 @@ int main(int argc, char** argv) client.follow(n.getUser()); } catch (const twitter::twitter_error& error) { - std::cout << "Twitter error while following @" << n.getUser().getScreenName() << ": " << error.what() << std::endl; + std::cout << "Twitter error while following @" + << n.getUser().getScreenName() << ": " << error.what() << std::endl; } } else if (n.getType() == twitter::notification::type::deletion) { - std::lock_guard potential_guard(potential_mutex); + std::lock_guard potentialGuard(potentialMutex); std::cout << "Tweet " << n.getTweetID() << " was deleted." << std::endl; - + deletions.insert(n.getTweetID()); } }); - + std::this_thread::sleep_for(std::chrono::minutes(1)); - + for (;;) { // Wait until 9am @@ -92,7 +102,10 @@ int main(int argc, char** argv) midtm->tm_min = 0; midtm->tm_sec = 0; auto to_until = std::chrono::system_clock::from_time_t(std::mktime(midtm)); - auto to_wait = std::chrono::duration_cast((to_until + std::chrono::hours(24 + 9)) - std::chrono::system_clock::now()); + auto to_wait = std::chrono::duration_cast( + (to_until + std::chrono::hours(24 + 9)) + - std::chrono::system_clock::now()); + int waitlen = to_wait.count(); if (waitlen == 0) { @@ -108,95 +121,138 @@ int main(int argc, char** argv) std::cout << "Sleeping for 1 minute..." << std::endl; } else if (waitlen < 60*60) { - std::cout << "Sleeping for " << (waitlen/60) << " minutes..." << std::endl; + std::cout << "Sleeping for " << (waitlen/60) << " minutes..." + << std::endl; } else if (waitlen == 60*60) { std::cout << "Sleeping for 1 hour..." << std::endl; } else if (waitlen < 60*60*24) { - std::cout << "Sleeping for " << (waitlen/60/60) << " hours..." << std::endl; + std::cout << "Sleeping for " << (waitlen/60/60) << " hours..." + << std::endl; } else if (waitlen == 60*60*24) { std::cout << "Sleeping for 1 day..." << std::endl; } else { - std::cout << "Sleeping for " << (waitlen/60/60/24) << " days..." << std::endl; + std::cout << "Sleeping for " << (waitlen/60/60/24) << " days..." + << std::endl; } - + std::this_thread::sleep_for(to_wait); - + + // The rest of the loop deals with the potential tweets + std::lock_guard potentialGuard(potentialMutex); + // Unfollow people who have unfollowed us try { std::set friends = client.getFriends(); std::set followers = client.getFollowers(); - std::list old_friends; - std::list new_followers; - std::set_difference(std::begin(friends), std::end(friends), std::begin(followers), std::end(followers), std::back_inserter(old_friends)); - std::set_difference(std::begin(followers), std::end(followers), std::begin(friends), std::end(friends), std::back_inserter(new_followers)); - - std::set old_friends_set; - for (auto f : old_friends) + std::list oldFriends; + std::set_difference( + std::begin(friends), + std::end(friends), + std::begin(followers), + std::end(followers), + std::back_inserter(oldFriends)); + + std::list newFollowers; + std::set_difference( + std::begin(followers), + std::end(followers), + std::begin(friends), + std::end(friends), + std::back_inserter(newFollowers)); + + std::set oldFriendsSet; + for (twitter::user_id f : oldFriends) { - old_friends_set.insert(f); - + oldFriendsSet.insert(f); + try { client.unfollow(f); } catch (const twitter::twitter_error& error) { - std::cout << "Twitter error while unfollowing: " << error.what() << std::endl; + std::cout << "Twitter error while unfollowing: " << error.what() + << std::endl; } } - - for (auto f : new_followers) + + for (twitter::user_id f : newFollowers) { try { client.follow(f); } catch (const twitter::twitter_error& error) { - std::cout << "Twitter error while following: " << error.what() << std::endl; + std::cout << "Twitter error while following: " << error.what() + << std::endl; } } - - std::lock_guard potential_guard(potential_mutex); - std::vector to_keep; - for (auto& pt : potential) + + // Filter the potential tweets for users that are still following us, and + // and for tweets that haven't been deleted. + std::map> toKeep; + + for (auto& p : potential) { - if ( - (old_friends_set.count(pt.getAuthor().getID()) == 0) && // The author has not unfollowed - (deletions.count(pt.getID()) == 0)) // The tweet was not deleted + // The author has not unfollowed + if (!oldFriendsSet.count(p.first)) { - to_keep.push_back(std::move(pt)); + std::vector userTweets; + + for (twitter::tweet& pt : p.second) + { + // The tweet was not deleted + if (!deletions.count(pt.getID())) + { + userTweets.push_back(std::move(pt)); + } + } + + if (!userTweets.empty()) + { + toKeep[p.first] = std::move(userTweets); + } } } - - potential = std::move(to_keep); + + potential = std::move(toKeep); deletions.clear(); } catch (const twitter::twitter_error& error) { - std::cout << "Twitter error while getting friends/followers: " << error.what() << std::endl; + std::cout << "Twitter error while getting friends/followers: " + << error.what() << std::endl; } - + // Tweet! if (!potential.empty()) { - auto to_quote = std::move(potential[rand() % potential.size()]); - potential.clear(); - - std::string caption = captions[rand() % captions.size()]; - std::string doc = caption + " " + to_quote.getURL(); - + std::uniform_int_distribution userDist(0, potential.size() - 1); + const std::vector& toQuoteUser = + std::next(std::begin(potential), userDist(rng))->second; + + std::uniform_int_distribution postDist(0, toQuoteUser.size() - 1); + const twitter::tweet& toQuote = toQuoteUser.at(postDist(rng)); + + std::uniform_int_distribution captionDist(0, captions.size() - 1); + const std::string& caption = captions.at(captionDist(rng)); + + std::string doc = caption + " " + toQuote.getURL(); + try { client.updateStatus(doc); - + std::cout << "Tweeted!" << std::endl; } catch (const twitter::twitter_error& error) { std::cout << "Error tweeting: " << error.what() << std::endl; } + + potential.clear(); } } } -- cgit 1.4.1