- Timestamp:
- 2015-01-26T03:46:03Z (10 years ago)
- Branches:
- master
- Children:
- dfaa46d
- Parents:
- 5eab298f
- git-author:
- jgeboski <jgeboski@…> (15-12-14 12:17:12)
- git-committer:
- jgeboski <jgeboski@…> (26-01-15 03:46:03)
- Location:
- protocols/twitter
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
protocols/twitter/twitter.c
r5eab298f r73ee390 31 31 32 32 GSList *twitter_connections = NULL; 33 34 static int twitter_filter_cmp(struct twitter_filter *tf1, 35 struct twitter_filter *tf2) 36 { 37 int i1 = 0; 38 int i2 = 0; 39 int i; 40 41 static const twitter_filter_type_t types[] = { 42 /* Order of the types */ 43 TWITTER_FILTER_TYPE_FOLLOW, 44 TWITTER_FILTER_TYPE_TRACK 45 }; 46 47 for (i = 0; i < G_N_ELEMENTS(types); i++) { 48 if (types[i] == tf1->type) { 49 i1 = i + 1; 50 break; 51 } 52 } 53 54 for (i = 0; i < G_N_ELEMENTS(types); i++) { 55 if (types[i] == tf2->type) { 56 i2 = i + 1; 57 break; 58 } 59 } 60 61 if (i1 != i2) { 62 /* With different types, return their difference */ 63 return i1 - i2; 64 } 65 66 /* With the same type, return the text comparison */ 67 return g_strcasecmp(tf1->text, tf2->text); 68 } 69 70 static gboolean twitter_filter_update(gpointer data, gint fd, 71 b_input_condition cond) 72 { 73 struct im_connection *ic = data; 74 struct twitter_data *td = ic->proto_data; 75 76 if (td->filters) { 77 twitter_open_filter_stream(ic); 78 } else if (td->filter_stream) { 79 http_close(td->filter_stream); 80 td->filter_stream = NULL; 81 } 82 83 td->filter_update_id = 0; 84 return FALSE; 85 } 86 87 static struct twitter_filter *twitter_filter_get(struct groupchat *c, 88 twitter_filter_type_t type, 89 const char *text) 90 { 91 struct twitter_data *td = c->ic->proto_data; 92 struct twitter_filter *tf = NULL; 93 struct twitter_filter tfc = {type, (char*) text}; 94 GSList *l; 95 96 for (l = td->filters; l; l = g_slist_next(l)) { 97 tf = l->data; 98 99 if (twitter_filter_cmp(tf, &tfc) == 0) 100 break; 101 102 tf = NULL; 103 } 104 105 if (!tf) { 106 tf = g_new0(struct twitter_filter, 1); 107 tf->type = type; 108 tf->text = g_strdup(text); 109 td->filters = g_slist_prepend(td->filters, tf); 110 } 111 112 if (!g_slist_find(tf->groupchats, c)) 113 tf->groupchats = g_slist_prepend(tf->groupchats, c); 114 115 if (td->filter_update_id > 0) 116 b_event_remove(td->filter_update_id); 117 118 /* Wait for other possible filter changes to avoid request spam */ 119 td->filter_update_id = b_timeout_add(TWITTER_FILTER_UPDATE_WAIT, 120 twitter_filter_update, c->ic); 121 return tf; 122 } 123 124 static void twitter_filter_free(struct twitter_filter *tf) 125 { 126 g_slist_free(tf->groupchats); 127 g_free(tf->text); 128 g_free(tf); 129 } 130 131 static void twitter_filter_remove(struct groupchat *c) 132 { 133 struct twitter_data *td = c->ic->proto_data; 134 struct twitter_filter *tf; 135 GSList *l = td->filters; 136 GSList *p; 137 138 while (l != NULL) { 139 tf = l->data; 140 tf->groupchats = g_slist_remove(tf->groupchats, c); 141 142 p = l; 143 l = g_slist_next(l); 144 145 if (!tf->groupchats) { 146 twitter_filter_free(tf); 147 td->filters = g_slist_delete_link(td->filters, p); 148 } 149 } 150 151 if (td->filter_update_id > 0) 152 b_event_remove(td->filter_update_id); 153 154 /* Wait for other possible filter changes to avoid request spam */ 155 td->filter_update_id = b_timeout_add(TWITTER_FILTER_UPDATE_WAIT, 156 twitter_filter_update, c->ic);} 157 158 static void twitter_filter_remove_all(struct im_connection *ic) 159 { 160 struct twitter_data *td = ic->proto_data; 161 GSList *chats = NULL; 162 struct twitter_filter *tf; 163 GSList *l = td->filters; 164 GSList *p; 165 166 while (l != NULL) { 167 tf = l->data; 168 169 /* Build up a list of groupchats to be freed */ 170 for (p = tf->groupchats; p; p = g_slist_next(p)) { 171 if (!g_slist_find(chats, p->data)) 172 chats = g_slist_prepend(chats, p->data); 173 } 174 175 p = l; 176 l = g_slist_next(l); 177 twitter_filter_free(p->data); 178 td->filters = g_slist_delete_link(td->filters, p); 179 } 180 181 l = chats; 182 183 while (l != NULL) { 184 p = l; 185 l = g_slist_next(l); 186 187 /* Freed each remaining groupchat */ 188 imcb_chat_free(p->data); 189 chats = g_slist_delete_link(chats, p); 190 } 191 192 if (td->filter_stream) { 193 http_close(td->filter_stream); 194 td->filter_stream = NULL; 195 } 196 } 197 198 static GSList *twitter_filter_parse(struct groupchat *c, const char *text) 199 { 200 char **fs = g_strsplit(text, ";", 0); 201 GSList *ret = NULL; 202 struct twitter_filter *tf; 203 char **f; 204 char *v; 205 int i; 206 int t; 207 208 static const twitter_filter_type_t types[] = { 209 TWITTER_FILTER_TYPE_FOLLOW, 210 TWITTER_FILTER_TYPE_TRACK 211 }; 212 213 static const char *typestrs[] = { 214 "follow", 215 "track" 216 }; 217 218 for (f = fs; *f; f++) { 219 if ((v = strchr(*f, ':')) == NULL) 220 continue; 221 222 *(v++) = 0; 223 224 for (t = -1, i = 0; i < G_N_ELEMENTS(types); i++) { 225 if (g_strcasecmp(typestrs[i], *f) == 0) { 226 t = i; 227 break; 228 } 229 } 230 231 if (t < 0 || strlen(v) == 0) 232 continue; 233 234 tf = twitter_filter_get(c, types[t], v); 235 ret = g_slist_prepend(ret, tf); 236 } 237 238 g_strfreev(fs); 239 return ret; 240 } 241 33 242 /** 34 243 * Main loop function … … 436 645 437 646 if (td) { 647 if (td->filter_update_id > 0) 648 b_event_remove(td->filter_update_id); 649 438 650 http_close(td->stream); 651 twitter_filter_remove_all(ic); 439 652 oauth_info_free(td->oauth_info); 440 653 g_free(td->user); … … 509 722 } 510 723 724 static struct groupchat *twitter_chat_join(struct im_connection *ic, 725 const char *room, const char *nick, 726 const char *password, set_t **sets) 727 { 728 struct groupchat *c = imcb_chat_new(ic, room); 729 GSList *fs = twitter_filter_parse(c, room); 730 GString *topic = g_string_new(""); 731 struct twitter_filter *tf; 732 GSList *l; 733 734 fs = g_slist_sort(fs, (GCompareFunc) twitter_filter_cmp); 735 736 for (l = fs; l; l = g_slist_next(l)) { 737 tf = l->data; 738 739 if (topic->len > 0) 740 g_string_append(topic, ", "); 741 742 if (tf->type == TWITTER_FILTER_TYPE_FOLLOW) 743 g_string_append_c(topic, '@'); 744 745 g_string_append(topic, tf->text); 746 } 747 748 if (topic->len > 0) 749 g_string_prepend(topic, "Twitter Filter: "); 750 751 imcb_chat_topic(c, NULL, topic->str, 0); 752 imcb_chat_add_buddy(c, ic->acc->user); 753 754 if (topic->len == 0) { 755 imcb_error(ic, "Failed to handle any filters"); 756 imcb_chat_free(c); 757 c = NULL; 758 } 759 760 g_string_free(topic, TRUE); 761 g_slist_free(fs); 762 763 return c; 764 } 765 511 766 static void twitter_chat_leave(struct groupchat *c) 512 767 { 513 768 struct twitter_data *td = c->ic->proto_data; 514 769 515 if (c != td->timeline_gc) 516 return; /* WTF? */ 770 if (c != td->timeline_gc) { 771 twitter_filter_remove(c); 772 imcb_chat_free(c); 773 return; 774 } 517 775 518 776 /* If the user leaves the channel: Fine. Rejoin him/her once new … … 748 1006 ret->chat_msg = twitter_chat_msg; 749 1007 ret->chat_invite = twitter_chat_invite; 1008 ret->chat_join = twitter_chat_join; 750 1009 ret->chat_leave = twitter_chat_leave; 751 1010 ret->keepalive = twitter_keepalive; -
protocols/twitter/twitter.h
r5eab298f r73ee390 45 45 } twitter_flags_t; 46 46 47 typedef enum 48 { 49 TWITTER_FILTER_TYPE_FOLLOW = 0, 50 TWITTER_FILTER_TYPE_TRACK 51 } twitter_filter_type_t; 52 47 53 struct twitter_log_data; 48 54 … … 58 64 59 65 GSList *follow_ids; 66 GSList *filters; 60 67 61 68 guint64 last_status_id; /* For undo */ 62 69 gint main_loop_id; 70 gint filter_update_id; 63 71 struct http_request *stream; 72 struct http_request *filter_stream; 64 73 struct groupchat *timeline_gc; 65 74 gint http_fails; … … 77 86 struct twitter_log_data *log; 78 87 int log_id; 88 }; 89 90 #define TWITTER_FILTER_UPDATE_WAIT 3000 91 struct twitter_filter 92 { 93 twitter_filter_type_t type; 94 char *text; 95 guint64 uid; 96 GSList *groupchats; 79 97 }; 80 98 -
protocols/twitter/twitter_lib.c
r5eab298f r73ee390 51 51 52 52 struct twitter_xml_user { 53 guint64 uid; 53 54 char *name; 54 55 char *screen_name; … … 61 62 guint64 id, rt_id; /* Usually equal, with RTs id == *original* id */ 62 63 guint64 reply_to; 64 gboolean from_filter; 63 65 }; 64 66 … … 392 394 { 393 395 struct twitter_xml_user *txu; 396 json_value *jv; 394 397 395 398 txu = g_new0(struct twitter_xml_user, 1); 396 399 txu->name = g_strdup(json_o_str(node, "name")); 397 400 txu->screen_name = g_strdup(json_o_str(node, "screen_name")); 401 402 jv = json_o_get(node, "id"); 403 txu->uid = jv->u.integer; 398 404 399 405 return txu; … … 657 663 658 664 /** 665 * Function that is called to see the filter statuses in groupchat windows. 666 */ 667 static void twitter_status_show_filter(struct im_connection *ic, struct twitter_xml_status *status) 668 { 669 struct twitter_data *td = ic->proto_data; 670 char *msg = twitter_msg_add_id(ic, status, ""); 671 struct twitter_filter *tf; 672 GSList *f; 673 GSList *l; 674 675 for (f = td->filters; f; f = g_slist_next(f)) { 676 tf = f->data; 677 678 switch (tf->type) { 679 case TWITTER_FILTER_TYPE_FOLLOW: 680 if (status->user->uid != tf->uid) 681 continue; 682 break; 683 684 case TWITTER_FILTER_TYPE_TRACK: 685 if (strcasestr(status->text, tf->text) == NULL) 686 continue; 687 break; 688 689 default: 690 continue; 691 } 692 693 for (l = tf->groupchats; l; l = g_slist_next(l)) { 694 imcb_chat_msg(l->data, status->user->screen_name, 695 msg ? msg : status->text, 0, 0); 696 } 697 } 698 699 g_free(msg); 700 } 701 702 /** 659 703 * Function that is called to see the statuses in a groupchat window. 660 704 */ … … 731 775 strip_newlines(status->text); 732 776 733 if (td->flags & TWITTER_MODE_CHAT) 777 if (status->from_filter) 778 twitter_status_show_filter(ic, status); 779 else if (td->flags & TWITTER_MODE_CHAT) 734 780 twitter_status_show_chat(ic, status); 735 781 else … … 745 791 } 746 792 747 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o );793 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter); 748 794 749 795 static void twitter_http_stream(struct http_request *req) … … 754 800 int len = 0; 755 801 char c, *nl; 802 gboolean from_filter; 756 803 757 804 if (!g_slist_find(twitter_connections, ic)) … … 762 809 763 810 if ((req->flags & HTTPC_EOF) || !req->reply_body) { 764 td->stream = NULL; 811 if (req == td->stream) 812 td->stream = NULL; 813 else if (req == td->filter_stream) 814 td->filter_stream = NULL; 815 765 816 imcb_error(ic, "Stream closed (%s)", req->status_string); 766 817 imc_logout(ic, TRUE); … … 779 830 780 831 if ((parsed = json_parse(req->reply_body, req->body_size))) { 781 twitter_stream_handle_object(ic, parsed); 832 from_filter = (req == td->filter_stream); 833 twitter_stream_handle_object(ic, parsed, from_filter); 782 834 } 783 835 json_value_free(parsed); … … 795 847 static gboolean twitter_stream_handle_status(struct im_connection *ic, struct twitter_xml_status *txs); 796 848 797 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o )849 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter) 798 850 { 799 851 struct twitter_data *td = ic->proto_data; … … 802 854 803 855 if ((txs = twitter_xt_get_status(o))) { 856 txs->from_filter = from_filter; 804 857 gboolean ret = twitter_stream_handle_status(ic, txs); 805 858 txs_free(txs); … … 899 952 } 900 953 954 static gboolean twitter_filter_stream(struct im_connection *ic) 955 { 956 struct twitter_data *td = ic->proto_data; 957 char *args[4] = {"follow", NULL, "track", NULL}; 958 GString *followstr = g_string_new(""); 959 GString *trackstr = g_string_new(""); 960 gboolean ret = FALSE; 961 struct twitter_filter *tf; 962 GSList *l; 963 964 for (l = td->filters; l; l = g_slist_next(l)) { 965 tf = l->data; 966 967 switch (tf->type) { 968 case TWITTER_FILTER_TYPE_FOLLOW: 969 if (followstr->len > 0) 970 g_string_append_c(followstr, ','); 971 972 g_string_append_printf(followstr, "%" G_GUINT64_FORMAT, 973 tf->uid); 974 break; 975 976 case TWITTER_FILTER_TYPE_TRACK: 977 if (trackstr->len > 0) 978 g_string_append_c(trackstr, ','); 979 980 g_string_append(trackstr, tf->text); 981 break; 982 983 default: 984 continue; 985 } 986 } 987 988 args[1] = followstr->str; 989 args[3] = trackstr->str; 990 991 if (td->filter_stream) 992 http_close(td->filter_stream); 993 994 if ((td->filter_stream = twitter_http(ic, TWITTER_FILTER_STREAM_URL, 995 twitter_http_stream, ic, 0, 996 args, 4))) { 997 /* This flag must be enabled or we'll get no data until EOF 998 (which err, kind of, defeats the purpose of a streaming API). */ 999 td->filter_stream->flags |= HTTPC_STREAMING; 1000 ret = TRUE; 1001 } 1002 1003 g_string_free(followstr, TRUE); 1004 g_string_free(trackstr, TRUE); 1005 1006 return ret; 1007 } 1008 1009 static void twitter_filter_users_post(struct http_request *req) 1010 { 1011 struct im_connection *ic = req->data; 1012 struct twitter_data *td; 1013 struct twitter_filter *tf; 1014 GList *users = NULL; 1015 json_value *parsed; 1016 json_value *id; 1017 const char *name; 1018 GString *fstr; 1019 GSList *l; 1020 GList *u; 1021 int i; 1022 1023 // Check if the connection is still active. 1024 if (!g_slist_find(twitter_connections, ic)) 1025 return; 1026 1027 td = ic->proto_data; 1028 1029 if (!(parsed = twitter_parse_response(ic, req))) 1030 return; 1031 1032 for (l = td->filters; l; l = g_slist_next(l)) { 1033 tf = l->data; 1034 1035 if (tf->type == TWITTER_FILTER_TYPE_FOLLOW) 1036 users = g_list_prepend(users, tf); 1037 } 1038 1039 if (parsed->type != json_array) 1040 goto finish; 1041 1042 for (i = 0; i < parsed->u.array.length; i++) { 1043 id = json_o_get(parsed->u.array.values[i], "id"); 1044 name = json_o_str(parsed->u.array.values[i], "screen_name"); 1045 1046 if (!name || !id || id->type != json_integer) 1047 continue; 1048 1049 for (u = users; u; u = g_list_next(u)) { 1050 tf = u->data; 1051 1052 if (g_strcasecmp(tf->text, name) == 0) { 1053 tf->uid = id->u.integer; 1054 users = g_list_delete_link(users, u); 1055 break; 1056 } 1057 } 1058 } 1059 1060 finish: 1061 json_value_free(parsed); 1062 twitter_filter_stream(ic); 1063 1064 if (!users) 1065 return; 1066 1067 fstr = g_string_new(""); 1068 1069 for (u = users; u; u = g_list_next(u)) { 1070 if (fstr->len > 0) 1071 g_string_append(fstr, ", "); 1072 1073 g_string_append(fstr, tf->text); 1074 } 1075 1076 imcb_error(ic, "Failed UID acquisitions: %s", fstr->str); 1077 1078 g_string_free(fstr, TRUE); 1079 g_list_free(users); 1080 } 1081 1082 gboolean twitter_open_filter_stream(struct im_connection *ic) 1083 { 1084 struct twitter_data *td = ic->proto_data; 1085 char *args[2] = {"screen_name", NULL}; 1086 GString *ustr = g_string_new(""); 1087 struct twitter_filter *tf; 1088 struct http_request *req; 1089 GSList *l; 1090 1091 for (l = td->filters; l; l = g_slist_next(l)) { 1092 tf = l->data; 1093 1094 if (tf->type != TWITTER_FILTER_TYPE_FOLLOW || tf->uid != 0) 1095 continue; 1096 1097 if (ustr->len > 0) 1098 g_string_append_c(ustr, ','); 1099 1100 g_string_append(ustr, tf->text); 1101 } 1102 1103 if (ustr->len == 0) { 1104 g_string_free(ustr, TRUE); 1105 return twitter_filter_stream(ic); 1106 } 1107 1108 args[1] = ustr->str; 1109 req = twitter_http(ic, TWITTER_USERS_LOOKUP_URL, 1110 twitter_filter_users_post, 1111 ic, 0, args, 2); 1112 1113 g_string_free(ustr, TRUE); 1114 return req != NULL; 1115 } 1116 901 1117 static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor); 902 1118 static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor); -
protocols/twitter/twitter_lib.h
r5eab298f r73ee390 79 79 #define TWITTER_REPORT_SPAM_URL "/users/report_spam.json" 80 80 81 /* Stream URLs */ 81 82 #define TWITTER_USER_STREAM_URL "https://userstream.twitter.com/1.1/user.json" 83 #define TWITTER_FILTER_STREAM_URL "https://stream.twitter.com/1.1/statuses/filter.json" 82 84 83 85 gboolean twitter_open_stream(struct im_connection *ic); 86 gboolean twitter_open_filter_stream(struct im_connection *ic); 84 87 gboolean twitter_get_timeline(struct im_connection *ic, gint64 next_cursor); 85 88 void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor);
Note: See TracChangeset
for help on using the changeset viewer.