diff options
Diffstat (limited to 'protocols/twitter/twitter.c')
| -rw-r--r-- | protocols/twitter/twitter.c | 263 | 
1 files changed, 261 insertions, 2 deletions
| diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index d2aafcb4..edc81427 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -30,6 +30,215 @@  #include "url.h"  GSList *twitter_connections = NULL; + +static int twitter_filter_cmp(struct twitter_filter *tf1, +                              struct twitter_filter *tf2) +{ +	int i1 = 0; +	int i2 = 0; +	int i; + +	static const twitter_filter_type_t types[] = { +		/* Order of the types */ +		TWITTER_FILTER_TYPE_FOLLOW, +		TWITTER_FILTER_TYPE_TRACK +	}; + +	for (i = 0; i < G_N_ELEMENTS(types); i++) { +		if (types[i] == tf1->type) { +			i1 = i + 1; +			break; +		} +	} + +	for (i = 0; i < G_N_ELEMENTS(types); i++) { +		if (types[i] == tf2->type) { +			i2 = i + 1; +			break; +		} +	} + +	if (i1 != i2) { +		/* With different types, return their difference */ +		return i1 - i2; +	} + +	/* With the same type, return the text comparison */ +	return g_strcasecmp(tf1->text, tf2->text); +} + +static gboolean twitter_filter_update(gpointer data, gint fd, +                                      b_input_condition cond) +{ +	struct im_connection *ic = data; +	struct twitter_data *td = ic->proto_data; + +	if (td->filters) { +		twitter_open_filter_stream(ic); +	} else if (td->filter_stream) { +		http_close(td->filter_stream); +		td->filter_stream = NULL; +	} + +	td->filter_update_id = 0; +	return FALSE; +} + +static struct twitter_filter *twitter_filter_get(struct groupchat *c, +                                                 twitter_filter_type_t type, +                                                 const char *text) +{ +	struct twitter_data *td = c->ic->proto_data; +	struct twitter_filter *tf = NULL; +	struct twitter_filter tfc = {type, (char*) text}; +	GSList *l; + +	for (l = td->filters; l; l = g_slist_next(l)) { +		tf = l->data; + +		if (twitter_filter_cmp(tf, &tfc) == 0) +			break; + +		tf = NULL; +	} + +	if (!tf) { +		tf = g_new0(struct twitter_filter, 1); +		tf->type = type; +		tf->text = g_strdup(text); +		td->filters = g_slist_prepend(td->filters, tf); +	} + +	if (!g_slist_find(tf->groupchats, c)) +		tf->groupchats = g_slist_prepend(tf->groupchats, c); + +	if (td->filter_update_id > 0) +		b_event_remove(td->filter_update_id); + +	/* Wait for other possible filter changes to avoid request spam */ +	td->filter_update_id = b_timeout_add(TWITTER_FILTER_UPDATE_WAIT, +					     twitter_filter_update, c->ic); +	return tf; +} + +static void twitter_filter_free(struct twitter_filter *tf) +{ +	g_slist_free(tf->groupchats); +	g_free(tf->text); +	g_free(tf); +} + +static void twitter_filter_remove(struct groupchat *c) +{ +	struct twitter_data *td = c->ic->proto_data; +	struct twitter_filter *tf; +	GSList *l = td->filters; +	GSList *p; + +	while (l != NULL) { +		tf = l->data; +		tf->groupchats = g_slist_remove(tf->groupchats, c); + +		p = l; +		l = g_slist_next(l); + +		if (!tf->groupchats) { +			twitter_filter_free(tf); +			td->filters = g_slist_delete_link(td->filters, p); +		} +	} + +	if (td->filter_update_id > 0) +		b_event_remove(td->filter_update_id); + +	/* Wait for other possible filter changes to avoid request spam */ +	td->filter_update_id = b_timeout_add(TWITTER_FILTER_UPDATE_WAIT, +					     twitter_filter_update, c->ic);} + +static void twitter_filter_remove_all(struct im_connection *ic) +{ +	struct twitter_data *td = ic->proto_data; +	GSList *chats = NULL; +	struct twitter_filter *tf; +	GSList *l = td->filters; +	GSList *p; + +	while (l != NULL) { +		tf = l->data; + +		/* Build up a list of groupchats to be freed */ +		for (p = tf->groupchats; p; p = g_slist_next(p)) { +			if (!g_slist_find(chats, p->data)) +				chats = g_slist_prepend(chats, p->data); +		} + +		p = l; +		l = g_slist_next(l); +		twitter_filter_free(p->data); +		td->filters = g_slist_delete_link(td->filters, p); +	} + +	l = chats; + +	while (l != NULL) { +		p = l; +		l = g_slist_next(l); + +		/* Freed each remaining groupchat */ +		imcb_chat_free(p->data); +		chats = g_slist_delete_link(chats, p); +	} + +	if (td->filter_stream) { +		http_close(td->filter_stream); +		td->filter_stream = NULL; +	} +} + +static GSList *twitter_filter_parse(struct groupchat *c, const char *text) +{ +	char **fs = g_strsplit(text, ";", 0); +	GSList *ret = NULL; +	struct twitter_filter *tf; +	char **f; +	char *v; +	int i; +	int t; + +	static const twitter_filter_type_t types[] = { +		TWITTER_FILTER_TYPE_FOLLOW, +		TWITTER_FILTER_TYPE_TRACK +	}; + +	static const char *typestrs[] = { +		"follow", +		"track" +	}; + +	for (f = fs; *f; f++) { +		if ((v = strchr(*f, ':')) == NULL) +			continue; + +		*(v++) = 0; + +		for (t = -1, i = 0; i < G_N_ELEMENTS(types); i++) { +			if (g_strcasecmp(typestrs[i], *f) == 0) { +				t = i; +				break; +			} +		} + +		if (t < 0 || strlen(v) == 0) +			continue; + +		tf = twitter_filter_get(c, types[t], v); +		ret = g_slist_prepend(ret, tf); +	} + +	g_strfreev(fs); +	return ret; +} +  /**   * Main loop function   */ @@ -435,7 +644,11 @@ static void twitter_logout(struct im_connection *ic)  		imcb_chat_free(td->timeline_gc);  	if (td) { +		if (td->filter_update_id > 0) +			b_event_remove(td->filter_update_id); +  		http_close(td->stream); +		twitter_filter_remove_all(ic);  		oauth_info_free(td->oauth_info);  		g_free(td->user);  		g_free(td->prefix); @@ -508,12 +721,57 @@ static void twitter_chat_invite(struct groupchat *c, char *who, char *message)  {  } +static struct groupchat *twitter_chat_join(struct im_connection *ic, +                                           const char *room, const char *nick, +                                           const char *password, set_t **sets) +{ +	struct groupchat *c = imcb_chat_new(ic, room); +	GSList *fs = twitter_filter_parse(c, room); +	GString *topic = g_string_new(""); +	struct twitter_filter *tf; +	GSList *l; + +	fs = g_slist_sort(fs, (GCompareFunc) twitter_filter_cmp); + +	for (l = fs; l; l = g_slist_next(l)) { +		tf = l->data; + +		if (topic->len > 0) +			g_string_append(topic, ", "); + +		if (tf->type == TWITTER_FILTER_TYPE_FOLLOW) +			g_string_append_c(topic, '@'); + +		g_string_append(topic, tf->text); +	} + +	if (topic->len > 0) +		g_string_prepend(topic, "Twitter Filter: "); + +	imcb_chat_topic(c, NULL, topic->str, 0); +	imcb_chat_add_buddy(c, ic->acc->user); + +	if (topic->len == 0) { +		imcb_error(ic, "Failed to handle any filters"); +		imcb_chat_free(c); +		c = NULL; +	} + +	g_string_free(topic, TRUE); +	g_slist_free(fs); + +	return c; +} +  static void twitter_chat_leave(struct groupchat *c)  {  	struct twitter_data *td = c->ic->proto_data; -	if (c != td->timeline_gc) -		return;		/* WTF? */ +	if (c != td->timeline_gc) { +		twitter_filter_remove(c); +		imcb_chat_free(c); +		return; +	}  	/* If the user leaves the channel: Fine. Rejoin him/her once new  	   tweets come in. */ @@ -747,6 +1005,7 @@ void twitter_initmodule()  	ret->remove_buddy = twitter_remove_buddy;  	ret->chat_msg = twitter_chat_msg;  	ret->chat_invite = twitter_chat_invite; +	ret->chat_join = twitter_chat_join;  	ret->chat_leave = twitter_chat_leave;  	ret->keepalive = twitter_keepalive;  	ret->add_permit = twitter_add_permit; | 
