about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2016-05-20 15:34:44 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2016-05-20 15:34:44 -0400
commit0ccac89815ee92c69fefc148cfb272faf7309136 (patch)
tree228775a433018c8a5fd20f0ebb0f8446057b2112 /src
parentf465dce27cf0f07039e29d8975ad98939f0df3a9 (diff)
downloadlibtwittercpp-0ccac89815ee92c69fefc148cfb272faf7309136.tar.gz
libtwittercpp-0ccac89815ee92c69fefc148cfb272faf7309136.tar.bz2
libtwittercpp-0ccac89815ee92c69fefc148cfb272faf7309136.zip
Started implementing user streams
You can now start a user stream and end it yourself. If it disconnects abnormally, it will reconnect with a backoff as described by Twitter. Some data structures have some fields parsed now; tweets have IDs, text, and authors. Users have IDs, screen names, and names. Notifications from the stream are parsed completely. The ability to follow and unfollow users has also been added, as well as the ability to get a list of friends and followers, and to reply to a tweet.
Diffstat (limited to 'src')
-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
13 files changed, 1661 insertions, 47 deletions
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 */