Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add direct message capability.

Also adds rudimentry error checking on both updates and direct messages.
  • Loading branch information...
commit b7245558f0f7d941dc28fd749743a7c13ce129d1 1 parent 7bd46ac
@paulrho authored
Showing with 93 additions and 7 deletions.
  1. +91 −6 bti.c
  2. +2 −1  bti.h
View
97 bti.c
@@ -64,7 +64,7 @@ static void display_help(void)
" --account accountname\n"
" --password password\n"
" --action action\n"
- " ('update', 'friends', 'public', 'replies', or 'user')\n"
+ " ('update', 'friends', 'public', 'replies', 'user', or 'direct')\n"
" --user screenname\n"
" --group groupname\n"
" --proxy PROXY:PORT\n"
@@ -248,6 +248,7 @@ static void bti_curl_buffer_free(struct bti_curl_buffer *buffer)
}
const char twitter_host[] = "http://api.twitter.com/1/statuses";
+const char twitter_host_simple[] = "http://api.twitter.com/1";
const char identica_host[] = "https://identi.ca/api/statuses";
const char twitter_name[] = "twitter";
const char identica_name[] = "identi.ca";
@@ -267,6 +268,7 @@ static const char mentions_uri[] = "/mentions.xml";
static const char replies_uri[] = "/replies.xml";
static const char retweet_uri[] = "/retweet/";
static const char group_uri[] = "/../statusnet/groups/timeline/";
+static const char direct_uri[] = "/direct_messages/new.xml";
static const char config_default[] = "/etc/bti";
static const char config_user_default[] = ".bti";
@@ -379,6 +381,61 @@ static void parse_timeline(char *document, struct session *session)
return;
}
+static int parse_response(char *document, struct session *session)
+{
+ xmlDocPtr doc;
+ xmlNodePtr current;
+
+ doc = xmlReadMemory(document, strlen(document),
+ "response.xml", NULL,
+ XML_PARSE_NOERROR);
+ if (doc == NULL)
+ return -EINVAL;
+
+ current = xmlDocGetRootElement(doc);
+ if (current == NULL) {
+ fprintf(stderr, "empty document\n");
+ xmlFreeDoc(doc);
+ return -EINVAL;
+ }
+
+ if (xmlStrcmp(current->name, (const xmlChar *) "status")) {
+ if (xmlStrcmp(current->name, (const xmlChar *) "direct_message")) {
+ if (xmlStrcmp(current->name, (const xmlChar *) "hash")) {
+ fprintf(stderr, "unexpected document type\n");
+ xmlFreeDoc(doc);
+ return -EINVAL;
+ } else {
+ xmlChar *text;
+ while (current != NULL) {
+ if (current->type == XML_ELEMENT_NODE)
+ if (!xmlStrcmp(current->name, (const xmlChar *)"error")) {
+ text = xmlNodeListGetString(doc, current->xmlChildrenNode, 1);
+ break;
+ }
+ if (current->children)
+ current = current->children;
+ else
+ current = current->next;
+ }
+
+ if (text) {
+ fprintf(stderr, "error condition detected = %s\n", text);
+ xmlFree(text);
+ } else
+ fprintf(stderr, "unknown error condition\n");
+
+ xmlFreeDoc(doc);
+ return -EINVAL;
+ }
+ }
+ }
+
+ xmlFreeDoc(doc);
+
+ return 0;
+}
+
static size_t curl_callback(void *buffer, size_t size, size_t nmemb,
void *userp)
{
@@ -629,6 +686,10 @@ static int send_request(struct session *session)
curl_easy_setopt(curl, CURLOPT_URL, endpoint);
break;
+ case ACTION_DIRECT:
+ /* NOT IMPLEMENTED - twitter requires authentication anyway */
+ break;
+
default:
break;
}
@@ -729,6 +790,12 @@ static int send_request(struct session *session)
retweet_uri, session->retweet);
is_post = 1;
break;
+ case ACTION_DIRECT:
+ escaped_tweet = oauth_url_escape(session->tweet);
+ sprintf(endpoint, "%s%s?user=%s&text=%s", twitter_host_simple,
+ direct_uri, session->user, escaped_tweet);
+ is_post = 1;
+ break;
default:
break;
}
@@ -762,9 +829,16 @@ static int send_request(struct session *session)
return -EIO;
}
- if ((session->action != ACTION_UPDATE) &&
- (session->action != ACTION_RETWEET))
- parse_timeline(reply, session);
+ if (!session->dry_run) {
+ if ((session->action != ACTION_UPDATE) &&
+ (session->action != ACTION_RETWEET) &&
+ (session->action != ACTION_DIRECT))
+ parse_timeline(reply, session);
+
+ if ((session->action == ACTION_UPDATE) ||
+ (session->action == ACTION_DIRECT))
+ return parse_response(reply, session);
+ }
}
return 0;
}
@@ -817,6 +891,15 @@ static void log_session(struct session *session, int retval)
fprintf(log_file, "%s: host=%s retrieving group timeline\n",
session->time, session->hostname);
break;
+ case ACTION_DIRECT:
+ if (retval)
+ fprintf(log_file, "%s: host=%s tweet failed\n",
+ session->time, session->hostname);
+ else
+ fprintf(log_file, "%s: host=%s tweet=%s\n",
+ session->time, session->hostname,
+ session->tweet);
+ break;
default:
break;
}
@@ -1268,6 +1351,8 @@ int main(int argc, char *argv[], char *envp[])
session->action = ACTION_GROUP;
else if (strcasecmp(optarg, "retweet") == 0)
session->action = ACTION_RETWEET;
+ else if (strcasecmp(optarg, "direct") == 0)
+ session->action = ACTION_DIRECT;
else
session->action = ACTION_UNKNOWN;
dbg("action = %d\n", session->action);
@@ -1403,7 +1488,7 @@ int main(int argc, char *argv[], char *envp[])
if (session->action == ACTION_UNKNOWN) {
fprintf(stderr, "Unknown action, valid actions are:\n"
- "'update', 'friends', 'public', 'replies', 'group' or 'user'.\n");
+ "'update', 'friends', 'public', 'replies', 'group', 'user' or 'direct'.\n");
goto exit;
}
@@ -1431,7 +1516,7 @@ int main(int argc, char *argv[], char *envp[])
dbg("retweet ID = %s\n", session->retweet);
}
- if (session->action == ACTION_UPDATE) {
+ if (session->action == ACTION_UPDATE || session->action == ACTION_DIRECT) {
if (session->background || !session->interactive)
tweet = get_string_from_stdin();
else
View
3  bti.h
@@ -34,7 +34,8 @@ enum action {
ACTION_PUBLIC = 8,
ACTION_GROUP = 16,
ACTION_RETWEET = 32,
- ACTION_UNKNOWN = 64
+ ACTION_DIRECT = 64,
+ ACTION_UNKNOWN = 128
};
struct session {
Please sign in to comment.
Something went wrong with that request. Please try again.