about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2018-08-30 19:22:20 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2018-08-30 19:22:20 -0400
commitcc57105f29b4095daad03273bc1a882368e75664 (patch)
tree2800240352fdcff0b009e6ced97aab4cb726dcb7 /src
parent4963c3dd55b765a33a16a77af432f2bfa12b8359 (diff)
downloadlibtwittercpp-cc57105f29b4095daad03273bc1a882368e75664.tar.gz
libtwittercpp-cc57105f29b4095daad03273bc1a882368e75664.tar.bz2
libtwittercpp-cc57105f29b4095daad03273bc1a882368e75664.zip
Removed stream functionality
The Twitter streaming API has officially sunset, so the stream functionality can no longer be used. Because of this, there is no need to keep it in the codebase.

refs #3
Diffstat (limited to 'src')
-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
5 files changed, 0 insertions, 1772 deletions
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"