Ignore:
Timestamp:
2015-01-26T03:46:03Z (5 years ago)
Author:
jgeboski <jgeboski@…>
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)
Message:

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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • protocols/twitter/twitter_lib.c

    r5eab298f r73ee390  
    5151
    5252struct twitter_xml_user {
     53        guint64 uid;
    5354        char *name;
    5455        char *screen_name;
     
    6162        guint64 id, rt_id; /* Usually equal, with RTs id == *original* id */
    6263        guint64 reply_to;
     64        gboolean from_filter;
    6365};
    6466
     
    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"));
     401       
     402        jv = json_o_get(node, "id");
     403        txu->uid = jv->u.integer;
    398404       
    399405        return txu;
     
    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 */
     
    731775                strip_newlines(status->text);
    732776       
    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)
    734780                twitter_status_show_chat(ic, status);
    735781        else
     
    745791}
    746792
    747 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o);
     793static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter);
    748794
    749795static void twitter_http_stream(struct http_request *req)
     
    754800        int len = 0;
    755801        char c, *nl;
     802        gboolean from_filter;
    756803       
    757804        if (!g_slist_find(twitter_connections, ic))
     
    762809       
    763810        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
    765816                imcb_error(ic, "Stream closed (%s)", req->status_string);
    766817                imc_logout(ic, TRUE);
     
    779830               
    780831                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);
    782834                }
    783835                json_value_free(parsed);
     
    795847static gboolean twitter_stream_handle_status(struct im_connection *ic, struct twitter_xml_status *txs);
    796848
    797 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o)
     849static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter)
    798850{
    799851        struct twitter_data *td = ic->proto_data;
     
    802854       
    803855        if ((txs = twitter_xt_get_status(o))) {
     856                txs->from_filter = from_filter;
    804857                gboolean ret = twitter_stream_handle_status(ic, txs);
    805858                txs_free(txs);
     
    899952}
    900953
     954static 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
     1009static 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
     1060finish:
     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
     1082gboolean 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
    9011117static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor);
    9021118static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor);
Note: See TracChangeset for help on using the changeset viewer.