about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/notification.cpp1212
-rw-r--r--src/notification.h192
-rw-r--r--src/stream.cpp295
-rw-r--r--src/stream.h71
-rw-r--r--src/twitter.h2
6 files changed, 0 insertions, 1774 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 7685af5..74b6977 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -14,10 +14,8 @@ add_library(twitter++
14 src/client.cpp 14 src/client.cpp
15 src/request.cpp 15 src/request.cpp
16 src/timeline.cpp 16 src/timeline.cpp
17 src/stream.cpp
18 src/tweet.cpp 17 src/tweet.cpp
19 src/codes.cpp 18 src/codes.cpp
20 src/notification.cpp
21 src/direct_message.cpp 19 src/direct_message.cpp
22 src/list.cpp 20 src/list.cpp
23 src/user.cpp 21 src/user.cpp
diff --git a/src/notification.cpp b/src/notification.cpp deleted file mode 100644 index 0d09fcf..0000000 --- a/src/notification.cpp +++ /dev/null
@@ -1,1212 +0,0 @@
1#include "notification.h"
2#include <cassert>
3#include <new>
4#include <json.hpp>
5#include "codes.h"
6#include "client.h"
7
8namespace twitter {
9
10 notification::type notification::getType() const
11 {
12 return _type;
13 }
14
15 notification::notification(const user& current_user, std::string data)
16 {
17 nlohmann::json json;
18
19 try
20 {
21 json = nlohmann::json::parse(data);
22 } catch (const std::invalid_argument& error)
23 {
24 std::throw_with_nested(invalid_response(data));
25 }
26
27 try
28 {
29 if (!json["event"].is_null())
30 {
31 std::string event = json["event"];
32 user source(json["source"].dump());
33 user target(json["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(json["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(json["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(json["target_object"].dump());
100 } else if (event == "list_destroyed")
101 {
102 _type = type::list_destroyed;
103
104 new(&_list) list(json["target_object"].dump());
105 } else if (event == "list_updated")
106 {
107 _type = type::list_updated;
108
109 new(&_list) list(json["target_object"].dump());
110 } else if (event == "list_member_added")
111 {
112 new(&_user_and_list._list) list(json["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(json["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(json["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(json["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(json["target_object"].dump());
172 } else {
173 _type = type::unknown;
174 }
175 } else if (!json["warning"].is_null())
176 {
177 new(&_warning) std::string(json["warning"]["message"].get<std::string>());
178
179 auto warning_code = json["warning"]["code"].get<std::string>();
180 if (warning_code == "FALLING_BEHIND")
181 {
182 _type = type::stall;
183 } else if (warning_code == "FOLLOWS_OVER_LIMIT")
184 {
185 _type = type::follow_limit;
186 } else {
187 _type = type::unknown_warning;
188 }
189 } else if (!json["delete"].is_null())
190 {
191 _type = type::deletion;
192
193 _user_id_and_tweet_id._tweet_id = json["delete"]["status"]["id"].get<tweet_id>();
194 _user_id_and_tweet_id._user_id = json["delete"]["status"]["user_id"].get<user_id>();
195 } else if (!json["scrub_geo"].is_null())
196 {
197 _type = type::scrub_location;
198
199 _user_id_and_tweet_id._tweet_id = json["scrub_geo"]["up_to_status_id"].get<tweet_id>();
200 _user_id_and_tweet_id._user_id = json["scrub_geo"]["user_id"].get<user_id>();
201 } else if (!json["limit"].is_null())
202 {
203 _type = type::limit;
204
205 _limit = json["limit"]["track"].get<int>();
206 } else if (!json["status_withheld"].is_null())
207 {
208 _type = type::withhold_status;
209
210 _withhold_status._user_id = json["status_withheld"]["user_id"].get<user_id>();
211 _withhold_status._tweet_id = json["status_withheld"]["id"].get<tweet_id>();
212
213 new(&_withhold_status._countries) std::vector<std::string>();
214 for (auto s : json["status_withheld"]["withheld_in_countries"])
215 {
216 _withhold_status._countries.push_back(s);
217 }
218 } else if (!json["user_withheld"].is_null())
219 {
220 _type = type::withhold_user;
221
222 _withhold_user._user_id = json["user_withheld"]["id"].get<user_id>();
223
224 new(&_withhold_user._countries) std::vector<std::string>();
225 for (auto s : json["user_withheld"]["withheld_in_countries"])
226 {
227 _withhold_user._countries.push_back(s);
228 }
229 } else if (!json["disconnect"].is_null())
230 {
231 _type = type::disconnect;
232
233 switch (json["disconnect"]["code"].get<int>())
234 {
235 case 1: _disconnect = disconnect_code::shutdown; break;
236 case 2: _disconnect = disconnect_code::duplicate; break;
237 case 4: _disconnect = disconnect_code::stall; break;
238 case 5: _disconnect = disconnect_code::normal; break;
239 case 6: _disconnect = disconnect_code::token_revoked; break;
240 case 7: _disconnect = disconnect_code::admin_logout; break;
241 case 9: _disconnect = disconnect_code::limit; break;
242 case 10: _disconnect = disconnect_code::exception; break;
243 case 11: _disconnect = disconnect_code::broker; break;
244 case 12: _disconnect = disconnect_code::load; break;
245 default: _disconnect = disconnect_code::unknown;
246 }
247 } else if (!json["friends"].is_null())
248 {
249 _type = type::friends;
250
251 new(&_friends) std::set<user_id>(std::begin(json["friends"]), std::end(json["friends"]));
252 } else if (!json["direct_message"].is_null())
253 {
254 _type = type::direct;
255
256 new(&_direct_message) direct_message(json["direct_message"].dump());
257 } else {
258 _type = type::tweet;
259
260 new(&_tweet) tweet(data);
261 }
262 } catch (const std::domain_error& error)
263 {
264 std::throw_with_nested(invalid_response(data));
265 }
266 }
267
268 notification::notification(const notification& other)
269 {
270 _type = other._type;
271
272 switch (_type)
273 {
274 case type::tweet:
275 {
276 new(&_tweet) tweet(other._tweet);
277
278 break;
279 }
280
281 case type::update_user:
282 case type::block:
283 case type::unblock:
284 case type::follow:
285 case type::followed:
286 case type::unfollow:
287 {
288 new(&_user) user(other._user);
289
290 break;
291 }
292
293 case type::favorite:
294 case type::favorited:
295 case type::unfavorite:
296 case type::unfavorited:
297 case type::quoted:
298 {
299 new(&_user_and_tweet._user) user(other._user_and_tweet._user);
300 new(&_user_and_tweet._tweet) tweet(other._user_and_tweet._tweet);
301
302 break;
303 }
304
305 case type::list_created:
306 case type::list_destroyed:
307 case type::list_updated:
308 {
309 new(&_list) list(other._list);
310
311 break;
312 }
313
314 case type::list_add:
315 case type::list_added:
316 case type::list_remove:
317 case type::list_removed:
318 case type::list_subscribe:
319 case type::list_subscribed:
320 case type::list_unsubscribe:
321 case type::list_unsubscribed:
322 {
323 new(&_user_and_list._user) user(other._user_and_list._user);
324 new(&_user_and_list._list) list(other._user_and_list._list);
325
326 break;
327 }
328
329 case type::stall:
330 case type::follow_limit:
331 case type::unknown_warning:
332 {
333 new(&_warning) std::string(other._warning);
334
335 break;
336 }
337
338 case type::deletion:
339 case type::scrub_location:
340 {
341 _user_id_and_tweet_id._user_id = other._user_id_and_tweet_id._user_id;
342 _user_id_and_tweet_id._tweet_id = other._user_id_and_tweet_id._tweet_id;
343
344 break;
345 }
346
347 case type::limit:
348 {
349 _limit = other._limit;
350
351 break;
352 }
353
354 case type::withhold_status:
355 {
356 _withhold_status._user_id = other._withhold_status._user_id;
357 _withhold_status._tweet_id = other._withhold_status._tweet_id;
358 new(&_withhold_status._countries) std::vector<std::string>(other._withhold_status._countries);
359
360 break;
361 }
362
363 case type::withhold_user:
364 {
365 _withhold_user._user_id = other._withhold_user._user_id;
366 new(&_withhold_user._countries) std::vector<std::string>(other._withhold_user._countries);
367
368 break;
369 }
370
371 case type::disconnect:
372 {
373 _disconnect = other._disconnect;
374
375 break;
376 }
377
378 case type::friends:
379 {
380 new(&_friends) std::set<user_id>(other._friends);
381
382 break;
383 }
384
385 case type::direct:
386 {
387 new(&_direct_message) direct_message(other._direct_message);
388
389 break;
390 }
391
392 case type::unknown:
393 case type::invalid:
394 {
395 break;
396 }
397 }
398 }
399
400 notification::notification(notification&& other) : notification()
401 {
402 swap(*this, other);
403 }
404
405 notification& notification::operator=(notification other)
406 {
407 swap(*this, other);
408
409 return *this;
410 }
411
412 notification::~notification()
413 {
414 switch (_type)
415 {
416 case type::tweet:
417 {
418 _tweet.~tweet();
419
420 break;
421 }
422
423 case type::update_user:
424 case type::block:
425 case type::unblock:
426 case type::follow:
427 case type::followed:
428 case type::unfollow:
429 {
430 _user.~user();
431
432 break;
433 }
434
435 case type::favorite:
436 case type::favorited:
437 case type::unfavorite:
438 case type::unfavorited:
439 case type::quoted:
440 {
441 _user_and_tweet._user.~user();
442 _user_and_tweet._tweet.~tweet();
443
444 break;
445 }
446
447 case type::list_created:
448 case type::list_destroyed:
449 case type::list_updated:
450 {
451 _list.~list();
452
453 break;
454 }
455
456 case type::list_add:
457 case type::list_added:
458 case type::list_remove:
459 case type::list_removed:
460 case type::list_subscribe:
461 case type::list_subscribed:
462 case type::list_unsubscribe:
463 case type::list_unsubscribed:
464 {
465 _user_and_list._user.~user();
466 _user_and_list._list.~list();
467
468 break;
469 }
470
471 case type::stall:
472 case type::follow_limit:
473 case type::unknown_warning:
474 {
475 using string_type = std::string;
476 _warning.~string_type();
477
478 break;
479 }
480
481 case type::withhold_status:
482 {
483 using list_type = std::vector<std::string>;
484 _withhold_status._countries.~list_type();
485
486 break;
487 }
488
489 case type::withhold_user:
490 {
491 using list_type = std::vector<std::string>;
492 _withhold_user._countries.~list_type();
493
494 break;
495 }
496
497 case type::friends:
498 {
499 using list_type = std::set<user_id>;
500 _friends.~list_type();
501
502 break;
503 }
504
505 case type::direct:
506 {
507 _direct_message.~direct_message();
508
509 break;
510 }
511
512 case type::deletion:
513 case type::scrub_location:
514 case type::limit:
515 case type::disconnect:
516 case type::unknown:
517 case type::invalid:
518 {
519 break;
520 }
521 }
522 }
523
524 void swap(notification& first, notification& second)
525 {
526 using type = notification::type;
527
528 type tempType = first._type;
529 tweet tempTweet;
530 user tempUser;
531 list tempList;
532 std::string tempWarning;
533 user_id tempUserId;
534 tweet_id tempTweetId;
535 int tempLimit;
536 std::vector<std::string> tempCountries;
537 disconnect_code tempDisconnectCode;
538 std::set<user_id> tempFriends;
539 direct_message tempDirectMessage;
540
541 switch (first._type)
542 {
543 case type::tweet:
544 {
545 tempTweet = std::move(first._tweet);
546
547 break;
548 }
549
550 case type::update_user:
551 case type::block:
552 case type::unblock:
553 case type::follow:
554 case type::followed:
555 case type::unfollow:
556 {
557 tempUser = std::move(first._user);
558
559 break;
560 }
561
562 case type::favorite:
563 case type::favorited:
564 case type::unfavorite:
565 case type::unfavorited:
566 case type::quoted:
567 {
568 tempTweet = std::move(first._user_and_tweet._tweet);
569 tempUser = std::move(first._user_and_tweet._user);
570
571 break;
572 }
573
574 case type::list_created:
575 case type::list_destroyed:
576 case type::list_updated:
577 {
578 tempList = std::move(first._list);
579
580 break;
581 }
582
583 case type::list_add:
584 case type::list_added:
585 case type::list_remove:
586 case type::list_removed:
587 case type::list_subscribe:
588 case type::list_subscribed:
589 case type::list_unsubscribe:
590 case type::list_unsubscribed:
591 {
592 tempList = std::move(first._user_and_list._list);
593 tempUser = std::move(first._user_and_list._user);
594
595 break;
596 }
597
598 case type::stall:
599 case type::follow_limit:
600 case type::unknown_warning:
601 {
602 tempWarning = std::move(first._warning);
603
604 break;
605 }
606
607 case type::deletion:
608 case type::scrub_location:
609 {
610 tempUserId = first._user_id_and_tweet_id._user_id;
611 tempTweetId = first._user_id_and_tweet_id._tweet_id;
612
613 break;
614 }
615
616 case type::limit:
617 {
618 tempLimit = first._limit;
619
620 break;
621 }
622
623 case type::withhold_status:
624 {
625 tempUserId = first._withhold_status._user_id;
626 tempTweetId = first._withhold_status._tweet_id;
627 tempCountries = std::move(first._withhold_status._countries);
628
629 break;
630 }
631
632 case type::withhold_user:
633 {
634 tempUserId = first._withhold_user._user_id;
635 tempCountries = std::move(first._withhold_user._countries);
636
637 break;
638 }
639
640 case type::disconnect:
641 {
642 tempDisconnectCode = first._disconnect;
643
644 break;
645 }
646
647 case type::friends:
648 {
649 tempFriends = std::move(first._friends);
650
651 break;
652 }
653
654 case type::direct:
655 {
656 tempDirectMessage = std::move(first._direct_message);
657
658 break;
659 }
660
661 case type::invalid:
662 case type::unknown:
663 {
664 break;
665 }
666 }
667
668 first.~notification();
669
670 first._type = second._type;
671
672 // Okay now you need to initialize the first with the data from the second
673 // And then destruct the second and initialize it with the data stored in temp
674 // This is hell
675
676 switch (second._type)
677 {
678 case type::tweet:
679 {
680 new(&first._tweet) tweet(std::move(second._tweet));
681
682 break;
683 }
684
685 case type::update_user:
686 case type::block:
687 case type::unblock:
688 case type::follow:
689 case type::followed:
690 case type::unfollow:
691 {
692 new(&first._user) user(std::move(second._user));
693
694 break;
695 }
696
697 case type::favorite:
698 case type::favorited:
699 case type::unfavorite:
700 case type::unfavorited:
701 case type::quoted:
702 {
703 new(&first._user_and_tweet._user) user(std::move(second._user_and_tweet._user));
704 new(&first._user_and_tweet._tweet) tweet(std::move(second._user_and_tweet._tweet));
705
706 break;
707 }
708
709 case type::list_created:
710 case type::list_destroyed:
711 case type::list_updated:
712 {
713 new(&first._list) list(std::move(second._list));
714
715 break;
716 }
717
718 case type::list_add:
719 case type::list_added:
720 case type::list_remove:
721 case type::list_removed:
722 case type::list_subscribe:
723 case type::list_subscribed:
724 case type::list_unsubscribe:
725 case type::list_unsubscribed:
726 {
727 new(&first._user_and_list._user) user(std::move(second._user_and_list._user));
728 new(&first._user_and_list._list) list(std::move(second._user_and_list._list));
729
730 break;
731 }
732
733 case type::stall:
734 case type::follow_limit:
735 case type::unknown_warning:
736 {
737 new(&first._warning) std::string(std::move(second._warning));
738
739 break;
740 }
741
742 case type::deletion:
743 case type::scrub_location:
744 {
745 first._user_id_and_tweet_id._user_id = second._user_id_and_tweet_id._user_id;
746 first._user_id_and_tweet_id._tweet_id = second._user_id_and_tweet_id._tweet_id;
747
748 break;
749 }
750
751 case type::limit:
752 {
753 first._limit = second._limit;
754
755 break;
756 }
757
758 case type::withhold_status:
759 {
760 first._withhold_status._user_id = second._withhold_status._user_id;
761 first._withhold_status._tweet_id = second._withhold_status._tweet_id;
762 new(&first._withhold_status._countries) std::vector<std::string>(std::move(second._withhold_status._countries));
763
764 break;
765 }
766
767 case type::withhold_user:
768 {
769 first._withhold_user._user_id = second._withhold_user._user_id;
770 new(&first._withhold_user._countries) std::vector<std::string>(std::move(second._withhold_user._countries));
771
772 break;
773 }
774
775 case type::disconnect:
776 {
777 first._disconnect = second._disconnect;
778
779 break;
780 }
781
782 case type::friends:
783 {
784 new(&first._friends) std::set<user_id>(std::move(second._friends));
785
786 break;
787 }
788
789 case type::direct:
790 {
791 new(&first._direct_message) direct_message(std::move(second._direct_message));
792
793 break;
794 }
795
796 case type::invalid:
797 case type::unknown:
798 {
799 break;
800 }
801 }
802
803 // Now destruct the second and initialize it with data from the first
804 second.~notification();
805
806 second._type = tempType;
807
808 switch (tempType)
809 {
810 case type::tweet:
811 {
812 new(&second._tweet) tweet(std::move(tempTweet));
813
814 break;
815 }
816
817 case type::update_user:
818 case type::block:
819 case type::unblock:
820 case type::follow:
821 case type::followed:
822 case type::unfollow:
823 {
824 new(&second._user) user(std::move(tempUser));
825
826 break;
827 }
828
829 case type::favorite:
830 case type::favorited:
831 case type::unfavorite:
832 case type::unfavorited:
833 case type::quoted:
834 {
835 new(&second._user_and_tweet._user) user(std::move(tempUser));
836 new(&second._user_and_tweet._tweet) tweet(std::move(tempTweet));
837
838 break;
839 }
840
841 case type::list_created:
842 case type::list_destroyed:
843 case type::list_updated:
844 {
845 new(&second._list) list(std::move(tempList));
846
847 break;
848 }
849
850 case type::list_add:
851 case type::list_added:
852 case type::list_remove:
853 case type::list_removed:
854 case type::list_subscribe:
855 case type::list_subscribed:
856 case type::list_unsubscribe:
857 case type::list_unsubscribed:
858 {
859 new(&second._user_and_list._user) user(std::move(tempUser));
860 new(&second._user_and_list._list) list(std::move(tempList));
861
862 break;
863 }
864
865 case type::stall:
866 case type::follow_limit:
867 case type::unknown_warning:
868 {
869 new(&second._warning) std::string(std::move(tempWarning));
870
871 break;
872 }
873
874 case type::deletion:
875 case type::scrub_location:
876 {
877 second._user_id_and_tweet_id._user_id = tempUserId;
878 second._user_id_and_tweet_id._tweet_id = tempTweetId;
879
880 break;
881 }
882
883 case type::limit:
884 {
885 second._limit = tempLimit;
886
887 break;
888 }
889
890 case type::withhold_status:
891 {
892 second._withhold_status._user_id = tempUserId;
893 second._withhold_status._tweet_id = tempTweetId;
894 new(&second._withhold_status._countries) std::vector<std::string>(std::move(tempCountries));
895
896 break;
897 }
898
899 case type::withhold_user:
900 {
901 second._withhold_user._user_id = tempUserId;
902 new(&second._withhold_user._countries) std::vector<std::string>(std::move(tempCountries));
903
904 break;
905 }
906
907 case type::disconnect:
908 {
909 second._disconnect = tempDisconnectCode;
910
911 break;
912 }
913
914 case type::friends:
915 {
916 new(&second._friends) std::set<user_id>(std::move(tempFriends));
917
918 break;
919 }
920
921 case type::direct:
922 {
923 new(&second._direct_message) direct_message(std::move(tempDirectMessage));
924
925 break;
926 }
927
928 case type::invalid:
929 case type::unknown:
930 {
931 break;
932 }
933 }
934 }
935
936 const tweet& notification::getTweet() const
937 {
938 switch (_type)
939 {
940 case type::tweet:
941 {
942 return _tweet;
943 }
944
945 case type::favorite:
946 case type::favorited:
947 case type::unfavorite:
948 case type::unfavorited:
949 case type::quoted:
950 {
951 return _user_and_tweet._tweet;
952 }
953
954 default:
955 {
956 assert(false);
957 }
958 }
959 }
960
961 const user& notification::getUser() const
962 {
963 switch (_type)
964 {
965 case type::update_user:
966 case type::block:
967 case type::unblock:
968 case type::follow:
969 case type::followed:
970 case type::unfollow:
971 {
972 return _user;
973 }
974
975 case type::favorite:
976 case type::favorited:
977 case type::unfavorite:
978 case type::unfavorited:
979 case type::quoted:
980 {
981 return _user_and_tweet._user;
982 }
983
984 case type::list_add:
985 case type::list_added:
986 case type::list_remove:
987 case type::list_removed:
988 case type::list_subscribe:
989 case type::list_subscribed:
990 case type::list_unsubscribe:
991 case type::list_unsubscribed:
992 {
993 return _user_and_list._user;
994 }
995
996 default:
997 {
998 assert(false);
999 }
1000 }
1001 }
1002
1003 const list& notification::getList() const
1004 {
1005 switch (_type)
1006 {
1007 case type::list_created:
1008 case type::list_destroyed:
1009 case type::list_updated:
1010 {
1011 return _list;
1012 }
1013
1014 case type::list_add:
1015 case type::list_added:
1016 case type::list_remove:
1017 case type::list_removed:
1018 case type::list_subscribe:
1019 case type::list_subscribed:
1020 case type::list_unsubscribe:
1021 case type::list_unsubscribed:
1022 {
1023 return _user_and_list._list;
1024 }
1025
1026 default:
1027 {
1028 assert(false);
1029 }
1030 }
1031 }
1032
1033 tweet_id notification::getTweetID() const
1034 {
1035 switch (_type)
1036 {
1037 case type::deletion:
1038 case type::scrub_location:
1039 {
1040 return _user_id_and_tweet_id._tweet_id;
1041 }
1042
1043 case type::withhold_status:
1044 {
1045 return _withhold_status._tweet_id;
1046 }
1047
1048 default:
1049 {
1050 assert(false);
1051 }
1052 }
1053 }
1054
1055 void notification::setTweetID(tweet_id _arg)
1056 {
1057 switch (_type)
1058 {
1059 case type::deletion:
1060 case type::scrub_location:
1061 {
1062 _user_id_and_tweet_id._tweet_id = _arg;;
1063 }
1064
1065 case type::withhold_status:
1066 {
1067 _withhold_status._tweet_id = _arg;
1068 }
1069
1070 default:
1071 {
1072 assert(false);
1073 }
1074 }
1075 }
1076
1077 user_id notification::getUserID() const
1078 {
1079 switch (_type)
1080 {
1081 case type::deletion:
1082 case type::scrub_location:
1083 {
1084 return _user_id_and_tweet_id._user_id;
1085 }
1086
1087 case type::withhold_status:
1088 {
1089 return _withhold_status._user_id;
1090 }
1091
1092 case type::withhold_user:
1093 {
1094 return _withhold_user._user_id;
1095 }
1096
1097 default:
1098 {
1099 assert(false);
1100 }
1101 }
1102 }
1103
1104 void notification::setUserID(user_id _arg)
1105 {
1106 switch (_type)
1107 {
1108 case type::deletion:
1109 case type::scrub_location:
1110 {
1111 _user_id_and_tweet_id._user_id = _arg;
1112 }
1113
1114 case type::withhold_status:
1115 {
1116 _withhold_status._user_id = _arg;
1117 }
1118
1119 case type::withhold_user:
1120 {
1121 _withhold_user._user_id = _arg;
1122 }
1123
1124 default:
1125 {
1126 assert(false);
1127 }
1128 }
1129 }
1130
1131 const std::vector<std::string>& notification::getCountries() const
1132 {
1133 switch (_type)
1134 {
1135 case type::withhold_status:
1136 {
1137 return _withhold_status._countries;
1138 }
1139
1140 case type::withhold_user:
1141 {
1142 return _withhold_user._countries;
1143 }
1144
1145 default:
1146 {
1147 assert(false);
1148 }
1149 }
1150 }
1151
1152 disconnect_code notification::getDisconnectCode() const
1153 {
1154 assert(_type == type::disconnect);
1155
1156 return _disconnect;
1157 }
1158
1159 void notification::setDisconnectCode(disconnect_code _arg)
1160 {
1161 assert(_type == type::disconnect);
1162
1163 _disconnect = _arg;
1164 }
1165
1166 const std::set<user_id>& notification::getFriends() const
1167 {
1168 assert(_type == type::friends);
1169
1170 return _friends;
1171 }
1172
1173 const direct_message& notification::getDirectMessage() const
1174 {
1175 assert(_type == type::direct);
1176
1177 return _direct_message;
1178 }
1179
1180 int notification::getLimit() const
1181 {
1182 assert(_type == type::limit);
1183
1184 return _limit;
1185 }
1186
1187 void notification::setLimit(int _arg)
1188 {
1189 assert(_type == type::limit);
1190
1191 _limit = _arg;
1192 }
1193
1194 const std::string& notification::getWarning() const
1195 {
1196 switch (_type)
1197 {
1198 case type::stall:
1199 case type::follow_limit:
1200 case type::unknown_warning:
1201 {
1202 return _warning;
1203 }
1204
1205 default:
1206 {
1207 assert(false);
1208 }
1209 }
1210 }
1211
1212};
diff --git a/src/notification.h b/src/notification.h deleted file mode 100644 index ad649c3..0000000 --- a/src/notification.h +++ /dev/null
@@ -1,192 +0,0 @@
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 class client;
15
16 enum class disconnect_code
17 {
18 shutdown,
19 duplicate,
20 stall,
21 normal,
22 token_revoked,
23 admin_logout,
24 limit,
25 exception,
26 broker,
27 load,
28 unknown
29 };
30
31 class notification {
32 public:
33 enum class type {
34 // Tweet object
35 tweet,
36
37 // User object
38 update_user,
39 block,
40 unblock,
41 follow,
42 followed,
43 unfollow,
44
45 // User and tweet
46 favorite,
47 favorited,
48 unfavorite,
49 unfavorited,
50 quoted,
51
52 // List
53 list_created,
54 list_destroyed,
55 list_updated,
56
57 // User and list
58 list_add,
59 list_added,
60 list_remove,
61 list_removed,
62 list_subscribe,
63 list_subscribed,
64 list_unsubscribe,
65 list_unsubscribed,
66
67 // Warning
68 stall,
69 follow_limit,
70 unknown_warning,
71
72 // User ID and tweet ID
73 deletion,
74 scrub_location,
75
76 // Special
77 limit,
78 withhold_status,
79 withhold_user,
80 disconnect,
81 friends,
82 direct,
83
84 // Nothing
85 unknown,
86 invalid
87 };
88
89 type getType() const;
90
91 notification() {}
92 notification(const user& currentUser, std::string data);
93
94 notification(const notification& other);
95 notification(notification&& other);
96 notification& operator=(notification other);
97 ~notification();
98
99 friend void swap(notification& first, notification& second);
100
101 const tweet& getTweet() const;
102 tweet& getTweet()
103 {
104 return const_cast<tweet&>(static_cast<const notification&>(*this).getTweet());
105 }
106
107 const user& getUser() const;
108 user& getUser()
109 {
110 return const_cast<user&>(static_cast<const notification&>(*this).getUser());
111 }
112
113 const list& getList() const;
114 list& getList()
115 {
116 return const_cast<list&>(static_cast<const notification&>(*this).getList());
117 }
118
119 tweet_id getTweetID() const;
120 void setTweetID(tweet_id _arg);
121
122 user_id getUserID() const;
123 void setUserID(user_id _arg);
124
125 const std::vector<std::string>& getCountries() const;
126 std::vector<std::string>& getCountries()
127 {
128 return const_cast<std::vector<std::string>&>(static_cast<const notification&>(*this).getCountries());
129 }
130
131 disconnect_code getDisconnectCode() const;
132 void setDisconnectCode(disconnect_code _arg);
133
134 const std::set<user_id>& getFriends() const;
135 std::set<user_id>& getFriends()
136 {
137 return const_cast<std::set<user_id>&>(static_cast<const notification&>(*this).getFriends());
138 }
139
140 const direct_message& getDirectMessage() const;
141 direct_message& getDirectMessage()
142 {
143 return const_cast<direct_message&>(static_cast<const notification&>(*this).getDirectMessage());
144 }
145
146 int getLimit() const;
147 void setLimit(int _arg);
148
149 const std::string& getWarning() const;
150 std::string& getWarning()
151 {
152 return const_cast<std::string&>(static_cast<const notification&>(*this).getWarning());
153 }
154
155 private:
156 union {
157 tweet _tweet;
158 user _user;
159 list _list;
160 struct {
161 user _user;
162 tweet _tweet;
163 } _user_and_tweet;
164 struct {
165 user _user;
166 list _list;
167 } _user_and_list;
168 std::string _warning;
169 struct {
170 user_id _user_id;
171 tweet_id _tweet_id;
172 } _user_id_and_tweet_id;
173 int _limit;
174 struct {
175 user_id _user_id;
176 tweet_id _tweet_id;
177 std::vector<std::string> _countries;
178 } _withhold_status;
179 struct {
180 user_id _user_id;
181 std::vector<std::string> _countries;
182 } _withhold_user;
183 disconnect_code _disconnect;
184 std::set<user_id> _friends;
185 direct_message _direct_message;
186 };
187 type _type = type::invalid;
188 };
189
190};
191
192#endif /* end of include guard: NOTIFICATION_H_69AEF4CC */
diff --git a/src/stream.cpp b/src/stream.cpp deleted file mode 100644 index 86d177c..0000000 --- a/src/stream.cpp +++ /dev/null
@@ -1,295 +0,0 @@
1#include "stream.h"
2#include <liboauthcpp/liboauthcpp.h>
3#include <curl_easy.h>
4#include <curl_header.h>
5#include "util.h"
6#include "notification.h"
7#include "request.h"
8
9namespace twitter {
10
11 stream::stream(
12 const auth& tauth,
13 notify_callback callback,
14 bool with_followings,
15 bool receive_all_replies,
16 std::list<std::string> track,
17 std::list<bounding_box> locations) :
18 _auth(tauth),
19 _notify(callback),
20 _currentUser(get(
21 _auth,
22 "https://api.twitter.com/1.1/account/verify_credentials.json")
23 .perform()),
24 _thread(&stream::run, this, generateUrl(with_followings, receive_all_replies, track, locations))
25 {
26 }
27
28 stream::~stream()
29 {
30 if (_thread.joinable())
31 {
32 _stop = true;
33 _thread.join();
34 }
35 }
36
37 std::string stream::generateUrl(
38 bool with_followings,
39 bool receive_all_replies,
40 std::list<std::string> track,
41 std::list<bounding_box> locations)
42 {
43 std::list<std::string> arguments;
44
45 if (receive_all_replies)
46 {
47 arguments.push_back("replies=all");
48 }
49
50 if (!with_followings)
51 {
52 arguments.push_back("with=user");
53 }
54
55 if (!track.empty())
56 {
57 std::ostringstream trackstr;
58 trackstr << "track=";
59
60 for (auto it = std::begin(track); it != std::end(track); it++)
61 {
62 if (it != std::begin(track))
63 {
64 trackstr << ",";
65 }
66
67 trackstr << OAuth::HttpEncodeQueryValue(*it);
68 }
69
70 arguments.push_back(trackstr.str());
71 }
72
73 if (!locations.empty())
74 {
75 std::ostringstream localstr;
76 localstr << "locations=";
77
78 for (auto it = std::begin(locations); it != std::end(locations); it++)
79 {
80 if (it != std::begin(locations))
81 {
82 localstr << ",";
83 }
84
85 localstr << (double)it->getSouthWestLongitude() << ",";
86 localstr << (double)it->getSouthWestLatitude() << ",";
87 localstr << (double)it->getNorthEastLongitude() << ",";
88 localstr << (double)it->getNorthEastLatitude();
89 }
90
91 arguments.push_back(localstr.str());
92 }
93
94 std::ostringstream urlstr;
95 urlstr << "https://userstream.twitter.com/1.1/user.json";
96
97 if (!arguments.empty())
98 {
99 urlstr << "?";
100 urlstr << implode(std::begin(arguments), std::end(arguments), "&");
101 }
102
103 return urlstr.str();
104 }
105
106 void stream::run(std::string url)
107 {
108 _backoff_type = backoff::none;
109 _backoff_amount = std::chrono::milliseconds(0);
110 for (;;)
111 {
112 curl::curl_ios<stream> ios(this, [] (void* contents, size_t size, size_t nmemb, void* userp) {
113 return static_cast<stream*>(userp)->write(static_cast<char*>(contents), size, nmemb);
114 });
115
116 curl::curl_easy conn(ios);
117 curl::curl_header headers;
118 std::string oauth_header;
119
120 try
121 {
122 oauth_header = _auth.getClient().getFormattedHttpHeader(OAuth::Http::Get, url, "");
123
124 if (!oauth_header.empty())
125 {
126 headers.add(oauth_header);
127 }
128 } catch (const OAuth::ParseError& error)
129 {
130 std::cout << "Error generating OAuth header:" << std::endl;
131 std::cout << error.what() << std::endl;
132 std::cout << "This is likely due to a malformed URL." << std::endl;
133
134 assert(false);
135 }
136
137 try
138 {
139 conn.add<CURLOPT_HEADERFUNCTION>(nullptr);
140 conn.add<CURLOPT_HEADERDATA>(nullptr);
141 conn.add<CURLOPT_XFERINFOFUNCTION>([] (void* cdata, curl_off_t, curl_off_t, curl_off_t, curl_off_t) {
142 return static_cast<stream*>(cdata)->progress();
143 });
144 conn.add<CURLOPT_XFERINFODATA>(this);
145 conn.add<CURLOPT_NOPROGRESS>(0);
146 //conn.add<CURLOPT_VERBOSE>(1);
147 //conn.add<CURLOPT_DEBUGFUNCTION>(my_trace);
148 conn.add<CURLOPT_URL>(url.c_str());
149 conn.add<CURLOPT_HTTPHEADER>(headers.get());
150 } catch (const curl::curl_exception& error)
151 {
152 error.print_traceback();
153
154 assert(false);
155 }
156
157 bool failure = false;
158 try
159 {
160 conn.perform();
161 } catch (const curl::curl_easy_exception& error)
162 {
163 failure = true;
164 if ((error.get_code() == CURLE_ABORTED_BY_CALLBACK) && _stop)
165 {
166 break;
167 } else {
168 if (_backoff_type == backoff::none)
169 {
170 _established = false;
171 _backoff_type = backoff::network;
172 _backoff_amount = std::chrono::milliseconds(0);
173 }
174 }
175 }
176
177 if (!failure)
178 {
179 long response_code = conn.get_info<CURLINFO_RESPONSE_CODE>().get();
180 if (response_code == 420)
181 {
182 if (_backoff_type == backoff::none)
183 {
184 _established = false;
185 _backoff_type = backoff::rate_limit;
186 _backoff_amount = std::chrono::minutes(1);
187 }
188 } else if (response_code != 200)
189 {
190 if (_backoff_type == backoff::none)
191 {
192 _established = false;
193 _backoff_type = backoff::http;
194 _backoff_amount = std::chrono::seconds(5);
195 }
196 } else {
197 if (_backoff_type == backoff::none)
198 {
199 _established = false;
200 _backoff_type = backoff::network;
201 _backoff_amount = std::chrono::milliseconds(0);
202 }
203 }
204 }
205
206 std::this_thread::sleep_for(_backoff_amount);
207
208 switch (_backoff_type)
209 {
210 case backoff::network:
211 {
212 if (_backoff_amount < std::chrono::seconds(16))
213 {
214 _backoff_amount += std::chrono::milliseconds(250);
215 }
216
217 break;
218 }
219
220 case backoff::http:
221 {
222 if (_backoff_amount < std::chrono::seconds(320))
223 {
224 _backoff_amount *= 2;
225 }
226
227 break;
228 }
229
230 case backoff::rate_limit:
231 {
232 _backoff_amount *= 2;
233
234 break;
235 }
236
237 case backoff::none:
238 {
239 break;
240 }
241 }
242 }
243 }
244
245 size_t stream::write(char* ptr, size_t size, size_t nmemb)
246 {
247 for (size_t i = 0; i < size*nmemb; i++)
248 {
249 if (ptr[i] == '\r')
250 {
251 i++; // Skip the \n
252
253 if (!_buffer.empty())
254 {
255 notification n(_currentUser, _buffer);
256 if (n.getType() == notification::type::friends)
257 {
258 _established = true;
259 _backoff_type = backoff::none;
260 _backoff_amount = std::chrono::milliseconds(0);
261 }
262
263 _notify(std::move(n));
264
265 _buffer = "";
266 }
267 } else {
268 _buffer.push_back(ptr[i]);
269 }
270 }
271
272 time(&_last_write);
273
274 return size*nmemb;
275 }
276
277 int stream::progress()
278 {
279 if (_stop)
280 {
281 return 1;
282 }
283
284 if (_established)
285 {
286 if (difftime(time(NULL), _last_write) >= 90)
287 {
288 return 1;
289 }
290 }
291
292 return 0;
293 }
294
295}
diff --git a/src/stream.h b/src/stream.h deleted file mode 100644 index f6ce91e..0000000 --- a/src/stream.h +++ /dev/null
@@ -1,71 +0,0 @@
1#ifndef STREAM_H_E9146952
2#define STREAM_H_E9146952
3
4#include <functional>
5#include <list>
6#include <string>
7#include <chrono>
8#include <thread>
9#include "bounding_box.h"
10#include "auth.h"
11#include "user.h"
12
13namespace twitter {
14
15 class notification;
16
17 class
18 [[deprecated("The Twitter streaming API will sunset on August 16th, 2018")]]
19 stream {
20 public:
21
22 typedef std::function<void(notification _notification)> notify_callback;
23
24 stream(
25 const auth& tauth,
26 notify_callback callback,
27 bool with_followings = true,
28 bool receive_all_replies = false,
29 std::list<std::string> track = {},
30 std::list<bounding_box> locations = {});
31
32 ~stream();
33
34 stream(const stream& other) = delete;
35 stream(stream&& other) = delete;
36 stream& operator=(const stream& other) = delete;
37 stream& operator=(stream&& other) = delete;
38
39 private:
40 enum class backoff {
41 none,
42 network,
43 http,
44 rate_limit
45 };
46
47 static std::string generateUrl(
48 bool with_followings,
49 bool receive_all_replies,
50 std::list<std::string> track,
51 std::list<bounding_box> locations);
52
53 void run(std::string url);
54 int progress();
55 size_t write(char* ptr, size_t size, size_t nmemb);
56
57 const auth& _auth;
58 notify_callback _notify;
59 bool _stop = false;
60 std::string _buffer;
61 time_t _last_write;
62 bool _established = false;
63 backoff _backoff_type = backoff::none;
64 std::chrono::milliseconds _backoff_amount;
65 user _currentUser;
66 std::thread _thread;
67 };
68
69}
70
71#endif /* end of include guard: STREAM_H_E9146952 */
diff --git a/src/twitter.h b/src/twitter.h index a4f336e..382cdce 100644 --- a/src/twitter.h +++ b/src/twitter.h
@@ -12,10 +12,8 @@ namespace twitter {
12#include "auth.h" 12#include "auth.h"
13#include "client.h" 13#include "client.h"
14#include "timeline.h" 14#include "timeline.h"
15#include "stream.h"
16#include "tweet.h" 15#include "tweet.h"
17#include "user.h" 16#include "user.h"
18#include "notification.h"
19#include "list.h" 17#include "list.h"
20#include "direct_message.h" 18#include "direct_message.h"
21#include "configuration.h" 19#include "configuration.h"