From 26d75f744913a8856e46f5fccbfda8f8336924a0 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Mon, 6 Aug 2018 22:51:15 -0400 Subject: Switched from streaming API to timeline polling This means the bot is also now single-threaded. --- ebooks.cpp | 170 +++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 115 insertions(+), 55 deletions(-) (limited to 'ebooks.cpp') diff --git a/ebooks.cpp b/ebooks.cpp index 8582919..0644132 100644 --- a/ebooks.cpp +++ b/ebooks.cpp @@ -12,20 +12,23 @@ #include #include +const auto QUEUE_TIMEOUT = std::chrono::minutes(1); +const auto POLL_TIMEOUT = std::chrono::minutes(5); + int main(int argc, char** args) { srand(time(NULL)); rand(); rand(); rand(); rand(); - + YAML::Node config = YAML::LoadFile("config.yml"); int delay = config["delay"].as(); - - 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()); - + + twitter::auth auth( + config["consumer_key"].as(), + config["consumer_secret"].as(), + config["access_key"].as(), + config["access_secret"].as()); + twitter::client client(auth); std::ifstream infile(config["corpus"].as().c_str()); @@ -37,10 +40,10 @@ int main(int argc, char** args) { line.pop_back(); } - + corpus += line + "\n"; } - + // Replace old-style freevars while I can't be bothered to remake the corpus yet std::vector fv_names; std::ifstream namefile("names.txt"); @@ -54,11 +57,11 @@ int main(int argc, char** args) { l.pop_back(); } - + fv_names.push_back(l); } } - + namefile.close(); std::cout << "Preprocessing corpus..." << std::endl; @@ -71,60 +74,114 @@ int main(int argc, char** args) { form.replace(pos, 6, fv_names[rand() % fv_names.size()]); } - + return form; }); - - twitter::stream user_stream(client, [&kgramstats, &client] (const twitter::notification& n) { - if (n.getType() == twitter::notification::type::tweet) + + std::list> postQueue; + + auto startedTime = std::chrono::system_clock::now(); + + auto queueTimer = std::chrono::system_clock::now(); + auto pollTimer = std::chrono::system_clock::now(); + auto genTimer = std::chrono::system_clock::now(); + + for (;;) + { + auto currentTime = std::chrono::system_clock::now(); + + if (currentTime >= genTimer) + { + std::string doc = kgramstats.randomSentence(140); + doc.resize(140); + + postQueue.emplace_back(std::move(doc), false, 0); + + int genwait = rand() % delay + 1; + + genTimer = currentTime + std::chrono::seconds(genwait); + } + + if (currentTime >= pollTimer) { - if ((!n.getTweet().isRetweet()) && (n.getTweet().getAuthor() != client.getUser())) + pollTimer = currentTime; + + try { - std::string original = n.getTweet().getText(); - std::string canonical; - std::transform(std::begin(original), std::end(original), std::back_inserter(canonical), [] (char ch) { - return std::tolower(ch); - }); - - if (canonical.find("@rawr_ebooks") != std::string::npos) + std::list newTweets = + client.getMentionsTimeline().poll(); + + for (const twitter::tweet& tweet : newTweets) { - std::string doc = n.getTweet().generateReplyPrefill(client.getUser()); - doc += kgramstats.randomSentence(140 - doc.length()); - doc.resize(140); + auto createdTime = + std::chrono::system_clock::from_time_t(tweet.getCreatedAt()); - try + if ( + // Ignore tweets from before the bot started up + createdTime > startedTime + // Ignore retweets + && !tweet.isRetweet() + // Ignore tweets from yourself + && tweet.getAuthor() != client.getUser()) { - client.replyToTweet(doc, n.getTweet()); - } catch (const twitter::twitter_error& error) - { - std::cout << "Twitter error while tweeting: " << error.what() << std::endl; + std::string doc = tweet.generateReplyPrefill(client.getUser()); + doc += kgramstats.randomSentence(140 - doc.length()); + doc.resize(140); + + postQueue.emplace_back(std::move(doc), true, tweet.getID()); } } + } catch (const twitter::rate_limit_exceeded&) + { + // Wait out the rate limit (10 minutes here and 5 below = 15). + pollTimer += std::chrono::minutes(10); + } catch (const twitter::twitter_error& e) + { + std::cout << "Twitter error while polling: " << e.what() << std::endl; } + + pollTimer += std::chrono::minutes(POLL_TIMEOUT); } - }); - - std::this_thread::sleep_for(std::chrono::minutes(1)); - std::cout << "Generating..." << std::endl; - for (;;) - { - std::string doc = kgramstats.randomSentence(140); - doc.resize(140); - - try + if ((currentTime >= queueTimer) && (!postQueue.empty())) { - client.updateStatus(doc); - } catch (const twitter::twitter_error& error) + auto post = postQueue.front(); + postQueue.pop_front(); + + try + { + if (std::get<1>(post)) + { + client.replyToTweet(std::get<0>(post), std::get<2>(post)); + } else { + client.updateStatus(std::get<0>(post)); + } + } catch (const twitter::twitter_error& error) + { + std::cout << "Twitter error while tweeting: " << error.what() + << std::endl; + } + + queueTimer = currentTime + std::chrono::minutes(QUEUE_TIMEOUT); + } + + auto soonestTimer = genTimer; + + if (pollTimer < soonestTimer) { - std::cout << "Twitter error while tweeting: " << error.what() << std::endl; + soonestTimer = pollTimer; } - - int waitlen = rand() % delay; - if (waitlen == 0) + + if ((queueTimer < soonestTimer) && (!postQueue.empty())) { - continue; - } else if (waitlen == 1) + soonestTimer = queueTimer; + } + + int waitlen = + std::chrono::duration_cast( + soonestTimer - currentTime).count(); + + if (waitlen == 1) { std::cout << "Sleeping for 1 second..." << std::endl; } else if (waitlen < 60) @@ -135,22 +192,25 @@ int main(int argc, char** args) 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(std::chrono::seconds(waitlen)); + std::this_thread::sleep_until(soonestTimer); } - + return 0; } -- cgit 1.4.1