Ticket #597: jgeboski_tracking_channels_r1060.patch

File jgeboski_tracking_channels_r1060.patch, 20.5 KB (added by dx, at 2014-12-24T18:33:00Z)

Whole new patch to do tracking channels by jgeboski, fixing many of the issues of the old one and adding more features

  • 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 
    8484        struct im_connection *ic = c->ic;
    8585        bee_t *bee = ic->bee;
    8686        bee_user_t *bu;
     87        gboolean temp;
    8788        char *s;
    8889       
    8990        /* 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 
    9192                return;
    9293       
    9394        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 );
    9499       
    95100        s = set_getstr( &ic->bee->set, "strip_html" );
    96101        if( ( g_strcasecmp( s, "always" ) == 0 ) ||
    97102            ( ( ic->flags & OPT_DOES_HTML ) && s ) )
    98103                strip_html( msg );
    99104       
    100         if( bu && bee->ui->chat_msg )
     105        if( bee->ui->chat_msg )
    101106                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 );
    104110}
    105111
    106112void 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  
    3030#include "url.h"
    3131
    3232GSList *twitter_connections = NULL;
     33
     34static 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
     70static 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
     87static 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
     124static 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
     131static 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
     158static 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
     198static 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
    33242/**
    34243 * Main loop function
    35244 */
    static void twitter_logout(struct im_connection *ic) 
    428637                imcb_chat_free(td->timeline_gc);
    429638
    430639        if (td) {
     640                if (td->filter_update_id > 0)
     641                        b_event_remove(td->filter_update_id);
     642
    431643                http_close(td->stream);
     644                twitter_filter_remove_all(ic);
    432645                oauth_info_free(td->oauth_info);
    433646                g_free(td->user);
    434647                g_free(td->prefix);
    static void twitter_chat_invite(struct groupchat *c, char *who, char *message) 
    501714{
    502715}
    503716
     717static 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
    504759static void twitter_chat_leave(struct groupchat *c)
    505760{
    506761        struct twitter_data *td = c->ic->proto_data;
    507762
    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        }
    510768
    511769        /* If the user leaves the channel: Fine. Rejoin him/her once new
    512770           tweets come in. */
    void twitter_initmodule() 
    740998        ret->remove_buddy = twitter_remove_buddy;
    741999        ret->chat_msg = twitter_chat_msg;
    7421000        ret->chat_invite = twitter_chat_invite;
     1001        ret->chat_join = twitter_chat_join;
    7431002        ret->chat_leave = twitter_chat_leave;
    7441003        ret->keepalive = twitter_keepalive;
    7451004        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 
    4444        TWITTER_GOT_MENTIONS   = 0x40000,
    4545} twitter_flags_t;
    4646
     47typedef enum
     48{
     49        TWITTER_FILTER_TYPE_FOLLOW = 0,
     50        TWITTER_FILTER_TYPE_TRACK
     51} twitter_filter_type_t;
     52
    4753struct twitter_log_data;
    4854
    4955struct twitter_data
    struct twitter_data 
    5763        guint64 timeline_id;
    5864
    5965        GSList *follow_ids;
     66        GSList *filters;
    6067       
    6168        guint64 last_status_id; /* For undo */
    6269        gint main_loop_id;
     70        gint filter_update_id;
    6371        struct http_request *stream;
     72        struct http_request *filter_stream;
    6473        struct groupchat *timeline_gc;
    6574        gint http_fails;
    6675        twitter_flags_t flags;
    struct twitter_data 
    7887        int log_id;
    7988};
    8089
     90#define TWITTER_FILTER_UPDATE_WAIT 3000
     91struct twitter_filter
     92{
     93        twitter_filter_type_t type;
     94        char *text;
     95        guint64 uid;
     96        GSList *groupchats;
     97};
     98
    8199struct twitter_user_data
    82100{
    83101        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 { 
    5050};
    5151
    5252struct twitter_xml_user {
     53        guint64 uid;
    5354        char *name;
    5455        char *screen_name;
    5556};
    struct twitter_xml_status { 
    6061        struct twitter_xml_user *user;
    6162        guint64 id, rt_id; /* Usually equal, with RTs id == *original* id */
    6263        guint64 reply_to;
     64        gboolean from_filter;
    6365};
    6466
    6567/**
    static void twitter_http_get_users_lookup(struct http_request *req) 
    391393struct twitter_xml_user *twitter_xt_get_user(const json_value *node)
    392394{
    393395        struct twitter_xml_user *txu;
     396        json_value *jv;
    394397       
    395398        txu = g_new0(struct twitter_xml_user, 1);
    396399        txu->name = g_strdup(json_o_str(node, "name"));
    397400        txu->screen_name = g_strdup(json_o_str(node, "screen_name"));
    398401       
     402        jv = json_o_get(node, "id");
     403        txu->uid = jv->u.integer;
     404       
    399405        return txu;
    400406}
    401407
    static char *twitter_msg_add_id(struct im_connection *ic, 
    656662}
    657663
    658664/**
     665 * Function that is called to see the filter statuses in groupchat windows.
     666 */
     667static 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/**
    659703 * Function that is called to see the statuses in a groupchat window.
    660704 */
    661705static 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 
    729773        if (set_getbool(&ic->acc->set, "strip_newlines"))
    730774                strip_newlines(status->text);
    731775       
    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)
    733779                twitter_status_show_chat(ic, status);
    734780        else
    735781                twitter_status_show_msg(ic, status);
    static void twitter_status_show(struct im_connection *ic, struct twitter_xml_sta 
    739785        td->timeline_id = MAX(td->timeline_id, status->rt_id);
    740786}
    741787
    742 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o);
     788static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter);
    743789
    744790static void twitter_http_stream(struct http_request *req)
    745791{
    static void twitter_http_stream(struct http_request *req) 
    748794        json_value *parsed;
    749795        int len = 0;
    750796        char c, *nl;
     797        gboolean from_filter;
    751798       
    752799        if (!g_slist_find(twitter_connections, ic))
    753800                return;
    static void twitter_http_stream(struct http_request *req) 
    756803        td = ic->proto_data;
    757804       
    758805        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
    760811                imcb_error(ic, "Stream closed (%s)", req->status_string);
    761812                imc_logout(ic, TRUE);
    762813                return;
    static void twitter_http_stream(struct http_request *req) 
    773824                req->reply_body[len] = '\0';
    774825               
    775826                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);
    777829                }
    778830                json_value_free(parsed);
    779831                req->reply_body[len] = c;
    static void twitter_http_stream(struct http_request *req) 
    789841static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value *o);
    790842static gboolean twitter_stream_handle_status(struct im_connection *ic, struct twitter_xml_status *txs);
    791843
    792 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o)
     844static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter)
    793845{
    794846        struct twitter_data *td = ic->proto_data;
    795847        struct twitter_xml_status *txs;
    796848        json_value *c;
    797849       
    798850        if ((txs = twitter_xt_get_status(o))) {
     851                txs->from_filter = from_filter;
    799852                gboolean ret = twitter_stream_handle_status(ic, txs);
    800853                txs_free(txs);
    801854                return ret;
    gboolean twitter_open_stream(struct im_connection *ic) 
    893946        return FALSE;
    894947}
    895948
     949static 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
     1004static 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
     1055finish:
     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
     1077gboolean 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
    8961112static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor);
    8971113static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor);
    8981114
  • 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  
    7878/* Report spam */
    7979#define TWITTER_REPORT_SPAM_URL "/users/report_spam.json"
    8080
     81/* Stream URLs */
    8182#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"
    8284
    8385gboolean twitter_open_stream(struct im_connection *ic);
     86gboolean twitter_open_filter_stream(struct im_connection *ic);
    8487gboolean twitter_get_timeline(struct im_connection *ic, gint64 next_cursor);
    8588void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor);
    8689void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor);