about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.gitmodules2
-rw-r--r--CMakeLists.txt3
-rw-r--r--src/client.cpp449
-rw-r--r--src/client.h77
-rw-r--r--src/direct_message.cpp10
-rw-r--r--src/direct_message.h15
-rw-r--r--src/list.cpp15
-rw-r--r--src/list.h16
-rw-r--r--src/notification.cpp848
-rw-r--r--src/notification.h145
-rw-r--r--src/tweet.cpp34
-rw-r--r--src/tweet.h18
-rw-r--r--src/twitter.h4
-rw-r--r--src/user.cpp46
-rw-r--r--src/user.h31
m---------vendor/curlcpp0
16 files changed, 1664 insertions, 49 deletions
diff --git a/.gitmodules b/.gitmodules index b701c21..93a21e3 100644 --- a/.gitmodules +++ b/.gitmodules
@@ -6,4 +6,4 @@
6 url = https://github.com/nlohmann/json 6 url = https://github.com/nlohmann/json
7[submodule "vendor/curlcpp"] 7[submodule "vendor/curlcpp"]
8 path = vendor/curlcpp 8 path = vendor/curlcpp
9 url = https://github.com/JosephP91/curlcpp 9 url = https://github.com/hatkirby/curlcpp
diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f290c9..3c43670 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -3,13 +3,14 @@ project (twitter++)
3 3
4include_directories(vendor/json/src) 4include_directories(vendor/json/src)
5 5
6set(LIBOAUTHCPP_BUILD_DEMOS FALSE)
6add_subdirectory(vendor/liboauthcpp/build EXCLUDE_FROM_ALL) 7add_subdirectory(vendor/liboauthcpp/build EXCLUDE_FROM_ALL)
7include_directories(vendor/liboauthcpp/include) 8include_directories(vendor/liboauthcpp/include)
8 9
9add_subdirectory(vendor/curlcpp) 10add_subdirectory(vendor/curlcpp)
10include_directories(${CURLCPP_SOURCE_DIR}/include) 11include_directories(${CURLCPP_SOURCE_DIR}/include)
11 12
12add_library(twitter++ src/client.cpp src/auth.cpp src/tweet.cpp src/codes.cpp) 13add_library(twitter++ src/client.cpp src/auth.cpp src/tweet.cpp src/codes.cpp src/notification.cpp src/direct_message.cpp src/list.cpp src/user.cpp)
13set_property(TARGET twitter++ PROPERTY CXX_STANDARD 11) 14set_property(TARGET twitter++ PROPERTY CXX_STANDARD 11)
14set_property(TARGET twitter++ PROPERTY CXX_STANDARD_REQUIRED ON) 15set_property(TARGET twitter++ PROPERTY CXX_STANDARD_REQUIRED ON)
15target_link_libraries(twitter++ oauthcpp curlcpp) 16target_link_libraries(twitter++ oauthcpp curlcpp)
diff --git a/src/client.cpp b/src/client.cpp index 26f3289..ffb651b 100644 --- a/src/client.cpp +++ b/src/client.cpp
@@ -4,7 +4,9 @@
4#include <algorithm> 4#include <algorithm>
5#include <liboauthcpp/liboauthcpp.h> 5#include <liboauthcpp/liboauthcpp.h>
6#include "util.h" 6#include "util.h"
7#include <unistd.h> 7#include <json.hpp>
8
9using nlohmann::json;
8 10
9// These are here for debugging curl stuff 11// These are here for debugging curl stuff
10 12
@@ -80,11 +82,29 @@ int my_trace(CURL *handle, curl_infotype type,
80 82
81namespace twitter { 83namespace twitter {
82 84
85 int client_stream_progress_callback_wrapper(void* cdata, curl_off_t, curl_off_t, curl_off_t, curl_off_t)
86 {
87 return static_cast<client::stream*>(cdata)->progress();
88 }
89
90 size_t client_stream_write_callback_wrapper(void* ptr, size_t size, size_t nmemb, void* cdata)
91 {
92 return static_cast<client::stream*>(cdata)->write(static_cast<char*>(ptr), size, nmemb);
93 }
94
83 client::client(const auth& _arg) 95 client::client(const auth& _arg)
84 { 96 {
85 _oauth_consumer = new OAuth::Consumer(_arg.getConsumerKey(), _arg.getConsumerSecret()); 97 _oauth_consumer = new OAuth::Consumer(_arg.getConsumerKey(), _arg.getConsumerSecret());
86 _oauth_token = new OAuth::Token(_arg.getAccessKey(), _arg.getAccessSecret()); 98 _oauth_token = new OAuth::Token(_arg.getAccessKey(), _arg.getAccessSecret());
87 _oauth_client = new OAuth::Client(_oauth_consumer, _oauth_token); 99 _oauth_client = new OAuth::Client(_oauth_consumer, _oauth_token);
100
101 std::string url = "https://api.twitter.com/1.1/account/verify_credentials.json";
102 long response_code;
103 std::string response_data;
104 if (performGet(url, response_code, response_data) && (response_code == 200))
105 {
106 _current_user = user(response_data);
107 }
88 } 108 }
89 109
90 client::~client() 110 client::~client()
@@ -94,11 +114,17 @@ namespace twitter {
94 delete _oauth_consumer; 114 delete _oauth_consumer;
95 } 115 }
96 116
97 response client::updateStatus(std::string msg, tweet& result, std::list<long> media_ids) 117 response client::updateStatus(std::string msg, tweet& result, tweet in_response_to, std::list<long> media_ids)
98 { 118 {
99 std::stringstream datastrstream; 119 std::stringstream datastrstream;
100 datastrstream << "status=" << OAuth::PercentEncode(msg); 120 datastrstream << "status=" << OAuth::PercentEncode(msg);
101 121
122 if (in_response_to)
123 {
124 datastrstream << "&in_reply_to_status_id=";
125 datastrstream << in_response_to.getID();
126 }
127
102 if (!media_ids.empty()) 128 if (!media_ids.empty())
103 { 129 {
104 datastrstream << "&media_ids="; 130 datastrstream << "&media_ids=";
@@ -109,7 +135,7 @@ namespace twitter {
109 std::string url = "https://api.twitter.com/1.1/statuses/update.json"; 135 std::string url = "https://api.twitter.com/1.1/statuses/update.json";
110 136
111 long response_code; 137 long response_code;
112 json response_data; 138 std::string response_data;
113 if (!performPost(url, datastr, response_code, response_data)) 139 if (!performPost(url, datastr, response_code, response_data))
114 { 140 {
115 return response::curl_error; 141 return response::curl_error;
@@ -147,7 +173,7 @@ namespace twitter {
147 } 173 }
148 174
149 long response_code; 175 long response_code;
150 json response_data; 176 std::string response_data;
151 if (!performMultiPost("https://upload.twitter.com/1.1/media/upload.json", form.get(), response_code, response_data)) 177 if (!performMultiPost("https://upload.twitter.com/1.1/media/upload.json", form.get(), response_code, response_data))
152 { 178 {
153 return response::curl_error; 179 return response::curl_error;
@@ -158,7 +184,8 @@ namespace twitter {
158 return codeForError(response_code, response_data); 184 return codeForError(response_code, response_data);
159 } 185 }
160 186
161 media_id = response_data["media_id"].get<long>(); 187 auto response_json = json::parse(response_data);
188 media_id = response_json["media_id"].get<long>();
162 189
163 curl_httppost* append_form_post = nullptr; 190 curl_httppost* append_form_post = nullptr;
164 curl_httppost* append_form_last = nullptr; 191 curl_httppost* append_form_last = nullptr;
@@ -197,7 +224,8 @@ namespace twitter {
197 return codeForError(response_code, response_data); 224 return codeForError(response_code, response_data);
198 } 225 }
199 226
200 if (response_data.find("processing_info") != response_data.end()) 227 response_json = json::parse(response_data);
228 if (response_json.find("processing_info") != response_json.end())
201 { 229 {
202 std::stringstream datastr; 230 std::stringstream datastr;
203 datastr << "https://upload.twitter.com/1.1/media/upload.json?command=STATUS&media_id=" << media_id; 231 datastr << "https://upload.twitter.com/1.1/media/upload.json?command=STATUS&media_id=" << media_id;
@@ -214,20 +242,183 @@ namespace twitter {
214 return codeForError(response_code, response_data); 242 return codeForError(response_code, response_data);
215 } 243 }
216 244
217 if (response_data["processing_info"]["state"] == "succeeded") 245 response_json = json::parse(response_data);
246 if (response_json["processing_info"]["state"] == "succeeded")
218 { 247 {
219 break; 248 break;
220 } 249 }
221 250
222 int ttw = response_data["processing_info"]["check_after_secs"].get<int>(); 251 int ttw = response_json["processing_info"]["check_after_secs"].get<int>();
223 sleep(ttw); 252 std::this_thread::sleep_for(std::chrono::seconds(ttw));
224 } 253 }
225 } 254 }
226 255
227 return response::ok; 256 return response::ok;
228 } 257 }
229 258
230 bool client::performGet(std::string url, long& response_code, json& result) 259 response client::follow(user_id toFollow)
260 {
261 std::stringstream datastrstream;
262 datastrstream << "follow=true&user_id=";
263 datastrstream << toFollow;
264
265 std::string datastr = datastrstream.str();
266 std::string url = "https://api.twitter.com/1.1/friendships/create.json";
267
268 long response_code;
269 std::string response_data;
270 if (!performPost(url, datastr, response_code, response_data))
271 {
272 return response::curl_error;
273 }
274
275 if (response_code == 200)
276 {
277 return response::ok;
278 } else {
279 return codeForError(response_code, response_data);
280 }
281 }
282
283 response client::follow(user toFollow)
284 {
285 return follow(toFollow.getID());
286 }
287
288 response client::unfollow(user_id toUnfollow)
289 {
290 std::stringstream datastrstream;
291 datastrstream << "user_id=";
292 datastrstream << toUnfollow;
293
294 std::string datastr = datastrstream.str();
295 std::string url = "https://api.twitter.com/1.1/friendships/destroy.json";
296
297 long response_code;
298 std::string response_data;
299 if (!performPost(url, datastr, response_code, response_data))
300 {
301 return response::curl_error;
302 }
303
304 if (response_code == 200)
305 {
306 return response::ok;
307 } else {
308 return codeForError(response_code, response_data);
309 }
310 }
311
312 response client::unfollow(user toUnfollow)
313 {
314 return unfollow(toUnfollow.getID());
315 }
316
317 const user& client::getUser() const
318 {
319 return _current_user;
320 }
321
322 response client::getFriends(std::set<user_id>& _ret)
323 {
324 if (!_current_user)
325 {
326 return response::unknown_error;
327 }
328
329 long long cursor = -1;
330 std::set<user_id> result;
331
332 while (cursor != 0)
333 {
334 std::stringstream urlstream;
335 urlstream << "https://api.twitter.com/1.1/friends/ids.json?user_id=";
336 urlstream << _current_user.getID();
337 urlstream << "&cursor=";
338 urlstream << cursor;
339
340 std::string url = urlstream.str();
341
342 long response_code;
343 std::string response_data;
344 if (!performGet(url, response_code, response_data))
345 {
346 return response::curl_error;
347 }
348
349 if (response_code == 200)
350 {
351 json rjs = json::parse(response_data);
352 cursor = rjs.at("next_cursor");
353 result.insert(std::begin(rjs.at("ids")), std::end(rjs.at("ids")));
354 } else {
355 return codeForError(response_code, response_data);
356 }
357 }
358
359 _ret = result;
360
361 return response::ok;
362 }
363
364 response client::getFollowers(std::set<user_id>& _ret)
365 {
366 if (!_current_user)
367 {
368 return response::unknown_error;
369 }
370
371 long long cursor = -1;
372 std::set<user_id> result;
373
374 while (cursor != 0)
375 {
376 std::stringstream urlstream;
377 urlstream << "https://api.twitter.com/1.1/followers/ids.json?user_id=";
378 urlstream << _current_user.getID();
379 urlstream << "&cursor=";
380 urlstream << cursor;
381
382 std::string url = urlstream.str();
383
384 long response_code;
385 std::string response_data;
386 if (!performGet(url, response_code, response_data))
387 {
388 return response::curl_error;
389 }
390
391 if (response_code == 200)
392 {
393 json rjs = json::parse(response_data);
394 cursor = rjs.at("next_cursor");
395 result.insert(std::begin(rjs.at("ids")), std::end(rjs.at("ids")));
396 } else {
397 return codeForError(response_code, response_data);
398 }
399 }
400
401 _ret = result;
402
403 return response::ok;
404 }
405
406 void client::setUserStreamNotifyCallback(stream::notify_callback callback)
407 {
408 _user_stream.setNotifyCallback(callback);
409 }
410
411 void client::startUserStream()
412 {
413 _user_stream.start();
414 }
415
416 void client::stopUserStream()
417 {
418 _user_stream.stop();
419 }
420
421 bool client::performGet(std::string url, long& response_code, std::string& result)
231 { 422 {
232 std::ostringstream output; 423 std::ostringstream output;
233 curl::curl_ios<std::ostringstream> ios(output); 424 curl::curl_ios<std::ostringstream> ios(output);
@@ -255,17 +446,12 @@ namespace twitter {
255 } 446 }
256 447
257 response_code = conn.get_info<CURLINFO_RESPONSE_CODE>().get(); 448 response_code = conn.get_info<CURLINFO_RESPONSE_CODE>().get();
258 if (output.str().empty()) 449 result = output.str();
259 {
260 result = json();
261 } else {
262 result = json::parse(output.str());
263 }
264 450
265 return true; 451 return true;
266 } 452 }
267 453
268 bool client::performPost(std::string url, std::string datastr, long& response_code, json& result) 454 bool client::performPost(std::string url, std::string datastr, long& response_code, std::string& result)
269 { 455 {
270 std::ostringstream output; 456 std::ostringstream output;
271 curl::curl_ios<std::ostringstream> ios(output); 457 curl::curl_ios<std::ostringstream> ios(output);
@@ -294,17 +480,12 @@ namespace twitter {
294 } 480 }
295 481
296 response_code = conn.get_info<CURLINFO_RESPONSE_CODE>().get(); 482 response_code = conn.get_info<CURLINFO_RESPONSE_CODE>().get();
297 if (output.str().empty()) 483 result = output.str();
298 {
299 result = json();
300 } else {
301 result = json::parse(output.str());
302 }
303 484
304 return true; 485 return true;
305 } 486 }
306 487
307 bool client::performMultiPost(std::string url, const curl_httppost* fields, long& response_code, json& result) 488 bool client::performMultiPost(std::string url, const curl_httppost* fields, long& response_code, std::string& result)
308 { 489 {
309 std::ostringstream output; 490 std::ostringstream output;
310 curl::curl_ios<std::ostringstream> ios(output); 491 curl::curl_ios<std::ostringstream> ios(output);
@@ -333,23 +514,19 @@ namespace twitter {
333 } 514 }
334 515
335 response_code = conn.get_info<CURLINFO_RESPONSE_CODE>().get(); 516 response_code = conn.get_info<CURLINFO_RESPONSE_CODE>().get();
336 517 result = output.str();
337 if (output.str().empty())
338 {
339 result = json();
340 } else {
341 result = json::parse(output.str());
342 }
343 518
344 return true; 519 return true;
345 } 520 }
346 521
347 response client::codeForError(int response_code, json response_data) const 522 response client::codeForError(int response_code, std::string response_data) const
348 { 523 {
524 auto response_json = json::parse(response_data);
525
349 std::set<int> error_codes; 526 std::set<int> error_codes;
350 if (response_data.find("errors") != response_data.end()) 527 if (response_json.find("errors") != response_json.end())
351 { 528 {
352 std::transform(std::begin(response_data["errors"]), std::end(response_data["errors"]), std::inserter(error_codes, std::begin(error_codes)), [] (const json& error) { 529 std::transform(std::begin(response_json["errors"]), std::end(response_json["errors"]), std::inserter(error_codes, std::begin(error_codes)), [] (const json& error) {
353 return error["code"].get<int>(); 530 return error["code"].get<int>();
354 }); 531 });
355 } 532 }
@@ -407,4 +584,208 @@ namespace twitter {
407 } 584 }
408 } 585 }
409 586
587 client::stream::stream(client& _client) : _client(_client)
588 {
589
590 }
591
592 bool client::stream::isRunning() const
593 {
594 return _thread.joinable();
595 }
596
597 void client::stream::setNotifyCallback(notify_callback _n)
598 {
599 std::lock_guard<std::mutex> _notify_lock(_notify_mutex);
600 _notify = _n;
601 }
602
603 void client::stream::start()
604 {
605 std::lock_guard<std::mutex> _running_lock(_running_mutex);
606
607 if (!_thread.joinable())
608 {
609 _thread = std::thread(&stream::run, this);
610 }
611 }
612
613 void client::stream::stop()
614 {
615 std::lock_guard<std::mutex> _running_lock(_running_mutex);
616
617 if (_thread.joinable())
618 {
619 _stop = true;
620 _thread.join();
621 _stop = false;
622 }
623 }
624
625 void client::stream::run()
626 {
627 curl::curl_easy conn;
628 std::string url = "https://userstream.twitter.com/1.1/user.json";
629
630 curl::curl_header headers;
631 std::string oauth_header = _client._oauth_client->getFormattedHttpHeader(OAuth::Http::Get, url, "");
632 if (!oauth_header.empty())
633 {
634 headers.add(oauth_header);
635 }
636
637 conn.add<CURLOPT_WRITEFUNCTION>(client_stream_write_callback_wrapper);
638 conn.add<CURLOPT_WRITEDATA>(this);
639 conn.add<CURLOPT_HEADERFUNCTION>(nullptr);
640 conn.add<CURLOPT_HEADERDATA>(nullptr);
641 conn.add<CURLOPT_XFERINFOFUNCTION>(client_stream_progress_callback_wrapper);
642 conn.add<CURLOPT_XFERINFODATA>(this);
643 conn.add<CURLOPT_NOPROGRESS>(0);
644 //conn.add<CURLOPT_VERBOSE>(1);
645 //conn.add<CURLOPT_DEBUGFUNCTION>(my_trace);
646 conn.add<CURLOPT_URL>(url.c_str());
647 conn.add<CURLOPT_HTTPHEADER>(headers.get());
648
649 _backoff_type = backoff::none;
650 _backoff_amount = std::chrono::milliseconds(0);
651 for (;;)
652 {
653 bool failure = false;
654 try {
655 conn.perform();
656 } catch (curl::curl_easy_exception error)
657 {
658 failure = true;
659 if ((error.get_code() == CURLE_ABORTED_BY_CALLBACK) && _stop)
660 {
661 break;
662 } else {
663 if (_backoff_type == backoff::none)
664 {
665 _established = false;
666 _backoff_type = backoff::network;
667 _backoff_amount = std::chrono::milliseconds(0);
668 }
669 }
670 }
671
672 if (!failure)
673 {
674 long response_code = conn.get_info<CURLINFO_RESPONSE_CODE>().get();
675 if (response_code == 420)
676 {
677 if (_backoff_type == backoff::none)
678 {
679 _established = false;
680 _backoff_type = backoff::rate_limit;
681 _backoff_amount = std::chrono::minutes(1);
682 }
683 } else if (response_code != 200)
684 {
685 if (_backoff_type == backoff::none)
686 {
687 _established = false;
688 _backoff_type = backoff::http;
689 _backoff_amount = std::chrono::seconds(5);
690 }
691 } else {
692 break;
693 }
694 }
695
696 std::this_thread::sleep_for(_backoff_amount);
697
698 switch (_backoff_type)
699 {
700 case backoff::network:
701 {
702 if (_backoff_amount < std::chrono::seconds(16))
703 {
704 _backoff_amount += std::chrono::milliseconds(250);
705 }
706
707 break;
708 }
709
710 case backoff::http:
711 {
712 if (_backoff_amount < std::chrono::seconds(320))
713 {
714 _backoff_amount *= 2;
715 }
716
717 break;
718 }
719
720 case backoff::rate_limit:
721 {
722 _backoff_amount *= 2;
723
724 break;
725 }
726 }
727 }
728 }
729
730 int client::stream::write(char* ptr, size_t size, size_t nmemb)
731 {
732 for (size_t i = 0; i < size*nmemb; i++)
733 {
734 if (ptr[i] == '\r')
735 {
736 i++; // Skip the \n
737
738 if (!_buffer.empty())
739 {
740 notification n(_buffer, _client._current_user);
741 if (n.getType() == notification::type::friends)
742 {
743 _established = true;
744 _backoff_type = backoff::none;
745 _backoff_amount = std::chrono::milliseconds(0);
746 }
747
748 {
749 std::lock_guard<std::mutex> _notify_lock(_notify_mutex);
750
751 if (_notify)
752 {
753 _notify(n);
754 }
755 }
756
757 _buffer = "";
758 }
759 } else {
760 _buffer.push_back(ptr[i]);
761 }
762 }
763
764 {
765 std::lock_guard<std::mutex> _stall_lock(_stall_mutex);
766 time(&_last_write);
767 }
768
769 return size*nmemb;
770 }
771
772 int client::stream::progress()
773 {
774 if (_stop)
775 {
776 return 1;
777 }
778
779 if (_established)
780 {
781 std::lock_guard<std::mutex> _stall_lock(_stall_mutex);
782 if (difftime(time(NULL), _last_write) >= 90)
783 {
784 return 1;
785 }
786 }
787
788 return 0;
789 }
790
410}; 791};
diff --git a/src/client.h b/src/client.h index 3a133e4..ae80ed9 100644 --- a/src/client.h +++ b/src/client.h
@@ -7,6 +7,12 @@
7#include <list> 7#include <list>
8#include <curl_easy.h> 8#include <curl_easy.h>
9#include <curl_header.h> 9#include <curl_header.h>
10#include <thread>
11#include <mutex>
12#include "notification.h"
13#include <set>
14#include <ctime>
15#include <chrono>
10 16
11namespace OAuth { 17namespace OAuth {
12 class Consumer; 18 class Consumer;
@@ -18,21 +24,82 @@ namespace twitter {
18 24
19 class client { 25 class client {
20 public: 26 public:
27 class stream {
28 public:
29 typedef std::function<void(notification _notification)> notify_callback;
30
31 stream(client& _client);
32
33 void setNotifyCallback(notify_callback _n);
34
35 bool isRunning() const;
36 void start();
37 void stop();
38
39 private:
40 enum class backoff {
41 none,
42 network,
43 http,
44 rate_limit
45 };
46
47 void run();
48 int progress();
49 int write(char* ptr, size_t size, size_t nmemb);
50
51 friend int client_stream_progress_callback_wrapper(void* stream, curl_off_t, curl_off_t, curl_off_t, curl_off_t);
52 friend size_t client_stream_write_callback_wrapper(void* ptr, size_t size, size_t nmemb, void* stream);
53
54 client& _client;
55 notify_callback _notify;
56 bool _stop = false;
57 std::thread _thread;
58 std::mutex _running_mutex;
59 std::mutex _notify_mutex;
60 std::mutex _stall_mutex;
61 std::string _buffer;
62 time_t _last_write;
63 bool _established = false;
64 backoff _backoff_type = backoff::none;
65 std::chrono::milliseconds _backoff_amount;
66 };
67
21 client(const auth& _auth); 68 client(const auth& _auth);
22 ~client(); 69 ~client();
23 70
24 response updateStatus(std::string msg, tweet& result, std::list<long> media_ids = {}); 71 response updateStatus(std::string msg, tweet& result, tweet in_response_to = tweet(), std::list<long> media_ids = {});
25 response uploadMedia(std::string media_type, const char* data, long data_length, long& media_id); 72 response uploadMedia(std::string media_type, const char* data, long data_length, long& media_id);
26 73
74 response follow(user_id toFollow);
75 response follow(user toFollow);
76
77 response unfollow(user_id toUnfollow);
78 response unfollow(user toUnfollow);
79
80 response getFriends(std::set<user_id>& result);
81 response getFollowers(std::set<user_id>& result);
82
83 const user& getUser() const;
84
85 void setUserStreamNotifyCallback(stream::notify_callback callback);
86 void startUserStream();
87 void stopUserStream();
88
27 private: 89 private:
90 friend class stream;
91
28 OAuth::Consumer* _oauth_consumer; 92 OAuth::Consumer* _oauth_consumer;
29 OAuth::Token* _oauth_token; 93 OAuth::Token* _oauth_token;
30 OAuth::Client* _oauth_client; 94 OAuth::Client* _oauth_client;
31 95
32 bool performGet(std::string url, long& response_code, json& result); 96 user _current_user;
33 bool performPost(std::string url, std::string dataStr, long& response_code, json& result); 97 stream _user_stream{*this};
34 bool performMultiPost(std::string url, const curl_httppost* fields, long& response_code, json& result); 98
35 response codeForError(int httpcode, json errors) const; 99 bool performGet(std::string url, long& response_code, std::string& result);
100 bool performPost(std::string url, std::string dataStr, long& response_code, std::string& result);
101 bool performMultiPost(std::string url, const curl_httppost* fields, long& response_code, std::string& result);
102 response codeForError(int httpcode, std::string errors) const;
36 }; 103 };
37 104
38}; 105};
diff --git a/src/direct_message.cpp b/src/direct_message.cpp new file mode 100644 index 0000000..d5b6f36 --- /dev/null +++ b/src/direct_message.cpp
@@ -0,0 +1,10 @@
1#include "direct_message.h"
2
3namespace twitter {
4
5 direct_message::direct_message(std::string data)
6 {
7
8 }
9
10};
diff --git a/src/direct_message.h b/src/direct_message.h new file mode 100644 index 0000000..4b5e285 --- /dev/null +++ b/src/direct_message.h
@@ -0,0 +1,15 @@
1#ifndef DIRECT_MESSAGE_H_2B2AE3F8
2#define DIRECT_MESSAGE_H_2B2AE3F8
3
4#include <string>
5
6namespace twitter {
7
8 class direct_message {
9 public:
10 direct_message(std::string data);
11 };
12
13};
14
15#endif /* end of include guard: DIRECT_MESSAGE_H_2B2AE3F8 */
diff --git a/src/list.cpp b/src/list.cpp new file mode 100644 index 0000000..49405d0 --- /dev/null +++ b/src/list.cpp
@@ -0,0 +1,15 @@
1#include "list.h"
2
3namespace twitter {
4
5 list::list()
6 {
7
8 }
9
10 list::list(std::string data)
11 {
12
13 }
14
15};
diff --git a/src/list.h b/src/list.h new file mode 100644 index 0000000..3876f2d --- /dev/null +++ b/src/list.h
@@ -0,0 +1,16 @@
1#ifndef LIST_H_D7DEA7D8
2#define LIST_H_D7DEA7D8
3
4#include <string>
5
6namespace twitter {
7
8 class list {
9 public:
10 list();
11 list(std::string data);
12 };
13
14};
15
16#endif /* end of include guard: LIST_H_D7DEA7D8 */
diff --git a/src/notification.cpp b/src/notification.cpp new file mode 100644 index 0000000..3dcdd90 --- /dev/null +++ b/src/notification.cpp
@@ -0,0 +1,848 @@
1#include "notification.h"
2#include <cassert>
3#include <new>
4#include <json.hpp>
5
6using nlohmann::json;
7
8namespace twitter {
9
10 notification::type notification::getType() const
11 {
12 return _type;
13 }
14
15 notification::notification() : _type(type::invalid)
16 {
17
18 }
19
20 notification::notification(std::string data, const user& current_user)
21 {
22 auto _data = json::parse(data);
23
24 if (_data.find("in_reply_to_status_id") != _data.end())
25 {
26 _type = type::tweet;
27
28 new(&_tweet) tweet(data);
29 } else if (_data.find("event") != _data.end())
30 {
31 std::string event = _data.at("event");
32 user source(_data.at("source").dump());
33 user target(_data.at("target").dump());
34
35 if (event == "user_update")
36 {
37 _type = type::update_user;
38
39 new(&_user) user(source);
40 } else if (event == "block")
41 {
42 _type = type::block;
43
44 new(&_user) user(target);
45 } else if (event == "unblock")
46 {
47 _type = type::unblock;
48
49 new(&_user) user(target);
50 } else if (event == "favorite")
51 {
52 new(&_user_and_tweet._tweet) tweet(_data.at("target_object").dump());
53
54 if (current_user == source)
55 {
56 _type = type::favorite;
57
58 new(&_user_and_tweet._user) user(target);
59 } else {
60 _type = type::favorited;
61
62 new(&_user_and_tweet._user) user(source);
63 }
64 } else if (event == "unfavorite")
65 {
66 new(&_user_and_tweet._tweet) tweet(_data.at("target_object").dump());
67
68 if (current_user == source)
69 {
70 _type = type::unfavorite;
71
72 new(&_user_and_tweet._user) user(target);
73 } else {
74 _type = type::unfavorited;
75
76 new(&_user_and_tweet._user) user(source);
77 }
78 } else if (event == "follow")
79 {
80 if (current_user == source)
81 {
82 _type = type::follow;
83
84 new(&_user) user(target);
85 } else {
86 _type = type::followed;
87
88 new(&_user) user(source);
89 }
90 } else if (event == "unfollow")
91 {
92 _type = type::unfollow;
93
94 new(&_user) user(target);
95 } else if (event == "list_created")
96 {
97 _type = type::list_created;
98
99 new(&_list) list(_data.at("target_object").dump());
100 } else if (event == "list_destroyed")
101 {
102 _type = type::list_destroyed;
103
104 new(&_list) list(_data.at("target_object").dump());
105 } else if (event == "list_updated")
106 {
107 _type = type::list_updated;
108
109 new(&_list) list(_data.at("target_object").dump());
110 } else if (event == "list_member_added")
111 {
112 new(&_user_and_list._list) list(_data.at("target_object").dump());
113
114 if (current_user == source)
115 {
116 _type = type::list_add;
117
118 new(&_user_and_list._user) user(target);
119 } else {
120 _type = type::list_added;
121
122 new(&_user_and_list._user) user(source);
123 }
124 } else if (event == "list_member_removed")
125 {
126 new(&_user_and_list._list) list(_data.at("target_object").dump());
127
128 if (current_user == source)
129 {
130 _type = type::list_remove;
131
132 new(&_user_and_list._user) user(target);
133 } else {
134 _type = type::list_removed;
135
136 new(&_user_and_list._user) user(source);
137 }
138 } else if (event == "list_member_subscribe")
139 {
140 new(&_user_and_list._list) list(_data.at("target_object").dump());
141
142 if (current_user == source)
143 {
144 _type = type::list_subscribe;
145
146 new(&_user_and_list._user) user(target);
147 } else {
148 _type = type::list_subscribed;
149
150 new(&_user_and_list._user) user(source);
151 }
152 } else if (event == "list_member_unsubscribe")
153 {
154 new(&_user_and_list._list) list(_data.at("target_object").dump());
155
156 if (current_user == source)
157 {
158 _type = type::list_unsubscribe;
159
160 new(&_user_and_list._user) user(target);
161 } else {
162 _type = type::list_unsubscribed;
163
164 new(&_user_and_list._user) user(source);
165 }
166 } else if (event == "quoted_tweet")
167 {
168 _type = type::quoted;
169
170 new(&_user_and_tweet._user) user(source);
171 new(&_user_and_tweet._tweet) tweet(_data.at("target_object").dump());
172 }
173 } else if (_data.find("warning") != _data.end())
174 {
175 new(&_warning) std::string(_data.at("warning").at("message").get<std::string>());
176
177 if (_data.at("warning").at("code") == "FALLING_BEHIND")
178 {
179 _type = type::stall;
180 } else if (_data.at("warning").at("code") == "FOLLOWS_OVER_LIMIT")
181 {
182 _type = type::follow_limit;
183 } else {
184 _type = type::unknown_warning;
185 }
186 } else if (_data.find("delete") != _data.end())
187 {
188 _type = type::deletion;
189
190 _user_id_and_tweet_id._tweet_id = _data.at("delete").at("status").at("id");
191 _user_id_and_tweet_id._user_id = _data.at("delete").at("status").at("user_id");
192 } else if (_data.find("scrub_geo") != _data.end())
193 {
194 _type = type::scrub_location;
195
196 _user_id_and_tweet_id._tweet_id = _data.at("scrub_geo").at("up_to_status_id");
197 _user_id_and_tweet_id._user_id = _data.at("scrub_geo").at("user_id");
198 } else if (_data.find("limit") != _data.end())
199 {
200 _type = type::limit;
201
202 _limit = _data.at("limit").at("track");
203 } else if (_data.find("status_withheld") != _data.end())
204 {
205 _type = type::withhold_status;
206
207 _withhold_status._user_id = _data.at("status_withheld").at("user_id");
208 _withhold_status._tweet_id = _data.at("status_withheld").at("id");
209
210 new(&_withhold_status._countries) std::vector<std::string>();
211 for (auto s : _data.at("status_withheld").at("withheld_in_countries"))
212 {
213 _withhold_status._countries.push_back(s);
214 }
215 } else if (_data.find("user_withheld") != _data.end())
216 {
217 _type = type::withhold_user;
218
219 _withhold_user._user_id = _data.at("user_withheld").at("id");
220
221 new(&_withhold_user._countries) std::vector<std::string>();
222 for (auto s : _data.at("user_withheld").at("withheld_in_countries"))
223 {
224 _withhold_user._countries.push_back(s);
225 }
226 } else if (_data.find("disconnect") != _data.end())
227 {
228 _type = type::disconnect;
229
230 switch (_data.at("disconnect").at("code").get<int>())
231 {
232 case 1: _disconnect = disconnect_code::shutdown; break;
233 case 2: _disconnect = disconnect_code::duplicate; break;
234 case 4: _disconnect = disconnect_code::stall; break;
235 case 5: _disconnect = disconnect_code::normal; break;
236 case 6: _disconnect = disconnect_code::token_revoked; break;
237 case 7: _disconnect = disconnect_code::admin_logout; break;
238 case 9: _disconnect = disconnect_code::limit; break;
239 case 10: _disconnect = disconnect_code::exception; break;
240 case 11: _disconnect = disconnect_code::broker; break;
241 case 12: _disconnect = disconnect_code::load; break;
242 default: _disconnect = disconnect_code::unknown;
243 }
244 } else if (_data.find("friends") != _data.end())
245 {
246 _type = type::friends;
247
248 new(&_friends) std::set<user_id>(_data.at("friends").begin(), _data.at("friends").end());
249 } else if (_data.find("direct_message") != _data.end())
250 {
251 _type = type::direct;
252
253 new(&_direct_message) direct_message(_data.at("direct_message").dump());
254 } else {
255 _type = type::unknown;
256 }
257 }
258
259 notification::notification(const notification& other)
260 {
261 _type = other._type;
262
263 switch (_type)
264 {
265 case type::tweet:
266 {
267 new(&_tweet) tweet(other._tweet);
268
269 break;
270 }
271
272 case type::update_user:
273 case type::block:
274 case type::unblock:
275 case type::follow:
276 case type::followed:
277 case type::unfollow:
278 {
279 new(&_user) user(other._user);
280
281 break;
282 }
283
284 case type::favorite:
285 case type::favorited:
286 case type::unfavorite:
287 case type::unfavorited:
288 case type::quoted:
289 {
290 new(&_user_and_tweet._user) user(other._user_and_tweet._user);
291 new(&_user_and_tweet._tweet) tweet(other._user_and_tweet._tweet);
292
293 break;
294 }
295
296 case type::list_created:
297 case type::list_destroyed:
298 case type::list_updated:
299 {
300 new(&_list) list(other._list);
301
302 break;
303 }
304
305 case type::list_add:
306 case type::list_added:
307 case type::list_remove:
308 case type::list_removed:
309 case type::list_subscribe:
310 case type::list_subscribed:
311 case type::list_unsubscribe:
312 case type::list_unsubscribed:
313 {
314 new(&_user_and_list._user) user(other._user_and_list._user);
315 new(&_user_and_list._list) list(other._user_and_list._list);
316
317 break;
318 }
319
320 case type::stall:
321 case type::follow_limit:
322 case type::unknown_warning:
323 {
324 new(&_warning) std::string(other._warning);
325
326 break;
327 }
328
329 case type::deletion:
330 case type::scrub_location:
331 {
332 _user_id_and_tweet_id._user_id = other._user_id_and_tweet_id._user_id;
333 _user_id_and_tweet_id._tweet_id = other._user_id_and_tweet_id._tweet_id;
334
335 break;
336 }
337
338 case type::limit:
339 {
340 _limit = other._limit;
341
342 break;
343 }
344
345 case type::withhold_status:
346 {
347 _withhold_status._user_id = other._withhold_status._user_id;
348 _withhold_status._tweet_id = other._withhold_status._tweet_id;
349 new(&_withhold_status._countries) std::vector<std::string>(other._withhold_status._countries);
350
351 break;
352 }
353
354 case type::withhold_user:
355 {
356 _withhold_user._user_id = other._withhold_user._user_id;
357 new(&_withhold_user._countries) std::vector<std::string>(other._withhold_user._countries);
358
359 break;
360 }
361
362 case type::disconnect:
363 {
364 _disconnect = other._disconnect;
365
366 break;
367 }
368
369 case type::friends:
370 {
371 new(&_friends) std::set<user_id>(other._friends);
372
373 break;
374 }
375
376 case type::direct:
377 {
378 new(&_direct_message) direct_message(other._direct_message);
379
380 break;
381 }
382 }
383 }
384
385 notification& notification::operator=(const notification& other)
386 {
387 this->~notification();
388
389 _type = other._type;
390
391 switch (_type)
392 {
393 case type::tweet:
394 {
395 new(&_tweet) tweet(other._tweet);
396
397 break;
398 }
399
400 case type::update_user:
401 case type::block:
402 case type::unblock:
403 case type::follow:
404 case type::followed:
405 case type::unfollow:
406 {
407 new(&_user) user(other._user);
408
409 break;
410 }
411
412 case type::favorite:
413 case type::favorited:
414 case type::unfavorite:
415 case type::unfavorited:
416 case type::quoted:
417 {
418 new(&_user_and_tweet._user) user(other._user_and_tweet._user);
419 new(&_user_and_tweet._tweet) tweet(other._user_and_tweet._tweet);
420
421 break;
422 }
423
424 case type::list_created:
425 case type::list_destroyed:
426 case type::list_updated:
427 {
428 new(&_list) list(other._list);
429
430 break;
431 }
432
433 case type::list_add:
434 case type::list_added:
435 case type::list_remove:
436 case type::list_removed:
437 case type::list_subscribe:
438 case type::list_subscribed:
439 case type::list_unsubscribe:
440 case type::list_unsubscribed:
441 {
442 new(&_user_and_list._user) user(other._user_and_list._user);
443 new(&_user_and_list._list) list(other._user_and_list._list);
444
445 break;
446 }
447
448 case type::stall:
449 case type::follow_limit:
450 case type::unknown_warning:
451 {
452 new(&_warning) std::string(other._warning);
453
454 break;
455 }
456
457 case type::deletion:
458 case type::scrub_location:
459 {
460 _user_id_and_tweet_id._user_id = other._user_id_and_tweet_id._user_id;
461 _user_id_and_tweet_id._tweet_id = other._user_id_and_tweet_id._tweet_id;
462
463 break;
464 }
465
466 case type::limit:
467 {
468 _limit = other._limit;
469
470 break;
471 }
472
473 case type::withhold_status:
474 {
475 _withhold_status._user_id = other._withhold_status._user_id;
476 _withhold_status._tweet_id = other._withhold_status._tweet_id;
477 new(&_withhold_status._countries) std::vector<std::string>(other._withhold_status._countries);
478
479 break;
480 }
481
482 case type::withhold_user:
483 {
484 _withhold_user._user_id = other._withhold_user._user_id;
485 new(&_withhold_user._countries) std::vector<std::string>(other._withhold_user._countries);
486
487 break;
488 }
489
490 case type::disconnect:
491 {
492 _disconnect = other._disconnect;
493
494 break;
495 }
496
497 case type::friends:
498 {
499 new(&_friends) std::set<user_id>(other._friends);
500
501 break;
502 }
503
504 case type::direct:
505 {
506 new(&_direct_message) direct_message(other._direct_message);
507
508 break;
509 }
510 }
511
512 return *this;
513 }
514
515 notification::~notification()
516 {
517 switch (_type)
518 {
519 case type::tweet:
520 {
521 _tweet.~tweet();
522
523 break;
524 }
525
526 case type::update_user:
527 case type::block:
528 case type::unblock:
529 case type::follow:
530 case type::followed:
531 case type::unfollow:
532 {
533 _user.~user();
534
535 break;
536 }
537
538 case type::favorite:
539 case type::favorited:
540 case type::unfavorite:
541 case type::unfavorited:
542 case type::quoted:
543 {
544 _user_and_tweet._user.~user();
545 _user_and_tweet._tweet.~tweet();
546
547 break;
548 }
549
550 case type::list_created:
551 case type::list_destroyed:
552 case type::list_updated:
553 {
554 _list.~list();
555
556 break;
557 }
558
559 case type::list_add:
560 case type::list_added:
561 case type::list_remove:
562 case type::list_removed:
563 case type::list_subscribe:
564 case type::list_subscribed:
565 case type::list_unsubscribe:
566 case type::list_unsubscribed:
567 {
568 _user_and_list._user.~user();
569 _user_and_list._list.~list();
570
571 break;
572 }
573
574 case type::stall:
575 case type::follow_limit:
576 {
577 using string_type = std::string;
578 _warning.~string_type();
579
580 break;
581 }
582
583 case type::withhold_status:
584 {
585 using list_type = std::vector<std::string>;
586 _withhold_status._countries.~list_type();
587
588 break;
589 }
590
591 case type::withhold_user:
592 {
593 using list_type = std::vector<std::string>;
594 _withhold_user._countries.~list_type();
595
596 break;
597 }
598
599 case type::friends:
600 {
601 using list_type = std::set<user_id>;
602 _friends.~list_type();
603
604 break;
605 }
606
607 case type::direct:
608 {
609 _direct_message.~direct_message();
610
611 break;
612 }
613 }
614 }
615
616 tweet notification::getTweet() const
617 {
618 switch (_type)
619 {
620 case type::tweet:
621 {
622 return _tweet;
623 }
624
625 case type::favorite:
626 case type::favorited:
627 case type::unfavorite:
628 case type::unfavorited:
629 case type::quoted:
630 {
631 return _user_and_tweet._tweet;
632 }
633
634 default:
635 {
636 assert(false);
637
638 return tweet();
639 }
640 }
641 }
642
643 user notification::getUser() const
644 {
645 switch (_type)
646 {
647 case type::update_user:
648 case type::block:
649 case type::unblock:
650 case type::follow:
651 case type::followed:
652 case type::unfollow:
653 {
654 return _user;
655 }
656
657 case type::favorite:
658 case type::favorited:
659 case type::unfavorite:
660 case type::unfavorited:
661 case type::quoted:
662 {
663 return _user_and_tweet._user;
664 }
665
666 case type::list_add:
667 case type::list_added:
668 case type::list_remove:
669 case type::list_removed:
670 case type::list_subscribe:
671 case type::list_subscribed:
672 case type::list_unsubscribe:
673 case type::list_unsubscribed:
674 {
675 return _user_and_list._user;
676 }
677
678 default:
679 {
680 assert(false);
681
682 return user();
683 }
684 }
685 }
686
687 list notification::getList() const
688 {
689 switch (_type)
690 {
691 case type::list_created:
692 case type::list_destroyed:
693 case type::list_updated:
694 {
695 return _list;
696 }
697
698 case type::list_add:
699 case type::list_added:
700 case type::list_remove:
701 case type::list_removed:
702 case type::list_subscribe:
703 case type::list_subscribed:
704 case type::list_unsubscribe:
705 case type::list_unsubscribed:
706 {
707 return _user_and_list._list;
708 }
709
710 default:
711 {
712 assert(false);
713
714 return list();
715 }
716 }
717 }
718
719 tweet_id notification::getTweetID() const
720 {
721 switch (_type)
722 {
723 case type::deletion:
724 case type::scrub_location:
725 {
726 return _user_id_and_tweet_id._tweet_id;
727 }
728
729 case type::withhold_status:
730 {
731 return _withhold_status._tweet_id;
732 }
733
734 default:
735 {
736 assert(false);
737
738 return 0;
739 }
740 }
741 }
742
743 user_id notification::getUserID() const
744 {
745 switch (_type)
746 {
747 case type::deletion:
748 case type::scrub_location:
749 {
750 return _user_id_and_tweet_id._user_id;
751 }
752
753 case type::withhold_status:
754 {
755 return _withhold_status._user_id;
756 }
757
758 case type::withhold_user:
759 {
760 return _withhold_user._user_id;
761 }
762
763 default:
764 {
765 assert(false);
766
767 return 0;
768 }
769 }
770 }
771
772 std::vector<std::string> notification::getCountries() const
773 {
774 switch (_type)
775 {
776 case type::withhold_status:
777 {
778 return _withhold_status._countries;
779 }
780
781 case type::withhold_user:
782 {
783 return _withhold_user._countries;
784 }
785
786 default:
787 {
788 assert(false);
789
790 return std::vector<std::string>();
791 }
792 }
793 }
794
795 disconnect_code notification::getDisconnectCode() const
796 {
797 assert(_type == type::disconnect);
798
799 return _disconnect;
800 }
801
802 std::set<user_id> notification::getFriends() const
803 {
804 assert(_type == type::friends);
805
806 return _friends;
807 }
808
809 direct_message notification::getDirectMessage() const
810 {
811 assert(_type == type::direct);
812
813 return _direct_message;
814 }
815
816 int notification::getLimit() const
817 {
818 assert(_type == type::limit);
819
820 return _limit;
821 }
822
823 std::string notification::getWarning() const
824 {
825 switch (_type)
826 {
827 case type::stall:
828 case type::follow_limit:
829 case type::unknown_warning:
830 {
831 return _warning;
832 }
833
834 default:
835 {
836 assert(false);
837
838 return "";
839 }
840 }
841 }
842
843 notification::operator bool() const
844 {
845 return _type != type::invalid;
846 }
847
848};
diff --git a/src/notification.h b/src/notification.h new file mode 100644 index 0000000..da83b0f --- /dev/null +++ b/src/notification.h
@@ -0,0 +1,145 @@
1#ifndef NOTIFICATION_H_69AEF4CC
2#define NOTIFICATION_H_69AEF4CC
3
4#include <string>
5#include <vector>
6#include <set>
7#include "tweet.h"
8#include "user.h"
9#include "list.h"
10#include "direct_message.h"
11
12namespace twitter {
13
14 enum class disconnect_code {
15 shutdown,
16 duplicate,
17 stall,
18 normal,
19 token_revoked,
20 admin_logout,
21 limit,
22 exception,
23 broker,
24 load,
25 unknown
26 };
27
28 class notification {
29 public:
30 enum class type {
31 // Tweet object
32 tweet,
33
34 // User object
35 update_user,
36 block,
37 unblock,
38 follow,
39 followed,
40 unfollow,
41
42 // User and tweet
43 favorite,
44 favorited,
45 unfavorite,
46 unfavorited,
47 quoted,
48
49 // List
50 list_created,
51 list_destroyed,
52 list_updated,
53
54 // User and list
55 list_add,
56 list_added,
57 list_remove,
58 list_removed,
59 list_subscribe,
60 list_subscribed,
61 list_unsubscribe,
62 list_unsubscribed,
63
64 // Warning
65 stall,
66 follow_limit,
67 unknown_warning,
68
69 // User ID and tweet ID
70 deletion,
71 scrub_location,
72
73 // Special
74 limit,
75 withhold_status,
76 withhold_user,
77 disconnect,
78 friends,
79 direct,
80
81 // Nothing
82 unknown,
83 invalid
84 };
85
86 type getType() const;
87
88 notification();
89 notification(std::string data, const user& current_user);
90 notification(const notification& other);
91 notification& operator=(const notification& other);
92 ~notification();
93
94 tweet getTweet() const;
95 user getUser() const;
96 list getList() const;
97 tweet_id getTweetID() const;
98 user_id getUserID() const;
99 std::vector<std::string> getCountries() const;
100 disconnect_code getDisconnectCode() const;
101 std::set<user_id> getFriends() const;
102 direct_message getDirectMessage() const;
103 int getLimit() const;
104 std::string getWarning() const;
105
106 operator bool() const;
107
108 private:
109 union {
110 tweet _tweet;
111 user _user;
112 list _list;
113 struct {
114 user _user;
115 tweet _tweet;
116 } _user_and_tweet;
117 struct {
118 user _user;
119 list _list;
120 } _user_and_list;
121 std::string _warning;
122 struct {
123 user_id _user_id;
124 tweet_id _tweet_id;
125 } _user_id_and_tweet_id;
126 int _limit;
127 struct {
128 user_id _user_id;
129 tweet_id _tweet_id;
130 std::vector<std::string> _countries;
131 } _withhold_status;
132 struct {
133 user_id _user_id;
134 std::vector<std::string> _countries;
135 } _withhold_user;
136 disconnect_code _disconnect;
137 std::set<user_id> _friends;
138 direct_message _direct_message;
139 };
140 type _type;
141 };
142
143};
144
145#endif /* end of include guard: NOTIFICATION_H_69AEF4CC */
diff --git a/src/tweet.cpp b/src/tweet.cpp index 3ba3aa3..e515595 100644 --- a/src/tweet.cpp +++ b/src/tweet.cpp
@@ -1,15 +1,41 @@
1#include "tweet.h" 1#include "tweet.h"
2#include <json.hpp>
3
4using nlohmann::json;
2 5
3namespace twitter { 6namespace twitter {
4 7
5 tweet::tweet() 8 tweet::tweet() : _valid(false)
9 {
10
11 }
12
13 tweet::tweet(std::string data) : _valid(true)
14 {
15 auto _data = json::parse(data);
16 _id = _data.at("id");
17 _text = _data.at("text");
18 _author = user(_data.at("user").dump());
19 }
20
21 tweet_id tweet::getID() const
22 {
23 return _id;
24 }
25
26 std::string tweet::getText() const
27 {
28 return _text;
29 }
30
31 const user& tweet::getAuthor() const
6 { 32 {
7 _valid = false; 33 return _author;
8 } 34 }
9 35
10 tweet::tweet(const json& data) 36 tweet::operator bool() const
11 { 37 {
12 _valid = true; 38 return _valid;
13 } 39 }
14 40
15}; 41};
diff --git a/src/tweet.h b/src/tweet.h index e099579..137776c 100644 --- a/src/tweet.h +++ b/src/tweet.h
@@ -1,19 +1,29 @@
1#ifndef TWEET_H_CE980721 1#ifndef TWEET_H_CE980721
2#define TWEET_H_CE980721 2#define TWEET_H_CE980721
3 3
4#include <json.hpp> 4#include <string>
5 5#include "user.h"
6using nlohmann::json;
7 6
8namespace twitter { 7namespace twitter {
9 8
9 typedef unsigned long long tweet_id;
10
10 class tweet { 11 class tweet {
11 public: 12 public:
12 tweet(); 13 tweet();
13 tweet(const json& _data); 14 tweet(std::string data);
15
16 tweet_id getID() const;
17 std::string getText() const;
18 const user& getAuthor() const;
19
20 operator bool() const;
14 21
15 private: 22 private:
16 bool _valid; 23 bool _valid;
24 tweet_id _id;
25 std::string _text;
26 user _author;
17 }; 27 };
18 28
19}; 29};
diff --git a/src/twitter.h b/src/twitter.h index f9534c6..d0b469e 100644 --- a/src/twitter.h +++ b/src/twitter.h
@@ -12,5 +12,9 @@ namespace twitter {
12#include "auth.h" 12#include "auth.h"
13#include "client.h" 13#include "client.h"
14#include "tweet.h" 14#include "tweet.h"
15#include "user.h"
16#include "notification.h"
17#include "list.h"
18#include "direct_message.h"
15 19
16#endif /* end of include guard: TWITTER_H_AC7A7666 */ 20#endif /* end of include guard: TWITTER_H_AC7A7666 */
diff --git a/src/user.cpp b/src/user.cpp new file mode 100644 index 0000000..9352938 --- /dev/null +++ b/src/user.cpp
@@ -0,0 +1,46 @@
1#include "user.h"
2#include <json.hpp>
3
4using nlohmann::json;
5
6namespace twitter {
7
8 user::user() : _valid(false)
9 {
10
11 }
12
13 user::user(std::string data) : _valid(true)
14 {
15 auto _data = json::parse(data);
16 _id = _data.at("id");
17 _screen_name = _data.at("screen_name");
18 _name = _data.at("name");
19 }
20
21 user_id user::getID() const
22 {
23 return _id;
24 }
25
26 std::string user::getScreenName() const
27 {
28 return _screen_name;
29 }
30
31 std::string user::getName() const
32 {
33 return _name;
34 }
35
36 user::operator bool() const
37 {
38 return _valid;
39 }
40
41 bool user::operator==(const user& other) const
42 {
43 return _id == other._id;
44 }
45
46};
diff --git a/src/user.h b/src/user.h new file mode 100644 index 0000000..0af40d6 --- /dev/null +++ b/src/user.h
@@ -0,0 +1,31 @@
1#ifndef USER_H_BF3AB38C
2#define USER_H_BF3AB38C
3
4#include <string>
5
6namespace twitter {
7
8 typedef unsigned long long user_id;
9
10 class user {
11 public:
12 user();
13 user(std::string data);
14
15 user_id getID() const;
16 std::string getScreenName() const;
17 std::string getName() const;
18
19 operator bool() const;
20 bool operator==(const user& other) const;
21
22 private:
23 bool _valid = false;
24 user_id _id;
25 std::string _screen_name;
26 std::string _name;
27 };
28
29};
30
31#endif /* end of include guard: USER_H_BF3AB38C */
diff --git a/vendor/curlcpp b/vendor/curlcpp
Subproject 0cdfb670d726252bdce4fb2a194e11de38a42e0 Subproject 0aaa6cae0b9969d2c5fbaafb5de661f9b1380f0