diff options
| -rw-r--r-- | protocols/twitter/twitter.c | 37 | ||||
| -rw-r--r-- | protocols/twitter/twitter.h | 7 | ||||
| -rw-r--r-- | protocols/twitter/twitter_lib.c | 369 | ||||
| -rw-r--r-- | protocols/twitter/twitter_lib.h | 10 | 
4 files changed, 330 insertions, 93 deletions
| diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 1cc7eaeb..b6b23fa5 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -28,8 +28,8 @@  /** - *  * Main loop function - *   */ + * Main loop function + */  gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond)  {  	struct im_connection *ic = data; @@ -37,6 +37,11 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond)  	if ((ic->flags & OPT_LOGGED_IN) != OPT_LOGGED_IN)  		return 0; +	// If the user uses multiple private message windows we need to get the  +	// users buddies. +	if (!set_getbool( &ic->acc->set, "use_groupchat" )) +		twitter_get_statuses_friends(ic, -1); +  	// Do stuff..  	twitter_get_home_timeline(ic, -1); @@ -47,6 +52,8 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond)  static void twitter_init( account_t *acc )  { +	set_t *s; +	s = set_add( &acc->set, "use_groupchat", "false", set_eval_bool, acc );  }  /** @@ -57,7 +64,7 @@ static void twitter_login( account_t *acc )  {  	struct im_connection *ic = imcb_new( acc );  	struct twitter_data *td = g_new0( struct twitter_data, 1 ); -	 +  	td->user = acc->user;  	td->pass = acc->pass;  	td->home_timeline_id = 0; @@ -67,11 +74,6 @@ static void twitter_login( account_t *acc )  	// Set the status to logged in.  	ic->flags = OPT_LOGGED_IN; -	// Try to get the buddies... -	//twitter_get_friends_ids(ic, -1); - -	//twitter_get_home_timeline(ic, -1); -  	// Run this once. After this queue the main loop function.  	twitter_main_loop(ic, -1, 0); @@ -80,6 +82,8 @@ static void twitter_login( account_t *acc )  	imcb_log( ic, "Connecting to twitter" );  	imcb_connected(ic); + +	twitter_connections = g_slist_append( twitter_connections, ic );  }  /** @@ -96,6 +100,8 @@ static void twitter_logout( struct im_connection *ic )  	{  		g_free( td );  	} + +	twitter_connections = g_slist_remove( twitter_connections, ic );  }  /** @@ -103,8 +109,11 @@ static void twitter_logout( struct im_connection *ic )   */  static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away )  { -	imcb_log( ic, "In twitter_buddy_msg..."); -	twitter_post_status(ic, message); +	// Let's just update the status. +//	if ( g_strcasecmp(who, ic->acc->user) == 0 ) +		twitter_post_status(ic, message); +//	else +//		twitter_direct_messages_new(ic, who, message);  	return( 0 );  } @@ -123,11 +132,6 @@ static void twitter_set_away( struct im_connection *ic, char *state, char *messa  static void twitter_set_my_name( struct im_connection *ic, char *info )  { -	imcb_log( ic, "In twitter_set_my_name..." ); -//	char * aap = twitter_http("http://gertje.org", NULL, ic, 1, "geert", "poep", NULL, 0); - -//	imcb_log( ic, aap ); -//	g_free(aap);  }  static void twitter_get_info(struct im_connection *ic, char *who)  @@ -217,5 +221,8 @@ void twitter_initmodule()  	ret->handle_cmp = g_strcasecmp;  	register_protocol(ret); + +	// Initialise the twitter_connections GSList. +	twitter_connections = NULL;  } diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 5b032929..05a861bb 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -40,4 +40,11 @@ struct twitter_data  	struct groupchat *home_timeline_gc;  }; +/** + * This has the same function as the msn_connections GSList. We use this to  + * make sure the connection is still alive in callbacks before we do anything + * else. + */ +GSList *twitter_connections; +  #endif //_TWITTER_H diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index d548b5f2..f07897ed 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -33,9 +33,13 @@  #include <errno.h>  #define TXL_STATUS 1 -#define TXL_ID 1 +#define TXL_USER 2 +#define TXL_ID 3 + +static void twitter_imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at );  struct twitter_xml_list { +	int type;  	int next_cursor;  	GSList *list;  	gpointer data; @@ -53,9 +57,54 @@ struct twitter_xml_status {  	guint64 id;  }; -void txl_free(struct twitter_xml_list *txl, int type); -void txs_free(struct twitter_xml_status *txs); -void txu_free(struct twitter_xml_user *txu); +/** + * Frees a twitter_xml_user struct. + */ +static void txu_free(struct twitter_xml_user *txu) +{ +	g_free(txu->name); +	g_free(txu->screen_name); +} + + +/** + * Frees a twitter_xml_status struct. + */ +static void txs_free(struct twitter_xml_status *txs) +{ +	g_free(txs->created_at); +	g_free(txs->text); +	txu_free(txs->user); +} + +/** + * Free a twitter_xml_list struct. + * type is the type of list the struct holds. + */ +static void txl_free(struct twitter_xml_list *txl) +{ +	GSList *l; +	for ( l = txl->list; l ; l = g_slist_next(l) ) +		if (txl->type == TXL_STATUS) +			txs_free((struct twitter_xml_status *)l->data); +		else if (txl->type == TXL_ID) +			g_free(l->data); +	g_slist_free(txl->list); +} + +/** + * Add a buddy if it is not allready added, set the status to logged in. + */ +static void twitter_add_buddy(struct im_connection *ic, char *name) +{ +	// Check if the buddy is allready in the buddy list. +	if (!user_findhandle( ic, name )) +	{ +		// The buddy is not in the list, add the buddy and set the status to logged in. +		imcb_add_buddy( ic, name, NULL ); +		imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); +	} +}  static void twitter_http_get_friends_ids(struct http_request *req); @@ -92,6 +141,9 @@ static xt_status twitter_xt_next_cursor( struct xt_node *node, struct twitter_xm  static xt_status twitter_xt_get_friends_id_list( struct xt_node *node, struct twitter_xml_list *txl )  {  	struct xt_node *child; +	 +	// Set the list type. +	txl->type = TXL_ID;  	// The root <statuses> node should hold the list of statuses <status>  	// Walk over the nodes children. @@ -122,6 +174,10 @@ static void twitter_http_get_friends_ids(struct http_request *req)  	ic = req->data; +	// Check if the connection is still active. +	if( !g_slist_find( twitter_connections, ic ) ) +		return; +  	// Check if the HTTP request went well.  	if (req->status_code != 200) {  		// It didn't go well, output the error and return. @@ -141,7 +197,7 @@ static void twitter_http_get_friends_ids(struct http_request *req)  	if (txl->next_cursor)  		twitter_get_friends_ids(ic, txl->next_cursor); -	txl_free(txl, TXL_ID); +	txl_free(txl);  	g_free(txl);  } @@ -171,6 +227,66 @@ static xt_status twitter_xt_get_user( struct xt_node *node, struct twitter_xml_u  }  /** + * Function to fill a twitter_xml_list struct. + * It sets: + *  - all <user>s from the <users> element. + */ +static xt_status twitter_xt_get_users( struct xt_node *node, struct twitter_xml_list *txl ) +{ +	struct twitter_xml_user *txu; +	struct xt_node *child; + +	// Set the type of the list. +	txl->type = TXL_USER; + +	// The root <users> node should hold the list of users <user> +	// Walk over the nodes children. +	for( child = node->children ; child ; child = child->next ) +	{ +		if ( g_strcasecmp( "user", child->name ) == 0) +		{ +			txu = g_new0(struct twitter_xml_user, 1); +			twitter_xt_get_user(child, txu); +			// Put the item in the front of the list. +			txl->list = g_slist_prepend (txl->list, txu); +		} +	} + +	return XT_HANDLED; +} + +/** + * Function to fill a twitter_xml_list struct. + * It calls twitter_xt_get_users to get the <user>s from a <users> element. + * It sets: + *  - the next_cursor. + */ +static xt_status twitter_xt_get_user_list( struct xt_node *node, struct twitter_xml_list *txl ) +{ +	struct xt_node *child; + +	// Set the type of the list. +	txl->type = TXL_USER; + +	// The root <user_list> node should hold a users <users> element +	// Walk over the nodes children. +	for( child = node->children ; child ; child = child->next ) +	{ +		if ( g_strcasecmp( "users", child->name ) == 0) +		{ +			twitter_xt_get_users(child, txl); +		} +		else if ( g_strcasecmp( "next_cursor", child->name ) == 0) +		{ +			twitter_xt_next_cursor(child, txl); +		} +	} + +	return XT_HANDLED; +} + + +/**   * Function to fill a twitter_xml_status struct.   * It sets:   *  - the status text and @@ -217,6 +333,9 @@ static xt_status twitter_xt_get_status_list( struct xt_node *node, struct twitte  	struct twitter_xml_status *txs;  	struct xt_node *child; +	// Set the type of the list. +	txl->type = TXL_STATUS; +  	// The root <statuses> node should hold the list of statuses <status>  	// Walk over the nodes children.  	for( child = node->children ; child ; child = child->next ) @@ -263,15 +382,70 @@ void twitter_get_home_timeline(struct im_connection *ic, int next_cursor)  }  /** + * Function that is called to see the statuses in a groupchat window. + */ +static void twitter_groupchat(struct im_connection *ic, GSList *list) +{ +	struct twitter_data *td = ic->proto_data; +	GSList *l = NULL; +	struct twitter_xml_status *status; +	struct groupchat *gc; + +	// Create a new groupchat if it does not exsist. +	if (!td->home_timeline_gc) +	{    +		td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" ); +		// Add the current user to the chat... +		imcb_chat_add_buddy( gc, ic->acc->user ); +	} +	else +	{    +		gc = td->home_timeline_gc; +	} + +	for ( l = list; l ; l = g_slist_next(l) ) +	{ +		status = l->data; +		twitter_add_buddy(ic, status->user->screen_name); +		// Say it! +		twitter_imcb_chat_msg (gc, status->user->screen_name, status->text, 0, 0 ); +		// Update the home_timeline_id to hold the highest id, so that by the next request +		// we won't pick up the updates allready in the list. +		td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id; +	} +} + +/** + * Function that is called to see statuses as private messages. + */ +static void twitter_private_message_chat(struct im_connection *ic, GSList *list) +{ +	struct twitter_data *td = ic->proto_data; +	GSList *l = NULL; +	struct twitter_xml_status *status; + +	for ( l = list; l ; l = g_slist_next(l) ) +	{ +		status = l->data; +		imcb_buddy_msg( ic, status->user->screen_name, status->text, 0, 0 ); +		// Update the home_timeline_id to hold the highest id, so that by the next request +		// we won't pick up the updates allready in the list. +		td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id; +	} +} + +/**   * Callback for getting the home timeline.   */  static void twitter_http_get_home_timeline(struct http_request *req)  { -	struct im_connection *ic = req->data;; +	struct im_connection *ic = req->data;  	struct xt_parser *parser;  	struct twitter_xml_list *txl; -	struct twitter_data *td = ic->proto_data; -	struct groupchat *gc; + +	// Check if the connection is still active. +	if( !g_slist_find( twitter_connections, ic ) ) +		return;  	// Check if the HTTP request went well.  	if (req->status_code != 200) { @@ -282,94 +456,92 @@ static void twitter_http_get_home_timeline(struct http_request *req)  	txl = g_new0(struct twitter_xml_list, 1);  	txl->list = NULL; -	 +  	// Parse the data.  	parser = xt_new( NULL, txl );  	xt_feed( parser, req->reply_body, req->body_size );  	// The root <statuses> node should hold the list of statuses <status>  	twitter_xt_get_status_list(parser->root, txl);  	xt_free( parser ); -	 -	GSList *l; -	struct twitter_xml_status *status; -	// Create a new groupchat if it does not exsist. -	if (!td->home_timeline_gc) -	{ -		td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" ); -		// Add the current user to the chat... -		imcb_chat_add_buddy( gc, ic->acc->user ); -	} +	// See if the user wants to see the messages in a groupchat window or as private messages. +	if (set_getbool( &ic->acc->set, "use_groupchat" )) +		twitter_groupchat(ic, txl->list);  	else -	{ -		gc = td->home_timeline_gc; -	} - -	for ( l = txl->list; l ; l = g_slist_next(l) ) -	{ -		status = l->data; -		// TODO Put the next part in a new function.... - -		// Ugly hack, to show current user in chat... -		if ( g_strcasecmp(status->user->screen_name, ic->acc->user) == 0) -		{ -			char *tmp = g_strdup_printf ("_%s_", status->user->screen_name); -			g_free(status->user->screen_name); -			status->user->screen_name = tmp; -		} - -		// Check if the buddy is allready in the buddy list. -		if (!user_findhandle( ic, status->user->screen_name )) -		{ -			// The buddy is not in the list, add the buddy... -			imcb_add_buddy( ic, status->user->screen_name, NULL ); -			imcb_buddy_status( ic, status->user->screen_name, OPT_LOGGED_IN, NULL, NULL ); -		} - -		// Say it! -		imcb_chat_msg (gc, status->user->screen_name, status->text, 0, 0 ); -		// Update the home_timeline_id to hold the highest id, so that by the next request -		// we won't pick up the updates allready in the list. -		td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id; -	} +		twitter_private_message_chat(ic, txl->list);  	// Free the structure.	 -	txl_free(txl, TXL_STATUS); +	txl_free(txl);  	g_free(txl);  }  /** - * Free a twitter_xml_list struct. - * type is the type of list the struct holds. + * Callback for getting (twitter)friends... + * + * Be afraid, be very afraid! This function will potentially add hundreds of "friends". "Who has  + * hundreds of friends?" you wonder? You probably not, since you are reading the source of  + * BitlBee... Get a life and meet new people!   */ -void txl_free(struct twitter_xml_list *txl, int type) +static void twitter_http_get_statuses_friends(struct http_request *req)  { -	GSList *l; +	struct im_connection *ic = req->data; +	struct xt_parser *parser; +	struct twitter_xml_list *txl; + +	// Check if the connection is still active. +	if( !g_slist_find( twitter_connections, ic ) ) +		return; + +	// Check if the HTTP request went well. +	if (req->status_code != 200) { +		// It didn't go well, output the error and return. +		imcb_error(ic, "Could not retrieve home/timeline. HTTP STATUS: %d", req->status_code); +		return; +	} + +	txl = g_new0(struct twitter_xml_list, 1); +	txl->list = NULL; + +	// Parse the data. +	parser = xt_new( NULL, txl ); +	xt_feed( parser, req->reply_body, req->body_size ); + +	// Get the user list from the parsed xml feed. +	twitter_xt_get_user_list(parser->root, txl); +	xt_free( parser ); + +	GSList *l = NULL; +	struct twitter_xml_user *user; +	// Add the users as buddies.  	for ( l = txl->list; l ; l = g_slist_next(l) ) -		if (type == TXL_STATUS) -			txs_free((struct twitter_xml_status *)l->data); -		else if (type == TXL_ID) -			g_free(l->data); -	g_slist_free(txl->list); -} +	{ +		user = l->data; +		twitter_add_buddy(ic, user->screen_name); +	} -/** - * Frees a twitter_xml_status struct. - */ -void txs_free(struct twitter_xml_status *txs) -{ -	g_free(txs->created_at); -	g_free(txs->text); -	txu_free(txs->user); +	// if the next_cursor is set to something bigger then 0 there are more friends to gather. +	if (txl->next_cursor > 0) +		twitter_get_statuses_friends(ic, txl->next_cursor); + +	// Free the structure. +	txl_free(txl); +	g_free(txl);  }  /** - * Frees a twitter_xml_user struct. + * Get the friends.   */ -void txu_free(struct twitter_xml_user *txu) +void twitter_get_statuses_friends(struct im_connection *ic, int next_cursor)  { -	g_free(txu->name); -	g_free(txu->screen_name); +	struct twitter_data *td = ic->proto_data; + +	char* args[2]; +	args[0] = "cursor"; +	args[1] = g_strdup_printf ("%d", next_cursor); + +	twitter_http(TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, td->user, td->pass, args, 2); + +	g_free(args[1]);  }  /** @@ -379,6 +551,10 @@ static void twitter_http_post_status(struct http_request *req)  {  	struct im_connection *ic = req->data; +	// Check if the connection is still active. +	if( !g_slist_find( twitter_connections, ic ) ) +		return; +  	// Check if the HTTP request went well.  	if (req->status_code != 200) {  		// It didn't go well, output the error and return. @@ -399,7 +575,52 @@ void twitter_post_status(struct im_connection *ic, char* msg)  	args[0] = "status";  	args[1] = msg;  	twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 2); -	g_free(args[1]); +//	g_free(args[1]);  } +/** + * Function to POST a new message to twitter. + */ +void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg) +{ +	struct twitter_data *td = ic->proto_data; + +	char* args[4]; +	args[0] = "screen_name"; +	args[1] = who; +	args[2] = "text"; +	args[3] = msg; +	// Use the same callback as for twitter_post_status, since it does basically the same. +	twitter_http(TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 4); +//	g_free(args[1]); +//	g_free(args[3]); +} + + +/** + * This function "overwrites" the imcb_chat_msg function. Because in the original the logged in user is filtered out. + */ +static void twitter_imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at ) +{ +	struct im_connection *ic = c->ic; +	char *wrapped; +	user_t *u; + +	u = user_findhandle( ic, who ); +	if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 )  +			|| ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) +		strip_html( msg ); + +	wrapped = word_wrap( msg, 425 ); +	if( c && u ) +	{    +		irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, "", wrapped ); +	} +	else +	{    +		imcb_log( ic, "Message from/to conversation %s@%p (unknown conv/user): %s", who, c, wrapped ); +	} +	g_free( wrapped ); +} + diff --git a/protocols/twitter/twitter_lib.h b/protocols/twitter/twitter_lib.h index 28ca871f..e47bfd95 100644 --- a/protocols/twitter/twitter_lib.h +++ b/protocols/twitter/twitter_lib.h @@ -50,9 +50,9 @@  /* Direct messages URLs */  #define TWITTER_DIRECT_MESSAGES_URL TWITTER_API_URL "/direct_messages.xml" -#define TWITTER_DIRECT_MESSAGENEW_URL TWITTER_API_URL "/direct_messages/new.xml" -#define TWITTER_DIRECT_MESSAGESSENT_URL TWITTER_API_URL "/direct_messages/sent.xml" -#define TWITTER_DIRECT_MESSAGEDESTROY_URL TWITTER_API_URL "/direct_messages/destroy/" +#define TWITTER_DIRECT_MESSAGES_NEW_URL TWITTER_API_URL "/direct_messages/new.xml" +#define TWITTER_DIRECT_MESSAGES_SENT_URL TWITTER_API_URL "/direct_messages/sent.xml" +#define TWITTER_DIRECT_MESSAGES_DESTROY_URL TWITTER_API_URL "/direct_messages/destroy/"  /* Friendships URLs */  #define TWITTER_FRIENDSHIPS_CREATE_URL TWITTER_API_URL "/friendships/create.xml" @@ -77,8 +77,10 @@  void twitter_get_friends_ids(struct im_connection *ic, int next_cursor);  void twitter_get_home_timeline(struct im_connection *ic, int next_cursor); +void twitter_get_statuses_friends(struct im_connection *ic, int next_cursor); -void twitter_post_status(struct im_connection *ic, char* msg); +void twitter_post_status(struct im_connection *ic, char *msg); +void twitter_direct_messages_new(struct im_connection *ic, char *who, char *message);  #endif //_TWITTER_LIB_H | 
