Ignore:
Timestamp:
2012-12-22T00:14:26Z (12 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Branches:
master
Children:
7d5afa6
Parents:
92d3044 (diff), 573e274 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merging JSON branch. It's very stable by now, and I want more testers.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • protocols/twitter/twitter.c

    r92d3044 rcc6fdf8  
    44*  Simple module to facilitate twitter functionality.                       *
    55*                                                                           *
    6 *  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
     6*  Copyright 2009-2010 Geert Mulders <g.c.w.m.mulders@gmail.com>            *
     7*  Copyright 2010-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
    78*                                                                           *
    89*  This library is free software; you can redistribute it and/or            *
     
    2930#include "url.h"
    3031
    31 #define twitter_msg( ic, fmt... ) \
    32         do {                                            \
    33                 struct twitter_data *td = ic->proto_data;   \
    34                 if( td->timeline_gc )                       \
    35                         imcb_chat_log( td->timeline_gc, fmt );  \
    36                 else                                        \
    37                         imcb_log( ic, fmt );                    \
    38         } while( 0 );
    39 
    4032GSList *twitter_connections = NULL;
    41 
    4233/**
    4334 * Main loop function
     
    6253        struct twitter_data *td = ic->proto_data;
    6354
     55        /* Create the room now that we "logged in". */
     56        if (td->flags & TWITTER_MODE_CHAT)
     57                twitter_groupchat_init(ic);
     58
    6459        imcb_log(ic, "Getting initial statuses");
    6560
    66         // Run this once. After this queue the main loop function.
     61        // Run this once. After this queue the main loop function (or open the
     62        // stream if available).
    6763        twitter_main_loop(ic, -1, 0);
    68 
    69         // Queue the main_loop
    70         // Save the return value, so we can remove the timeout on logout.
    71         td->main_loop_id =
    72             b_timeout_add(set_getint(&ic->acc->set, "fetch_interval") * 1000, twitter_main_loop, ic);
     64       
     65        if (set_getbool(&ic->acc->set, "stream")) {
     66                /* That fetch was just to get backlog, the stream will give
     67                   us the rest. \o/ */
     68                twitter_open_stream(ic);
     69               
     70                /* Stream sends keepalives (empty lines) or actual data at
     71                   least twice a minute. Disconnect if this stops. */
     72                ic->flags |= OPT_PONGS;
     73        } else {
     74                /* Not using the streaming API, so keep polling the old-
     75                   fashioned way. :-( */
     76                td->main_loop_id =
     77                    b_timeout_add(set_getint(&ic->acc->set, "fetch_interval") * 1000,
     78                                  twitter_main_loop, ic);
     79        }
     80}
     81
     82struct groupchat *twitter_groupchat_init(struct im_connection *ic)
     83{
     84        char *name_hint;
     85        struct groupchat *gc;
     86        struct twitter_data *td = ic->proto_data;
     87        GSList *l;
     88
     89        if (td->timeline_gc)
     90                return td->timeline_gc;
     91
     92        td->timeline_gc = gc = imcb_chat_new(ic, "twitter/timeline");
     93
     94        name_hint = g_strdup_printf("%s_%s", td->prefix, ic->acc->user);
     95        imcb_chat_name_hint(gc, name_hint);
     96        g_free(name_hint);
     97
     98        for (l = ic->bee->users; l; l = l->next) {
     99                bee_user_t *bu = l->data;
     100                if (bu->ic == ic)
     101                        imcb_chat_add_buddy(gc, bu->handle);
     102        }
     103        imcb_chat_add_buddy(gc, ic->acc->user);
     104       
     105        return gc;
    73106}
    74107
     
    83116        if (set_getbool(&ic->acc->set, "oauth") && !td->oauth_info)
    84117                twitter_oauth_start(ic);
    85         else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "one") != 0 &&
    86                 !(td->flags & TWITTER_HAVE_FRIENDS)) {
     118        else if (!(td->flags & TWITTER_MODE_ONE) &&
     119                !(td->flags & TWITTER_HAVE_FRIENDS)) {
    87120                imcb_log(ic, "Getting contact list");
    88121                twitter_get_friends_ids(ic, -1);
    89                 //twitter_get_statuses_friends(ic, -1);
    90122        } else
    91123                twitter_main_loop_start(ic);
     
    187219}
    188220
    189 
    190 static char *set_eval_mode(set_t * set, char *value)
    191 {
    192         if (g_strcasecmp(value, "one") == 0 ||
    193             g_strcasecmp(value, "many") == 0 || g_strcasecmp(value, "chat") == 0)
    194                 return value;
    195         else
    196                 return NULL;
    197 }
    198 
    199221int twitter_url_len_diff(gchar *msg, unsigned int target_len)
    200222{
     
    233255                return TRUE;
    234256
    235         imcb_error(ic, "Maximum message length exceeded: %d > %d", len, max);
     257        twitter_log(ic, "Maximum message length exceeded: %d > %d", len, max);
    236258
    237259        return FALSE;
     260}
     261
     262static char *set_eval_commands(set_t * set, char *value)
     263{
     264        if (g_strcasecmp(value, "strict") == 0 )
     265                return value;
     266        else
     267                return set_eval_bool(set, value);
     268}
     269
     270static char *set_eval_mode(set_t * set, char *value)
     271{
     272        if (g_strcasecmp(value, "one") == 0 ||
     273            g_strcasecmp(value, "many") == 0 || g_strcasecmp(value, "chat") == 0)
     274                return value;
     275        else
     276                return NULL;
    238277}
    239278
     
    257296        s->flags |= ACC_SET_OFFLINE_ONLY;
    258297
    259         s = set_add(&acc->set, "commands", "true", set_eval_bool, acc);
     298        s = set_add(&acc->set, "commands", "true", set_eval_commands, acc);
    260299
    261300        s = set_add(&acc->set, "fetch_interval", "60", set_eval_int, acc);
     
    274313
    275314        s = set_add(&acc->set, "show_ids", "true", set_eval_bool, acc);
    276         s->flags |= ACC_SET_OFFLINE_ONLY;
    277315
    278316        s = set_add(&acc->set, "show_old_mentions", "20", set_eval_int, acc);
    279317
    280318        s = set_add(&acc->set, "strip_newlines", "false", set_eval_bool, acc);
     319       
     320        if (strcmp(acc->prpl->name, "twitter") == 0) {
     321                s = set_add(&acc->set, "stream", "true", set_eval_bool, acc);
     322                s->flags |= ACC_SET_OFFLINE_ONLY;
     323        }
    281324}
    282325
    283326/**
    284  * Login method. Since the twitter API works with seperate HTTP request we
     327 * Login method. Since the twitter API works with separate HTTP request we
    285328 * only save the user and pass to the twitter_data object.
    286329 */
     
    298341                imc_logout(ic, FALSE);
    299342                return;
     343        }
     344
     345        if (!strstr(url.host, "twitter.com") &&
     346            set_getbool(&ic->acc->set, "stream")) {
     347                imcb_error(ic, "Warning: The streaming API is only supported by Twitter, "
     348                               "and you seem to be connecting to a different service.");
    300349        }
    301350
     
    340389        imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL);
    341390
    342         if (set_getbool(&acc->set, "show_ids"))
    343                 td->log = g_new0(struct twitter_log_data, TWITTER_LOG_LENGTH);
     391        td->log = g_new0(struct twitter_log_data, TWITTER_LOG_LENGTH);
     392        td->log_id = -1;
     393       
     394        s = set_getstr(&ic->acc->set, "mode");
     395        if (g_strcasecmp(s, "one") == 0)
     396                td->flags |= TWITTER_MODE_ONE;
     397        else if (g_strcasecmp(s, "many") == 0)
     398                td->flags |= TWITTER_MODE_MANY;
     399        else
     400                td->flags |= TWITTER_MODE_CHAT;
    344401
    345402        twitter_login_finish(ic);
     
    363420
    364421        if (td) {
     422                http_close(td->stream);
    365423                oauth_info_free(td->oauth_info);
    366424                g_free(td->user);
     
    495553 *  Returns 0 if the user provides garbage.
    496554 */
    497 static guint64 twitter_message_id_from_command_arg(struct im_connection *ic, struct twitter_data *td, char *arg) {
     555static guint64 twitter_message_id_from_command_arg(struct im_connection *ic, char *arg, bee_user_t **bu_) {
     556        struct twitter_data *td = ic->proto_data;
    498557        struct twitter_user_data *tud;
    499         bee_user_t *bu;
     558        bee_user_t *bu = NULL;
    500559        guint64 id = 0;
    501         if (g_str_has_prefix(arg, "#") &&
    502                 sscanf(arg + 1, "%" G_GUINT64_FORMAT, &id) == 1) {
    503                 if (id < TWITTER_LOG_LENGTH && td->log)
     560       
     561        if (bu_)
     562                *bu_ = NULL;
     563        if (!arg || !arg[0])
     564                return 0;
     565       
     566        if (arg[0] != '#' && (bu = bee_user_by_handle(ic->bee, ic, arg))) {
     567                if ((tud = bu->data))
     568                        id = tud->last_id;
     569        } else {
     570                if (arg[0] == '#')
     571                        arg++;
     572                if (sscanf(arg, "%" G_GINT64_MODIFIER "x", &id) == 1 &&
     573                    id < TWITTER_LOG_LENGTH) {
     574                        bu = td->log[id].bu;
    504575                        id = td->log[id].id;
    505         } else if ((bu = bee_user_by_handle(ic->bee, ic, arg)) &&
    506                 (tud = bu->data) && tud->last_id)
    507                 id = tud->last_id;
    508         else if (sscanf(arg, "%" G_GUINT64_FORMAT, &id) == 1){
    509                 if (id < TWITTER_LOG_LENGTH && td->log)
    510                         id = td->log[id].id;
    511         }
     576                        /* Beware of dangling pointers! */
     577                        if (!g_slist_find(ic->bee->users, bu))
     578                                bu = NULL;
     579                } else if (sscanf(arg, "%" G_GINT64_MODIFIER "d", &id) == 1) {
     580                        /* Allow normal tweet IDs as well; not a very useful
     581                           feature but it's always been there. Just ignore
     582                           very low IDs to avoid accidents. */
     583                        if (id < 1000000)
     584                                id = 0;
     585                }
     586        }
     587        if (bu_)
     588                *bu_ = bu;
    512589        return id;
    513590}
     
    517594        struct twitter_data *td = ic->proto_data;
    518595        char *cmds, **cmd, *new = NULL;
    519         guint64 in_reply_to = 0;
     596        guint64 in_reply_to = 0, id;
     597        gboolean allow_post =
     598                g_strcasecmp(set_getstr(&ic->acc->set, "commands"), "strict") != 0;
     599        bee_user_t *bu = NULL;
    520600
    521601        cmds = g_strdup(message);
     
    523603
    524604        if (cmd[0] == NULL) {
    525                 g_free(cmds);
    526                 return;
    527         } else if (!set_getbool(&ic->acc->set, "commands")) {
    528                 /* Not supporting commands. */
     605                goto eof;
     606        } else if (!set_getbool(&ic->acc->set, "commands") && allow_post) {
     607                /* Not supporting commands if "commands" is set to true/strict. */
    529608        } else if (g_strcasecmp(cmd[0], "undo") == 0) {
    530                 guint64 id;
    531 
    532609                if (cmd[1] == NULL)
    533610                        twitter_status_destroy(ic, td->last_status_id);
    534                 else if (sscanf(cmd[1], "%" G_GUINT64_FORMAT, &id) == 1) {
    535                         if (id < TWITTER_LOG_LENGTH && td->log)
    536                                 id = td->log[id].id;
    537                        
     611                else if ((id = twitter_message_id_from_command_arg(ic, cmd[1], NULL)))
    538612                        twitter_status_destroy(ic, id);
    539                 } else
    540                         twitter_msg(ic, "Could not undo last action");
    541 
    542                 g_free(cmds);
    543                 return;
     613                else
     614                        twitter_log(ic, "Could not undo last action");
     615
     616                goto eof;
    544617        } else if (g_strcasecmp(cmd[0], "favourite") == 0 && cmd[1]) {
    545                 guint64 id;
    546                 if ((id = twitter_message_id_from_command_arg(ic, td, cmd[1]))) {
     618                if ((id = twitter_message_id_from_command_arg(ic, cmd[1], NULL))) {
    547619                        twitter_favourite_tweet(ic, id);
    548620                } else {
    549                         twitter_msg(ic, "Please provide a message ID or username.");
     621                        twitter_log(ic, "Please provide a message ID or username.");
    550622                }
    551                 g_free(cmds);
    552                 return;
     623                goto eof;
    553624        } else if (g_strcasecmp(cmd[0], "follow") == 0 && cmd[1]) {
    554625                twitter_add_buddy(ic, cmd[1], NULL);
    555                 g_free(cmds);
    556                 return;
     626                goto eof;
    557627        } else if (g_strcasecmp(cmd[0], "unfollow") == 0 && cmd[1]) {
    558628                twitter_remove_buddy(ic, cmd[1], NULL);
    559                 g_free(cmds);
    560                 return;
     629                goto eof;
    561630        } else if ((g_strcasecmp(cmd[0], "report") == 0 ||
    562631                    g_strcasecmp(cmd[0], "spam") == 0) && cmd[1]) {
    563                 char * screen_name;
    564                 guint64 id;
    565                 screen_name = cmd[1];
     632                char *screen_name;
     633               
    566634                /* Report nominally works on users but look up the user who
    567635                   posted the given ID if the user wants to do it that way */
    568                 if (g_str_has_prefix(cmd[1], "#") &&
    569                     sscanf(cmd[1] + 1, "%" G_GUINT64_FORMAT, &id) == 1) {
    570                         if (id < TWITTER_LOG_LENGTH && td->log) {
    571                                 if (g_slist_find(ic->bee->users, td->log[id].bu)) {
    572                                         screen_name = td->log[id].bu->handle;
    573                                 }
    574                         }
    575                 }
     636                twitter_message_id_from_command_arg(ic, cmd[1], &bu);
     637                if (bu)
     638                        screen_name = bu->handle;
     639                else
     640                        screen_name = cmd[1];
     641               
    576642                twitter_report_spam(ic, screen_name);
    577                 g_free(cmds);
    578                 return;
     643                goto eof;
    579644        } else if (g_strcasecmp(cmd[0], "rt") == 0 && cmd[1]) {
    580                 guint64 id = twitter_message_id_from_command_arg(ic, td, cmd[1]);
     645                id = twitter_message_id_from_command_arg(ic, cmd[1], NULL);
    581646
    582647                td->last_status_id = 0;
     
    584649                        twitter_status_retweet(ic, id);
    585650                else
    586                         twitter_msg(ic, "User `%s' does not exist or didn't "
     651                        twitter_log(ic, "User `%s' does not exist or didn't "
    587652                                    "post any statuses recently", cmd[1]);
    588653
    589                 g_free(cmds);
    590                 return;
     654                goto eof;
    591655        } else if (g_strcasecmp(cmd[0], "reply") == 0 && cmd[1] && cmd[2]) {
    592                 struct twitter_user_data *tud;
    593                 bee_user_t *bu = NULL;
    594                 guint64 id = 0;
    595 
    596                 if (g_str_has_prefix(cmd[1], "#") &&
    597                     sscanf(cmd[1] + 1, "%" G_GUINT64_FORMAT, &id) == 1 &&
    598                     (id < TWITTER_LOG_LENGTH) && td->log) {
    599                         bu = td->log[id].bu;
    600                         if (g_slist_find(ic->bee->users, bu))
    601                                 id = td->log[id].id;
    602                         else
    603                                 bu = NULL;
    604                 } else if ((bu = bee_user_by_handle(ic->bee, ic, cmd[1])) &&
    605                     (tud = bu->data) && tud->last_id) {
    606                         id = tud->last_id;
    607                 } else if (sscanf(cmd[1], "%" G_GUINT64_FORMAT, &id) == 1 &&
    608                            (id < TWITTER_LOG_LENGTH) && td->log) {
    609                         bu = td->log[id].bu;
    610                         if (g_slist_find(ic->bee->users, bu))
    611                                 id = td->log[id].id;
    612                         else
    613                                 bu = NULL;
    614                 }
    615 
     656                id = twitter_message_id_from_command_arg(ic, cmd[1], &bu);
    616657                if (!id || !bu) {
    617                         twitter_msg(ic, "User `%s' does not exist or didn't "
     658                        twitter_log(ic, "User `%s' does not exist or didn't "
    618659                                    "post any statuses recently", cmd[1]);
    619                         g_free(cmds);
    620                         return;
     660                        goto eof;
    621661                }
    622662                message = new = g_strdup_printf("@%s %s", bu->handle, message + (cmd[2] - cmd[0]));
    623663                in_reply_to = id;
     664                allow_post = TRUE;
    624665        } else if (g_strcasecmp(cmd[0], "post") == 0) {
    625666                message += 5;
    626         }
    627 
    628         {
     667                allow_post = TRUE;
     668        }
     669
     670        if (allow_post) {
    629671                char *s;
    630                 bee_user_t *bu;
    631 
    632                 if (!twitter_length_check(ic, message)) {
    633                         g_free(new);
    634                         g_free(cmds);
    635                         return;
    636                 }
     672
     673                if (!twitter_length_check(ic, message))
     674                        goto eof;
    637675
    638676                s = cmd[0] + strlen(cmd[0]) - 1;
     
    657695                td->last_status_id = 0;
    658696                twitter_post_status(ic, message, in_reply_to);
    659                 g_free(new);
    660         }
     697        } else {
     698                twitter_log(ic, "Unknown command: %s", cmd[0]);
     699        }
     700eof:
     701        g_free(new);
    661702        g_free(cmds);
    662703}
     704
     705void twitter_log(struct im_connection *ic, char *format, ... )
     706{
     707        struct twitter_data *td = ic->proto_data;
     708        va_list params;
     709        char *text;
     710       
     711        va_start(params, format);
     712        text = g_strdup_vprintf(format, params);
     713        va_end(params);
     714       
     715        if (td->timeline_gc)
     716                imcb_chat_log(td->timeline_gc, "%s", text);
     717        else
     718                imcb_log(ic, "%s", text);
     719       
     720        g_free(text);
     721}
     722
    663723
    664724void twitter_initmodule()
Note: See TracChangeset for help on using the changeset viewer.