about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2018-08-05 11:14:39 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2018-08-05 11:14:39 -0400
commit38203b745ae1903ba0804ed5532b78d255bea635 (patch)
tree52f57ce85e65361c534c461abc82fb1ed9768d49
parent7c44fd17bb6be54a2ea4b60761e91053ca988977 (diff)
downloadlibtwittercpp-38203b745ae1903ba0804ed5532b78d255bea635.tar.gz
libtwittercpp-38203b745ae1903ba0804ed5532b78d255bea635.tar.bz2
libtwittercpp-38203b745ae1903ba0804ed5532b78d255bea635.zip
Added timeline polling
Because the streaming API will sunset on August 16th, it is essential to implement timeline polling so that the library can still be used to access tweets. This commit breaks the current API even for clients still using the streaming API because the OAuth connection data was pulled from twitter::client into twitter::auth. Other than that, almost everything works the same, and if a client wants to update their libtwitter++ within the next week and a half, they are free to.
-rw-r--r--CMakeLists.txt16
-rw-r--r--src/auth.cpp45
-rw-r--r--src/auth.h44
-rw-r--r--src/client.cpp460
-rw-r--r--src/client.h66
-rw-r--r--src/notification.cpp460
-rw-r--r--src/notification.h58
-rw-r--r--src/request.cpp264
-rw-r--r--src/request.h73
-rw-r--r--src/stream.cpp98
-rw-r--r--src/stream.h34
-rw-r--r--src/timeline.cpp82
-rw-r--r--src/timeline.h31
-rw-r--r--src/tweet.cpp8
-rw-r--r--src/twitter.h1
-rw-r--r--src/util.h16
16 files changed, 934 insertions, 822 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c38613..08851cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -10,7 +10,19 @@ include_directories(vendor/liboauthcpp/include)
10add_subdirectory(vendor/curlcpp) 10add_subdirectory(vendor/curlcpp)
11include_directories(${CURLCPP_SOURCE_DIR}/include) 11include_directories(${CURLCPP_SOURCE_DIR}/include)
12 12
13add_library(twitter++ src/client.cpp src/stream.cpp src/auth.cpp src/tweet.cpp src/codes.cpp src/notification.cpp src/direct_message.cpp src/list.cpp src/user.cpp src/configuration.cpp) 13add_library(twitter++
14set_property(TARGET twitter++ PROPERTY CXX_STANDARD 11) 14 src/client.cpp
15 src/request.cpp
16 src/timeline.cpp
17 src/stream.cpp
18 src/tweet.cpp
19 src/codes.cpp
20 src/notification.cpp
21 src/direct_message.cpp
22 src/list.cpp
23 src/user.cpp
24 src/configuration.cpp)
25
26set_property(TARGET twitter++ PROPERTY CXX_STANDARD 14)
15set_property(TARGET twitter++ PROPERTY CXX_STANDARD_REQUIRED ON) 27set_property(TARGET twitter++ PROPERTY CXX_STANDARD_REQUIRED ON)
16target_link_libraries(twitter++ oauthcpp curlcpp curl pthread) 28target_link_libraries(twitter++ oauthcpp curlcpp curl pthread)
diff --git a/src/auth.cpp b/src/auth.cpp deleted file mode 100644 index f0f17e0..0000000 --- a/src/auth.cpp +++ /dev/null
@@ -1,45 +0,0 @@
1#include "auth.h"
2
3namespace twitter {
4
5 void auth::setConsumerKey(std::string _arg)
6 {
7 _consumer_key = _arg;
8 }
9
10 void auth::setConsumerSecret(std::string _arg)
11 {
12 _consumer_secret = _arg;
13 }
14
15 void auth::setAccessKey(std::string _arg)
16 {
17 _access_key = _arg;
18 }
19
20 void auth::setAccessSecret(std::string _arg)
21 {
22 _access_secret = _arg;
23 }
24
25 std::string auth::getConsumerKey() const
26 {
27 return _consumer_key;
28 }
29
30 std::string auth::getConsumerSecret() const
31 {
32 return _consumer_secret;
33 }
34
35 std::string auth::getAccessKey() const
36 {
37 return _access_key;
38 }
39
40 std::string auth::getAccessSecret() const
41 {
42 return _access_secret;
43 }
44
45};
diff --git a/src/auth.h b/src/auth.h index ef07e81..30c178c 100644 --- a/src/auth.h +++ b/src/auth.h
@@ -2,28 +2,36 @@
2#define AUTH_H_48EF85FD 2#define AUTH_H_48EF85FD
3 3
4#include <string> 4#include <string>
5#include "../vendor/liboauthcpp/include/liboauthcpp/liboauthcpp.h"
5 6
6namespace twitter { 7namespace twitter {
7 8
8 class auth { 9 class auth {
9 public: 10 public:
10 void setConsumerKey(std::string _arg); 11
11 void setConsumerSecret(std::string _arg); 12 auth(
12 void setAccessKey(std::string _arg); 13 std::string consumerKey,
13 void setAccessSecret(std::string _arg); 14 std::string consumerSecret,
14 15 std::string accessKey,
15 std::string getConsumerKey() const; 16 std::string accessSecret) :
16 std::string getConsumerSecret() const; 17 consumer_(std::move(consumerKey), std::move(consumerSecret)),
17 std::string getAccessKey() const; 18 token_(std::move(accessKey), std::move(accessSecret)),
18 std::string getAccessSecret() const; 19 client_(&consumer_, &token_)
19 20 {
20 private: 21 }
21 std::string _consumer_key; 22
22 std::string _consumer_secret; 23 const OAuth::Client& getClient() const
23 std::string _access_key; 24 {
24 std::string _access_secret; 25 return client_;
26 }
27
28 private:
29
30 OAuth::Consumer consumer_;
31 OAuth::Token token_;
32 OAuth::Client client_;
25 }; 33 };
26 34
27}; 35};
28 36
29#endif /* end of include guard: AUTH_H_48EF85FD */ 37#endif /* end of include guard: AUTH_H_48EF85FD */
diff --git a/src/client.cpp b/src/client.cpp index e16e30b..820db80 100644 --- a/src/client.cpp +++ b/src/client.cpp
@@ -2,385 +2,65 @@
2#include <sstream> 2#include <sstream>
3#include <set> 3#include <set>
4#include <algorithm> 4#include <algorithm>
5#include <liboauthcpp/liboauthcpp.h>
6#include <curl_easy.h>
7#include <curl_header.h>
8#include <json.hpp> 5#include <json.hpp>
9#include <thread> 6#include <thread>
10 7#include "request.h"
11// These are here for debugging curl stuff
12
13static
14void dump(const char *text,
15 FILE *stream, unsigned char *ptr, size_t size)
16{
17 size_t i;
18 size_t c;
19 unsigned int width=80;
20
21 fprintf(stream, "%s, %10.10ld bytes (0x%8.8lx)\n",
22 text, (long)size, (long)size);
23
24 for(i=0; i<size; i+= width) {
25 fprintf(stream, "%4.4lx: ", (long)i);
26
27 /* show hex to the left
28 for(c = 0; c < width; c++) {
29 if(i+c < size)
30 fprintf(stream, "%02x ", ptr[i+c]);
31 else
32 fputs(" ", stream);
33 }*/
34
35 /* show data on the right */
36 for(c = 0; (c < width) && (i+c < size); c++) {
37 char x = (ptr[i+c] >= 0x20 && ptr[i+c] < 0x80) ? ptr[i+c] : '.';
38 fputc(x, stream);
39 }
40
41 fputc('\n', stream); /* newline */
42 }
43}
44
45static
46int my_trace(CURL *handle, curl_infotype type,
47 char *data, size_t size,
48 void *userp)
49{
50 const char *text;
51 (void)handle; /* prevent compiler warning */
52
53 switch (type) {
54 case CURLINFO_TEXT:
55 fprintf(stderr, "== Info: %s", data);
56 default: /* in case a new one is introduced to shock us */
57 return 0;
58
59 case CURLINFO_HEADER_OUT:
60 text = "=> Send header";
61 break;
62 case CURLINFO_DATA_OUT:
63 text = "=> Send data";
64 break;
65 case CURLINFO_SSL_DATA_OUT:
66 text = "=> Send SSL data";
67 break;
68 case CURLINFO_HEADER_IN:
69 text = "<= Recv header";
70 break;
71 case CURLINFO_DATA_IN:
72 text = "<= Recv data";
73 break;
74 case CURLINFO_SSL_DATA_IN:
75 text = "<= Recv SSL data";
76 break;
77 }
78
79 dump(text, stderr, (unsigned char *)data, size);
80 return 0;
81}
82 8
83namespace twitter { 9namespace twitter {
84
85 class request
86 {
87 public:
88
89 explicit request(std::string url) try
90 : _ios(_output), _conn(_ios)
91 {
92 _conn.add<CURLOPT_URL>(url.c_str());
93 } catch (const curl::curl_easy_exception& error)
94 {
95 error.print_traceback();
96
97 assert(false);
98 }
99
100 std::string perform()
101 {
102 try
103 {
104 _conn.perform();
105 } catch (const curl::curl_easy_exception& error)
106 {
107 std::throw_with_nested(connection_error());
108 }
109
110 int response_code = _conn.get_info<CURLINFO_RESPONSE_CODE>().get();
111 std::string result = _output.str();
112
113 if (response_code / 100 != 2)
114 {
115 nlohmann::json response_json;
116
117 try
118 {
119 response_json = nlohmann::json::parse(result);
120 } catch (const std::invalid_argument& e)
121 {
122 std::throw_with_nested(invalid_response(result));
123 }
124
125 for (nlohmann::json& error : response_json["errors"])
126 {
127 int error_code;
128 std::string error_message;
129
130 try
131 {
132 error_code = error["code"].get<int>();
133 error_message = error["message"].get<std::string>();
134 } catch (const std::domain_error& e)
135 {
136 std::throw_with_nested(invalid_response(result));
137 }
138
139 switch (error_code)
140 {
141 case 32:
142 case 135:
143 case 215:
144 throw bad_auth(error_message);
145
146 case 44:
147 throw invalid_media(error_message);
148
149 case 64:
150 throw account_suspended(error_message);
151
152 case 88:
153 throw rate_limit_exceeded(error_message);
154
155 case 89:
156 throw bad_token(error_message);
157
158 case 130:
159 throw server_overloaded(error_message);
160
161 case 131:
162 throw server_error(error_message);
163
164 case 185:
165 throw update_limit_exceeded(error_message);
166
167 case 186:
168 throw bad_length(error_message);
169
170 case 187:
171 throw duplicate_status(error_message);
172
173 case 226:
174 throw suspected_spam(error_message);
175
176 case 261:
177 throw write_restricted(error_message);
178 }
179 }
180 10
181 if (response_code == 429) 11 client::client(
182 { 12 const auth& _arg) :
183 throw rate_limit_exceeded("HTTP 429 Too Many Requests"); 13 auth_(_arg),
184 } else if (response_code == 500) 14 currentUser_(
185 { 15 get(auth_,
186 throw server_error("HTTP 500 Internal Server Error");
187 } else if (response_code == 502)
188 {
189 throw server_unavailable("HTTP 502 Bad Gateway");
190 } else if (response_code == 503)
191 {
192 throw server_overloaded("HTTP 503 Service Unavailable");
193 } else if (response_code == 504)
194 {
195 throw server_timeout("HTTP 504 Gateway Timeout");
196 }
197
198 throw unknown_error(response_code, result);
199 }
200
201 return result;
202 }
203
204 private:
205
206 std::ostringstream _output;
207 curl::curl_ios<std::ostringstream> _ios;
208
209 protected:
210
211 curl::curl_easy _conn;
212 };
213
214 class get : public request
215 {
216 public:
217
218 get(const OAuth::Client& oauth_client, std::string url) try
219 : request(url)
220 {
221 std::string oauth_header = oauth_client.getFormattedHttpHeader(OAuth::Http::Get, url, "");
222 if (!oauth_header.empty())
223 {
224 _headers.add(std::move(oauth_header));
225 }
226
227 _conn.add<CURLOPT_HTTPHEADER>(_headers.get());
228 } catch (const OAuth::ParseError& error)
229 {
230 std::cout << "Error generating OAuth header:" << std::endl;
231 std::cout << error.what() << std::endl;
232 std::cout << "This is likely due to a malformed URL." << std::endl;
233
234 assert(false);
235 } catch (const curl::curl_easy_exception& error)
236 {
237 error.print_traceback();
238
239 assert(false);
240 }
241
242 private:
243
244 curl::curl_header _headers;
245 };
246
247 class post : public request
248 {
249 public:
250
251 post(const OAuth::Client& oauth_client, std::string url, std::string datastr) try
252 : request(url)
253 {
254 std::string oauth_header = oauth_client.getFormattedHttpHeader(OAuth::Http::Post, url, datastr);
255 if (!oauth_header.empty())
256 {
257 _headers.add(std::move(oauth_header));
258 }
259
260 _conn.add<CURLOPT_HTTPHEADER>(_headers.get());
261 _conn.add<CURLOPT_COPYPOSTFIELDS>(datastr.c_str());
262 } catch (const OAuth::ParseError& error)
263 {
264 std::cout << "Error generating OAuth header:" << std::endl;
265 std::cout << error.what() << std::endl;
266 std::cout << "This is likely due to a malformed URL." << std::endl;
267
268 assert(false);
269 } catch (const curl::curl_easy_exception& error)
270 {
271 error.print_traceback();
272
273 assert(false);
274 }
275
276 private:
277
278 curl::curl_header _headers;
279 };
280
281 class multipost : public request
282 {
283 public:
284
285 multipost(const OAuth::Client& oauth_client, std::string url, const curl_httppost* fields) try
286 : request(url)
287 {
288 std::string oauth_header = oauth_client.getFormattedHttpHeader(OAuth::Http::Post, url, "");
289 if (!oauth_header.empty())
290 {
291 _headers.add(std::move(oauth_header));
292 }
293
294 _conn.add<CURLOPT_HTTPHEADER>(_headers.get());
295 _conn.add<CURLOPT_HTTPPOST>(fields);
296 } catch (const OAuth::ParseError& error)
297 {
298 std::cout << "Error generating OAuth header:" << std::endl;
299 std::cout << error.what() << std::endl;
300 std::cout << "This is likely due to a malformed URL." << std::endl;
301
302 assert(false);
303 } catch (const curl::curl_easy_exception& error)
304 {
305 error.print_traceback();
306
307 assert(false);
308 }
309
310 private:
311
312 curl::curl_header _headers;
313 };
314
315 client::client(const auth& _arg)
316 {
317 _oauth_consumer =
318 make_unique<OAuth::Consumer>(
319 _arg.getConsumerKey(),
320 _arg.getConsumerSecret());
321
322 _oauth_token =
323 make_unique<OAuth::Token>(
324 _arg.getAccessKey(),
325 _arg.getAccessSecret());
326
327 _oauth_client =
328 make_unique<OAuth::Client>(
329 _oauth_consumer.get(),
330 _oauth_token.get());
331
332 _current_user =
333 make_unique<user>(
334 get(*_oauth_client,
335 "https://api.twitter.com/1.1/account/verify_credentials.json") 16 "https://api.twitter.com/1.1/account/verify_credentials.json")
336 .perform()); 17 .perform())
18 {
337 } 19 }
338 20
339 client::~client() = default;
340
341 tweet client::updateStatus(std::string msg, std::list<long> media_ids) const 21 tweet client::updateStatus(std::string msg, std::list<long> media_ids) const
342 { 22 {
343 std::stringstream datastrstream; 23 std::stringstream datastrstream;
344 datastrstream << "status=" << OAuth::PercentEncode(msg); 24 datastrstream << "status=" << OAuth::PercentEncode(msg);
345 25
346 if (!media_ids.empty()) 26 if (!media_ids.empty())
347 { 27 {
348 datastrstream << "&media_ids="; 28 datastrstream << "&media_ids=";
349 datastrstream << twitter::implode(std::begin(media_ids), std::end(media_ids), ","); 29 datastrstream << twitter::implode(std::begin(media_ids), std::end(media_ids), ",");
350 } 30 }
351 31
352 return tweet( 32 return tweet(
353 post(*_oauth_client, 33 post(auth_,
354 "https://api.twitter.com/1.1/statuses/update.json", 34 "https://api.twitter.com/1.1/statuses/update.json",
355 datastrstream.str()) 35 datastrstream.str())
356 .perform()); 36 .perform());
357 } 37 }
358 38
359 tweet client::replyToTweet(std::string msg, tweet_id in_response_to, std::list<long> media_ids) const 39 tweet client::replyToTweet(std::string msg, tweet_id in_response_to, std::list<long> media_ids) const
360 { 40 {
361 std::stringstream datastrstream; 41 std::stringstream datastrstream;
362 datastrstream << "status=" << OAuth::PercentEncode(msg); 42 datastrstream << "status=" << OAuth::PercentEncode(msg);
363 datastrstream << "&in_reply_to_status_id="; 43 datastrstream << "&in_reply_to_status_id=";
364 datastrstream << in_response_to; 44 datastrstream << in_response_to;
365 45
366 if (!media_ids.empty()) 46 if (!media_ids.empty())
367 { 47 {
368 datastrstream << "&media_ids="; 48 datastrstream << "&media_ids=";
369 datastrstream << twitter::implode(std::begin(media_ids), std::end(media_ids), ","); 49 datastrstream << twitter::implode(std::begin(media_ids), std::end(media_ids), ",");
370 } 50 }
371 51
372 return tweet( 52 return tweet(
373 post(*_oauth_client, 53 post(auth_,
374 "https://api.twitter.com/1.1/statuses/update.json", 54 "https://api.twitter.com/1.1/statuses/update.json",
375 datastrstream.str()) 55 datastrstream.str())
376 .perform()); 56 .perform());
377 } 57 }
378 58
379 long client::uploadMedia(std::string media_type, const char* data, long data_length) const try 59 long client::uploadMedia(std::string media_type, const char* data, long data_length) const try
380 { 60 {
381 curl::curl_form form; 61 curl::curl_form form;
382 std::string str_data_length = std::to_string(data_length); 62 std::string str_data_length = std::to_string(data_length);
383 63
384 curl::curl_pair<CURLformoption, std::string> command_name(CURLFORM_COPYNAME, "command"); 64 curl::curl_pair<CURLformoption, std::string> command_name(CURLFORM_COPYNAME, "command");
385 curl::curl_pair<CURLformoption, std::string> command_cont(CURLFORM_COPYCONTENTS, "INIT"); 65 curl::curl_pair<CURLformoption, std::string> command_cont(CURLFORM_COPYCONTENTS, "INIT");
386 curl::curl_pair<CURLformoption, std::string> bytes_name(CURLFORM_COPYNAME, "total_bytes"); 66 curl::curl_pair<CURLformoption, std::string> bytes_name(CURLFORM_COPYNAME, "total_bytes");
@@ -397,15 +77,15 @@ namespace twitter {
397 curl::curl_pair<CURLformoption, std::string> category_cont(CURLFORM_COPYCONTENTS, "tweet_gif"); 77 curl::curl_pair<CURLformoption, std::string> category_cont(CURLFORM_COPYCONTENTS, "tweet_gif");
398 form.add(category_name, category_cont); 78 form.add(category_name, category_cont);
399 } 79 }
400 80
401 std::string init_response = 81 std::string init_response =
402 multipost(*_oauth_client, 82 multipost(auth_,
403 "https://upload.twitter.com/1.1/media/upload.json", 83 "https://upload.twitter.com/1.1/media/upload.json",
404 form.get()) 84 form.get())
405 .perform(); 85 .perform();
406 86
407 long media_id; 87 long media_id;
408 88
409 try 89 try
410 { 90 {
411 nlohmann::json response_json = nlohmann::json::parse(init_response); 91 nlohmann::json response_json = nlohmann::json::parse(init_response);
@@ -429,29 +109,29 @@ namespace twitter {
429 { 109 {
430 assert(false); 110 assert(false);
431 } 111 }
432 112
433 multipost(*_oauth_client, "https://upload.twitter.com/1.1/media/upload.json", append_form_post).perform(); 113 multipost(auth_, "https://upload.twitter.com/1.1/media/upload.json", append_form_post).perform();
434 114
435 curl_formfree(append_form_post); 115 curl_formfree(append_form_post);
436 116
437 curl::curl_form finalize_form; 117 curl::curl_form finalize_form;
438 std::string str_media_id = std::to_string(media_id); 118 std::string str_media_id = std::to_string(media_id);
439 119
440 curl::curl_pair<CURLformoption, std::string> command3_name(CURLFORM_COPYNAME, "command"); 120 curl::curl_pair<CURLformoption, std::string> command3_name(CURLFORM_COPYNAME, "command");
441 curl::curl_pair<CURLformoption, std::string> command3_cont(CURLFORM_COPYCONTENTS, "FINALIZE"); 121 curl::curl_pair<CURLformoption, std::string> command3_cont(CURLFORM_COPYCONTENTS, "FINALIZE");
442 curl::curl_pair<CURLformoption, std::string> media_id_name(CURLFORM_COPYNAME, "media_id"); 122 curl::curl_pair<CURLformoption, std::string> media_id_name(CURLFORM_COPYNAME, "media_id");
443 curl::curl_pair<CURLformoption, std::string> media_id_cont(CURLFORM_COPYCONTENTS, str_media_id); 123 curl::curl_pair<CURLformoption, std::string> media_id_cont(CURLFORM_COPYCONTENTS, str_media_id);
444 finalize_form.add(command3_name, command3_cont); 124 finalize_form.add(command3_name, command3_cont);
445 finalize_form.add(media_id_name, media_id_cont); 125 finalize_form.add(media_id_name, media_id_cont);
446 126
447 std::string finalize_response = 127 std::string finalize_response =
448 multipost(*_oauth_client, 128 multipost(auth_,
449 "https://upload.twitter.com/1.1/media/upload.json", 129 "https://upload.twitter.com/1.1/media/upload.json",
450 finalize_form.get()) 130 finalize_form.get())
451 .perform(); 131 .perform();
452 132
453 nlohmann::json finalize_json; 133 nlohmann::json finalize_json;
454 134
455 try 135 try
456 { 136 {
457 finalize_json = nlohmann::json::parse(finalize_response); 137 finalize_json = nlohmann::json::parse(finalize_response);
@@ -459,26 +139,26 @@ namespace twitter {
459 { 139 {
460 std::throw_with_nested(invalid_response(finalize_response)); 140 std::throw_with_nested(invalid_response(finalize_response));
461 } 141 }
462 142
463 if (finalize_json.find("processing_info") != finalize_json.end()) 143 if (finalize_json.find("processing_info") != finalize_json.end())
464 { 144 {
465 std::stringstream datastr; 145 std::stringstream datastr;
466 datastr << "https://upload.twitter.com/1.1/media/upload.json?command=STATUS&media_id=" << media_id; 146 datastr << "https://upload.twitter.com/1.1/media/upload.json?command=STATUS&media_id=" << media_id;
467 147
468 for (;;) 148 for (;;)
469 { 149 {
470 std::string status_response = get(*_oauth_client, datastr.str()).perform(); 150 std::string status_response = get(auth_, datastr.str()).perform();
471 151
472 try 152 try
473 { 153 {
474 nlohmann::json status_json = nlohmann::json::parse(status_response); 154 nlohmann::json status_json = nlohmann::json::parse(status_response);
475 std::string state = status_json["processing_info"]["state"].get<std::string>(); 155 std::string state = status_json["processing_info"]["state"].get<std::string>();
476 156
477 if (state == "succeeded") 157 if (state == "succeeded")
478 { 158 {
479 break; 159 break;
480 } 160 }
481 161
482 int ttw = status_json["processing_info"]["check_after_secs"].get<int>(); 162 int ttw = status_json["processing_info"]["check_after_secs"].get<int>();
483 std::this_thread::sleep_for(std::chrono::seconds(ttw)); 163 std::this_thread::sleep_for(std::chrono::seconds(ttw));
484 } catch (const std::invalid_argument& error) 164 } catch (const std::invalid_argument& error)
@@ -490,20 +170,18 @@ namespace twitter {
490 } 170 }
491 } 171 }
492 } 172 }
493 173
494 return media_id; 174 return media_id;
495 } catch (const curl::curl_exception& error) 175 } catch (const curl::curl_exception& error)
496 { 176 {
497 error.print_traceback(); 177 std::throw_with_nested(connection_error());
498
499 assert(false);
500 } 178 }
501 179
502 std::set<user_id> client::getFriends(user_id id) const 180 std::set<user_id> client::getFriends(user_id id) const
503 { 181 {
504 long long cursor = -1; 182 long long cursor = -1;
505 std::set<user_id> result; 183 std::set<user_id> result;
506 184
507 while (cursor != 0) 185 while (cursor != 0)
508 { 186 {
509 std::stringstream urlstream; 187 std::stringstream urlstream;
@@ -511,14 +189,14 @@ namespace twitter {
511 urlstream << id; 189 urlstream << id;
512 urlstream << "&cursor="; 190 urlstream << "&cursor=";
513 urlstream << cursor; 191 urlstream << cursor;
514 192
515 std::string url = urlstream.str(); 193 std::string url = urlstream.str();
516 std::string response_data = get(*_oauth_client, url).perform(); 194 std::string response_data = get(auth_, url).perform();
517 195
518 try 196 try
519 { 197 {
520 nlohmann::json rjs = nlohmann::json::parse(response_data); 198 nlohmann::json rjs = nlohmann::json::parse(response_data);
521 199
522 cursor = rjs["next_cursor"].get<long long>(); 200 cursor = rjs["next_cursor"].get<long long>();
523 result.insert(std::begin(rjs["ids"]), std::end(rjs["ids"])); 201 result.insert(std::begin(rjs["ids"]), std::end(rjs["ids"]));
524 } catch (const std::invalid_argument& error) 202 } catch (const std::invalid_argument& error)
@@ -529,25 +207,25 @@ namespace twitter {
529 std::throw_with_nested(invalid_response(response_data)); 207 std::throw_with_nested(invalid_response(response_data));
530 } 208 }
531 } 209 }
532 210
533 return result; 211 return result;
534 } 212 }
535 213
536 std::set<user_id> client::getFriends(const user& id) const 214 std::set<user_id> client::getFriends(const user& id) const
537 { 215 {
538 return getFriends(id.getID()); 216 return getFriends(id.getID());
539 } 217 }
540 218
541 std::set<user_id> client::getFriends() const 219 std::set<user_id> client::getFriends() const
542 { 220 {
543 return getFriends(getUser().getID()); 221 return getFriends(getUser().getID());
544 } 222 }
545 223
546 std::set<user_id> client::getFollowers(user_id id) const 224 std::set<user_id> client::getFollowers(user_id id) const
547 { 225 {
548 long long cursor = -1; 226 long long cursor = -1;
549 std::set<user_id> result; 227 std::set<user_id> result;
550 228
551 while (cursor != 0) 229 while (cursor != 0)
552 { 230 {
553 std::stringstream urlstream; 231 std::stringstream urlstream;
@@ -555,14 +233,14 @@ namespace twitter {
555 urlstream << id; 233 urlstream << id;
556 urlstream << "&cursor="; 234 urlstream << "&cursor=";
557 urlstream << cursor; 235 urlstream << cursor;
558 236
559 std::string url = urlstream.str(); 237 std::string url = urlstream.str();
560 std::string response_data = get(*_oauth_client, url).perform(); 238 std::string response_data = get(auth_, url).perform();
561 239
562 try 240 try
563 { 241 {
564 nlohmann::json rjs = nlohmann::json::parse(response_data); 242 nlohmann::json rjs = nlohmann::json::parse(response_data);
565 243
566 cursor = rjs["next_cursor"].get<long long>(); 244 cursor = rjs["next_cursor"].get<long long>();
567 result.insert(std::begin(rjs["ids"]), std::end(rjs["ids"])); 245 result.insert(std::begin(rjs["ids"]), std::end(rjs["ids"]));
568 } catch (const std::invalid_argument& error) 246 } catch (const std::invalid_argument& error)
@@ -573,43 +251,43 @@ namespace twitter {
573 std::throw_with_nested(invalid_response(response_data)); 251 std::throw_with_nested(invalid_response(response_data));
574 } 252 }
575 } 253 }
576 254
577 return result; 255 return result;
578 } 256 }
579 257
580 std::set<user_id> client::getFollowers(const user& id) const 258 std::set<user_id> client::getFollowers(const user& id) const
581 { 259 {
582 return getFollowers(id.getID()); 260 return getFollowers(id.getID());
583 } 261 }
584 262
585 std::set<user_id> client::getFollowers() const 263 std::set<user_id> client::getFollowers() const
586 { 264 {
587 return getFollowers(getUser().getID()); 265 return getFollowers(getUser().getID());
588 } 266 }
589 267
590 void client::follow(user_id toFollow) const 268 void client::follow(user_id toFollow) const
591 { 269 {
592 std::stringstream datastrstream; 270 std::stringstream datastrstream;
593 datastrstream << "follow=true&user_id="; 271 datastrstream << "follow=true&user_id=";
594 datastrstream << toFollow; 272 datastrstream << toFollow;
595 273
596 post(*_oauth_client, "https://api.twitter.com/1.1/friendships/create.json", datastrstream.str()).perform(); 274 post(auth_, "https://api.twitter.com/1.1/friendships/create.json", datastrstream.str()).perform();
597 } 275 }
598 276
599 void client::follow(const user& toFollow) const 277 void client::follow(const user& toFollow) const
600 { 278 {
601 return follow(toFollow.getID()); 279 return follow(toFollow.getID());
602 } 280 }
603 281
604 void client::unfollow(user_id toUnfollow) const 282 void client::unfollow(user_id toUnfollow) const
605 { 283 {
606 std::stringstream datastrstream; 284 std::stringstream datastrstream;
607 datastrstream << "user_id="; 285 datastrstream << "user_id=";
608 datastrstream << toUnfollow; 286 datastrstream << toUnfollow;
609 287
610 post(*_oauth_client, "https://api.twitter.com/1.1/friendships/destroy.json", datastrstream.str()).perform(); 288 post(auth_, "https://api.twitter.com/1.1/friendships/destroy.json", datastrstream.str()).perform();
611 } 289 }
612 290
613 void client::unfollow(const user& toUnfollow) const 291 void client::unfollow(const user& toUnfollow) const
614 { 292 {
615 return unfollow(toUnfollow.getID()); 293 return unfollow(toUnfollow.getID());
@@ -617,23 +295,23 @@ namespace twitter {
617 295
618 const user& client::getUser() const 296 const user& client::getUser() const
619 { 297 {
620 return *_current_user; 298 return currentUser_;
621 } 299 }
622 300
623 const configuration& client::getConfiguration() const 301 const configuration& client::getConfiguration() const
624 { 302 {
625 if (!_configuration || (difftime(time(NULL), _last_configuration_update) > 60*60*24)) 303 if (!_configuration || (difftime(time(NULL), _last_configuration_update) > 60*60*24))
626 { 304 {
627 _configuration = 305 _configuration =
628 make_unique<configuration>( 306 std::make_unique<configuration>(
629 get(*_oauth_client, 307 get(auth_,
630 "https://api.twitter.com/1.1/help/configuration.json") 308 "https://api.twitter.com/1.1/help/configuration.json")
631 .perform()); 309 .perform());
632 310
633 _last_configuration_update = time(NULL); 311 _last_configuration_update = time(NULL);
634 } 312 }
635 313
636 return *_configuration; 314 return *_configuration;
637 } 315 }
638 316
639}; 317};
diff --git a/src/client.h b/src/client.h index 2230dbb..c0a63eb 100644 --- a/src/client.h +++ b/src/client.h
@@ -10,58 +10,66 @@
10#include "auth.h" 10#include "auth.h"
11#include "configuration.h" 11#include "configuration.h"
12#include "util.h" 12#include "util.h"
13 13#include "timeline.h"
14namespace OAuth {
15 class Consumer;
16 class Token;
17 class Client;
18};
19 14
20namespace twitter { 15namespace twitter {
21 16
22 class client { 17 class client {
23 public: 18 public:
24 19
25 client(const auth& _auth); 20 client(const auth& arg);
26 ~client(); 21
27
28 tweet updateStatus(std::string msg, std::list<long> media_ids = {}) const; 22 tweet updateStatus(std::string msg, std::list<long> media_ids = {}) const;
29 long uploadMedia(std::string media_type, const char* data, long data_length) const; 23 long uploadMedia(std::string media_type, const char* data, long data_length) const;
30 24
31 tweet replyToTweet(std::string msg, tweet_id in_response_to, std::list<long> media_ids = {}) const; 25 tweet replyToTweet(std::string msg, tweet_id in_response_to, std::list<long> media_ids = {}) const;
32 26
33 std::set<user_id> getFriends(user_id id) const; 27 std::set<user_id> getFriends(user_id id) const;
34 std::set<user_id> getFriends(const user& u) const; 28 std::set<user_id> getFriends(const user& u) const;
35 std::set<user_id> getFriends() const; 29 std::set<user_id> getFriends() const;
36 30
37 std::set<user_id> getFollowers(user_id id) const; 31 std::set<user_id> getFollowers(user_id id) const;
38 std::set<user_id> getFollowers(const user& u) const; 32 std::set<user_id> getFollowers(const user& u) const;
39 std::set<user_id> getFollowers() const; 33 std::set<user_id> getFollowers() const;
40 34
41 void follow(user_id toFollow) const; 35 void follow(user_id toFollow) const;
42 void follow(const user& toFollow) const; 36 void follow(const user& toFollow) const;
43 37
44 void unfollow(user_id toUnfollow) const; 38 void unfollow(user_id toUnfollow) const;
45 void unfollow(const user& toUnfollow) const; 39 void unfollow(const user& toUnfollow) const;
46 40
47 const user& getUser() const; 41 const user& getUser() const;
48 42
49 const configuration& getConfiguration() const; 43 const configuration& getConfiguration() const;
50 44
45 timeline& getHomeTimeline()
46 {
47 return homeTimeline_;
48 }
49
50 timeline& getMentionsTimeline()
51 {
52 return mentionsTimeline_;
53 }
54
51 private: 55 private:
52 56
53 friend class stream; 57 const auth& auth_;
54 58
55 std::unique_ptr<OAuth::Consumer> _oauth_consumer; 59 user currentUser_;
56 std::unique_ptr<OAuth::Token> _oauth_token; 60
57 std::unique_ptr<OAuth::Client> _oauth_client;
58
59 std::unique_ptr<user> _current_user;
60
61 mutable std::unique_ptr<configuration> _configuration; 61 mutable std::unique_ptr<configuration> _configuration;
62 mutable time_t _last_configuration_update; 62 mutable time_t _last_configuration_update;
63
64 timeline homeTimeline_ {
65 auth_,
66 "https://api.twitter.com/1.1/statuses/home_timeline.json"};
67
68 timeline mentionsTimeline_ {
69 auth_,
70 "https://api.twitter.com/1.1/statuses/mentions_timeline.json"};
63 }; 71 };
64 72
65}; 73};
66 74
67#endif /* end of include guard: TWITTER_H_ABFF6A12 */ 75#endif /* end of include guard: TWITTER_H_ABFF6A12 */
diff --git a/src/notification.cpp b/src/notification.cpp index 0e46112..0d09fcf 100644 --- a/src/notification.cpp +++ b/src/notification.cpp
@@ -6,18 +6,16 @@
6#include "client.h" 6#include "client.h"
7 7
8namespace twitter { 8namespace twitter {
9 9
10 notification::type notification::getType() const 10 notification::type notification::getType() const
11 { 11 {
12 return _type; 12 return _type;
13 } 13 }
14 14
15 notification::notification(const client& tclient, std::string data) 15 notification::notification(const user& current_user, std::string data)
16 { 16 {
17 const user& current_user = tclient.getUser();
18
19 nlohmann::json json; 17 nlohmann::json json;
20 18
21 try 19 try
22 { 20 {
23 json = nlohmann::json::parse(data); 21 json = nlohmann::json::parse(data);
@@ -25,7 +23,7 @@ namespace twitter {
25 { 23 {
26 std::throw_with_nested(invalid_response(data)); 24 std::throw_with_nested(invalid_response(data));
27 } 25 }
28 26
29 try 27 try
30 { 28 {
31 if (!json["event"].is_null()) 29 if (!json["event"].is_null())
@@ -33,48 +31,48 @@ namespace twitter {
33 std::string event = json["event"]; 31 std::string event = json["event"];
34 user source(json["source"].dump()); 32 user source(json["source"].dump());
35 user target(json["target"].dump()); 33 user target(json["target"].dump());
36 34
37 if (event == "user_update") 35 if (event == "user_update")
38 { 36 {
39 _type = type::update_user; 37 _type = type::update_user;
40 38
41 new(&_user) user(source); 39 new(&_user) user(source);
42 } else if (event == "block") 40 } else if (event == "block")
43 { 41 {
44 _type = type::block; 42 _type = type::block;
45 43
46 new(&_user) user(target); 44 new(&_user) user(target);
47 } else if (event == "unblock") 45 } else if (event == "unblock")
48 { 46 {
49 _type = type::unblock; 47 _type = type::unblock;
50 48
51 new(&_user) user(target); 49 new(&_user) user(target);
52 } else if (event == "favorite") 50 } else if (event == "favorite")
53 { 51 {
54 new(&_user_and_tweet._tweet) tweet(json["target_object"].dump()); 52 new(&_user_and_tweet._tweet) tweet(json["target_object"].dump());
55 53
56 if (current_user == source) 54 if (current_user == source)
57 { 55 {
58 _type = type::favorite; 56 _type = type::favorite;
59 57
60 new(&_user_and_tweet._user) user(target); 58 new(&_user_and_tweet._user) user(target);
61 } else { 59 } else {
62 _type = type::favorited; 60 _type = type::favorited;
63 61
64 new(&_user_and_tweet._user) user(source); 62 new(&_user_and_tweet._user) user(source);
65 } 63 }
66 } else if (event == "unfavorite") 64 } else if (event == "unfavorite")
67 { 65 {
68 new(&_user_and_tweet._tweet) tweet(json["target_object"].dump()); 66 new(&_user_and_tweet._tweet) tweet(json["target_object"].dump());
69 67
70 if (current_user == source) 68 if (current_user == source)
71 { 69 {
72 _type = type::unfavorite; 70 _type = type::unfavorite;
73 71
74 new(&_user_and_tweet._user) user(target); 72 new(&_user_and_tweet._user) user(target);
75 } else { 73 } else {
76 _type = type::unfavorited; 74 _type = type::unfavorited;
77 75
78 new(&_user_and_tweet._user) user(source); 76 new(&_user_and_tweet._user) user(source);
79 } 77 }
80 } else if (event == "follow") 78 } else if (event == "follow")
@@ -82,93 +80,93 @@ namespace twitter {
82 if (current_user == source) 80 if (current_user == source)
83 { 81 {
84 _type = type::follow; 82 _type = type::follow;
85 83
86 new(&_user) user(target); 84 new(&_user) user(target);
87 } else { 85 } else {
88 _type = type::followed; 86 _type = type::followed;
89 87
90 new(&_user) user(source); 88 new(&_user) user(source);
91 } 89 }
92 } else if (event == "unfollow") 90 } else if (event == "unfollow")
93 { 91 {
94 _type = type::unfollow; 92 _type = type::unfollow;
95 93
96 new(&_user) user(target); 94 new(&_user) user(target);
97 } else if (event == "list_created") 95 } else if (event == "list_created")
98 { 96 {
99 _type = type::list_created; 97 _type = type::list_created;
100 98
101 new(&_list) list(json["target_object"].dump()); 99 new(&_list) list(json["target_object"].dump());
102 } else if (event == "list_destroyed") 100 } else if (event == "list_destroyed")
103 { 101 {
104 _type = type::list_destroyed; 102 _type = type::list_destroyed;
105 103
106 new(&_list) list(json["target_object"].dump()); 104 new(&_list) list(json["target_object"].dump());
107 } else if (event == "list_updated") 105 } else if (event == "list_updated")
108 { 106 {
109 _type = type::list_updated; 107 _type = type::list_updated;
110 108
111 new(&_list) list(json["target_object"].dump()); 109 new(&_list) list(json["target_object"].dump());
112 } else if (event == "list_member_added") 110 } else if (event == "list_member_added")
113 { 111 {
114 new(&_user_and_list._list) list(json["target_object"].dump()); 112 new(&_user_and_list._list) list(json["target_object"].dump());
115 113
116 if (current_user == source) 114 if (current_user == source)
117 { 115 {
118 _type = type::list_add; 116 _type = type::list_add;
119 117
120 new(&_user_and_list._user) user(target); 118 new(&_user_and_list._user) user(target);
121 } else { 119 } else {
122 _type = type::list_added; 120 _type = type::list_added;
123 121
124 new(&_user_and_list._user) user(source); 122 new(&_user_and_list._user) user(source);
125 } 123 }
126 } else if (event == "list_member_removed") 124 } else if (event == "list_member_removed")
127 { 125 {
128 new(&_user_and_list._list) list(json["target_object"].dump()); 126 new(&_user_and_list._list) list(json["target_object"].dump());
129 127
130 if (current_user == source) 128 if (current_user == source)
131 { 129 {
132 _type = type::list_remove; 130 _type = type::list_remove;
133 131
134 new(&_user_and_list._user) user(target); 132 new(&_user_and_list._user) user(target);
135 } else { 133 } else {
136 _type = type::list_removed; 134 _type = type::list_removed;
137 135
138 new(&_user_and_list._user) user(source); 136 new(&_user_and_list._user) user(source);
139 } 137 }
140 } else if (event == "list_member_subscribe") 138 } else if (event == "list_member_subscribe")
141 { 139 {
142 new(&_user_and_list._list) list(json["target_object"].dump()); 140 new(&_user_and_list._list) list(json["target_object"].dump());
143 141
144 if (current_user == source) 142 if (current_user == source)
145 { 143 {
146 _type = type::list_subscribe; 144 _type = type::list_subscribe;
147 145
148 new(&_user_and_list._user) user(target); 146 new(&_user_and_list._user) user(target);
149 } else { 147 } else {
150 _type = type::list_subscribed; 148 _type = type::list_subscribed;
151 149
152 new(&_user_and_list._user) user(source); 150 new(&_user_and_list._user) user(source);
153 } 151 }
154 } else if (event == "list_member_unsubscribe") 152 } else if (event == "list_member_unsubscribe")
155 { 153 {
156 new(&_user_and_list._list) list(json["target_object"].dump()); 154 new(&_user_and_list._list) list(json["target_object"].dump());
157 155
158 if (current_user == source) 156 if (current_user == source)
159 { 157 {
160 _type = type::list_unsubscribe; 158 _type = type::list_unsubscribe;
161 159
162 new(&_user_and_list._user) user(target); 160 new(&_user_and_list._user) user(target);
163 } else { 161 } else {
164 _type = type::list_unsubscribed; 162 _type = type::list_unsubscribed;
165 163
166 new(&_user_and_list._user) user(source); 164 new(&_user_and_list._user) user(source);
167 } 165 }
168 } else if (event == "quoted_tweet") 166 } else if (event == "quoted_tweet")
169 { 167 {
170 _type = type::quoted; 168 _type = type::quoted;
171 169
172 new(&_user_and_tweet._user) user(source); 170 new(&_user_and_tweet._user) user(source);
173 new(&_user_and_tweet._tweet) tweet(json["target_object"].dump()); 171 new(&_user_and_tweet._tweet) tweet(json["target_object"].dump());
174 } else { 172 } else {
@@ -177,7 +175,7 @@ namespace twitter {
177 } else if (!json["warning"].is_null()) 175 } else if (!json["warning"].is_null())
178 { 176 {
179 new(&_warning) std::string(json["warning"]["message"].get<std::string>()); 177 new(&_warning) std::string(json["warning"]["message"].get<std::string>());
180 178
181 auto warning_code = json["warning"]["code"].get<std::string>(); 179 auto warning_code = json["warning"]["code"].get<std::string>();
182 if (warning_code == "FALLING_BEHIND") 180 if (warning_code == "FALLING_BEHIND")
183 { 181 {
@@ -191,27 +189,27 @@ namespace twitter {
191 } else if (!json["delete"].is_null()) 189 } else if (!json["delete"].is_null())
192 { 190 {
193 _type = type::deletion; 191 _type = type::deletion;
194 192
195 _user_id_and_tweet_id._tweet_id = json["delete"]["status"]["id"].get<tweet_id>(); 193 _user_id_and_tweet_id._tweet_id = json["delete"]["status"]["id"].get<tweet_id>();
196 _user_id_and_tweet_id._user_id = json["delete"]["status"]["user_id"].get<user_id>(); 194 _user_id_and_tweet_id._user_id = json["delete"]["status"]["user_id"].get<user_id>();
197 } else if (!json["scrub_geo"].is_null()) 195 } else if (!json["scrub_geo"].is_null())
198 { 196 {
199 _type = type::scrub_location; 197 _type = type::scrub_location;
200 198
201 _user_id_and_tweet_id._tweet_id = json["scrub_geo"]["up_to_status_id"].get<tweet_id>(); 199 _user_id_and_tweet_id._tweet_id = json["scrub_geo"]["up_to_status_id"].get<tweet_id>();
202 _user_id_and_tweet_id._user_id = json["scrub_geo"]["user_id"].get<user_id>(); 200 _user_id_and_tweet_id._user_id = json["scrub_geo"]["user_id"].get<user_id>();
203 } else if (!json["limit"].is_null()) 201 } else if (!json["limit"].is_null())
204 { 202 {
205 _type = type::limit; 203 _type = type::limit;
206 204
207 _limit = json["limit"]["track"].get<int>(); 205 _limit = json["limit"]["track"].get<int>();
208 } else if (!json["status_withheld"].is_null()) 206 } else if (!json["status_withheld"].is_null())
209 { 207 {
210 _type = type::withhold_status; 208 _type = type::withhold_status;
211 209
212 _withhold_status._user_id = json["status_withheld"]["user_id"].get<user_id>(); 210 _withhold_status._user_id = json["status_withheld"]["user_id"].get<user_id>();
213 _withhold_status._tweet_id = json["status_withheld"]["id"].get<tweet_id>(); 211 _withhold_status._tweet_id = json["status_withheld"]["id"].get<tweet_id>();
214 212
215 new(&_withhold_status._countries) std::vector<std::string>(); 213 new(&_withhold_status._countries) std::vector<std::string>();
216 for (auto s : json["status_withheld"]["withheld_in_countries"]) 214 for (auto s : json["status_withheld"]["withheld_in_countries"])
217 { 215 {
@@ -220,9 +218,9 @@ namespace twitter {
220 } else if (!json["user_withheld"].is_null()) 218 } else if (!json["user_withheld"].is_null())
221 { 219 {
222 _type = type::withhold_user; 220 _type = type::withhold_user;
223 221
224 _withhold_user._user_id = json["user_withheld"]["id"].get<user_id>(); 222 _withhold_user._user_id = json["user_withheld"]["id"].get<user_id>();
225 223
226 new(&_withhold_user._countries) std::vector<std::string>(); 224 new(&_withhold_user._countries) std::vector<std::string>();
227 for (auto s : json["user_withheld"]["withheld_in_countries"]) 225 for (auto s : json["user_withheld"]["withheld_in_countries"])
228 { 226 {
@@ -231,7 +229,7 @@ namespace twitter {
231 } else if (!json["disconnect"].is_null()) 229 } else if (!json["disconnect"].is_null())
232 { 230 {
233 _type = type::disconnect; 231 _type = type::disconnect;
234 232
235 switch (json["disconnect"]["code"].get<int>()) 233 switch (json["disconnect"]["code"].get<int>())
236 { 234 {
237 case 1: _disconnect = disconnect_code::shutdown; break; 235 case 1: _disconnect = disconnect_code::shutdown; break;
@@ -249,16 +247,16 @@ namespace twitter {
249 } else if (!json["friends"].is_null()) 247 } else if (!json["friends"].is_null())
250 { 248 {
251 _type = type::friends; 249 _type = type::friends;
252 250
253 new(&_friends) std::set<user_id>(std::begin(json["friends"]), std::end(json["friends"])); 251 new(&_friends) std::set<user_id>(std::begin(json["friends"]), std::end(json["friends"]));
254 } else if (!json["direct_message"].is_null()) 252 } else if (!json["direct_message"].is_null())
255 { 253 {
256 _type = type::direct; 254 _type = type::direct;
257 255
258 new(&_direct_message) direct_message(json["direct_message"].dump()); 256 new(&_direct_message) direct_message(json["direct_message"].dump());
259 } else { 257 } else {
260 _type = type::tweet; 258 _type = type::tweet;
261 259
262 new(&_tweet) tweet(data); 260 new(&_tweet) tweet(data);
263 } 261 }
264 } catch (const std::domain_error& error) 262 } catch (const std::domain_error& error)
@@ -266,20 +264,20 @@ namespace twitter {
266 std::throw_with_nested(invalid_response(data)); 264 std::throw_with_nested(invalid_response(data));
267 } 265 }
268 } 266 }
269 267
270 notification::notification(const notification& other) 268 notification::notification(const notification& other)
271 { 269 {
272 _type = other._type; 270 _type = other._type;
273 271
274 switch (_type) 272 switch (_type)
275 { 273 {
276 case type::tweet: 274 case type::tweet:
277 { 275 {
278 new(&_tweet) tweet(other._tweet); 276 new(&_tweet) tweet(other._tweet);
279 277
280 break; 278 break;
281 } 279 }
282 280
283 case type::update_user: 281 case type::update_user:
284 case type::block: 282 case type::block:
285 case type::unblock: 283 case type::unblock:
@@ -288,10 +286,10 @@ namespace twitter {
288 case type::unfollow: 286 case type::unfollow:
289 { 287 {
290 new(&_user) user(other._user); 288 new(&_user) user(other._user);
291 289
292 break; 290 break;
293 } 291 }
294 292
295 case type::favorite: 293 case type::favorite:
296 case type::favorited: 294 case type::favorited:
297 case type::unfavorite: 295 case type::unfavorite:
@@ -300,19 +298,19 @@ namespace twitter {
300 { 298 {
301 new(&_user_and_tweet._user) user(other._user_and_tweet._user); 299 new(&_user_and_tweet._user) user(other._user_and_tweet._user);
302 new(&_user_and_tweet._tweet) tweet(other._user_and_tweet._tweet); 300 new(&_user_and_tweet._tweet) tweet(other._user_and_tweet._tweet);
303 301
304 break; 302 break;
305 } 303 }
306 304
307 case type::list_created: 305 case type::list_created:
308 case type::list_destroyed: 306 case type::list_destroyed:
309 case type::list_updated: 307 case type::list_updated:
310 { 308 {
311 new(&_list) list(other._list); 309 new(&_list) list(other._list);
312 310
313 break; 311 break;
314 } 312 }
315 313
316 case type::list_add: 314 case type::list_add:
317 case type::list_added: 315 case type::list_added:
318 case type::list_remove: 316 case type::list_remove:
@@ -324,73 +322,73 @@ namespace twitter {
324 { 322 {
325 new(&_user_and_list._user) user(other._user_and_list._user); 323 new(&_user_and_list._user) user(other._user_and_list._user);
326 new(&_user_and_list._list) list(other._user_and_list._list); 324 new(&_user_and_list._list) list(other._user_and_list._list);
327 325
328 break; 326 break;
329 } 327 }
330 328
331 case type::stall: 329 case type::stall:
332 case type::follow_limit: 330 case type::follow_limit:
333 case type::unknown_warning: 331 case type::unknown_warning:
334 { 332 {
335 new(&_warning) std::string(other._warning); 333 new(&_warning) std::string(other._warning);
336 334
337 break; 335 break;
338 } 336 }
339 337
340 case type::deletion: 338 case type::deletion:
341 case type::scrub_location: 339 case type::scrub_location:
342 { 340 {
343 _user_id_and_tweet_id._user_id = other._user_id_and_tweet_id._user_id; 341 _user_id_and_tweet_id._user_id = other._user_id_and_tweet_id._user_id;
344 _user_id_and_tweet_id._tweet_id = other._user_id_and_tweet_id._tweet_id; 342 _user_id_and_tweet_id._tweet_id = other._user_id_and_tweet_id._tweet_id;
345 343
346 break; 344 break;
347 } 345 }
348 346
349 case type::limit: 347 case type::limit:
350 { 348 {
351 _limit = other._limit; 349 _limit = other._limit;
352 350
353 break; 351 break;
354 } 352 }
355 353
356 case type::withhold_status: 354 case type::withhold_status:
357 { 355 {
358 _withhold_status._user_id = other._withhold_status._user_id; 356 _withhold_status._user_id = other._withhold_status._user_id;
359 _withhold_status._tweet_id = other._withhold_status._tweet_id; 357 _withhold_status._tweet_id = other._withhold_status._tweet_id;
360 new(&_withhold_status._countries) std::vector<std::string>(other._withhold_status._countries); 358 new(&_withhold_status._countries) std::vector<std::string>(other._withhold_status._countries);
361 359
362 break; 360 break;
363 } 361 }
364 362
365 case type::withhold_user: 363 case type::withhold_user:
366 { 364 {
367 _withhold_user._user_id = other._withhold_user._user_id; 365 _withhold_user._user_id = other._withhold_user._user_id;
368 new(&_withhold_user._countries) std::vector<std::string>(other._withhold_user._countries); 366 new(&_withhold_user._countries) std::vector<std::string>(other._withhold_user._countries);
369 367
370 break; 368 break;
371 } 369 }
372 370
373 case type::disconnect: 371 case type::disconnect:
374 { 372 {
375 _disconnect = other._disconnect; 373 _disconnect = other._disconnect;
376 374
377 break; 375 break;
378 } 376 }
379 377
380 case type::friends: 378 case type::friends:
381 { 379 {
382 new(&_friends) std::set<user_id>(other._friends); 380 new(&_friends) std::set<user_id>(other._friends);
383 381
384 break; 382 break;
385 } 383 }
386 384
387 case type::direct: 385 case type::direct:
388 { 386 {
389 new(&_direct_message) direct_message(other._direct_message); 387 new(&_direct_message) direct_message(other._direct_message);
390 388
391 break; 389 break;
392 } 390 }
393 391
394 case type::unknown: 392 case type::unknown:
395 case type::invalid: 393 case type::invalid:
396 { 394 {
@@ -398,19 +396,19 @@ namespace twitter {
398 } 396 }
399 } 397 }
400 } 398 }
401 399
402 notification::notification(notification&& other) : notification() 400 notification::notification(notification&& other) : notification()
403 { 401 {
404 swap(*this, other); 402 swap(*this, other);
405 } 403 }
406 404
407 notification& notification::operator=(notification other) 405 notification& notification::operator=(notification other)
408 { 406 {
409 swap(*this, other); 407 swap(*this, other);
410 408
411 return *this; 409 return *this;
412 } 410 }
413 411
414 notification::~notification() 412 notification::~notification()
415 { 413 {
416 switch (_type) 414 switch (_type)
@@ -418,10 +416,10 @@ namespace twitter {
418 case type::tweet: 416 case type::tweet:
419 { 417 {
420 _tweet.~tweet(); 418 _tweet.~tweet();
421 419
422 break; 420 break;
423 } 421 }
424 422
425 case type::update_user: 423 case type::update_user:
426 case type::block: 424 case type::block:
427 case type::unblock: 425 case type::unblock:
@@ -430,10 +428,10 @@ namespace twitter {
430 case type::unfollow: 428 case type::unfollow:
431 { 429 {
432 _user.~user(); 430 _user.~user();
433 431
434 break; 432 break;
435 } 433 }
436 434
437 case type::favorite: 435 case type::favorite:
438 case type::favorited: 436 case type::favorited:
439 case type::unfavorite: 437 case type::unfavorite:
@@ -442,19 +440,19 @@ namespace twitter {
442 { 440 {
443 _user_and_tweet._user.~user(); 441 _user_and_tweet._user.~user();
444 _user_and_tweet._tweet.~tweet(); 442 _user_and_tweet._tweet.~tweet();
445 443
446 break; 444 break;
447 } 445 }
448 446
449 case type::list_created: 447 case type::list_created:
450 case type::list_destroyed: 448 case type::list_destroyed:
451 case type::list_updated: 449 case type::list_updated:
452 { 450 {
453 _list.~list(); 451 _list.~list();
454 452
455 break; 453 break;
456 } 454 }
457 455
458 case type::list_add: 456 case type::list_add:
459 case type::list_added: 457 case type::list_added:
460 case type::list_remove: 458 case type::list_remove:
@@ -466,51 +464,51 @@ namespace twitter {
466 { 464 {
467 _user_and_list._user.~user(); 465 _user_and_list._user.~user();
468 _user_and_list._list.~list(); 466 _user_and_list._list.~list();
469 467
470 break; 468 break;
471 } 469 }
472 470
473 case type::stall: 471 case type::stall:
474 case type::follow_limit: 472 case type::follow_limit:
475 case type::unknown_warning: 473 case type::unknown_warning:
476 { 474 {
477 using string_type = std::string; 475 using string_type = std::string;
478 _warning.~string_type(); 476 _warning.~string_type();
479 477
480 break; 478 break;
481 } 479 }
482 480
483 case type::withhold_status: 481 case type::withhold_status:
484 { 482 {
485 using list_type = std::vector<std::string>; 483 using list_type = std::vector<std::string>;
486 _withhold_status._countries.~list_type(); 484 _withhold_status._countries.~list_type();
487 485
488 break; 486 break;
489 } 487 }
490 488
491 case type::withhold_user: 489 case type::withhold_user:
492 { 490 {
493 using list_type = std::vector<std::string>; 491 using list_type = std::vector<std::string>;
494 _withhold_user._countries.~list_type(); 492 _withhold_user._countries.~list_type();
495 493
496 break; 494 break;
497 } 495 }
498 496
499 case type::friends: 497 case type::friends:
500 { 498 {
501 using list_type = std::set<user_id>; 499 using list_type = std::set<user_id>;
502 _friends.~list_type(); 500 _friends.~list_type();
503 501
504 break; 502 break;
505 } 503 }
506 504
507 case type::direct: 505 case type::direct:
508 { 506 {
509 _direct_message.~direct_message(); 507 _direct_message.~direct_message();
510 508
511 break; 509 break;
512 } 510 }
513 511
514 case type::deletion: 512 case type::deletion:
515 case type::scrub_location: 513 case type::scrub_location:
516 case type::limit: 514 case type::limit:
@@ -522,11 +520,11 @@ namespace twitter {
522 } 520 }
523 } 521 }
524 } 522 }
525 523
526 void swap(notification& first, notification& second) 524 void swap(notification& first, notification& second)
527 { 525 {
528 using type = notification::type; 526 using type = notification::type;
529 527
530 type tempType = first._type; 528 type tempType = first._type;
531 tweet tempTweet; 529 tweet tempTweet;
532 user tempUser; 530 user tempUser;
@@ -539,16 +537,16 @@ namespace twitter {
539 disconnect_code tempDisconnectCode; 537 disconnect_code tempDisconnectCode;
540 std::set<user_id> tempFriends; 538 std::set<user_id> tempFriends;
541 direct_message tempDirectMessage; 539 direct_message tempDirectMessage;
542 540
543 switch (first._type) 541 switch (first._type)
544 { 542 {
545 case type::tweet: 543 case type::tweet:
546 { 544 {
547 tempTweet = std::move(first._tweet); 545 tempTweet = std::move(first._tweet);
548 546
549 break; 547 break;
550 } 548 }
551 549
552 case type::update_user: 550 case type::update_user:
553 case type::block: 551 case type::block:
554 case type::unblock: 552 case type::unblock:
@@ -557,10 +555,10 @@ namespace twitter {
557 case type::unfollow: 555 case type::unfollow:
558 { 556 {
559 tempUser = std::move(first._user); 557 tempUser = std::move(first._user);
560 558
561 break; 559 break;
562 } 560 }
563 561
564 case type::favorite: 562 case type::favorite:
565 case type::favorited: 563 case type::favorited:
566 case type::unfavorite: 564 case type::unfavorite:
@@ -569,19 +567,19 @@ namespace twitter {
569 { 567 {
570 tempTweet = std::move(first._user_and_tweet._tweet); 568 tempTweet = std::move(first._user_and_tweet._tweet);
571 tempUser = std::move(first._user_and_tweet._user); 569 tempUser = std::move(first._user_and_tweet._user);
572 570
573 break; 571 break;
574 } 572 }
575 573
576 case type::list_created: 574 case type::list_created:
577 case type::list_destroyed: 575 case type::list_destroyed:
578 case type::list_updated: 576 case type::list_updated:
579 { 577 {
580 tempList = std::move(first._list); 578 tempList = std::move(first._list);
581 579
582 break; 580 break;
583 } 581 }
584 582
585 case type::list_add: 583 case type::list_add:
586 case type::list_added: 584 case type::list_added:
587 case type::list_remove: 585 case type::list_remove:
@@ -593,97 +591,97 @@ namespace twitter {
593 { 591 {
594 tempList = std::move(first._user_and_list._list); 592 tempList = std::move(first._user_and_list._list);
595 tempUser = std::move(first._user_and_list._user); 593 tempUser = std::move(first._user_and_list._user);
596 594
597 break; 595 break;
598 } 596 }
599 597
600 case type::stall: 598 case type::stall:
601 case type::follow_limit: 599 case type::follow_limit:
602 case type::unknown_warning: 600 case type::unknown_warning:
603 { 601 {
604 tempWarning = std::move(first._warning); 602 tempWarning = std::move(first._warning);
605 603
606 break; 604 break;
607 } 605 }
608 606
609 case type::deletion: 607 case type::deletion:
610 case type::scrub_location: 608 case type::scrub_location:
611 { 609 {
612 tempUserId = first._user_id_and_tweet_id._user_id; 610 tempUserId = first._user_id_and_tweet_id._user_id;
613 tempTweetId = first._user_id_and_tweet_id._tweet_id; 611 tempTweetId = first._user_id_and_tweet_id._tweet_id;
614 612
615 break; 613 break;
616 } 614 }
617 615
618 case type::limit: 616 case type::limit:
619 { 617 {
620 tempLimit = first._limit; 618 tempLimit = first._limit;
621 619
622 break; 620 break;
623 } 621 }
624 622
625 case type::withhold_status: 623 case type::withhold_status:
626 { 624 {
627 tempUserId = first._withhold_status._user_id; 625 tempUserId = first._withhold_status._user_id;
628 tempTweetId = first._withhold_status._tweet_id; 626 tempTweetId = first._withhold_status._tweet_id;
629 tempCountries = std::move(first._withhold_status._countries); 627 tempCountries = std::move(first._withhold_status._countries);
630 628
631 break; 629 break;
632 } 630 }
633 631
634 case type::withhold_user: 632 case type::withhold_user:
635 { 633 {
636 tempUserId = first._withhold_user._user_id; 634 tempUserId = first._withhold_user._user_id;
637 tempCountries = std::move(first._withhold_user._countries); 635 tempCountries = std::move(first._withhold_user._countries);
638 636
639 break; 637 break;
640 } 638 }
641 639
642 case type::disconnect: 640 case type::disconnect:
643 { 641 {
644 tempDisconnectCode = first._disconnect; 642 tempDisconnectCode = first._disconnect;
645 643
646 break; 644 break;
647 } 645 }
648 646
649 case type::friends: 647 case type::friends:
650 { 648 {
651 tempFriends = std::move(first._friends); 649 tempFriends = std::move(first._friends);
652 650
653 break; 651 break;
654 } 652 }
655 653
656 case type::direct: 654 case type::direct:
657 { 655 {
658 tempDirectMessage = std::move(first._direct_message); 656 tempDirectMessage = std::move(first._direct_message);
659 657
660 break; 658 break;
661 } 659 }
662 660
663 case type::invalid: 661 case type::invalid:
664 case type::unknown: 662 case type::unknown:
665 { 663 {
666 break; 664 break;
667 } 665 }
668 } 666 }
669 667
670 first.~notification(); 668 first.~notification();
671 669
672 first._type = second._type; 670 first._type = second._type;
673 671
674 // Okay now you need to initialize the first with the data from the second 672 // Okay now you need to initialize the first with the data from the second
675 // And then destruct the second and initialize it with the data stored in temp 673 // And then destruct the second and initialize it with the data stored in temp
676 // This is hell 674 // This is hell
677 675
678 switch (second._type) 676 switch (second._type)
679 { 677 {
680 case type::tweet: 678 case type::tweet:
681 { 679 {
682 new(&first._tweet) tweet(std::move(second._tweet)); 680 new(&first._tweet) tweet(std::move(second._tweet));
683 681
684 break; 682 break;
685 } 683 }
686 684
687 case type::update_user: 685 case type::update_user:
688 case type::block: 686 case type::block:
689 case type::unblock: 687 case type::unblock:
@@ -692,10 +690,10 @@ namespace twitter {
692 case type::unfollow: 690 case type::unfollow:
693 { 691 {
694 new(&first._user) user(std::move(second._user)); 692 new(&first._user) user(std::move(second._user));
695 693
696 break; 694 break;
697 } 695 }
698 696
699 case type::favorite: 697 case type::favorite:
700 case type::favorited: 698 case type::favorited:
701 case type::unfavorite: 699 case type::unfavorite:
@@ -704,19 +702,19 @@ namespace twitter {
704 { 702 {
705 new(&first._user_and_tweet._user) user(std::move(second._user_and_tweet._user)); 703 new(&first._user_and_tweet._user) user(std::move(second._user_and_tweet._user));
706 new(&first._user_and_tweet._tweet) tweet(std::move(second._user_and_tweet._tweet)); 704 new(&first._user_and_tweet._tweet) tweet(std::move(second._user_and_tweet._tweet));
707 705
708 break; 706 break;
709 } 707 }
710 708
711 case type::list_created: 709 case type::list_created:
712 case type::list_destroyed: 710 case type::list_destroyed:
713 case type::list_updated: 711 case type::list_updated:
714 { 712 {
715 new(&first._list) list(std::move(second._list)); 713 new(&first._list) list(std::move(second._list));
716 714
717 break; 715 break;
718 } 716 }
719 717
720 case type::list_add: 718 case type::list_add:
721 case type::list_added: 719 case type::list_added:
722 case type::list_remove: 720 case type::list_remove:
@@ -728,94 +726,94 @@ namespace twitter {
728 { 726 {
729 new(&first._user_and_list._user) user(std::move(second._user_and_list._user)); 727 new(&first._user_and_list._user) user(std::move(second._user_and_list._user));
730 new(&first._user_and_list._list) list(std::move(second._user_and_list._list)); 728 new(&first._user_and_list._list) list(std::move(second._user_and_list._list));
731 729
732 break; 730 break;
733 } 731 }
734 732
735 case type::stall: 733 case type::stall:
736 case type::follow_limit: 734 case type::follow_limit:
737 case type::unknown_warning: 735 case type::unknown_warning:
738 { 736 {
739 new(&first._warning) std::string(std::move(second._warning)); 737 new(&first._warning) std::string(std::move(second._warning));
740 738
741 break; 739 break;
742 } 740 }
743 741
744 case type::deletion: 742 case type::deletion:
745 case type::scrub_location: 743 case type::scrub_location:
746 { 744 {
747 first._user_id_and_tweet_id._user_id = second._user_id_and_tweet_id._user_id; 745 first._user_id_and_tweet_id._user_id = second._user_id_and_tweet_id._user_id;
748 first._user_id_and_tweet_id._tweet_id = second._user_id_and_tweet_id._tweet_id; 746 first._user_id_and_tweet_id._tweet_id = second._user_id_and_tweet_id._tweet_id;
749 747
750 break; 748 break;
751 } 749 }
752 750
753 case type::limit: 751 case type::limit:
754 { 752 {
755 first._limit = second._limit; 753 first._limit = second._limit;
756 754
757 break; 755 break;
758 } 756 }
759 757
760 case type::withhold_status: 758 case type::withhold_status:
761 { 759 {
762 first._withhold_status._user_id = second._withhold_status._user_id; 760 first._withhold_status._user_id = second._withhold_status._user_id;
763 first._withhold_status._tweet_id = second._withhold_status._tweet_id; 761 first._withhold_status._tweet_id = second._withhold_status._tweet_id;
764 new(&first._withhold_status._countries) std::vector<std::string>(std::move(second._withhold_status._countries)); 762 new(&first._withhold_status._countries) std::vector<std::string>(std::move(second._withhold_status._countries));
765 763
766 break; 764 break;
767 } 765 }
768 766
769 case type::withhold_user: 767 case type::withhold_user:
770 { 768 {
771 first._withhold_user._user_id = second._withhold_user._user_id; 769 first._withhold_user._user_id = second._withhold_user._user_id;
772 new(&first._withhold_user._countries) std::vector<std::string>(std::move(second._withhold_user._countries)); 770 new(&first._withhold_user._countries) std::vector<std::string>(std::move(second._withhold_user._countries));
773 771
774 break; 772 break;
775 } 773 }
776 774
777 case type::disconnect: 775 case type::disconnect:
778 { 776 {
779 first._disconnect = second._disconnect; 777 first._disconnect = second._disconnect;
780 778
781 break; 779 break;
782 } 780 }
783 781
784 case type::friends: 782 case type::friends:
785 { 783 {
786 new(&first._friends) std::set<user_id>(std::move(second._friends)); 784 new(&first._friends) std::set<user_id>(std::move(second._friends));
787 785
788 break; 786 break;
789 } 787 }
790 788
791 case type::direct: 789 case type::direct:
792 { 790 {
793 new(&first._direct_message) direct_message(std::move(second._direct_message)); 791 new(&first._direct_message) direct_message(std::move(second._direct_message));
794 792
795 break; 793 break;
796 } 794 }
797 795
798 case type::invalid: 796 case type::invalid:
799 case type::unknown: 797 case type::unknown:
800 { 798 {
801 break; 799 break;
802 } 800 }
803 } 801 }
804 802
805 // Now destruct the second and initialize it with data from the first 803 // Now destruct the second and initialize it with data from the first
806 second.~notification(); 804 second.~notification();
807 805
808 second._type = tempType; 806 second._type = tempType;
809 807
810 switch (tempType) 808 switch (tempType)
811 { 809 {
812 case type::tweet: 810 case type::tweet:
813 { 811 {
814 new(&second._tweet) tweet(std::move(tempTweet)); 812 new(&second._tweet) tweet(std::move(tempTweet));
815 813
816 break; 814 break;
817 } 815 }
818 816
819 case type::update_user: 817 case type::update_user:
820 case type::block: 818 case type::block:
821 case type::unblock: 819 case type::unblock:
@@ -824,10 +822,10 @@ namespace twitter {
824 case type::unfollow: 822 case type::unfollow:
825 { 823 {
826 new(&second._user) user(std::move(tempUser)); 824 new(&second._user) user(std::move(tempUser));
827 825
828 break; 826 break;
829 } 827 }
830 828
831 case type::favorite: 829 case type::favorite:
832 case type::favorited: 830 case type::favorited:
833 case type::unfavorite: 831 case type::unfavorite:
@@ -836,19 +834,19 @@ namespace twitter {
836 { 834 {
837 new(&second._user_and_tweet._user) user(std::move(tempUser)); 835 new(&second._user_and_tweet._user) user(std::move(tempUser));
838 new(&second._user_and_tweet._tweet) tweet(std::move(tempTweet)); 836 new(&second._user_and_tweet._tweet) tweet(std::move(tempTweet));
839 837
840 break; 838 break;
841 } 839 }
842 840
843 case type::list_created: 841 case type::list_created:
844 case type::list_destroyed: 842 case type::list_destroyed:
845 case type::list_updated: 843 case type::list_updated:
846 { 844 {
847 new(&second._list) list(std::move(tempList)); 845 new(&second._list) list(std::move(tempList));
848 846
849 break; 847 break;
850 } 848 }
851 849
852 case type::list_add: 850 case type::list_add:
853 case type::list_added: 851 case type::list_added:
854 case type::list_remove: 852 case type::list_remove:
@@ -860,73 +858,73 @@ namespace twitter {
860 { 858 {
861 new(&second._user_and_list._user) user(std::move(tempUser)); 859 new(&second._user_and_list._user) user(std::move(tempUser));
862 new(&second._user_and_list._list) list(std::move(tempList)); 860 new(&second._user_and_list._list) list(std::move(tempList));
863 861
864 break; 862 break;
865 } 863 }
866 864
867 case type::stall: 865 case type::stall:
868 case type::follow_limit: 866 case type::follow_limit:
869 case type::unknown_warning: 867 case type::unknown_warning:
870 { 868 {
871 new(&second._warning) std::string(std::move(tempWarning)); 869 new(&second._warning) std::string(std::move(tempWarning));
872 870
873 break; 871 break;
874 } 872 }
875 873
876 case type::deletion: 874 case type::deletion:
877 case type::scrub_location: 875 case type::scrub_location:
878 { 876 {
879 second._user_id_and_tweet_id._user_id = tempUserId; 877 second._user_id_and_tweet_id._user_id = tempUserId;
880 second._user_id_and_tweet_id._tweet_id = tempTweetId; 878 second._user_id_and_tweet_id._tweet_id = tempTweetId;
881 879
882 break; 880 break;
883 } 881 }
884 882
885 case type::limit: 883 case type::limit:
886 { 884 {
887 second._limit = tempLimit; 885 second._limit = tempLimit;
888 886
889 break; 887 break;
890 } 888 }
891 889
892 case type::withhold_status: 890 case type::withhold_status:
893 { 891 {
894 second._withhold_status._user_id = tempUserId; 892 second._withhold_status._user_id = tempUserId;
895 second._withhold_status._tweet_id = tempTweetId; 893 second._withhold_status._tweet_id = tempTweetId;
896 new(&second._withhold_status._countries) std::vector<std::string>(std::move(tempCountries)); 894 new(&second._withhold_status._countries) std::vector<std::string>(std::move(tempCountries));
897 895
898 break; 896 break;
899 } 897 }
900 898
901 case type::withhold_user: 899 case type::withhold_user:
902 { 900 {
903 second._withhold_user._user_id = tempUserId; 901 second._withhold_user._user_id = tempUserId;
904 new(&second._withhold_user._countries) std::vector<std::string>(std::move(tempCountries)); 902 new(&second._withhold_user._countries) std::vector<std::string>(std::move(tempCountries));
905 903
906 break; 904 break;
907 } 905 }
908 906
909 case type::disconnect: 907 case type::disconnect:
910 { 908 {
911 second._disconnect = tempDisconnectCode; 909 second._disconnect = tempDisconnectCode;
912 910
913 break; 911 break;
914 } 912 }
915 913
916 case type::friends: 914 case type::friends:
917 { 915 {
918 new(&second._friends) std::set<user_id>(std::move(tempFriends)); 916 new(&second._friends) std::set<user_id>(std::move(tempFriends));
919 917
920 break; 918 break;
921 } 919 }
922 920
923 case type::direct: 921 case type::direct:
924 { 922 {
925 new(&second._direct_message) direct_message(std::move(tempDirectMessage)); 923 new(&second._direct_message) direct_message(std::move(tempDirectMessage));
926 924
927 break; 925 break;
928 } 926 }
929 927
930 case type::invalid: 928 case type::invalid:
931 case type::unknown: 929 case type::unknown:
932 { 930 {
@@ -934,7 +932,7 @@ namespace twitter {
934 } 932 }
935 } 933 }
936 } 934 }
937 935
938 const tweet& notification::getTweet() const 936 const tweet& notification::getTweet() const
939 { 937 {
940 switch (_type) 938 switch (_type)
@@ -943,7 +941,7 @@ namespace twitter {
943 { 941 {
944 return _tweet; 942 return _tweet;
945 } 943 }
946 944
947 case type::favorite: 945 case type::favorite:
948 case type::favorited: 946 case type::favorited:
949 case type::unfavorite: 947 case type::unfavorite:
@@ -952,14 +950,14 @@ namespace twitter {
952 { 950 {
953 return _user_and_tweet._tweet; 951 return _user_and_tweet._tweet;
954 } 952 }
955 953
956 default: 954 default:
957 { 955 {
958 assert(false); 956 assert(false);
959 } 957 }
960 } 958 }
961 } 959 }
962 960
963 const user& notification::getUser() const 961 const user& notification::getUser() const
964 { 962 {
965 switch (_type) 963 switch (_type)
@@ -973,7 +971,7 @@ namespace twitter {
973 { 971 {
974 return _user; 972 return _user;
975 } 973 }
976 974
977 case type::favorite: 975 case type::favorite:
978 case type::favorited: 976 case type::favorited:
979 case type::unfavorite: 977 case type::unfavorite:
@@ -982,7 +980,7 @@ namespace twitter {
982 { 980 {
983 return _user_and_tweet._user; 981 return _user_and_tweet._user;
984 } 982 }
985 983
986 case type::list_add: 984 case type::list_add:
987 case type::list_added: 985 case type::list_added:
988 case type::list_remove: 986 case type::list_remove:
@@ -994,25 +992,25 @@ namespace twitter {
994 { 992 {
995 return _user_and_list._user; 993 return _user_and_list._user;
996 } 994 }
997 995
998 default: 996 default:
999 { 997 {
1000 assert(false); 998 assert(false);
1001 } 999 }
1002 } 1000 }
1003 } 1001 }
1004 1002
1005 const list& notification::getList() const 1003 const list& notification::getList() const
1006 { 1004 {
1007 switch (_type) 1005 switch (_type)
1008 { 1006 {
1009 case type::list_created: 1007 case type::list_created:
1010 case type::list_destroyed: 1008 case type::list_destroyed:
1011 case type::list_updated: 1009 case type::list_updated:
1012 { 1010 {
1013 return _list; 1011 return _list;
1014 } 1012 }
1015 1013
1016 case type::list_add: 1014 case type::list_add:
1017 case type::list_added: 1015 case type::list_added:
1018 case type::list_remove: 1016 case type::list_remove:
@@ -1024,14 +1022,14 @@ namespace twitter {
1024 { 1022 {
1025 return _user_and_list._list; 1023 return _user_and_list._list;
1026 } 1024 }
1027 1025
1028 default: 1026 default:
1029 { 1027 {
1030 assert(false); 1028 assert(false);
1031 } 1029 }
1032 } 1030 }
1033 } 1031 }
1034 1032
1035 tweet_id notification::getTweetID() const 1033 tweet_id notification::getTweetID() const
1036 { 1034 {
1037 switch (_type) 1035 switch (_type)
@@ -1041,19 +1039,19 @@ namespace twitter {
1041 { 1039 {
1042 return _user_id_and_tweet_id._tweet_id; 1040 return _user_id_and_tweet_id._tweet_id;
1043 } 1041 }
1044 1042
1045 case type::withhold_status: 1043 case type::withhold_status:
1046 { 1044 {
1047 return _withhold_status._tweet_id; 1045 return _withhold_status._tweet_id;
1048 } 1046 }
1049 1047
1050 default: 1048 default:
1051 { 1049 {
1052 assert(false); 1050 assert(false);
1053 } 1051 }
1054 } 1052 }
1055 } 1053 }
1056 1054
1057 void notification::setTweetID(tweet_id _arg) 1055 void notification::setTweetID(tweet_id _arg)
1058 { 1056 {
1059 switch (_type) 1057 switch (_type)
@@ -1063,34 +1061,34 @@ namespace twitter {
1063 { 1061 {
1064 _user_id_and_tweet_id._tweet_id = _arg;; 1062 _user_id_and_tweet_id._tweet_id = _arg;;
1065 } 1063 }
1066 1064
1067 case type::withhold_status: 1065 case type::withhold_status:
1068 { 1066 {
1069 _withhold_status._tweet_id = _arg; 1067 _withhold_status._tweet_id = _arg;
1070 } 1068 }
1071 1069
1072 default: 1070 default:
1073 { 1071 {
1074 assert(false); 1072 assert(false);
1075 } 1073 }
1076 } 1074 }
1077 } 1075 }
1078 1076
1079 user_id notification::getUserID() const 1077 user_id notification::getUserID() const
1080 { 1078 {
1081 switch (_type) 1079 switch (_type)
1082 { 1080 {
1083 case type::deletion: 1081 case type::deletion:
1084 case type::scrub_location: 1082 case type::scrub_location:
1085 { 1083 {
1086 return _user_id_and_tweet_id._user_id; 1084 return _user_id_and_tweet_id._user_id;
1087 } 1085 }
1088 1086
1089 case type::withhold_status: 1087 case type::withhold_status:
1090 { 1088 {
1091 return _withhold_status._user_id; 1089 return _withhold_status._user_id;
1092 } 1090 }
1093 1091
1094 case type::withhold_user: 1092 case type::withhold_user:
1095 { 1093 {
1096 return _withhold_user._user_id; 1094 return _withhold_user._user_id;
@@ -1102,22 +1100,22 @@ namespace twitter {
1102 } 1100 }
1103 } 1101 }
1104 } 1102 }
1105 1103
1106 void notification::setUserID(user_id _arg) 1104 void notification::setUserID(user_id _arg)
1107 { 1105 {
1108 switch (_type) 1106 switch (_type)
1109 { 1107 {
1110 case type::deletion: 1108 case type::deletion:
1111 case type::scrub_location: 1109 case type::scrub_location:
1112 { 1110 {
1113 _user_id_and_tweet_id._user_id = _arg; 1111 _user_id_and_tweet_id._user_id = _arg;
1114 } 1112 }
1115 1113
1116 case type::withhold_status: 1114 case type::withhold_status:
1117 { 1115 {
1118 _withhold_status._user_id = _arg; 1116 _withhold_status._user_id = _arg;
1119 } 1117 }
1120 1118
1121 case type::withhold_user: 1119 case type::withhold_user:
1122 { 1120 {
1123 _withhold_user._user_id = _arg; 1121 _withhold_user._user_id = _arg;
@@ -1129,16 +1127,16 @@ namespace twitter {
1129 } 1127 }
1130 } 1128 }
1131 } 1129 }
1132 1130
1133 const std::vector<std::string>& notification::getCountries() const 1131 const std::vector<std::string>& notification::getCountries() const
1134 { 1132 {
1135 switch (_type) 1133 switch (_type)
1136 { 1134 {
1137 case type::withhold_status: 1135 case type::withhold_status:
1138 { 1136 {
1139 return _withhold_status._countries; 1137 return _withhold_status._countries;
1140 } 1138 }
1141 1139
1142 case type::withhold_user: 1140 case type::withhold_user:
1143 { 1141 {
1144 return _withhold_user._countries; 1142 return _withhold_user._countries;
@@ -1150,49 +1148,49 @@ namespace twitter {
1150 } 1148 }
1151 } 1149 }
1152 } 1150 }
1153 1151
1154 disconnect_code notification::getDisconnectCode() const 1152 disconnect_code notification::getDisconnectCode() const
1155 { 1153 {
1156 assert(_type == type::disconnect); 1154 assert(_type == type::disconnect);
1157 1155
1158 return _disconnect; 1156 return _disconnect;
1159 } 1157 }
1160 1158
1161 void notification::setDisconnectCode(disconnect_code _arg) 1159 void notification::setDisconnectCode(disconnect_code _arg)
1162 { 1160 {
1163 assert(_type == type::disconnect); 1161 assert(_type == type::disconnect);
1164 1162
1165 _disconnect = _arg; 1163 _disconnect = _arg;
1166 } 1164 }
1167 1165
1168 const std::set<user_id>& notification::getFriends() const 1166 const std::set<user_id>& notification::getFriends() const
1169 { 1167 {
1170 assert(_type == type::friends); 1168 assert(_type == type::friends);
1171 1169
1172 return _friends; 1170 return _friends;
1173 } 1171 }
1174 1172
1175 const direct_message& notification::getDirectMessage() const 1173 const direct_message& notification::getDirectMessage() const
1176 { 1174 {
1177 assert(_type == type::direct); 1175 assert(_type == type::direct);
1178 1176
1179 return _direct_message; 1177 return _direct_message;
1180 } 1178 }
1181 1179
1182 int notification::getLimit() const 1180 int notification::getLimit() const
1183 { 1181 {
1184 assert(_type == type::limit); 1182 assert(_type == type::limit);
1185 1183
1186 return _limit; 1184 return _limit;
1187 } 1185 }
1188 1186
1189 void notification::setLimit(int _arg) 1187 void notification::setLimit(int _arg)
1190 { 1188 {
1191 assert(_type == type::limit); 1189 assert(_type == type::limit);
1192 1190
1193 _limit = _arg; 1191 _limit = _arg;
1194 } 1192 }
1195 1193
1196 const std::string& notification::getWarning() const 1194 const std::string& notification::getWarning() const
1197 { 1195 {
1198 switch (_type) 1196 switch (_type)
@@ -1203,12 +1201,12 @@ namespace twitter {
1203 { 1201 {
1204 return _warning; 1202 return _warning;
1205 } 1203 }
1206 1204
1207 default: 1205 default:
1208 { 1206 {
1209 assert(false); 1207 assert(false);
1210 } 1208 }
1211 } 1209 }
1212 } 1210 }
1213 1211
1214}; 1212};
diff --git a/src/notification.h b/src/notification.h index a6dd6f4..ad649c3 100644 --- a/src/notification.h +++ b/src/notification.h
@@ -10,9 +10,9 @@
10#include "direct_message.h" 10#include "direct_message.h"
11 11
12namespace twitter { 12namespace twitter {
13 13
14 class client; 14 class client;
15 15
16 enum class disconnect_code 16 enum class disconnect_code
17 { 17 {
18 shutdown, 18 shutdown,
@@ -27,13 +27,13 @@ namespace twitter {
27 load, 27 load,
28 unknown 28 unknown
29 }; 29 };
30 30
31 class notification { 31 class notification {
32 public: 32 public:
33 enum class type { 33 enum class type {
34 // Tweet object 34 // Tweet object
35 tweet, 35 tweet,
36 36
37 // User object 37 // User object
38 update_user, 38 update_user,
39 block, 39 block,
@@ -41,19 +41,19 @@ namespace twitter {
41 follow, 41 follow,
42 followed, 42 followed,
43 unfollow, 43 unfollow,
44 44
45 // User and tweet 45 // User and tweet
46 favorite, 46 favorite,
47 favorited, 47 favorited,
48 unfavorite, 48 unfavorite,
49 unfavorited, 49 unfavorited,
50 quoted, 50 quoted,
51 51
52 // List 52 // List
53 list_created, 53 list_created,
54 list_destroyed, 54 list_destroyed,
55 list_updated, 55 list_updated,
56 56
57 // User and list 57 // User and list
58 list_add, 58 list_add,
59 list_added, 59 list_added,
@@ -63,16 +63,16 @@ namespace twitter {
63 list_subscribed, 63 list_subscribed,
64 list_unsubscribe, 64 list_unsubscribe,
65 list_unsubscribed, 65 list_unsubscribed,
66 66
67 // Warning 67 // Warning
68 stall, 68 stall,
69 follow_limit, 69 follow_limit,
70 unknown_warning, 70 unknown_warning,
71 71
72 // User ID and tweet ID 72 // User ID and tweet ID
73 deletion, 73 deletion,
74 scrub_location, 74 scrub_location,
75 75
76 // Special 76 // Special
77 limit, 77 limit,
78 withhold_status, 78 withhold_status,
@@ -80,78 +80,78 @@ namespace twitter {
80 disconnect, 80 disconnect,
81 friends, 81 friends,
82 direct, 82 direct,
83 83
84 // Nothing 84 // Nothing
85 unknown, 85 unknown,
86 invalid 86 invalid
87 }; 87 };
88 88
89 type getType() const; 89 type getType() const;
90 90
91 notification() {} 91 notification() {}
92 notification(const client& tclient, std::string data); 92 notification(const user& currentUser, std::string data);
93 93
94 notification(const notification& other); 94 notification(const notification& other);
95 notification(notification&& other); 95 notification(notification&& other);
96 notification& operator=(notification other); 96 notification& operator=(notification other);
97 ~notification(); 97 ~notification();
98 98
99 friend void swap(notification& first, notification& second); 99 friend void swap(notification& first, notification& second);
100 100
101 const tweet& getTweet() const; 101 const tweet& getTweet() const;
102 tweet& getTweet() 102 tweet& getTweet()
103 { 103 {
104 return const_cast<tweet&>(static_cast<const notification&>(*this).getTweet()); 104 return const_cast<tweet&>(static_cast<const notification&>(*this).getTweet());
105 } 105 }
106 106
107 const user& getUser() const; 107 const user& getUser() const;
108 user& getUser() 108 user& getUser()
109 { 109 {
110 return const_cast<user&>(static_cast<const notification&>(*this).getUser()); 110 return const_cast<user&>(static_cast<const notification&>(*this).getUser());
111 } 111 }
112 112
113 const list& getList() const; 113 const list& getList() const;
114 list& getList() 114 list& getList()
115 { 115 {
116 return const_cast<list&>(static_cast<const notification&>(*this).getList()); 116 return const_cast<list&>(static_cast<const notification&>(*this).getList());
117 } 117 }
118 118
119 tweet_id getTweetID() const; 119 tweet_id getTweetID() const;
120 void setTweetID(tweet_id _arg); 120 void setTweetID(tweet_id _arg);
121 121
122 user_id getUserID() const; 122 user_id getUserID() const;
123 void setUserID(user_id _arg); 123 void setUserID(user_id _arg);
124 124
125 const std::vector<std::string>& getCountries() const; 125 const std::vector<std::string>& getCountries() const;
126 std::vector<std::string>& getCountries() 126 std::vector<std::string>& getCountries()
127 { 127 {
128 return const_cast<std::vector<std::string>&>(static_cast<const notification&>(*this).getCountries()); 128 return const_cast<std::vector<std::string>&>(static_cast<const notification&>(*this).getCountries());
129 } 129 }
130 130
131 disconnect_code getDisconnectCode() const; 131 disconnect_code getDisconnectCode() const;
132 void setDisconnectCode(disconnect_code _arg); 132 void setDisconnectCode(disconnect_code _arg);
133 133
134 const std::set<user_id>& getFriends() const; 134 const std::set<user_id>& getFriends() const;
135 std::set<user_id>& getFriends() 135 std::set<user_id>& getFriends()
136 { 136 {
137 return const_cast<std::set<user_id>&>(static_cast<const notification&>(*this).getFriends()); 137 return const_cast<std::set<user_id>&>(static_cast<const notification&>(*this).getFriends());
138 } 138 }
139 139
140 const direct_message& getDirectMessage() const; 140 const direct_message& getDirectMessage() const;
141 direct_message& getDirectMessage() 141 direct_message& getDirectMessage()
142 { 142 {
143 return const_cast<direct_message&>(static_cast<const notification&>(*this).getDirectMessage()); 143 return const_cast<direct_message&>(static_cast<const notification&>(*this).getDirectMessage());
144 } 144 }
145 145
146 int getLimit() const; 146 int getLimit() const;
147 void setLimit(int _arg); 147 void setLimit(int _arg);
148 148
149 const std::string& getWarning() const; 149 const std::string& getWarning() const;
150 std::string& getWarning() 150 std::string& getWarning()
151 { 151 {
152 return const_cast<std::string&>(static_cast<const notification&>(*this).getWarning()); 152 return const_cast<std::string&>(static_cast<const notification&>(*this).getWarning());
153 } 153 }
154 154
155 private: 155 private:
156 union { 156 union {
157 tweet _tweet; 157 tweet _tweet;
@@ -186,7 +186,7 @@ namespace twitter {
186 }; 186 };
187 type _type = type::invalid; 187 type _type = type::invalid;
188 }; 188 };
189 189
190}; 190};
191 191
192#endif /* end of include guard: NOTIFICATION_H_69AEF4CC */ 192#endif /* end of include guard: NOTIFICATION_H_69AEF4CC */
diff --git a/src/request.cpp b/src/request.cpp new file mode 100644 index 0000000..a79c7f0 --- /dev/null +++ b/src/request.cpp
@@ -0,0 +1,264 @@
1#include "request.h"
2#include <json.hpp>
3#include "codes.h"
4
5// These are here for debugging curl stuff
6
7static
8void dump(const char *text,
9 FILE *stream, unsigned char *ptr, size_t size)
10{
11 size_t i;
12 size_t c;
13 unsigned int width=80;
14
15 fprintf(stream, "%s, %10.10ld bytes (0x%8.8lx)\n",
16 text, (long)size, (long)size);
17
18 for(i=0; i<size; i+= width) {
19 fprintf(stream, "%4.4lx: ", (long)i);
20
21 /* show hex to the left
22 for(c = 0; c < width; c++) {
23 if(i+c < size)
24 fprintf(stream, "%02x ", ptr[i+c]);
25 else
26 fputs(" ", stream);
27 }*/
28
29 /* show data on the right */
30 for(c = 0; (c < width) && (i+c < size); c++) {
31 char x = (ptr[i+c] >= 0x20 && ptr[i+c] < 0x80) ? ptr[i+c] : '.';
32 fputc(x, stream);
33 }
34
35 fputc('\n', stream); /* newline */
36 }
37}
38
39static
40int my_trace(CURL *handle, curl_infotype type,
41 char *data, size_t size,
42 void *userp)
43{
44 const char *text;
45 (void)handle; /* prevent compiler warning */
46
47 switch (type) {
48 case CURLINFO_TEXT:
49 fprintf(stderr, "== Info: %s", data);
50 default: /* in case a new one is introduced to shock us */
51 return 0;
52
53 case CURLINFO_HEADER_OUT:
54 text = "=> Send header";
55 break;
56 case CURLINFO_DATA_OUT:
57 text = "=> Send data";
58 break;
59 case CURLINFO_SSL_DATA_OUT:
60 text = "=> Send SSL data";
61 break;
62 case CURLINFO_HEADER_IN:
63 text = "<= Recv header";
64 break;
65 case CURLINFO_DATA_IN:
66 text = "<= Recv data";
67 break;
68 case CURLINFO_SSL_DATA_IN:
69 text = "<= Recv SSL data";
70 break;
71 }
72
73 dump(text, stderr, (unsigned char *)data, size);
74 return 0;
75}
76
77namespace twitter {
78
79 request::request(
80 std::string url) try :
81 ios_(output_),
82 conn_(ios_)
83 {
84 conn_.add<CURLOPT_URL>(url.c_str());
85 } catch (const curl::curl_easy_exception& error)
86 {
87 std::throw_with_nested(connection_error());
88 }
89
90 std::string request::perform()
91 {
92 try
93 {
94 conn_.perform();
95 } catch (const curl::curl_easy_exception& error)
96 {
97 std::throw_with_nested(connection_error());
98 }
99
100 int response_code = conn_.get_info<CURLINFO_RESPONSE_CODE>().get();
101 std::string result = output_.str();
102
103 if (response_code / 100 != 2)
104 {
105 nlohmann::json response_json;
106
107 try
108 {
109 response_json = nlohmann::json::parse(result);
110 } catch (const std::invalid_argument& e)
111 {
112 std::throw_with_nested(invalid_response(result));
113 }
114
115 for (nlohmann::json& error : response_json["errors"])
116 {
117 int error_code;
118 std::string error_message;
119
120 try
121 {
122 error_code = error["code"].get<int>();
123 error_message = error["message"].get<std::string>();
124 } catch (const std::domain_error& e)
125 {
126 std::throw_with_nested(invalid_response(result));
127 }
128
129 switch (error_code)
130 {
131 case 32:
132 case 135:
133 case 215:
134 throw bad_auth(error_message);
135
136 case 44:
137 throw invalid_media(error_message);
138
139 case 64:
140 throw account_suspended(error_message);
141
142 case 88:
143 throw rate_limit_exceeded(error_message);
144
145 case 89:
146 throw bad_token(error_message);
147
148 case 130:
149 throw server_overloaded(error_message);
150
151 case 131:
152 throw server_error(error_message);
153
154 case 185:
155 throw update_limit_exceeded(error_message);
156
157 case 186:
158 throw bad_length(error_message);
159
160 case 187:
161 throw duplicate_status(error_message);
162
163 case 226:
164 throw suspected_spam(error_message);
165
166 case 261:
167 throw write_restricted(error_message);
168 }
169 }
170
171 if (response_code == 429)
172 {
173 throw rate_limit_exceeded("HTTP 429 Too Many Requests");
174 } else if (response_code == 500)
175 {
176 throw server_error("HTTP 500 Internal Server Error");
177 } else if (response_code == 502)
178 {
179 throw server_unavailable("HTTP 502 Bad Gateway");
180 } else if (response_code == 503)
181 {
182 throw server_overloaded("HTTP 503 Service Unavailable");
183 } else if (response_code == 504)
184 {
185 throw server_timeout("HTTP 504 Gateway Timeout");
186 }
187
188 throw unknown_error(response_code, result);
189 }
190
191 return result;
192 }
193
194 get::get(
195 const auth& tauth,
196 std::string url) try :
197 request(url)
198 {
199 std::string oauthHeader =
200 tauth.getClient().getFormattedHttpHeader(OAuth::Http::Get, url, "");
201
202 if (!oauthHeader.empty())
203 {
204 headers_.add(std::move(oauthHeader));
205 }
206
207 conn_.add<CURLOPT_HTTPHEADER>(headers_.get());
208 } catch (const OAuth::ParseError& error)
209 {
210 std::throw_with_nested(connection_error());
211 } catch (const curl::curl_easy_exception& error)
212 {
213 std::throw_with_nested(connection_error());
214 }
215
216 post::post(
217 const auth& tauth,
218 std::string url,
219 std::string datastr) try :
220 request(url)
221 {
222 std::string oauthHeader =
223 tauth.getClient().getFormattedHttpHeader(OAuth::Http::Post, url, datastr);
224
225 if (!oauthHeader.empty())
226 {
227 headers_.add(std::move(oauthHeader));
228 }
229
230 conn_.add<CURLOPT_HTTPHEADER>(headers_.get());
231 conn_.add<CURLOPT_COPYPOSTFIELDS>(datastr.c_str());
232 } catch (const OAuth::ParseError& error)
233 {
234 std::throw_with_nested(connection_error());
235 } catch (const curl::curl_easy_exception& error)
236 {
237 std::throw_with_nested(connection_error());
238 }
239
240 multipost::multipost(
241 const auth& tauth,
242 std::string url,
243 const curl_httppost* fields) try :
244 request(url)
245 {
246 std::string oauthHeader =
247 tauth.getClient().getFormattedHttpHeader(OAuth::Http::Post, url, "");
248
249 if (!oauthHeader.empty())
250 {
251 headers_.add(std::move(oauthHeader));
252 }
253
254 conn_.add<CURLOPT_HTTPHEADER>(headers_.get());
255 conn_.add<CURLOPT_HTTPPOST>(fields);
256 } catch (const OAuth::ParseError& error)
257 {
258 std::throw_with_nested(connection_error());
259 } catch (const curl::curl_easy_exception& error)
260 {
261 std::throw_with_nested(connection_error());
262 }
263
264}
diff --git a/src/request.h b/src/request.h new file mode 100644 index 0000000..1672d71 --- /dev/null +++ b/src/request.h
@@ -0,0 +1,73 @@
1#ifndef REQUEST_H_9D3C30E2
2#define REQUEST_H_9D3C30E2
3
4#include <string>
5#include <sstream>
6#include <curl_easy.h>
7#include <curl_header.h>
8#include "auth.h"
9
10namespace twitter {
11
12 class request
13 {
14 public:
15
16 explicit request(std::string url);
17
18 std::string perform();
19
20 private:
21
22 std::ostringstream output_;
23 curl::curl_ios<std::ostringstream> ios_;
24
25 protected:
26
27 curl::curl_easy conn_;
28 };
29
30 class get : public request
31 {
32 public:
33
34 get(
35 const auth& tauth,
36 std::string url);
37
38 private:
39
40 curl::curl_header headers_;
41 };
42
43 class post : public request
44 {
45 public:
46
47 post(
48 const auth& tauth,
49 std::string url,
50 std::string datastr);
51
52 private:
53
54 curl::curl_header headers_;
55 };
56
57 class multipost : public request
58 {
59 public:
60
61 multipost(
62 const auth& tauth,
63 std::string url,
64 const curl_httppost* fields);
65
66 private:
67
68 curl::curl_header headers_;
69 };
70
71}
72
73#endif /* end of include guard: REQUEST_H_9D3C30E2 */
diff --git a/src/stream.cpp b/src/stream.cpp index cb55ee8..86d177c 100644 --- a/src/stream.cpp +++ b/src/stream.cpp
@@ -4,23 +4,27 @@
4#include <curl_header.h> 4#include <curl_header.h>
5#include "util.h" 5#include "util.h"
6#include "notification.h" 6#include "notification.h"
7#include "client.h" 7#include "request.h"
8 8
9namespace twitter { 9namespace twitter {
10 10
11 stream::stream( 11 stream::stream(
12 const client& tclient, 12 const auth& tauth,
13 notify_callback callback, 13 notify_callback callback,
14 bool with_followings, 14 bool with_followings,
15 bool receive_all_replies, 15 bool receive_all_replies,
16 std::list<std::string> track, 16 std::list<std::string> track,
17 std::list<bounding_box> locations) : 17 std::list<bounding_box> locations) :
18 _client(tclient), 18 _auth(tauth),
19 _notify(callback), 19 _notify(callback),
20 _currentUser(get(
21 _auth,
22 "https://api.twitter.com/1.1/account/verify_credentials.json")
23 .perform()),
20 _thread(&stream::run, this, generateUrl(with_followings, receive_all_replies, track, locations)) 24 _thread(&stream::run, this, generateUrl(with_followings, receive_all_replies, track, locations))
21 { 25 {
22 } 26 }
23 27
24 stream::~stream() 28 stream::~stream()
25 { 29 {
26 if (_thread.joinable()) 30 if (_thread.joinable())
@@ -29,7 +33,7 @@ namespace twitter {
29 _thread.join(); 33 _thread.join();
30 } 34 }
31 } 35 }
32 36
33 std::string stream::generateUrl( 37 std::string stream::generateUrl(
34 bool with_followings, 38 bool with_followings,
35 bool receive_all_replies, 39 bool receive_all_replies,
@@ -37,68 +41,68 @@ namespace twitter {
37 std::list<bounding_box> locations) 41 std::list<bounding_box> locations)
38 { 42 {
39 std::list<std::string> arguments; 43 std::list<std::string> arguments;
40 44
41 if (receive_all_replies) 45 if (receive_all_replies)
42 { 46 {
43 arguments.push_back("replies=all"); 47 arguments.push_back("replies=all");
44 } 48 }
45 49
46 if (!with_followings) 50 if (!with_followings)
47 { 51 {
48 arguments.push_back("with=user"); 52 arguments.push_back("with=user");
49 } 53 }
50 54
51 if (!track.empty()) 55 if (!track.empty())
52 { 56 {
53 std::ostringstream trackstr; 57 std::ostringstream trackstr;
54 trackstr << "track="; 58 trackstr << "track=";
55 59
56 for (auto it = std::begin(track); it != std::end(track); it++) 60 for (auto it = std::begin(track); it != std::end(track); it++)
57 { 61 {
58 if (it != std::begin(track)) 62 if (it != std::begin(track))
59 { 63 {
60 trackstr << ","; 64 trackstr << ",";
61 } 65 }
62 66
63 trackstr << OAuth::HttpEncodeQueryValue(*it); 67 trackstr << OAuth::HttpEncodeQueryValue(*it);
64 } 68 }
65 69
66 arguments.push_back(trackstr.str()); 70 arguments.push_back(trackstr.str());
67 } 71 }
68 72
69 if (!locations.empty()) 73 if (!locations.empty())
70 { 74 {
71 std::ostringstream localstr; 75 std::ostringstream localstr;
72 localstr << "locations="; 76 localstr << "locations=";
73 77
74 for (auto it = std::begin(locations); it != std::end(locations); it++) 78 for (auto it = std::begin(locations); it != std::end(locations); it++)
75 { 79 {
76 if (it != std::begin(locations)) 80 if (it != std::begin(locations))
77 { 81 {
78 localstr << ","; 82 localstr << ",";
79 } 83 }
80 84
81 localstr << (double)it->getSouthWestLongitude() << ","; 85 localstr << (double)it->getSouthWestLongitude() << ",";
82 localstr << (double)it->getSouthWestLatitude() << ","; 86 localstr << (double)it->getSouthWestLatitude() << ",";
83 localstr << (double)it->getNorthEastLongitude() << ","; 87 localstr << (double)it->getNorthEastLongitude() << ",";
84 localstr << (double)it->getNorthEastLatitude(); 88 localstr << (double)it->getNorthEastLatitude();
85 } 89 }
86 90
87 arguments.push_back(localstr.str()); 91 arguments.push_back(localstr.str());
88 } 92 }
89 93
90 std::ostringstream urlstr; 94 std::ostringstream urlstr;
91 urlstr << "https://userstream.twitter.com/1.1/user.json"; 95 urlstr << "https://userstream.twitter.com/1.1/user.json";
92 96
93 if (!arguments.empty()) 97 if (!arguments.empty())
94 { 98 {
95 urlstr << "?"; 99 urlstr << "?";
96 urlstr << implode(std::begin(arguments), std::end(arguments), "&"); 100 urlstr << implode(std::begin(arguments), std::end(arguments), "&");
97 } 101 }
98 102
99 return urlstr.str(); 103 return urlstr.str();
100 } 104 }
101 105
102 void stream::run(std::string url) 106 void stream::run(std::string url)
103 { 107 {
104 _backoff_type = backoff::none; 108 _backoff_type = backoff::none;
@@ -108,15 +112,15 @@ namespace twitter {
108 curl::curl_ios<stream> ios(this, [] (void* contents, size_t size, size_t nmemb, void* userp) { 112 curl::curl_ios<stream> ios(this, [] (void* contents, size_t size, size_t nmemb, void* userp) {
109 return static_cast<stream*>(userp)->write(static_cast<char*>(contents), size, nmemb); 113 return static_cast<stream*>(userp)->write(static_cast<char*>(contents), size, nmemb);
110 }); 114 });
111 115
112 curl::curl_easy conn(ios); 116 curl::curl_easy conn(ios);
113 curl::curl_header headers; 117 curl::curl_header headers;
114 std::string oauth_header; 118 std::string oauth_header;
115 119
116 try 120 try
117 { 121 {
118 oauth_header = _client._oauth_client->getFormattedHttpHeader(OAuth::Http::Get, url, ""); 122 oauth_header = _auth.getClient().getFormattedHttpHeader(OAuth::Http::Get, url, "");
119 123
120 if (!oauth_header.empty()) 124 if (!oauth_header.empty())
121 { 125 {
122 headers.add(oauth_header); 126 headers.add(oauth_header);
@@ -126,10 +130,10 @@ namespace twitter {
126 std::cout << "Error generating OAuth header:" << std::endl; 130 std::cout << "Error generating OAuth header:" << std::endl;
127 std::cout << error.what() << std::endl; 131 std::cout << error.what() << std::endl;
128 std::cout << "This is likely due to a malformed URL." << std::endl; 132 std::cout << "This is likely due to a malformed URL." << std::endl;
129 133
130 assert(false); 134 assert(false);
131 } 135 }
132 136
133 try 137 try
134 { 138 {
135 conn.add<CURLOPT_HEADERFUNCTION>(nullptr); 139 conn.add<CURLOPT_HEADERFUNCTION>(nullptr);
@@ -146,10 +150,10 @@ namespace twitter {
146 } catch (const curl::curl_exception& error) 150 } catch (const curl::curl_exception& error)
147 { 151 {
148 error.print_traceback(); 152 error.print_traceback();
149 153
150 assert(false); 154 assert(false);
151 } 155 }
152 156
153 bool failure = false; 157 bool failure = false;
154 try 158 try
155 { 159 {
@@ -169,7 +173,7 @@ namespace twitter {
169 } 173 }
170 } 174 }
171 } 175 }
172 176
173 if (!failure) 177 if (!failure)
174 { 178 {
175 long response_code = conn.get_info<CURLINFO_RESPONSE_CODE>().get(); 179 long response_code = conn.get_info<CURLINFO_RESPONSE_CODE>().get();
@@ -198,7 +202,7 @@ namespace twitter {
198 } 202 }
199 } 203 }
200 } 204 }
201 205
202 std::this_thread::sleep_for(_backoff_amount); 206 std::this_thread::sleep_for(_backoff_amount);
203 207
204 switch (_backoff_type) 208 switch (_backoff_type)
@@ -209,35 +213,35 @@ namespace twitter {
209 { 213 {
210 _backoff_amount += std::chrono::milliseconds(250); 214 _backoff_amount += std::chrono::milliseconds(250);
211 } 215 }
212 216
213 break; 217 break;
214 } 218 }
215 219
216 case backoff::http: 220 case backoff::http:
217 { 221 {
218 if (_backoff_amount < std::chrono::seconds(320)) 222 if (_backoff_amount < std::chrono::seconds(320))
219 { 223 {
220 _backoff_amount *= 2; 224 _backoff_amount *= 2;
221 } 225 }
222 226
223 break; 227 break;
224 } 228 }
225 229
226 case backoff::rate_limit: 230 case backoff::rate_limit:
227 { 231 {
228 _backoff_amount *= 2; 232 _backoff_amount *= 2;
229 233
230 break; 234 break;
231 } 235 }
232 236
233 case backoff::none: 237 case backoff::none:
234 { 238 {
235 break; 239 break;
236 } 240 }
237 } 241 }
238 } 242 }
239 } 243 }
240 244
241 size_t stream::write(char* ptr, size_t size, size_t nmemb) 245 size_t stream::write(char* ptr, size_t size, size_t nmemb)
242 { 246 {
243 for (size_t i = 0; i < size*nmemb; i++) 247 for (size_t i = 0; i < size*nmemb; i++)
@@ -245,38 +249,38 @@ namespace twitter {
245 if (ptr[i] == '\r') 249 if (ptr[i] == '\r')
246 { 250 {
247 i++; // Skip the \n 251 i++; // Skip the \n
248 252
249 if (!_buffer.empty()) 253 if (!_buffer.empty())
250 { 254 {
251 notification n(_client, _buffer); 255 notification n(_currentUser, _buffer);
252 if (n.getType() == notification::type::friends) 256 if (n.getType() == notification::type::friends)
253 { 257 {
254 _established = true; 258 _established = true;
255 _backoff_type = backoff::none; 259 _backoff_type = backoff::none;
256 _backoff_amount = std::chrono::milliseconds(0); 260 _backoff_amount = std::chrono::milliseconds(0);
257 } 261 }
258 262
259 _notify(std::move(n)); 263 _notify(std::move(n));
260 264
261 _buffer = ""; 265 _buffer = "";
262 } 266 }
263 } else { 267 } else {
264 _buffer.push_back(ptr[i]); 268 _buffer.push_back(ptr[i]);
265 } 269 }
266 } 270 }
267 271
268 time(&_last_write); 272 time(&_last_write);
269 273
270 return size*nmemb; 274 return size*nmemb;
271 } 275 }
272 276
273 int stream::progress() 277 int stream::progress()
274 { 278 {
275 if (_stop) 279 if (_stop)
276 { 280 {
277 return 1; 281 return 1;
278 } 282 }
279 283
280 if (_established) 284 if (_established)
281 { 285 {
282 if (difftime(time(NULL), _last_write) >= 90) 286 if (difftime(time(NULL), _last_write) >= 90)
@@ -284,8 +288,8 @@ namespace twitter {
284 return 1; 288 return 1;
285 } 289 }
286 } 290 }
287 291
288 return 0; 292 return 0;
289 } 293 }
290 294
291} 295}
diff --git a/src/stream.h b/src/stream.h index b682ce2..f6ce91e 100644 --- a/src/stream.h +++ b/src/stream.h
@@ -7,32 +7,35 @@
7#include <chrono> 7#include <chrono>
8#include <thread> 8#include <thread>
9#include "bounding_box.h" 9#include "bounding_box.h"
10#include "auth.h"
11#include "user.h"
10 12
11namespace twitter { 13namespace twitter {
12 14
13 class client;
14 class notification; 15 class notification;
15 16
16 class stream { 17 class
18 [[deprecated("The Twitter streaming API will sunset on August 16th, 2018")]]
19 stream {
17 public: 20 public:
18 21
19 typedef std::function<void(notification _notification)> notify_callback; 22 typedef std::function<void(notification _notification)> notify_callback;
20 23
21 stream( 24 stream(
22 const client& tclient, 25 const auth& tauth,
23 notify_callback callback, 26 notify_callback callback,
24 bool with_followings = true, 27 bool with_followings = true,
25 bool receive_all_replies = false, 28 bool receive_all_replies = false,
26 std::list<std::string> track = {}, 29 std::list<std::string> track = {},
27 std::list<bounding_box> locations = {}); 30 std::list<bounding_box> locations = {});
28 31
29 ~stream(); 32 ~stream();
30 33
31 stream(const stream& other) = delete; 34 stream(const stream& other) = delete;
32 stream(stream&& other) = delete; 35 stream(stream&& other) = delete;
33 stream& operator=(const stream& other) = delete; 36 stream& operator=(const stream& other) = delete;
34 stream& operator=(stream&& other) = delete; 37 stream& operator=(stream&& other) = delete;
35 38
36 private: 39 private:
37 enum class backoff { 40 enum class backoff {
38 none, 41 none,
@@ -40,18 +43,18 @@ namespace twitter {
40 http, 43 http,
41 rate_limit 44 rate_limit
42 }; 45 };
43 46
44 static std::string generateUrl( 47 static std::string generateUrl(
45 bool with_followings, 48 bool with_followings,
46 bool receive_all_replies, 49 bool receive_all_replies,
47 std::list<std::string> track, 50 std::list<std::string> track,
48 std::list<bounding_box> locations); 51 std::list<bounding_box> locations);
49 52
50 void run(std::string url); 53 void run(std::string url);
51 int progress(); 54 int progress();
52 size_t write(char* ptr, size_t size, size_t nmemb); 55 size_t write(char* ptr, size_t size, size_t nmemb);
53 56
54 const client& _client; 57 const auth& _auth;
55 notify_callback _notify; 58 notify_callback _notify;
56 bool _stop = false; 59 bool _stop = false;
57 std::string _buffer; 60 std::string _buffer;
@@ -59,9 +62,10 @@ namespace twitter {
59 bool _established = false; 62 bool _established = false;
60 backoff _backoff_type = backoff::none; 63 backoff _backoff_type = backoff::none;
61 std::chrono::milliseconds _backoff_amount; 64 std::chrono::milliseconds _backoff_amount;
65 user _currentUser;
62 std::thread _thread; 66 std::thread _thread;
63 }; 67 };
64 68
65} 69}
66 70
67#endif /* end of include guard: STREAM_H_E9146952 */ 71#endif /* end of include guard: STREAM_H_E9146952 */
diff --git a/src/timeline.cpp b/src/timeline.cpp new file mode 100644 index 0000000..fd75a69 --- /dev/null +++ b/src/timeline.cpp
@@ -0,0 +1,82 @@
1#include "timeline.h"
2#include <sstream>
3#include <json.hpp>
4#include "codes.h"
5#include "util.h"
6#include "request.h"
7
8namespace twitter {
9
10 timeline::timeline(
11 const auth& tauth,
12 std::string url) :
13 auth_(tauth),
14 url_(std::move(url))
15 {
16 }
17
18 std::list<tweet> timeline::poll()
19 {
20 tweet_id maxId;
21 std::list<tweet> result;
22
23 for (int i = 0; i < 5; i++)
24 {
25 std::ostringstream urlstr;
26 urlstr << url_;
27
28 std::list<std::string> arguments;
29
30 if (i > 0)
31 {
32 arguments.push_back("max_id=" + std::to_string(maxId));
33 }
34
35 if (hasSince_)
36 {
37 arguments.push_back("since_id=" + std::to_string(sinceId_));
38 }
39
40 if (!arguments.empty())
41 {
42 urlstr << "?";
43 urlstr << implode(std::begin(arguments), std::end(arguments), "&");
44 }
45
46 std::string theUrl = urlstr.str();
47 std::string response = get(auth_, theUrl).perform();
48
49 try
50 {
51 nlohmann::json rjs = nlohmann::json::parse(response);
52
53 if (rjs.empty())
54 {
55 break;
56 }
57
58 for (auto& single : rjs)
59 {
60 result.emplace_back(single.dump());
61 }
62 } catch (const std::invalid_argument& error)
63 {
64 std::throw_with_nested(invalid_response(response));
65 } catch (const std::domain_error& error)
66 {
67 std::throw_with_nested(invalid_response(response));
68 }
69
70 maxId = result.back().getID() - 1;
71 }
72
73 if (!result.empty())
74 {
75 sinceId_ = result.front().getID();
76 hasSince_ = true;
77 }
78
79 return result;
80 }
81
82};
diff --git a/src/timeline.h b/src/timeline.h new file mode 100644 index 0000000..60ce78d --- /dev/null +++ b/src/timeline.h
@@ -0,0 +1,31 @@
1#ifndef TIMELINE_H_D359681C
2#define TIMELINE_H_D359681C
3
4#include <functional>
5#include <list>
6#include <string>
7#include "auth.h"
8#include "tweet.h"
9
10namespace twitter {
11
12 class timeline {
13 public:
14
15 timeline(
16 const auth& tauth,
17 std::string url);
18
19 std::list<tweet> poll();
20
21 private:
22
23 const auth& auth_;
24 std::string url_;
25 bool hasSince_ = false;
26 tweet_id sinceId_;
27 };
28
29}
30
31#endif /* end of include guard: TIMELINE_H_D359681C */
diff --git a/src/tweet.cpp b/src/tweet.cpp index 864bcd8..1c869f9 100644 --- a/src/tweet.cpp +++ b/src/tweet.cpp
@@ -12,13 +12,13 @@ namespace twitter {
12 auto json = nlohmann::json::parse(data); 12 auto json = nlohmann::json::parse(data);
13 _id = json["id"].get<tweet_id>(); 13 _id = json["id"].get<tweet_id>();
14 _text = json["text"].get<std::string>(); 14 _text = json["text"].get<std::string>();
15 _author = make_unique<user>(json["user"].dump()); 15 _author = std::make_unique<user>(json["user"].dump());
16 16
17 if (!json["retweeted_status"].is_null()) 17 if (!json["retweeted_status"].is_null())
18 { 18 {
19 _is_retweet = true; 19 _is_retweet = true;
20 20
21 _retweeted_status = make_unique<tweet>(json["retweeted_status"].dump()); 21 _retweeted_status = std::make_unique<tweet>(json["retweeted_status"].dump());
22 } 22 }
23 23
24 if (!json["entities"].is_null()) 24 if (!json["entities"].is_null())
@@ -45,12 +45,12 @@ namespace twitter {
45 _valid = other._valid; 45 _valid = other._valid;
46 _id = other._id; 46 _id = other._id;
47 _text = other._text; 47 _text = other._text;
48 _author = make_unique<user>(*other._author); 48 _author = std::make_unique<user>(*other._author);
49 _is_retweet = other._is_retweet; 49 _is_retweet = other._is_retweet;
50 50
51 if (_is_retweet) 51 if (_is_retweet)
52 { 52 {
53 _retweeted_status = make_unique<tweet>(*other._retweeted_status); 53 _retweeted_status = std::make_unique<tweet>(*other._retweeted_status);
54 } 54 }
55 55
56 _mentions = other._mentions; 56 _mentions = other._mentions;
diff --git a/src/twitter.h b/src/twitter.h index 1ba4394..a4f336e 100644 --- a/src/twitter.h +++ b/src/twitter.h
@@ -11,6 +11,7 @@ namespace twitter {
11#include "util.h" 11#include "util.h"
12#include "auth.h" 12#include "auth.h"
13#include "client.h" 13#include "client.h"
14#include "timeline.h"
14#include "stream.h" 15#include "stream.h"
15#include "tweet.h" 16#include "tweet.h"
16#include "user.h" 17#include "user.h"
diff --git a/src/util.h b/src/util.h index 9084e81..8cbe054 100644 --- a/src/util.h +++ b/src/util.h
@@ -6,31 +6,25 @@
6#include <memory> 6#include <memory>
7 7
8namespace twitter { 8namespace twitter {
9 9
10 template <class InputIterator> 10 template <class InputIterator>
11 std::string implode(InputIterator first, InputIterator last, std::string delimiter) 11 std::string implode(InputIterator first, InputIterator last, std::string delimiter)
12 { 12 {
13 std::stringstream result; 13 std::stringstream result;
14 14
15 for (InputIterator it = first; it != last; it++) 15 for (InputIterator it = first; it != last; it++)
16 { 16 {
17 if (it != first) 17 if (it != first)
18 { 18 {
19 result << delimiter; 19 result << delimiter;
20 } 20 }
21 21
22 result << *it; 22 result << *it;
23 } 23 }
24 24
25 return result.str(); 25 return result.str();
26 } 26 }
27 27
28 template<typename T, typename... Args>
29 std::unique_ptr<T> make_unique(Args&&... args)
30 {
31 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
32 }
33
34}; 28};
35 29
36#endif /* end of include guard: UTIL_H_440DEAA0 */ 30#endif /* end of include guard: UTIL_H_440DEAA0 */