summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2017-02-19 15:36:40 -0500
committerKelly Rauchenberger <fefferburbia@gmail.com>2017-02-19 15:36:40 -0500
commit95b7ae535c0b005466e34096f5ce427046a403d2 (patch)
tree806d031c7894322c56d2457d64a51f893b3c38ba
downloadfather-95b7ae535c0b005466e34096f5ce427046a403d2.tar.gz
father-95b7ae535c0b005466e34096f5ce427046a403d2.tar.bz2
father-95b7ae535c0b005466e34096f5ce427046a403d2.zip
Created bot
-rw-r--r--.gitmodules9
-rw-r--r--CMakeLists.txt13
-rw-r--r--father.cpp260
m---------vendor/libtwittercpp0
m---------vendor/verbly0
m---------vendor/yaml-cpp0
6 files changed, 282 insertions, 0 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ec4d45e --- /dev/null +++ b/.gitmodules
@@ -0,0 +1,9 @@
1[submodule "vendor/libtwittercpp"]
2 path = vendor/libtwittercpp
3 url = git@github.com:hatkirby/libtwittercpp
4[submodule "vendor/yaml-cpp"]
5 path = vendor/yaml-cpp
6 url = git@github.com:jbeder/yaml-cpp
7[submodule "vendor/verbly"]
8 path = vendor/verbly
9 url = git@github.com:hatkirby/verbly
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8266a02 --- /dev/null +++ b/CMakeLists.txt
@@ -0,0 +1,13 @@
1cmake_minimum_required (VERSION 3.1)
2project (father)
3
4add_subdirectory(vendor/libtwittercpp)
5add_subdirectory(vendor/verbly)
6add_subdirectory(vendor/yaml-cpp EXCLUDE_FROM_ALL)
7
8include_directories(vendor/libtwittercpp/src vendor/yaml-cpp/include vendor/verbly/lib)
9add_executable(father father.cpp)
10set_property(TARGET father PROPERTY CXX_STANDARD 11)
11set_property(TARGET father PROPERTY CXX_STANDARD_REQUIRED ON)
12target_link_libraries(father verbly yaml-cpp twitter++)
13
diff --git a/father.cpp b/father.cpp new file mode 100644 index 0000000..7ea5080 --- /dev/null +++ b/father.cpp
@@ -0,0 +1,260 @@
1#include <twitter.h>
2#include <random>
3#include <yaml-cpp/yaml.h>
4#include <iostream>
5#include <thread>
6#include <chrono>
7#include <string>
8#include <algorithm>
9#include <set>
10#include <list>
11#include <iterator>
12#include <verbly.h>
13
14template <class OutputIterator>
15void split(std::string input, std::string delimiter, OutputIterator out)
16{
17 while (!input.empty())
18 {
19 int divider = input.find(delimiter);
20 if (divider == std::string::npos)
21 {
22 *out = input;
23 out++;
24
25 input = "";
26 } else {
27 *out = input.substr(0, divider);
28 out++;
29
30 input = input.substr(divider+delimiter.length());
31 }
32 }
33}
34
35template <class Container>
36Container split(std::string input, std::string delimiter)
37{
38 Container result;
39
40 split(input, delimiter, std::back_inserter(result));
41
42 return result;
43}
44
45verbly::word findWordOfType(
46 verbly::database& database,
47 std::string form,
48 verbly::part_of_speech partOfSpeech)
49{
50 std::vector<verbly::word> isThing = database.words(
51 (verbly::notion::partOfSpeech == partOfSpeech)
52 && (verbly::form::text == form)).all();
53
54 if (isThing.empty())
55 {
56 return {};
57 } else {
58 return isThing.front();
59 }
60}
61
62int main(int argc, char** argv)
63{
64 std::random_device randomDevice;
65 std::mt19937 rng{randomDevice()};
66
67 if (argc != 2)
68 {
69 std::cout << "usage: father [configfile]" << std::endl;
70 return -1;
71 }
72
73 std::string configfile(argv[1]);
74 YAML::Node config = YAML::LoadFile(configfile);
75
76 twitter::auth auth;
77 auth.setConsumerKey(config["consumer_key"].as<std::string>());
78 auth.setConsumerSecret(config["consumer_secret"].as<std::string>());
79 auth.setAccessKey(config["access_key"].as<std::string>());
80 auth.setAccessSecret(config["access_secret"].as<std::string>());
81
82 std::set<twitter::user_id> streamedFriends;
83
84 verbly::database database(config["verbly_datafile"].as<std::string>());
85
86 twitter::client client(auth);
87
88 std::cout << "Starting streaming..." << std::endl;
89 twitter::stream userStream(client, [&] (const twitter::notification& n) {
90 if (n.getType() == twitter::notification::type::friends)
91 {
92 streamedFriends = n.getFriends();
93 } else if (n.getType() == twitter::notification::type::follow)
94 {
95 streamedFriends.insert(n.getUser().getID());
96 } else if (n.getType() == twitter::notification::type::unfollow)
97 {
98 streamedFriends.erase(n.getUser().getID());
99 } else if (n.getType() == twitter::notification::type::tweet)
100 {
101 if (
102 // Only monitor people you are following
103 (streamedFriends.count(n.getTweet().getAuthor().getID()) == 1)
104 // Ignore retweets
105 && (!n.getTweet().isRetweet())
106 // Ignore messages
107 && (n.getTweet().getText().front() != '@')
108 )
109 {
110 std::vector<std::string> tokens =
111 split<std::vector<std::string>>(n.getTweet().getText(), " ");
112
113 std::vector<std::string> canonical;
114 for (std::string token : tokens)
115 {
116 std::string canonStr;
117 for (char ch : token)
118 {
119 if (std::isalpha(ch))
120 {
121 canonStr += std::tolower(ch);
122 }
123 }
124
125 canonical.push_back(canonStr);
126 }
127
128 std::vector<std::string>::iterator imIt =
129 std::find(std::begin(canonical), std::end(canonical), "im");
130
131 if (imIt != std::end(canonical))
132 {
133 imIt++;
134
135 if (imIt != std::end(canonical))
136 {
137 verbly::token name;
138
139 verbly::word firstAdverb = findWordOfType(
140 database,
141 *imIt,
142 verbly::part_of_speech::adverb);
143
144 if (firstAdverb.isValid())
145 {
146 std::vector<std::string>::iterator adjIt = imIt;
147 adjIt++;
148
149 if (adjIt != std::end(canonical))
150 {
151 verbly::word secondAdjective = findWordOfType(
152 database,
153 *adjIt,
154 verbly::part_of_speech::adjective);
155
156 if (secondAdjective.isValid())
157 {
158 name << verbly::token::capitalize(firstAdverb);
159 name << verbly::token::capitalize(secondAdjective);
160 }
161 }
162 }
163
164 if (name.isEmpty())
165 {
166 verbly::word firstAdjective = findWordOfType(
167 database,
168 *imIt,
169 verbly::part_of_speech::adjective);
170
171 if (firstAdjective.isValid())
172 {
173 name = verbly::token::capitalize(firstAdjective);
174 }
175 }
176
177 if ((!name.isEmpty())
178 && (std::bernoulli_distribution(1.0/10.0)(rng)))
179 {
180 verbly::token action = {
181 "Hi",
182 verbly::token::punctuation(",", name),
183 "I'm Dad."};
184
185 std::string result =
186 n.getTweet().generateReplyPrefill(client.getUser())
187 + action.compile();
188
189 if (result.length() <= 140)
190 {
191 try
192 {
193 client.replyToTweet(result, n.getTweet());
194 } catch (const twitter::twitter_error& e)
195 {
196 std::cout << "Twitter error: " << e.what() << std::endl;
197 }
198 }
199 }
200 }
201 }
202 }
203 } else if (n.getType() == twitter::notification::type::followed)
204 {
205 try
206 {
207 client.follow(n.getUser());
208 } catch (const twitter::twitter_error& error)
209 {
210 std::cout << "Twitter error while following @"
211 << n.getUser().getScreenName() << ": " << error.what() << std::endl;
212 }
213 }
214 });
215
216 std::this_thread::sleep_for(std::chrono::minutes(1));
217
218 // Every once in a while, check if we've lost any followers, and if we have,
219 // unfollow the people who have unfollowed us.
220 for (;;)
221 {
222 try
223 {
224 std::set<twitter::user_id> friends = client.getFriends();
225 std::set<twitter::user_id> followers = client.getFollowers();
226
227 std::list<twitter::user_id> oldFriends;
228 std::set_difference(
229 std::begin(friends),
230 std::end(friends),
231 std::begin(followers),
232 std::end(followers),
233 std::back_inserter(oldFriends));
234
235 for (auto f : oldFriends)
236 {
237 client.unfollow(f);
238 }
239
240 std::list<twitter::user_id> newFollowers;
241 std::set_difference(
242 std::begin(followers),
243 std::end(followers),
244 std::begin(friends),
245 std::end(friends),
246 std::back_inserter(newFollowers));
247
248 for (auto f : newFollowers)
249 {
250 client.follow(f);
251 }
252 } catch (const twitter::twitter_error& e)
253 {
254 std::cout << "Twitter error: " << e.what() << std::endl;
255 }
256
257 std::this_thread::sleep_for(std::chrono::hours(4));
258 }
259}
260
diff --git a/vendor/libtwittercpp b/vendor/libtwittercpp new file mode 160000
Subproject df906121dd862c0f704e44f28ee079158c431c4
diff --git a/vendor/verbly b/vendor/verbly new file mode 160000
Subproject 8916c6911c23ca7ff7e78a90b30a295c2b9b9c2
diff --git a/vendor/yaml-cpp b/vendor/yaml-cpp new file mode 160000
Subproject bedb28fdb4fd52d97e02f6cb946cae631037089