From f221fa9d721f5df96107010bb95c12dc9d19700e Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Mon, 16 Oct 2017 11:23:22 -0400 Subject: Made insultifer bot --- CMakeLists.txt | 12 +- chemist.cpp | 183 ------------------------------ data.txt | 310 ++++++--------------------------------------------- insult.cpp | 108 ++++++++++++++++++ patterner.cpp | 138 +++++++++++++++++++++++ patterner.h | 21 ++++ vendor/libtwittercpp | 2 +- vendor/verbly | 2 +- 8 files changed, 306 insertions(+), 470 deletions(-) delete mode 100644 chemist.cpp create mode 100644 insult.cpp create mode 100644 patterner.cpp create mode 100644 patterner.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 49b0d2e..b9a143f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,12 @@ cmake_minimum_required (VERSION 3.1) -project (chemist) - -set(CMAKE_BUILD_TYPE Debug) +project (insult) add_subdirectory(vendor/verbly) add_subdirectory(vendor/yaml-cpp EXCLUDE_FROM_ALL) add_subdirectory(vendor/libtwittercpp) include_directories(vendor/verbly/lib vendor/yaml-cpp/include vendor/libtwittercpp/src) -add_executable(chemist chemist.cpp) -set_property(TARGET chemist PROPERTY CXX_STANDARD 11) -set_property(TARGET chemist PROPERTY CXX_STANDARD_REQUIRED ON) -target_link_libraries(chemist verbly twitter++ yaml-cpp) +add_executable(insult insult.cpp patterner.cpp) +set_property(TARGET insult PROPERTY CXX_STANDARD 11) +set_property(TARGET insult PROPERTY CXX_STANDARD_REQUIRED ON) +target_link_libraries(insult verbly twitter++ yaml-cpp) diff --git a/chemist.cpp b/chemist.cpp deleted file mode 100644 index 06e1992..0000000 --- a/chemist.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, char** argv) -{ - if (argc != 2) - { - std::cout << "usage: chemist [configfile]" << std::endl; - return -1; - } - - 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()); - - twitter::client client(auth); - - std::map> groups; - std::ifstream datafile(config["forms_file"].as()); - if (!datafile.is_open()) - { - std::cout << "Could not find datafile" << 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); - } - } - } - - std::random_device random_device; - std::mt19937 random_engine{random_device()}; - - verbly::data database {config["verbly_datafile"].as()}; - for (;;) - { - std::cout << "Generating tweet" << std::endl; - std::string action = "{Main}"; - 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 == "NOUN") - { - result = database.nouns().is_not_proper().random().limit(1).with_complexity(1).run().front().singular_form(); - } else if (canontkn == "ATTRIBUTE") - { - result = database.nouns().random().limit(1).full_hyponym_of(database.nouns().with_wnid(100024264).limit(1).run().front()).run().front().singular_form(); - } else if (canontkn == "ADJECTIVE") - { - result = database.adjectives().with_complexity(1).random().limit(1).run().front().base_form(); - } else if (canontkn == "VERBING") - { - result = database.verbs().random().limit(1).run().front().ing_form(); - } else if (canontkn == "YEAR") - { - std::uniform_int_distribution yeardist(1916,2015); - int year = yeardist(random_engine); - result = std::to_string(year); - } else if (canontkn == "REGION") - { - auto hem1 = database.nouns().with_singular_form("eastern hemisphere").limit(1).run().front(); - auto hem2 = database.nouns().with_singular_form("western hemisphere").limit(1).run().front(); - verbly::filter region{hem1, hem2}; - region.set_orlogic(true); - - result = database.nouns().full_part_holonym_of(region).random().limit(1).run().front().singular_form(); - } else if (canontkn == "FAMOUSNAME") - { - auto person = database.nouns().with_singular_form("person").limit(1).run().front(); - auto ptypes = database.nouns().full_hyponym_of({person}).is_class().random().limit(1).run().front(); - result = database.nouns().instance_of({ptypes}).random().limit(1).run().front().singular_form(); - } else if (canontkn == "BODYPART") - { - auto bp = database.nouns().with_singular_form("body part").limit(1).run().front(); - result = database.nouns().full_hyponym_of({bp}).with_complexity(1).random().limit(1).run().front().singular_form(); - } else if (canontkn == "\\N") - { - result = "\n"; - } else { - auto group = groups[canontkn]; - std::uniform_int_distribution groupdist(0, group.size()-1); - int groupind = groupdist(random_engine); - result = group[groupind]; - } - - 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 = verbly::split>(result, " "); - for (auto& word : words) - { - word[0] = std::toupper(word[0]); - } - - finalresult = verbly::implode(std::begin(words), std::end(words), " "); - } else { - finalresult = result; - } - - action.replace(tknloc, action.find("}")-tknloc+1, finalresult); - } - - action.resize(140); - - try - { - client.updateStatus(action); - - std::cout << "Tweeted!" << 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)); - } -} diff --git a/data.txt b/data.txt index 77179de..f25657f 100644 --- a/data.txt +++ b/data.txt @@ -1,279 +1,33 @@ MAIN -{NAME} ({CLASS}){\n}{PRIMARY}{\n}{SECONDARY} - -PRIMARY -Used to treat {SYNDROME} -Treats the {adjective} symptoms of {SYNDROME} -Cures {SYNDROME} -Approved to treat {SYNDROME} and {SYNDROME} -Prescribed for {SYNDROME} -Used with {EXISTENT} to treat {SYNDROME} -Used recreationally as {CLASS:indefinite} -Used recreationally for {verbing} - -SECONDARY -Developed after {year} {Noun} Pandemic -Often used off-label for {SYNDROME} -Notable for its {adjective} effect -Controlled in the US due to its {adjective} effect -Developed in {year} to replace {EXISTENT} -May cause {ADJECTIVE} feelings -Obsoleted by {EXISTENT} -Contraindicated by {SYNDROME} -Contraindicated by {EXISTENT} -Decreases the {adjective} effects of {EXISTENT} - -SYNDROME -irritable {noun} syndrome -{adjective} {noun} syndrome -severe {noun} -major {adjective} disorder -{adjective} {noun} disorder -obsessive {noun} disorder -clinical {noun} -{adjective} personality disorder -respiratory {adjective} disease -{bodypart} cancer -restless {bodypart} -{bodypart} failure -congenital {noun} disease -{FamousName}'s disease -{adjective} fever -hypo{noun}ism -{EXISTENT} overdose -{CLASS} overdose -{CLASS} discontinuation syndrome -{noun} syndrome -low {ATTRIBUTE} - -CLASSIFIER -{CLASS:indefinite} -{NAME} -{EXISTENT} - -CLASS -analgesic -painkiller -anaesthetic -antihistamine -anticonvulsant -antiepileptic -antidepressant -antimigraine -antipsychotic -benzodiazepine -antiparkinsonian -immunosuppressive -antianaemia -anticoagulant -blood thinner -antiarrhythmic -antithrombotic -antifungal -anti-infective -anti-inflammatory -disinfectant -antiseptic -antiemetic -diuretic -opiod painkiller -antiulcer -laxative -sedative -hormone -estrogen -androgen -contraceptive -ovulation inducer -thyroid stimulant -antithyroid -insulin -vaccine -oxytocin -SSRI -anxiolytic -antipanic agent -tricyclic -tetracyclic -MAOI -SNRI -antiandrogen -psychedelic -vitamin -probiotic -antibiotic -antiviral drug -stimulant -depressant -aphrodisiac -{EXISTENT} prodrug - -EXISTENT -fluoxetine -Prozac -sertraline -Zoloft -escitalopram -Lexapro -venlafaxine -Effexor -aripiprazole -Abilify -alprazolam -Xanax -diazepam -Valium -lamotrigine -Lamictal -gabapentin -Neurontin -acetaminophen -Tylenol -ibuprofin -Advil -lurasidone -Latuda -lithium -activated charcoal -estradiol -AndroGel -ziprasidone -Geodon -risperidone -Risperdal -quetiapine -Seroquel -Cymbalta -duloxetine -bupropion -Welbutrin -buspirone -Buspar -oxycontin -Oxycodone -Concerta -methylphenidate -Ritalin -Vyvanse -lisdexamfetamine -Adderall -epinephrine -adrenaline -testosterone gel -Cialis -Viagra -heroin -morphine -crystal meth -mirtazapine -Remeron -Luvox -fluvoxamine - -NAME -{PRENAME}{NAMEMID}{NAMEIFX} - -PRENAME -Oxy -Ari -Zi -Quetia -Mor -Her -Cia -Via -He -Con -Flu -Ser -Es -Ven -Al -Dia -Lamo -Gaba -Aceta -Ibu -Lura -Ris -Li -Estra -Du -Bus -Epin -Co -Lido -Pro -Pri -Bu -Levo -Ro -Me -Dibu -Des -Ha -Mir - -NAMEMID -pipra -pi -to -stero -cer -oxe -tra -cita -lo -la -fa -pra -zo -ze -tri -pen -mino -pro -si -peri -thi -loxe -con -epher -piva -bupi -va -piva -flu -oxy -taz -{NAMEMID}{NAMEMID} -{NAMEMID}{NAMEMID} - -NAMEIFX -zole -ne -tine -lin -tamine -gra -phine -ta -line -pram -xine -lam -pam -gine -tin -phen -fin -done -um -diol -tin -rone -ine -caine -rane -ide -epine \ No newline at end of file +{INSULT} +{INSULT} +{INSULT} +{INSULT} +{Insult} +{INSULT}! +{INSULT}! +{INSULT}!!!! +{INSULT}!!!! +{INSULT}? +{INSULT}???? +{INSULT}?!?!?!?! + +INSULT +{START} {END} +What the {WORD} did you say to me you little {WORD}? + +INSULT,START +{WORD} off +{WORD} you +{WORD} you and the horse you rode in on +{WORD} you in particular +go {WORD} yourself +go to {WORD} +leave me the {WORD} alone +you're a piece of {WORD} +what the {WORD} +what the {WORD}ing {WORD} +kindly catch the 9am train to {Word}sville + +INSULT,END +you piece of {WORD} \ No newline at end of file diff --git a/insult.cpp b/insult.cpp new file mode 100644 index 0000000..2612d4e --- /dev/null +++ b/insult.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "patterner.h" + +int main(int argc, char** argv) +{ + if (argc != 2) + { + std::cout << "usage: insult [configfile]" << std::endl; + return -1; + } + + 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()); + + twitter::client client(auth); + + std::random_device randomDevice; + std::mt19937 rng(randomDevice()); + + try + { + verbly::database database(config["verbly_datafile"].as()); + patterner pgen(config["forms_file"].as(), database, rng); + + std::cout << "Starting streaming..." << std::endl; + + twitter::stream userStream(client, [&pgen, &client] + (const twitter::notification& n) { + if (n.getType() == twitter::notification::type::tweet) + { + if ((!n.getTweet().isRetweet()) + && (n.getTweet().getAuthor() != client.getUser())) + { + 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("@teammeanies") != std::string::npos) + { + std::string doc = + n.getTweet().generateReplyPrefill(client.getUser()); + + doc += pgen.generate(); + doc.resize(140); + + try + { + client.replyToTweet(doc, n.getTweet()); + } catch (const twitter::twitter_error& error) + { + std::cout << "Twitter error while tweeting: " + << error.what() << std::endl; + } + } + } + } + }); + + std::this_thread::sleep_for(std::chrono::minutes(1)); + + for (;;) + { + std::cout << "Generating tweet..." << std::endl; + + std::string action = pgen.generate(); + action.resize(140); + + std::cout << action << std::endl; + + try + { + client.updateStatus(action); + + std::cout << "Tweeted!" << 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)); + } + } catch (std::invalid_argument& e) + { + std::cout << e.what() << std::endl; + return -1; + } +} diff --git a/patterner.cpp b/patterner.cpp new file mode 100644 index 0000000..2c92428 --- /dev/null +++ b/patterner.cpp @@ -0,0 +1,138 @@ +#include "patterner.h" +#include +#include + +patterner::patterner( + std::string datapath, + verbly::database& data, + std::mt19937& rng) : + data_(data), + rng_(rng) +{ + std::ifstream datafile(datapath); + if (!datafile.is_open()) + { + throw std::invalid_argument("Could not find datafile"); + } + + bool newgroup = true; + std::string line; + std::list curgroups; + while (getline(datafile, line)) + { + if (line.back() == '\r') + { + line.pop_back(); + } + + if (newgroup) + { + curgroups = verbly::split>(line, ","); + newgroup = false; + } else { + if (line.empty()) + { + newgroup = true; + } else { + for (std::string curgroup : curgroups) + { + groups_[curgroup].push_back(line); + } + } + } + } +} + +std::string patterner::generate() +{ + std::string action = "{MAIN}"; + 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 == "WORD") + { + result = data_.words( + (verbly::word::forms(verbly::inflection::base) %= + (verbly::form::complexity == 1) + && (verbly::form::length == 4) + && (verbly::form::proper == false) + && (verbly::pronunciation::numOfSyllables == 1)) + && !(verbly::word::usageDomains %= + (verbly::notion::wnid == 106718862))) // Blacklist ethnic slurs + .first().getBaseForm().getText(); + } else if (canontkn == "\\N") + { + result = "\n"; + } else { + auto group = groups_[canontkn]; + std::uniform_int_distribution groupdist(0, group.size()-1); + int groupind = groupdist(rng_); + result = group[groupind]; + } + + 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 = verbly::split>(result, " "); + for (auto& word : words) + { + if (word[0] == '{') + { + word[1] = std::toupper(word[1]); + + for (int k=2; k +#include + +class patterner { +public: + + patterner(std::string datafile, verbly::database& data, std::mt19937& rng); + + std::string generate(); + +private: + + std::map> groups_; + verbly::database& data_; + std::mt19937& rng_; +}; + +#endif /* end of include guard: PATTERNER_H_AB6883F5 */ diff --git a/vendor/libtwittercpp b/vendor/libtwittercpp index d90a1e7..df90612 160000 --- a/vendor/libtwittercpp +++ b/vendor/libtwittercpp @@ -1 +1 @@ -Subproject commit d90a1e74c77ba67f25a812609fd49d479bc464dd +Subproject commit df906121dd862c0f704e44f28ee079158c431c41 diff --git a/vendor/verbly b/vendor/verbly index 1f898f3..1fd518d 160000 --- a/vendor/verbly +++ b/vendor/verbly @@ -1 +1 @@ -Subproject commit 1f898f3bd66c29672275c2c884b17ba662ced626 +Subproject commit 1fd518d1c2b1d4e88ad88218b606a284b7128107 -- cgit 1.4.1