2008/09/03

はてな
v8エンジン盛り上がってますね。
さて、今日は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のこの辺に置いときます。汚いソースですが、よろしければ参考まで...

2008/08/27

はてな
ま、ほぼ差異のないソースですが...
windowsでしか動作確認出来てないので、動かなかったら直して下さい。
よかったらもってって下さい。

2008/07/31

2008/07/07

はてな
twitter APIはXMLにリクエスト投げてもBUSY画面でtext/htmlなヘッダを返す時があるので、gtktwitterでは応答ヘッダがapplication/xmlじゃ無いときは処理しない様にしてあったんですが、wassrpod.plはAPI応答にtext/htmlを返していたので少し手を入れさせて頂き、見事gtktwitterでwassrにポストする事が出来ました。あとプロフアイコンもPROXY経由で取れる様に修正しました。おかしかったら直して下さい(id:yappo)
wassrpod経由でgtktwitter
動かすには、環境変数「HTTP_PROXY」に「localhost:9277」を設定してgtktwitterを起動すればok。ただtwitterの設定が残ってるだろうから、UN*Xならば「~/.config/gtktwitter/config」を、Windowsならば「~/Application Data/gtktwitter/config」を修正して起動して下さい。同じアカウント名使ってるならばそのままでも動くかも。

ま、gtkwassrもあるんですけどね...

関連URL:YappoLogs: WassrPodというMacからwassrを快適に使うツールを作ったよ

2008/05/09

はてな
twitterを辞めてwassrに移ってしまった皆様、常にwassrだけ常駐されておられる皆様、こんにちわ。
vimperatorでtwitter出来るvimperator plugin「twitter.js」を改造してwassr出来る「wassr.js」を作ってみたよ。
機能としては
  • ステータス更新
  • ステータス参照
  • TODO管理
  • 足跡参照
が出来ます。まずステータス参照は「twitter.js」と同じく
:wassr
ステータス更新は
:wassr wassrかわいいよwassr
TODOは
:wassr -todo
で一覧表示
:wassr -todo+ かめはめ波を発射する
でTODO追加。
追加後のID一覧が表示されます
:wassr -todo- XXXXXXX
で削除。
XXXXXXXはTODOのID
:wassr -todo* XXXXXXX
で開始
:wassr -todo/ XXXXXXX
で停止
:wassr -todo! XXXXXXX
で完了となります。TODOのIDは補完が効くので<tab>で選んで下さい。
また
:wassr -footmark
で足跡の一覧も見れます。
オプションは
:wassr<tab>
で候補が出ます。
よろしければどぞ。
wassr.js

wassrかわいいよwassr

はてな
twitter検索しまくり - noreplyのブログ
twitter_viewで,fとかすると今表示されてるステータスをfav
http://d.hatena.ne.jp/noreply/20080503/1209785708
twitter_viewってtwitteViewer.jsの事だったんだろうかと悩み中...

2008/04/22

はてな
Google App EngineでwebSimple.pyを使って作りました。

twitterのfollower発言で、漢字が読めなかった貴方。「@mattn_jp それなんて読むの?」とか聞くのが恥ずかしい貴方。そんな貴方にピッタリのサービスです。twitter followerの発言に「読み」を付けて表示します。(まぁ私は難しい単語なんて使いませんが...)

文字の分解にはYahoo! JAPANの「日本語形態素解析Webサービス」を使用しています。

色の変っている部分にマウスを当てると、ツールチップにて読みを教えてくれるようになっています。

よみふったー
で、いきなりですが「よみふったー」のソースです。
ライブラリとしてはwebSimple.pyのほかにPyWrapperに含まれるElementTree、BeautifulSoupを使っています。
#!-*- coding:utf-8 -*-
import os
import re
import base64
import xmllib
import logging
import elementtree.SimpleXMLTreeBuilder as xmlbuilder
import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from BeautifulSoup import BeautifulSoup
from webSimple import Simple as webSimple

class MainPage(webapp.RequestHandler):
  def post(self):
    twitter_user = self.request.get('twitter_user').encode('utf-8', 'replace')
    template_valuse = {
        'twitter_user' : twitter_user,
        'statuses' : []
    }
    try:
      twitter_url = "http://twitter.com/statuses/user_timeline/%s.xml" % twitter_user
      twitter = webSimple({ 'base_url': twitter_url })
  
      yahoo = webSimple({
        'base_url': 'http://api.jlp.yahoo.co.jp/MAService/V1/parse',
        'param' : { 'appid' : 'xxxxxxxx', 'results' : 'ma', },
      })
  
      xml = twitter.get().content
      r = re.compile(r'(\&#\d+;)')
      for st in BeautifulSoup(xml)('status'):
        name = r.sub(lambda x: unichr(int(x.group(1)[2:-1])), st.user.screen_name.string).encode('utf-8', 'replace')
        msg = r.sub(lambda x : unichr(int(x.group(1)[2:-1])), st.text.string).encode('utf-8', 'replace')
        xml = yahoo.get({ 'sentence': msg }).content
        words = []
        for word in BeautifulSoup(xml)('word'):
          words.append({
            'reading' : word.reading.string.encode('utf-8', 'replace'),
            'pos' : word.pos.string.encode('utf-8', 'replace'),
            'surface' : word.surface.string.encode('utf-8', 'replace'),
          })
        template_valuse['statuses'].append({
           'screen_name' : name,
           'words' : words,
        })
    except Exception, e:
      template_valuse['error'] = e
      pass
    path = os.path.join(os.path.dirname(__file__), 'yomifutter.html')
    self.response.out.write(template.render(path, template_valuse))

  def get(self):
    path = os.path.join(os.path.dirname(__file__), 'yomifutter.html')
    self.response.out.write(template.render(path, {}))

def main():
  application = webapp.WSGIApplication([('/yomifutter/', MainPage)], debug=True)
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
  main()
適当なコードで申し訳ない...汗

新規投稿