source: protocols/twitter/twitter_lib.c @ b4dd253

Last change on this file since b4dd253 was b4dd253, checked in by Geert Mulders <g.c.w.m.mulders@…>, at 2009-12-02T18:08:40Z

home/timeline is now displayed in a groupchat instead of private window.

  • Property mode set to 100644
File size: 11.0 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Simple module to facilitate twitter functionality.                       *
5*                                                                           *
6*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
7*                                                                           *
8*  This library is free software; you can redistribute it and/or            *
9*  modify it under the terms of the GNU Lesser General Public               *
10*  License as published by the Free Software Foundation, version            *
11*  2.1.                                                                     *
12*                                                                           *
13*  This library is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
16*  Lesser General Public License for more details.                          *
17*                                                                           *
18*  You should have received a copy of the GNU Lesser General Public License *
19*  along with this library; if not, write to the Free Software Foundation,  *
20*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
21*                                                                           *
22****************************************************************************/
23
24#include "twitter_http.h"
25#include "twitter.h"
26#include "bitlbee.h"
27#include "url.h"
28#include "misc.h"
29#include "base64.h"
30#include "xmltree.h"
31#include "twitter_lib.h"
32#include <ctype.h>
33#include <errno.h>
34
35#define TXL_STATUS 1
36#define TXL_ID 1
37
38struct twitter_xml_list {
39        int next_cursor;
40        GSList *list;
41        gpointer data;
42};
43
44struct twitter_xml_user {
45        char *name;
46        char *screen_name;
47};
48
49struct twitter_xml_status {
50        char *created_at;
51        char *text;
52        struct twitter_xml_user *user;
53        guint64 id;
54};
55
56void txl_free(struct twitter_xml_list *txl, int type);
57void txs_free(struct twitter_xml_status *txs);
58void txu_free(struct twitter_xml_user *txu);
59
60static void twitter_http_get_friends_ids(struct http_request *req);
61
62/**
63 * Get the friends ids.
64 */
65void twitter_get_friends_ids(struct im_connection *ic, int next_cursor)
66{
67        struct twitter_data *td = ic->proto_data;
68
69        // Primitive, but hey! It works...     
70        char* args[2];
71        args[0] = "cursor";
72        args[1] = g_strdup_printf ("%d", next_cursor);
73        twitter_http(TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, td->user, td->pass, args, 2);
74
75        g_free(args[1]);
76}
77
78/**
79 * Function to help fill a list.
80 */
81static xt_status twitter_xt_next_cursor( struct xt_node *node, struct twitter_xml_list *txl )
82{
83        // Do something with the cursor.
84        txl->next_cursor = atoi(node->text);
85
86        return XT_HANDLED;
87}
88
89/**
90 * Fill a list of ids.
91 */
92static xt_status twitter_xt_get_friends_id_list( struct xt_node *node, struct twitter_xml_list *txl )
93{
94        struct xt_node *child;
95
96        // The root <statuses> node should hold the list of statuses <status>
97        // Walk over the nodes children.
98        for( child = node->children ; child ; child = child->next )
99        {
100                if ( g_strcasecmp( "id", child->name ) == 0)
101                {
102                        // Add the item to the list.
103                        txl->list = g_slist_append (txl->list, g_memdup( node->text, node->text_len + 1 ));
104                }
105                else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
106                {
107                        twitter_xt_next_cursor(child, txl);
108                }
109        }
110
111        return XT_HANDLED;
112}
113
114/**
115 * Callback for getting the friends ids.
116 */
117static void twitter_http_get_friends_ids(struct http_request *req)
118{
119        struct im_connection *ic;
120        struct xt_parser *parser;
121        struct twitter_xml_list *txl;
122
123        ic = req->data;
124
125        // Check if the HTTP request went well.
126        if (req->status_code != 200) {
127                // It didn't go well, output the error and return.
128                imcb_error(ic, "Could not retrieve friends. HTTP STATUS: %d", req->status_code);
129                return;
130        }
131
132        txl = g_new0(struct twitter_xml_list, 1);
133        txl->list = NULL;
134
135        // Parse the data.
136        parser = xt_new( NULL, txl );
137        xt_feed( parser, req->reply_body, req->body_size );
138        twitter_xt_get_friends_id_list(parser->root, txl);
139        xt_free( parser );
140
141        if (txl->next_cursor)
142                twitter_get_friends_ids(ic, txl->next_cursor);
143
144        txl_free(txl, TXL_ID);
145        g_free(txl);
146}
147
148/**
149 * Function to fill a twitter_xml_user struct.
150 * It sets:
151 *  - the name and
152 *  - the screen_name.
153 */
154static xt_status twitter_xt_get_user( struct xt_node *node, struct twitter_xml_user *txu )
155{
156        struct xt_node *child;
157
158        // Walk over the nodes children.
159        for( child = node->children ; child ; child = child->next )
160        {
161                if ( g_strcasecmp( "name", child->name ) == 0)
162                {
163                        txu->name = g_memdup( child->text, child->text_len + 1 );
164                }
165                else if (g_strcasecmp( "screen_name", child->name ) == 0)
166                {
167                        txu->screen_name = g_memdup( child->text, child->text_len + 1 );
168                }
169        }
170        return XT_HANDLED;
171}
172
173/**
174 * Function to fill a twitter_xml_status struct.
175 * It sets:
176 *  - the status text and
177 *  - the created_at timestamp and
178 *  - the status id and
179 *  - the user in a twitter_xml_user struct.
180 */
181static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml_status *txs )
182{
183        struct xt_node *child;
184
185        // Walk over the nodes children.
186        for( child = node->children ; child ; child = child->next )
187        {
188                if ( g_strcasecmp( "text", child->name ) == 0)
189                {
190                        txs->text = g_memdup( child->text, child->text_len + 1 );
191                }
192                else if (g_strcasecmp( "created_at", child->name ) == 0)
193                {
194                        txs->created_at = g_memdup( child->text, child->text_len + 1 );
195                }
196                else if (g_strcasecmp( "user", child->name ) == 0)
197                {
198                        txs->user = g_new0(struct twitter_xml_user, 1);
199                        twitter_xt_get_user( child, txs->user );
200                }
201                else if (g_strcasecmp( "id", child->name ) == 0)
202                {
203                        txs->id = g_ascii_strtoull (child->text, NULL, 10);
204                }
205        }
206        return XT_HANDLED;
207}
208
209/**
210 * Function to fill a twitter_xml_list struct.
211 * It sets:
212 *  - all <status>es within the <status> element and
213 *  - the next_cursor.
214 */
215static xt_status twitter_xt_get_status_list( struct xt_node *node, struct twitter_xml_list *txl )
216{
217        struct twitter_xml_status *txs;
218        struct xt_node *child;
219
220        // The root <statuses> node should hold the list of statuses <status>
221        // Walk over the nodes children.
222        for( child = node->children ; child ; child = child->next )
223        {
224                if ( g_strcasecmp( "status", child->name ) == 0)
225                {
226                        txs = g_new0(struct twitter_xml_status, 1);
227                        twitter_xt_get_status(child, txs);
228                        // Put the item in the front of the list.
229                        txl->list = g_slist_prepend (txl->list, txs);
230                }
231                else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
232                {
233                        twitter_xt_next_cursor(child, txl);
234                }
235        }
236
237        return XT_HANDLED;
238}
239
240static void twitter_http_get_home_timeline(struct http_request *req);
241
242/**
243 * Get the timeline.
244 */
245void twitter_get_home_timeline(struct im_connection *ic, int next_cursor)
246{
247        struct twitter_data *td = ic->proto_data;
248
249        char* args[4];
250        args[0] = "cursor";
251        args[1] = g_strdup_printf ("%d", next_cursor);
252        if (td->home_timeline_id) {
253                args[2] = "since_id";
254                args[3] = g_strdup_printf ("%llu", td->home_timeline_id);
255        }
256
257        twitter_http(TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, td->user, td->pass, args, td->home_timeline_id ? 4 : 2);
258
259        g_free(args[1]);
260        if (td->home_timeline_id) {
261                g_free(args[3]);
262        }
263}
264
265/**
266 * Callback for getting the home timeline.
267 */
268static void twitter_http_get_home_timeline(struct http_request *req)
269{
270        struct im_connection *ic = req->data;;
271        struct xt_parser *parser;
272        struct twitter_xml_list *txl;
273        struct twitter_data *td = ic->proto_data;
274        struct groupchat *gc;
275
276        // Check if the HTTP request went well.
277        if (req->status_code != 200) {
278                // It didn't go well, output the error and return.
279                imcb_error(ic, "Could not retrieve home/timeline. HTTP STATUS: %d", req->status_code);
280                return;
281        }
282
283        txl = g_new0(struct twitter_xml_list, 1);
284        txl->list = NULL;
285       
286        // Parse the data.
287        parser = xt_new( NULL, txl );
288        xt_feed( parser, req->reply_body, req->body_size );
289        // The root <statuses> node should hold the list of statuses <status>
290        twitter_xt_get_status_list(parser->root, txl);
291        xt_free( parser );
292       
293        GSList *l;
294        struct twitter_xml_status *status;
295
296        // Create a new groupchat if it does not exsist.
297        if (!td->home_timeline_gc)
298        {
299                td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" );
300                // Add the current user to the chat...
301                imcb_chat_add_buddy( gc, ic->acc->user );
302        }
303        else
304        {
305                gc = td->home_timeline_gc;
306        }
307
308        for ( l = txl->list; l ; l = g_slist_next(l) )
309        {
310                status = l->data;
311                // TODO Put the next part in a new function....
312
313                // Ugly hack, to show current user in chat...
314                if ( g_strcasecmp(status->user->screen_name, ic->acc->user) == 0)
315                {
316                        char *tmp = g_strdup_printf ("_%s_", status->user->screen_name);
317                        g_free(status->user->screen_name);
318                        status->user->screen_name = tmp;
319                }
320
321                // Check if the buddy is allready in the buddy list.
322                if (!user_findhandle( ic, status->user->screen_name ))
323                {
324                        // The buddy is not in the list, add the buddy...
325                        imcb_add_buddy( ic, status->user->screen_name, NULL );
326                        imcb_buddy_status( ic, status->user->screen_name, OPT_LOGGED_IN, NULL, NULL );
327                }
328
329                // Say it!
330                imcb_chat_msg (gc, status->user->screen_name, status->text, 0, 0 );
331                // Update the home_timeline_id to hold the highest id, so that by the next request
332                // we won't pick up the updates allready in the list.
333                td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id;
334        }
335
336        // Free the structure. 
337        txl_free(txl, TXL_STATUS);
338        g_free(txl);
339}
340
341/**
342 * Free a twitter_xml_list struct.
343 * type is the type of list the struct holds.
344 */
345void txl_free(struct twitter_xml_list *txl, int type)
346{
347        GSList *l;
348        for ( l = txl->list; l ; l = g_slist_next(l) )
349                if (type == TXL_STATUS)
350                        txs_free((struct twitter_xml_status *)l->data);
351                else if (type == TXL_ID)
352                        g_free(l->data);
353        g_slist_free(txl->list);
354}
355
356/**
357 * Frees a twitter_xml_status struct.
358 */
359void txs_free(struct twitter_xml_status *txs)
360{
361        g_free(txs->created_at);
362        g_free(txs->text);
363        txu_free(txs->user);
364}
365
366/**
367 * Frees a twitter_xml_user struct.
368 */
369void txu_free(struct twitter_xml_user *txu)
370{
371        g_free(txu->name);
372        g_free(txu->screen_name);
373}
374
375/**
376 * Callback after sending a new update to twitter.
377 */
378static void twitter_http_post_status(struct http_request *req)
379{
380        struct im_connection *ic = req->data;
381
382        // Check if the HTTP request went well.
383        if (req->status_code != 200) {
384                // It didn't go well, output the error and return.
385                imcb_error(ic, "Could not post tweed... HTTP STATUS: %d", req->status_code);
386                imcb_error(ic, req->reply_body);
387                return;
388        }
389}
390
391/**
392 * Function to POST a new status to twitter.
393 */ 
394void twitter_post_status(struct im_connection *ic, char* msg)
395{
396        struct twitter_data *td = ic->proto_data;
397
398        char* args[2];
399        args[0] = "status";
400        args[1] = msg;
401        twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 2);
402        g_free(args[1]);
403}
404
405
Note: See TracBrowser for help on using the repository browser.