From 46b7119fab32b05923dbf9cda7cd6ce62f572846 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Mon, 9 Jan 2017 14:01:51 -0500 Subject: Initial commit --- cadence.cpp | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 cadence.cpp (limited to 'cadence.cpp') diff --git a/cadence.cpp b/cadence.cpp new file mode 100644 index 0000000..c91ca4a --- /dev/null +++ b/cadence.cpp @@ -0,0 +1,220 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" + +const std::vector moods = {"party", "chill", "crazy", "happy", "sad", "instrumental", "vocal"}; + +int main(int argc, char** argv) +{ + if (argc != 2) + { + std::cout << "usage: cadence [configfile]" << std::endl; + return -1; + } + + std::string configfile(argv[1]); + YAML::Node config = YAML::LoadFile(configfile); + + // Set up the Twitter client. + 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::client client(auth); + + // Read in the forms file. + std::map> groups; + std::ifstream datafile(config["forms"].as()); + if (!datafile.is_open()) + { + std::cout << "Could not find forms file." << std::endl; + return 1; + } + + bool newgroup = true; + std::string line; + std::string curgroup; + while (getline(datafile, line)) + { + if (line.back() == '\r') + { + line.pop_back(); + } + + if (newgroup) + { + curgroup = line; + newgroup = false; + } else { + if (line.empty()) + { + newgroup = true; + } else { + groups[curgroup].push_back(line); + } + } + } + + // Initialize the random number generator. + std::random_device random_device; + std::mt19937 random_engine{random_device()}; + + // Connect to the AcousticBrainz data file. + sqlite3* ppdb = nullptr; + + if (sqlite3_open_v2(config["acoustic_datafile"].as().c_str(), &ppdb, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) + { + // We still have to free the resources allocated. In the event that + // allocation failed, ppdb will be null and sqlite3_close_v2 will just + // ignore it. + sqlite3_close_v2(ppdb); + + std::cout << "Could not open acoustic datafile." << std::endl; + + return 1; + } + + for (;;) + { + std::cout << "Generating tweet..." << std::endl; + + std::string mood = moods[std::uniform_int_distribution(0, moods.size()-1)(random_engine)]; + + std::string songTitle; + std::string songArtist; + + sqlite3_stmt* ppstmt; + std::string query = "SELECT title, artist FROM songs WHERE category = ? ORDER BY RANDOM() LIMIT 1"; + + if (sqlite3_prepare_v2(ppdb, query.c_str(), query.length(), &ppstmt, NULL) != SQLITE_OK) + { + std::cout << "Error reading from acoustic datafile:" << std::endl; + std::cout << sqlite3_errmsg(ppdb) << std::endl; + + return 1; + } + + sqlite3_bind_text(ppstmt, 1, mood.c_str(), mood.length(), SQLITE_TRANSIENT); + + if (sqlite3_step(ppstmt) == SQLITE_ROW) + { + songTitle = reinterpret_cast(sqlite3_column_blob(ppstmt, 0)); + songArtist = reinterpret_cast(sqlite3_column_blob(ppstmt, 1)); + } else { + std::cout << "Error reading from acoustic datafile:" << std::endl; + std::cout << sqlite3_errmsg(ppdb) << std::endl; + + return 1; + } + + sqlite3_finalize(ppstmt); + + std::string action = "{" + cadence::uppercase(mood) + "}"; + int tknloc; + while ((tknloc = action.find("{")) != std::string::npos) + { + std::string token = action.substr(tknloc+1, action.find("}")-tknloc-1); + std::string modifier; + int modloc; + if ((modloc = token.find(":")) != std::string::npos) + { + modifier = token.substr(modloc+1); + token = token.substr(0, modloc); + } + + std::string canontkn; + std::transform(std::begin(token), std::end(token), std::back_inserter(canontkn), [] (char ch) { + return std::toupper(ch); + }); + + std::string result; + if (canontkn == "\\N") + { + result = "\n"; + } else if (canontkn == "ARTIST") + { + result = songArtist; + } else if (canontkn == "SONG") + { + result = "\"" + songTitle + "\""; + } else if (groups.count(canontkn)) { + auto& group = groups.at(canontkn); + std::uniform_int_distribution dist(0, group.size() - 1); + + result = group[dist(random_engine)]; + } else { + std::cout << "Badly formatted forms file:" << std::endl; + std::cout << "No such form as " + canontkn << std::endl; + + return 1; + } + + if (modifier == "indefinite") + { + if ((result.length() > 1) && (isupper(result[0])) && (isupper(result[1]))) + { + result = "an " + result; + } else if ((result[0] == 'a') || (result[0] == 'e') || (result[0] == 'i') || (result[0] == 'o') || (result[0] == 'u')) + { + result = "an " + result; + } else { + result = "a " + result; + } + } + + std::string finalresult; + if (islower(token[0])) + { + std::transform(std::begin(result), std::end(result), std::back_inserter(finalresult), [] (char ch) { + return std::tolower(ch); + }); + } else if (isupper(token[0]) && !isupper(token[1])) + { + auto words = cadence::split>(result, " "); + for (auto& word : words) + { + word[0] = std::toupper(word[0]); + } + + finalresult = cadence::implode(std::begin(words), std::end(words), " "); + } else { + finalresult = result; + } + + action.replace(tknloc, action.find("}")-tknloc+1, finalresult); + } + + if (action.size() <= 140) + { + try + { + client.updateStatus(action); + + std::cout << action << std::endl; + } catch (const twitter::twitter_error& e) + { + std::cout << "Twitter error: " << e.what() << std::endl; + } + + std::cout << "Waiting..." << std::endl; + + std::this_thread::sleep_for(std::chrono::hours(1)); + + std::cout << std::endl; + } else { + std::cout << "Tweet was too long, regenerating." << std::endl; + } + } +} -- cgit 1.4.1