Ticket #597: jgeboski_tracking_channels_r1060.patch
File jgeboski_tracking_channels_r1060.patch, 20.5 KB (added by , at 2014-12-24T18:33:00Z) |
---|
-
protocols/bee_chat.c
From e1ac9bef8cbebdf72af03f8ee319b0f3946294b1 Mon Sep 17 00:00:00 2001 From: jgeboski <jgeboski@gmail.com> Date: Tue, 16 Dec 2014 15:20:52 -0500 Subject: [PATCH] bee-chat: create temporary users for unknown chat participants The imcb_chat_msg() function is unable to send messages to a chat with a user who was not previously added. This function should allow for the sending of messages with users who are not added. This is suitable for protocols which are sending messages to a chat from random users or a large amount of users which join and part frequently. --- protocols/bee_chat.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/protocols/bee_chat.c b/protocols/bee_chat.c index 349e054..1db4296 100644
a b void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t fl 84 84 struct im_connection *ic = c->ic; 85 85 bee_t *bee = ic->bee; 86 86 bee_user_t *bu; 87 gboolean temp; 87 88 char *s; 88 89 89 90 /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */ … … void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t fl 91 92 return; 92 93 93 94 bu = bee_user_by_handle( bee, ic, who ); 95 temp = ( bu == NULL ); 96 97 if( temp ) 98 bu = bee_user_new( bee, ic, who, BEE_USER_ONLINE ); 94 99 95 100 s = set_getstr( &ic->bee->set, "strip_html" ); 96 101 if( ( g_strcasecmp( s, "always" ) == 0 ) || 97 102 ( ( ic->flags & OPT_DOES_HTML ) && s ) ) 98 103 strip_html( msg ); 99 104 100 if( b u && bee->ui->chat_msg )105 if( bee->ui->chat_msg ) 101 106 bee->ui->chat_msg( bee, c, bu, msg, sent_at ); 102 else 103 imcb_chat_log( c, "Message from unknown participant %s: %s", who, msg ); 107 108 if( temp ) 109 bee_user_free( bee, bu ); 104 110 } 105 111 106 112 void imcb_chat_log( struct groupchat *c, char *format, ... ) -
protocols/twitter/twitter.c
-- 2.0.0+fc2 From 724fdfb57980c3e45e88e8071a2fcd51f813f426 Mon Sep 17 00:00:00 2001 From: jgeboski <jgeboski@gmail.com> Date: Mon, 15 Dec 2014 07:17:12 -0500 Subject: [PATCH] twitter: implemented filter based group chats Filter group chats allow for the ability to read the tweets of select users without actually following the users, and/or track keywords or hashtags. A filter group chat can have multiple users, keywords, or hashtags. These users, keywords, or hashtags can span multiple group chats. This allows for rather robust filter organization. The underlying structure for the filters is based on linked list, as using the glib hash tables requires >= glib-2.16 for sanity. Since the glib requirement of bitlbee is only 2.14, linked list are used in order to prevent an overly complex implementation. The idea for this patch was inspired by Artem Savkov's "Twitter search channels" patch. In order to use the filter group chats, a group chat must be added to the twitter account. The channel room name is either follow:username, track:keyword, and/or track:#hashtag. Multiple elements can be used by separating each element by a semicolon. --- protocols/twitter/twitter.c | 263 +++++++++++++++++++++++++++++++++++++++- protocols/twitter/twitter.h | 18 +++ protocols/twitter/twitter_lib.c | 226 +++++++++++++++++++++++++++++++++- protocols/twitter/twitter_lib.h | 3 + 4 files changed, 503 insertions(+), 7 deletions(-) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 2a6ae88..c4eb024 100644
a b 30 30 #include "url.h" 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 35 244 */ … … static void twitter_logout(struct im_connection *ic) 428 637 imcb_chat_free(td->timeline_gc); 429 638 430 639 if (td) { 640 if (td->filter_update_id > 0) 641 b_event_remove(td->filter_update_id); 642 431 643 http_close(td->stream); 644 twitter_filter_remove_all(ic); 432 645 oauth_info_free(td->oauth_info); 433 646 g_free(td->user); 434 647 g_free(td->prefix); … … static void twitter_chat_invite(struct groupchat *c, char *who, char *message) 501 714 { 502 715 } 503 716 717 static struct groupchat *twitter_chat_join(struct im_connection *ic, 718 const char *room, const char *nick, 719 const char *password, set_t **sets) 720 { 721 struct groupchat *c = imcb_chat_new(ic, room); 722 GSList *fs = twitter_filter_parse(c, room); 723 GString *topic = g_string_new(""); 724 struct twitter_filter *tf; 725 GSList *l; 726 727 fs = g_slist_sort(fs, (GCompareFunc) twitter_filter_cmp); 728 729 for (l = fs; l; l = g_slist_next(l)) { 730 tf = l->data; 731 732 if (topic->len > 0) 733 g_string_append(topic, ", "); 734 735 if (tf->type == TWITTER_FILTER_TYPE_FOLLOW) 736 g_string_append_c(topic, '@'); 737 738 g_string_append(topic, tf->text); 739 } 740 741 if (topic->len > 0) 742 g_string_prepend(topic, "Twitter Filter: "); 743 744 imcb_chat_topic(c, NULL, topic->str, 0); 745 imcb_chat_add_buddy(c, ic->acc->user); 746 747 if (topic->len == 0) { 748 imcb_error(ic, "Failed to handle any filters"); 749 imcb_chat_free(c); 750 c = NULL; 751 } 752 753 g_string_free(topic, TRUE); 754 g_slist_free(fs); 755 756 return c; 757 } 758 504 759 static void twitter_chat_leave(struct groupchat *c) 505 760 { 506 761 struct twitter_data *td = c->ic->proto_data; 507 762 508 if (c != td->timeline_gc) 509 return; /* WTF? */ 763 if (c != td->timeline_gc) { 764 twitter_filter_remove(c); 765 imcb_chat_free(c); 766 return; 767 } 510 768 511 769 /* If the user leaves the channel: Fine. Rejoin him/her once new 512 770 tweets come in. */ … … void twitter_initmodule() 740 998 ret->remove_buddy = twitter_remove_buddy; 741 999 ret->chat_msg = twitter_chat_msg; 742 1000 ret->chat_invite = twitter_chat_invite; 1001 ret->chat_join = twitter_chat_join; 743 1002 ret->chat_leave = twitter_chat_leave; 744 1003 ret->keepalive = twitter_keepalive; 745 1004 ret->add_permit = twitter_add_permit; -
protocols/twitter/twitter.h
diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 8792b7c..00230cc 100644
a b typedef enum 44 44 TWITTER_GOT_MENTIONS = 0x40000, 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 49 55 struct twitter_data … … struct twitter_data 57 63 guint64 timeline_id; 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; 66 75 twitter_flags_t flags; … … struct twitter_data 78 87 int log_id; 79 88 }; 80 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; 97 }; 98 81 99 struct twitter_user_data 82 100 { 83 101 guint64 last_id; -
protocols/twitter/twitter_lib.c
diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index b1995e7..6c32232 100644
a b struct twitter_xml_list { 50 50 }; 51 51 52 52 struct twitter_xml_user { 53 guint64 uid; 53 54 char *name; 54 55 char *screen_name; 55 56 }; … … struct twitter_xml_status { 60 61 struct twitter_xml_user *user; 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 65 67 /** … … static void twitter_http_get_users_lookup(struct http_request *req) 391 393 struct twitter_xml_user *twitter_xt_get_user(const json_value *node) 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")); 398 401 402 jv = json_o_get(node, "id"); 403 txu->uid = jv->u.integer; 404 399 405 return txu; 400 406 } 401 407 … … static char *twitter_msg_add_id(struct im_connection *ic, 656 662 } 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 */ 661 705 static void twitter_status_show_chat(struct im_connection *ic, struct twitter_xml_status *status) … … static void twitter_status_show(struct im_connection *ic, struct twitter_xml_sta 729 773 if (set_getbool(&ic->acc->set, "strip_newlines")) 730 774 strip_newlines(status->text); 731 775 732 if (td->flags & TWITTER_MODE_CHAT) 776 if (status->from_filter) 777 twitter_status_show_filter(ic, status); 778 else if (td->flags & TWITTER_MODE_CHAT) 733 779 twitter_status_show_chat(ic, status); 734 780 else 735 781 twitter_status_show_msg(ic, status); … … static void twitter_status_show(struct im_connection *ic, struct twitter_xml_sta 739 785 td->timeline_id = MAX(td->timeline_id, status->rt_id); 740 786 } 741 787 742 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o );788 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter); 743 789 744 790 static void twitter_http_stream(struct http_request *req) 745 791 { … … static void twitter_http_stream(struct http_request *req) 748 794 json_value *parsed; 749 795 int len = 0; 750 796 char c, *nl; 797 gboolean from_filter; 751 798 752 799 if (!g_slist_find(twitter_connections, ic)) 753 800 return; … … static void twitter_http_stream(struct http_request *req) 756 803 td = ic->proto_data; 757 804 758 805 if ((req->flags & HTTPC_EOF) || !req->reply_body) { 759 td->stream = NULL; 806 if (req == td->stream) 807 td->stream = NULL; 808 else if (req == td->filter_stream) 809 td->filter_stream = NULL; 810 760 811 imcb_error(ic, "Stream closed (%s)", req->status_string); 761 812 imc_logout(ic, TRUE); 762 813 return; … … static void twitter_http_stream(struct http_request *req) 773 824 req->reply_body[len] = '\0'; 774 825 775 826 if ((parsed = json_parse(req->reply_body, req->body_size))) { 776 twitter_stream_handle_object(ic, parsed); 827 from_filter = (req == td->filter_stream); 828 twitter_stream_handle_object(ic, parsed, from_filter); 777 829 } 778 830 json_value_free(parsed); 779 831 req->reply_body[len] = c; … … static void twitter_http_stream(struct http_request *req) 789 841 static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value *o); 790 842 static gboolean twitter_stream_handle_status(struct im_connection *ic, struct twitter_xml_status *txs); 791 843 792 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o )844 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter) 793 845 { 794 846 struct twitter_data *td = ic->proto_data; 795 847 struct twitter_xml_status *txs; 796 848 json_value *c; 797 849 798 850 if ((txs = twitter_xt_get_status(o))) { 851 txs->from_filter = from_filter; 799 852 gboolean ret = twitter_stream_handle_status(ic, txs); 800 853 txs_free(txs); 801 854 return ret; … … gboolean twitter_open_stream(struct im_connection *ic) 893 946 return FALSE; 894 947 } 895 948 949 static gboolean twitter_filter_stream(struct im_connection *ic) 950 { 951 struct twitter_data *td = ic->proto_data; 952 char *args[4] = {"follow", NULL, "track", NULL}; 953 GString *followstr = g_string_new(""); 954 GString *trackstr = g_string_new(""); 955 gboolean ret = FALSE; 956 struct twitter_filter *tf; 957 GSList *l; 958 959 for (l = td->filters; l; l = g_slist_next(l)) { 960 tf = l->data; 961 962 switch (tf->type) { 963 case TWITTER_FILTER_TYPE_FOLLOW: 964 if (followstr->len > 0) 965 g_string_append_c(followstr, ','); 966 967 g_string_append_printf(followstr, "%" G_GUINT64_FORMAT, 968 tf->uid); 969 break; 970 971 case TWITTER_FILTER_TYPE_TRACK: 972 if (trackstr->len > 0) 973 g_string_append_c(trackstr, ','); 974 975 g_string_append(trackstr, tf->text); 976 break; 977 978 default: 979 continue; 980 } 981 } 982 983 args[1] = followstr->str; 984 args[3] = trackstr->str; 985 986 if (td->filter_stream) 987 http_close(td->filter_stream); 988 989 if ((td->filter_stream = twitter_http(ic, TWITTER_FILTER_STREAM_URL, 990 twitter_http_stream, ic, 0, 991 args, 4))) { 992 /* This flag must be enabled or we'll get no data until EOF 993 (which err, kind of, defeats the purpose of a streaming API). */ 994 td->filter_stream->flags |= HTTPC_STREAMING; 995 ret = TRUE; 996 } 997 998 g_string_free(followstr, TRUE); 999 g_string_free(trackstr, TRUE); 1000 1001 return ret; 1002 } 1003 1004 static void twitter_filter_users_post(struct http_request *req) 1005 { 1006 struct im_connection *ic = req->data; 1007 struct twitter_data *td; 1008 struct twitter_filter *tf; 1009 GList *users = NULL; 1010 json_value *parsed; 1011 json_value *id; 1012 const char *name; 1013 GString *fstr; 1014 GSList *l; 1015 GList *u; 1016 int i; 1017 1018 // Check if the connection is still active. 1019 if (!g_slist_find(twitter_connections, ic)) 1020 return; 1021 1022 td = ic->proto_data; 1023 1024 if (!(parsed = twitter_parse_response(ic, req))) 1025 return; 1026 1027 for (l = td->filters; l; l = g_slist_next(l)) { 1028 tf = l->data; 1029 1030 if (tf->type == TWITTER_FILTER_TYPE_FOLLOW) 1031 users = g_list_prepend(users, tf); 1032 } 1033 1034 if (parsed->type != json_array) 1035 goto finish; 1036 1037 for (i = 0; i < parsed->u.array.length; i++) { 1038 id = json_o_get(parsed->u.array.values[i], "id"); 1039 name = json_o_str(parsed->u.array.values[i], "screen_name"); 1040 1041 if (!name || !id || id->type != json_integer) 1042 continue; 1043 1044 for (u = users; u; u = g_list_next(u)) { 1045 tf = u->data; 1046 1047 if (g_strcasecmp(tf->text, name) == 0) { 1048 tf->uid = id->u.integer; 1049 users = g_list_delete_link(users, u); 1050 break; 1051 } 1052 } 1053 } 1054 1055 finish: 1056 json_value_free(parsed); 1057 twitter_filter_stream(ic); 1058 1059 if (!users) 1060 return; 1061 1062 fstr = g_string_new(""); 1063 1064 for (u = users; u; u = g_list_next(u)) { 1065 if (fstr->len > 0) 1066 g_string_append(fstr, ", "); 1067 1068 g_string_append(fstr, tf->text); 1069 } 1070 1071 imcb_error(ic, "Failed UID acquisitions: %s", fstr->str); 1072 1073 g_string_free(fstr, TRUE); 1074 g_list_free(users); 1075 } 1076 1077 gboolean twitter_open_filter_stream(struct im_connection *ic) 1078 { 1079 struct twitter_data *td = ic->proto_data; 1080 char *args[2] = {"screen_name", NULL}; 1081 GString *ustr = g_string_new(""); 1082 struct twitter_filter *tf; 1083 struct http_request *req; 1084 GSList *l; 1085 1086 for (l = td->filters; l; l = g_slist_next(l)) { 1087 tf = l->data; 1088 1089 if (tf->type != TWITTER_FILTER_TYPE_FOLLOW || tf->uid != 0) 1090 continue; 1091 1092 if (ustr->len > 0) 1093 g_string_append_c(ustr, ','); 1094 1095 g_string_append(ustr, tf->text); 1096 } 1097 1098 if (ustr->len == 0) { 1099 g_string_free(ustr, TRUE); 1100 return twitter_filter_stream(ic); 1101 } 1102 1103 args[1] = ustr->str; 1104 req = twitter_http(ic, TWITTER_USERS_LOOKUP_URL, 1105 twitter_filter_users_post, 1106 ic, 0, args, 2); 1107 1108 g_string_free(ustr, TRUE); 1109 return req != NULL; 1110 } 1111 896 1112 static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor); 897 1113 static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor); 898 1114 -
protocols/twitter/twitter_lib.h
diff --git a/protocols/twitter/twitter_lib.h b/protocols/twitter/twitter_lib.h index 7fd3b80..ee10310 100644
a b 78 78 /* Report spam */ 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); 86 89 void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor);