summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt12
-rw-r--r--chemist.cpp183
-rw-r--r--data.txt310
-rw-r--r--insult.cpp108
-rw-r--r--patterner.cpp138
-rw-r--r--patterner.h21
m---------vendor/libtwittercpp0
m---------vendor/verbly0
8 files changed, 304 insertions, 468 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 49b0d2e..b9a143f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -1,14 +1,12 @@
1cmake_minimum_required (VERSION 3.1) 1cmake_minimum_required (VERSION 3.1)
2project (chemist) 2project (insult)
3
4set(CMAKE_BUILD_TYPE Debug)
5 3
6add_subdirectory(vendor/verbly) 4add_subdirectory(vendor/verbly)
7add_subdirectory(vendor/yaml-cpp EXCLUDE_FROM_ALL) 5add_subdirectory(vendor/yaml-cpp EXCLUDE_FROM_ALL)
8add_subdirectory(vendor/libtwittercpp) 6add_subdirectory(vendor/libtwittercpp)
9 7
10include_directories(vendor/verbly/lib vendor/yaml-cpp/include vendor/libtwittercpp/src) 8include_directories(vendor/verbly/lib vendor/yaml-cpp/include vendor/libtwittercpp/src)
11add_executable(chemist chemist.cpp) 9add_executable(insult insult.cpp patterner.cpp)
12set_property(TARGET chemist PROPERTY CXX_STANDARD 11) 10set_property(TARGET insult PROPERTY CXX_STANDARD 11)
13set_property(TARGET chemist PROPERTY CXX_STANDARD_REQUIRED ON) 11set_property(TARGET insult PROPERTY CXX_STANDARD_REQUIRED ON)
14target_link_libraries(chemist verbly twitter++ yaml-cpp) 12target_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 @@
1#include <yaml-cpp/yaml.h>
2#include <iostream>
3#include <sstream>
4#include <verbly.h>
5#include <fstream>
6#include <twitter.h>
7#include <random>
8#include <chrono>
9#include <thread>
10
11int main(int argc, char** argv)
12{
13 if (argc != 2)
14 {
15 std::cout << "usage: chemist [configfile]" << std::endl;
16 return -1;
17 }
18
19 std::string configfile(argv[1]);
20 YAML::Node config = YAML::LoadFile(configfile);
21
22 twitter::auth auth;
23 auth.setConsumerKey(config["consumer_key"].as<std::string>());
24 auth.setConsumerSecret(config["consumer_secret"].as<std::string>());
25 auth.setAccessKey(config["access_key"].as<std::string>());
26 auth.setAccessSecret(config["access_secret"].as<std::string>());
27
28 twitter::client client(auth);
29
30 std::map<std::string, std::vector<std::string>> groups;
31 std::ifstream datafile(config["forms_file"].as<std::string>());
32 if (!datafile.is_open())
33 {
34 std::cout << "Could not find datafile" << std::endl;
35 return 1;
36 }
37
38 bool newgroup = true;
39 std::string line;
40 std::string curgroup;
41 while (getline(datafile, line))
42 {
43 if (line.back() == '\r')
44 {
45 line.pop_back();
46 }
47
48 if (newgroup)
49 {
50 curgroup = line;
51 newgroup = false;
52 } else {
53 if (line.empty())
54 {
55 newgroup = true;
56 } else {
57 groups[curgroup].push_back(line);
58 }
59 }
60 }
61
62 std::random_device random_device;
63 std::mt19937 random_engine{random_device()};
64
65 verbly::data database {config["verbly_datafile"].as<std::string>()};
66 for (;;)
67 {
68 std::cout << "Generating tweet" << std::endl;
69 std::string action = "{Main}";
70 int tknloc;
71 while ((tknloc = action.find("{")) != std::string::npos)
72 {
73 std::string token = action.substr(tknloc+1, action.find("}")-tknloc-1);
74 std::string modifier;
75 int modloc;
76 if ((modloc = token.find(":")) != std::string::npos)
77 {
78 modifier = token.substr(modloc+1);
79 token = token.substr(0, modloc);
80 }
81
82 std::string canontkn;
83 std::transform(std::begin(token), std::end(token), std::back_inserter(canontkn), [] (char ch) {
84 return std::toupper(ch);
85 });
86
87 std::string result;
88 if (canontkn == "NOUN")
89 {
90 result = database.nouns().is_not_proper().random().limit(1).with_complexity(1).run().front().singular_form();
91 } else if (canontkn == "ATTRIBUTE")
92 {
93 result = database.nouns().random().limit(1).full_hyponym_of(database.nouns().with_wnid(100024264).limit(1).run().front()).run().front().singular_form();
94 } else if (canontkn == "ADJECTIVE")
95 {
96 result = database.adjectives().with_complexity(1).random().limit(1).run().front().base_form();
97 } else if (canontkn == "VERBING")
98 {
99 result = database.verbs().random().limit(1).run().front().ing_form();
100 } else if (canontkn == "YEAR")
101 {
102 std::uniform_int_distribution<int> yeardist(1916,2015);
103 int year = yeardist(random_engine);
104 result = std::to_string(year);
105 } else if (canontkn == "REGION")
106 {
107 auto hem1 = database.nouns().with_singular_form("eastern hemisphere").limit(1).run().front();
108 auto hem2 = database.nouns().with_singular_form("western hemisphere").limit(1).run().front();
109 verbly::filter<verbly::noun> region{hem1, hem2};
110 region.set_orlogic(true);
111
112 result = database.nouns().full_part_holonym_of(region).random().limit(1).run().front().singular_form();
113 } else if (canontkn == "FAMOUSNAME")
114 {
115 auto person = database.nouns().with_singular_form("person").limit(1).run().front();
116 auto ptypes = database.nouns().full_hyponym_of({person}).is_class().random().limit(1).run().front();
117 result = database.nouns().instance_of({ptypes}).random().limit(1).run().front().singular_form();
118 } else if (canontkn == "BODYPART")
119 {
120 auto bp = database.nouns().with_singular_form("body part").limit(1).run().front();
121 result = database.nouns().full_hyponym_of({bp}).with_complexity(1).random().limit(1).run().front().singular_form();
122 } else if (canontkn == "\\N")
123 {
124 result = "\n";
125 } else {
126 auto group = groups[canontkn];
127 std::uniform_int_distribution<int> groupdist(0, group.size()-1);
128 int groupind = groupdist(random_engine);
129 result = group[groupind];
130 }
131
132 if (modifier == "indefinite")
133 {
134 if ((result.length() > 1) && (isupper(result[0])) && (isupper(result[1])))
135 {
136 result = "an " + result;
137 } else if ((result[0] == 'a') || (result[0] == 'e') || (result[0] == 'i') || (result[0] == 'o') || (result[0] == 'u'))
138 {
139 result = "an " + result;
140 } else {
141 result = "a " + result;
142 }
143 }
144
145 std::string finalresult;
146 if (islower(token[0]))
147 {
148 std::transform(std::begin(result), std::end(result), std::back_inserter(finalresult), [] (char ch) {
149 return std::tolower(ch);
150 });
151 } else if (isupper(token[0]) && !isupper(token[1]))
152 {
153 auto words = verbly::split<std::list<std::string>>(result, " ");
154 for (auto& word : words)
155 {
156 word[0] = std::toupper(word[0]);
157 }
158
159 finalresult = verbly::implode(std::begin(words), std::end(words), " ");
160 } else {
161 finalresult = result;
162 }
163
164 action.replace(tknloc, action.find("}")-tknloc+1, finalresult);
165 }
166
167 action.resize(140);
168
169 try
170 {
171 client.updateStatus(action);
172
173 std::cout << "Tweeted!" << std::endl;
174 } catch (const twitter::twitter_error& e)
175 {
176 std::cout << "Twitter error: " << e.what() << std::endl;
177 }
178
179 std::cout << "Waiting..." << std::endl;
180
181 std::this_thread::sleep_for(std::chrono::hours(1));
182 }
183}
diff --git a/data.txt b/data.txt index 77179de..f25657f 100644 --- a/data.txt +++ b/data.txt
@@ -1,279 +1,33 @@
1MAIN 1MAIN
2{NAME} ({CLASS}){\n}{PRIMARY}{\n}{SECONDARY} 2{INSULT}
3 3{INSULT}
4PRIMARY 4{INSULT}
5Used to treat {SYNDROME} 5{INSULT}
6Treats the {adjective} symptoms of {SYNDROME} 6{Insult}
7Cures {SYNDROME} 7{INSULT}!
8Approved to treat {SYNDROME} and {SYNDROME} 8{INSULT}!
9Prescribed for {SYNDROME} 9{INSULT}!!!!
10Used with {EXISTENT} to treat {SYNDROME} 10{INSULT}!!!!
11Used recreationally as {CLASS:indefinite} 11{INSULT}?
12Used recreationally for {verbing} 12{INSULT}????
13 13{INSULT}?!?!?!?!
14SECONDARY 14
15Developed after {year} {Noun} Pandemic 15INSULT
16Often used off-label for {SYNDROME} 16{START} {END}
17Notable for its {adjective} effect 17What the {WORD} did you say to me you little {WORD}?
18Controlled in the US due to its {adjective} effect 18
19Developed in {year} to replace {EXISTENT} 19INSULT,START
20May cause {ADJECTIVE} feelings 20{WORD} off
21Obsoleted by {EXISTENT} 21{WORD} you
22Contraindicated by {SYNDROME} 22{WORD} you and the horse you rode in on
23Contraindicated by {EXISTENT} 23{WORD} you in particular
24Decreases the {adjective} effects of {EXISTENT} 24go {WORD} yourself
25 25go to {WORD}
26SYNDROME 26leave me the {WORD} alone
27irritable {noun} syndrome 27you're a piece of {WORD}
28{adjective} {noun} syndrome 28what the {WORD}
29severe {noun} 29what the {WORD}ing {WORD}
30major {adjective} disorder 30kindly catch the 9am train to {Word}sville
31{adjective} {noun} disorder 31
32obsessive {noun} disorder 32INSULT,END
33clinical {noun} 33you piece of {WORD} \ No newline at end of file
34{adjective} personality disorder
35respiratory {adjective} disease
36{bodypart} cancer
37restless {bodypart}
38{bodypart} failure
39congenital {noun} disease
40{FamousName}'s disease
41{adjective} fever
42hypo{noun}ism
43{EXISTENT} overdose
44{CLASS} overdose
45{CLASS} discontinuation syndrome
46{noun} syndrome
47low {ATTRIBUTE}
48
49CLASSIFIER
50{CLASS:indefinite}
51{NAME}
52{EXISTENT}
53
54CLASS
55analgesic
56painkiller
57anaesthetic
58antihistamine
59anticonvulsant
60antiepileptic
61antidepressant
62antimigraine
63antipsychotic
64benzodiazepine
65antiparkinsonian
66immunosuppressive
67antianaemia
68anticoagulant
69blood thinner
70antiarrhythmic
71antithrombotic
72antifungal
73anti-infective
74anti-inflammatory
75disinfectant
76antiseptic
77antiemetic
78diuretic
79opiod painkiller
80antiulcer
81laxative
82sedative
83hormone
84estrogen
85androgen
86contraceptive
87ovulation inducer
88thyroid stimulant
89antithyroid
90insulin
91vaccine
92oxytocin
93SSRI
94anxiolytic
95antipanic agent
96tricyclic
97tetracyclic
98MAOI
99SNRI
100antiandrogen
101psychedelic
102vitamin
103probiotic
104antibiotic
105antiviral drug
106stimulant
107depressant
108aphrodisiac
109{EXISTENT} prodrug
110
111EXISTENT
112fluoxetine
113Prozac
114sertraline
115Zoloft
116escitalopram
117Lexapro
118venlafaxine
119Effexor
120aripiprazole
121Abilify
122alprazolam
123Xanax
124diazepam
125Valium
126lamotrigine
127Lamictal
128gabapentin
129Neurontin
130acetaminophen
131Tylenol
132ibuprofin
133Advil
134lurasidone
135Latuda
136lithium
137activated charcoal
138estradiol
139AndroGel
140ziprasidone
141Geodon
142risperidone
143Risperdal
144quetiapine
145Seroquel
146Cymbalta
147duloxetine
148bupropion
149Welbutrin
150buspirone
151Buspar
152oxycontin
153Oxycodone
154Concerta
155methylphenidate
156Ritalin
157Vyvanse
158lisdexamfetamine
159Adderall
160epinephrine
161adrenaline
162testosterone gel
163Cialis
164Viagra
165heroin
166morphine
167crystal meth
168mirtazapine
169Remeron
170Luvox
171fluvoxamine
172
173NAME
174{PRENAME}{NAMEMID}{NAMEIFX}
175
176PRENAME
177Oxy
178Ari
179Zi
180Quetia
181Mor
182Her
183Cia
184Via
185He
186Con
187Flu
188Ser
189Es
190Ven
191Al
192Dia
193Lamo
194Gaba
195Aceta
196Ibu
197Lura
198Ris
199Li
200Estra
201Du
202Bus
203Epin
204Co
205Lido
206Pro
207Pri
208Bu
209Levo
210Ro
211Me
212Dibu
213Des
214Ha
215Mir
216
217NAMEMID
218pipra
219pi
220to
221stero
222cer
223oxe
224tra
225cita
226lo
227la
228fa
229pra
230zo
231ze
232tri
233pen
234mino
235pro
236si
237peri
238thi
239loxe
240con
241epher
242piva
243bupi
244va
245piva
246flu
247oxy
248taz
249{NAMEMID}{NAMEMID}
250{NAMEMID}{NAMEMID}
251
252NAMEIFX
253zole
254ne
255tine
256lin
257tamine
258gra
259phine
260ta
261line
262pram
263xine
264lam
265pam
266gine
267tin
268phen
269fin
270done
271um
272diol
273tin
274rone
275ine
276caine
277rane
278ide
279epine \ 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 @@
1#include <yaml-cpp/yaml.h>
2#include <iostream>
3#include <sstream>
4#include <verbly.h>
5#include <fstream>
6#include <twitter.h>
7#include <random>
8#include <chrono>
9#include <thread>
10#include "patterner.h"
11
12int main(int argc, char** argv)
13{
14 if (argc != 2)
15 {
16 std::cout << "usage: insult [configfile]" << std::endl;
17 return -1;
18 }
19
20 std::string configfile(argv[1]);
21 YAML::Node config = YAML::LoadFile(configfile);
22
23 twitter::auth auth;
24 auth.setConsumerKey(config["consumer_key"].as<std::string>());
25 auth.setConsumerSecret(config["consumer_secret"].as<std::string>());
26 auth.setAccessKey(config["access_key"].as<std::string>());
27 auth.setAccessSecret(config["access_secret"].as<std::string>());
28
29 twitter::client client(auth);
30
31 std::random_device randomDevice;
32 std::mt19937 rng(randomDevice());
33
34 try
35 {
36 verbly::database database(config["verbly_datafile"].as<std::string>());
37 patterner pgen(config["forms_file"].as<std::string>(), database, rng);
38
39 std::cout << "Starting streaming..." << std::endl;
40
41 twitter::stream userStream(client, [&pgen, &client]
42 (const twitter::notification& n) {
43 if (n.getType() == twitter::notification::type::tweet)
44 {
45 if ((!n.getTweet().isRetweet())
46 && (n.getTweet().getAuthor() != client.getUser()))
47 {
48 std::string original = n.getTweet().getText();
49 std::string canonical;
50
51 std::transform(std::begin(original), std::end(original),
52 std::back_inserter(canonical), [] (char ch)
53 {
54 return std::tolower(ch);
55 });
56
57 if (canonical.find("@teammeanies") != std::string::npos)
58 {
59 std::string doc =
60 n.getTweet().generateReplyPrefill(client.getUser());
61
62 doc += pgen.generate();
63 doc.resize(140);
64
65 try
66 {
67 client.replyToTweet(doc, n.getTweet());
68 } catch (const twitter::twitter_error& error)
69 {
70 std::cout << "Twitter error while tweeting: "
71 << error.what() << std::endl;
72 }
73 }
74 }
75 }
76 });
77
78 std::this_thread::sleep_for(std::chrono::minutes(1));
79
80 for (;;)
81 {
82 std::cout << "Generating tweet..." << std::endl;
83
84 std::string action = pgen.generate();
85 action.resize(140);
86
87 std::cout << action << std::endl;
88
89 try
90 {
91 client.updateStatus(action);
92
93 std::cout << "Tweeted!" << std::endl;
94 } catch (const twitter::twitter_error& e)
95 {
96 std::cout << "Twitter error: " << e.what() << std::endl;
97 }
98
99 std::cout << "Waiting..." << std::endl;
100
101 std::this_thread::sleep_for(std::chrono::hours(1));
102 }
103 } catch (std::invalid_argument& e)
104 {
105 std::cout << e.what() << std::endl;
106 return -1;
107 }
108}
diff --git a/patterner.cpp b/patterner.cpp new file mode 100644 index 0000000..2c92428 --- /dev/null +++ b/patterner.cpp
@@ -0,0 +1,138 @@
1#include "patterner.h"
2#include <fstream>
3#include <stdexcept>
4
5patterner::patterner(
6 std::string datapath,
7 verbly::database& data,
8 std::mt19937& rng) :
9 data_(data),
10 rng_(rng)
11{
12 std::ifstream datafile(datapath);
13 if (!datafile.is_open())
14 {
15 throw std::invalid_argument("Could not find datafile");
16 }
17
18 bool newgroup = true;
19 std::string line;
20 std::list<std::string> curgroups;
21 while (getline(datafile, line))
22 {
23 if (line.back() == '\r')
24 {
25 line.pop_back();
26 }
27
28 if (newgroup)
29 {
30 curgroups = verbly::split<std::list<std::string>>(line, ",");
31 newgroup = false;
32 } else {
33 if (line.empty())
34 {
35 newgroup = true;
36 } else {
37 for (std::string curgroup : curgroups)
38 {
39 groups_[curgroup].push_back(line);
40 }
41 }
42 }
43 }
44}
45
46std::string patterner::generate()
47{
48 std::string action = "{MAIN}";
49 int tknloc;
50 while ((tknloc = action.find("{")) != std::string::npos)
51 {
52 std::string token = action.substr(tknloc+1, action.find("}")-tknloc-1);
53 std::string modifier;
54 int modloc;
55 if ((modloc = token.find(":")) != std::string::npos)
56 {
57 modifier = token.substr(modloc+1);
58 token = token.substr(0, modloc);
59 }
60
61 std::string canontkn;
62 std::transform(std::begin(token), std::end(token),
63 std::back_inserter(canontkn), [] (char ch) {
64 return std::toupper(ch);
65 });
66
67 std::string result;
68 if (canontkn == "WORD")
69 {
70 result = data_.words(
71 (verbly::word::forms(verbly::inflection::base) %=
72 (verbly::form::complexity == 1)
73 && (verbly::form::length == 4)
74 && (verbly::form::proper == false)
75 && (verbly::pronunciation::numOfSyllables == 1))
76 && !(verbly::word::usageDomains %=
77 (verbly::notion::wnid == 106718862))) // Blacklist ethnic slurs
78 .first().getBaseForm().getText();
79 } else if (canontkn == "\\N")
80 {
81 result = "\n";
82 } else {
83 auto group = groups_[canontkn];
84 std::uniform_int_distribution<int> groupdist(0, group.size()-1);
85 int groupind = groupdist(rng_);
86 result = group[groupind];
87 }
88
89 if (modifier == "indefinite")
90 {
91 if ((result.length() > 1) && (isupper(result[0])) && (isupper(result[1])))
92 {
93 result = "an " + result;
94 } else if ((result[0] == 'a') || (result[0] == 'e') || (result[0] == 'i') || (result[0] == 'o') || (result[0] == 'u'))
95 {
96 result = "an " + result;
97 } else {
98 result = "a " + result;
99 }
100 }
101
102 std::string finalresult;
103 if (islower(token[0]))
104 {
105 std::transform(std::begin(result), std::end(result), std::back_inserter(finalresult), [] (char ch) {
106 return std::tolower(ch);
107 });
108 } else if (isupper(token[0]) && !isupper(token[1]))
109 {
110 auto words = verbly::split<std::list<std::string>>(result, " ");
111 for (auto& word : words)
112 {
113 if (word[0] == '{')
114 {
115 word[1] = std::toupper(word[1]);
116
117 for (int k=2; k<word.length(); k++)
118 {
119 if (std::isalpha(word[k]))
120 {
121 word[k] = std::tolower(word[k]);
122 }
123 }
124 } else {
125 word[0] = std::toupper(word[0]);
126 }
127 }
128
129 finalresult = verbly::implode(std::begin(words), std::end(words), " ");
130 } else {
131 finalresult = result;
132 }
133
134 action.replace(tknloc, action.find("}")-tknloc+1, finalresult);
135 }
136
137 return action;
138}
diff --git a/patterner.h b/patterner.h new file mode 100644 index 0000000..df631b1 --- /dev/null +++ b/patterner.h
@@ -0,0 +1,21 @@
1#ifndef PATTERNER_H_AB6883F5
2#define PATTERNER_H_AB6883F5
3
4#include <verbly.h>
5#include <random>
6
7class patterner {
8public:
9
10 patterner(std::string datafile, verbly::database& data, std::mt19937& rng);
11
12 std::string generate();
13
14private:
15
16 std::map<std::string, std::vector<std::string>> groups_;
17 verbly::database& data_;
18 std::mt19937& rng_;
19};
20
21#endif /* end of include guard: PATTERNER_H_AB6883F5 */
diff --git a/vendor/libtwittercpp b/vendor/libtwittercpp
Subproject d90a1e74c77ba67f25a812609fd49d479bc464d Subproject df906121dd862c0f704e44f28ee079158c431c4
diff --git a/vendor/verbly b/vendor/verbly
Subproject 1f898f3bd66c29672275c2c884b17ba662ced62 Subproject 1fd518d1c2b1d4e88ad88218b606a284b712810