さて、今日はv8エンジンのshell.ccを弄って、シェル内部にTwitterオブジェクトを作ってみました。とはいっても、仕組みは簡単。FunctionTemplateから得たPrototypeTemplateにメソッドを追加、さらにInstanceTemplateにプロパティusernameとpasswordを足しているだけです。
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
v8::Local<v8::Template> p = t->PrototypeTemplate();
p->Set("friendsTimeline", v8::FunctionTemplate::New(TwitterFriendsTimeline));
p->Set("updateStatus", v8::FunctionTemplate::New(TwitterUpdateStatus));
v8::Local<v8::ObjectTemplate> i = t->InstanceTemplate();
i->Set(v8::String::New("username"), v8::String::New(""));
i->Set(v8::String::New("password"), v8::String::New(""));
global->Set(v8::String::New("Twitter"), t);
このTwitterFriendsTimelineには以下の様なコードを...
v8::Handle<v8::Value> TwitterFriendsTimeline(const v8::Arguments& args) {
v8::Handle<v8::Value> ret = v8::Undefined();
v8::Local<v8::Object> This = args.This();
v8::HandleScope handle_scope;
v8::TryCatch try_catch;
v8::String::AsciiValue username(This->Get(v8::String::New("username")));
v8::String::AsciiValue password(This->Get(v8::String::New("password")));
CURL* curl = NULL;
CURLcode res = CURLE_OK;
char *auth;
response_data = NULL;
response_size = 0;
curl = curl_easy_init();
if (!curl) return v8::ThrowException(v8::String::New("Error: unknown"));
int n = strlen(*username) + strlen(*password);
auth = (char*) malloc(n + 2);
memset(auth, 0, n + 2);
strcpy(auth, *username);
strcat(auth, ":");
strcat(auth, *password);
curl_easy_setopt(curl, CURLOPT_URL, "http://twitter.com/statuses/friends_timeline.json");
curl_easy_setopt(curl, CURLOPT_USERPWD, auth);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
free(auth);
if (res == CURLE_OK) {
char* json = NULL;
json = (char*) malloc(response_size+1);
memset(json, 0, response_size+1);
memcpy(json, (char*) response_data, response_size);
v8::Handle<v8::String> source = v8::String::New(json);
free(json);
v8::Handle<v8::Script> script = v8::Script::Compile(source);
if (script.IsEmpty()) {
ret = v8::ThrowException(v8::String::New("Error: unknown"));
goto leave;
}
v8::Handle<v8::Value> result = script->Run();
if (script.IsEmpty()) {
ret = v8::ThrowException(v8::String::New("Error: unknown"));
goto leave;
}
if (!result->IsArray()) {
ret = v8::ThrowException(v8::String::New("Error: unknown"));
goto leave;
}
ret = result;
}
leave:
if (response_data) free(response_data);
response_data = NULL;
return ret;
}
さらにTwitterUpdateStatusには
v8::Handle<v8::Value> TwitterUpdateStatus(const v8::Arguments& args) {
v8::Handle<v8::Value> ret = v8::Undefined();
v8::Local<v8::Object> This = args.This();
v8::HandleScope handle_scope;
v8::TryCatch try_catch;
if (args.Length() != 1) return v8::ThrowException(v8::String::New("Error: usage(message)"));
v8::String::AsciiValue username(This->Get(v8::String::New("username")));
v8::String::AsciiValue password(This->Get(v8::String::New("password")));
v8::Handle<v8::Object> global = v8::Context::GetCurrent()->Global();
v8::Local<v8::Function> func = v8::Function::Cast(*global->Get(v8::String::New("encodeURIComponent")));
v8::Local<v8::Value> funcargs[1];
funcargs[0] = args[0]->ToString();
v8::Local<v8::Value> result_val = func->Call(global, 1, funcargs);
v8::String::AsciiValue escaped(result_val->ToString());
char* status = url_encode_alloc(*escaped);
CURL* curl = NULL;
CURLcode res = CURLE_OK;
char *auth;
char url[2048];
response_data = NULL;
response_size = 0;
curl = curl_easy_init();
if (!curl) return v8::ThrowException(v8::String::New("Error: unknown"));
memset(url, 0, sizeof(url));
strncpy(url, "http://twitter.com/statuses/update.xml", sizeof(url)-1);
strncat(url, "?status=", sizeof(url)-1);;
strncat(url, status, sizeof(url)-1);
free(status);
int n = strlen(*username) + strlen(*password);
auth = (char*) malloc(n + 2);
memset(auth, 0, n + 2);
strcpy(auth, *username);
strcat(auth, ":");
strcat(auth, *password);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_USERPWD, auth);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "");
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
free(auth);
if (res != CURLE_OK) {
ret = v8::ThrowException(v8::String::New("Error: unknown"));
}
if (response_data) free(response_data);
response_data = NULL;
return ret;
}
これだけ準備出来れば、あとは
var twitter = new Twitter()
twitter.username = "my-username"
twitter.password = "my-password"
var statuses = twitter.friendsTimeline();
for (var n = 0; n < statuses.length; n++) {
print(statuses[n].user.screen_name, ":", statuses[n].text);
}
twitter.updateStatus("v8エンジン12気筒");
こんなスクリプト書いて
# shell twitter.js
とかやればfollowerのステータスが出た後でメッセージがポストされます。簡単ですね。
ちなみに既存のPrintではAsciiValueを使いますが、AsciiValueではUTF-8が8bit落ちするのでsetlocaleを使ってワイドキャラでprintfする様修正しています。
コードはcodereposのこの辺に置いときます。汚いソースですが、よろしければ参考まで...