about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-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 */