2010/10/10


PerlのGrowlライブラリって色々ある訳ですが、誰かが書いたGrowlするアプリのコードを実行しようとした際にWindowsやLinuxで動かないと少し悲しくなります。
そこでGrowl::Anyってのがあればいいんじゃね?って事で書いてみました。
Mac::Growl、notify-send、Desktop::Notify、Net::GrowlClient、Growl::GNTPのどれかがインストールされていれば使えます。
mattn's p5-Growl-Any at master - GitHub

perl module that provide any growl application

http://github.com/mattn/p5-Growl-Any
今日はこれを使って、twitterのhome_timelineをGrowlするスクリプトを書いてみました。
#!perl

use strict;
use warnings;
use Config::Pit;
use Encode;
use Growl::Any;
use Net::Twitter::Lite;

my $config = pit_get("twitter-growler");
unless ( $config->{access_token_secret} ) {
    $config = pit_get(
        "twitter-growler",
        require => {
            consumer_key    => "YOUR_CONSUMER-KEY",
            consumer_secret => "YOUR-CONSUMER-SECRET",
        }
    );
    my $nt = Net::Twitter::Lite->new( %{$config} );
    $| = 1;
    print "Authorize this app at:\n ", $nt->get_authorization_url,
      "\nAnd enter the PIN: ";
    my $pin = <STDIN>;
    chomp $pin;
    my ( $access_token, $access_token_secret, $user_id, $screen_name ) =
      $nt->request_access_token( verifier => $pin );
    $config->{access_token}        = $access_token;
    $config->{access_token_secret} = $access_token_secret;
    pit_set( "twitter-growler", data => $config );
    exit;
}

my $nt = Net::Twitter::Lite->new( %{$config} );
$nt->access_token( $config->{access_token} );
$nt->access_token_secret( $config->{access_token_secret} );

my $growl = Growl::Any->new();
$growl->register( "Growl/Twitter", ["tweet"] );

my $last_id = undef;
while (1) {
    my @sl;
    if ($last_id) {
        @sl = $nt->friends_timeline( { count => 200, since_id => $last_id } );
    }
    else {
        @sl = $nt->friends_timeline( { count => 5 } );
    }
    for my $s ( @{ $sl[0] } ) {
        $last_id = $s->{id} unless $last_id;
        $growl->notify(
            "tweet",
            encode_utf8( $s->{user}{screen_name} ),
            encode_utf8( $s->{text} ),
            $s->{user}{profile_image_url},
        );
        sleep(4);
    }
}
上に書いたモジュールのどれかが入っていれば(notify-sendはコマンド)、MacでもLinuxでもWindowsでも動くはずです。

twitter-growler1
なお、実は現在Growl::Anyのパラメータをutf8フラグ付きにしようかどうか迷っているので、もしかするとその修正を入れた後にこのスクリプトを動かすと文字化けしてしまうかもしれませんので注意です。

今後ネットに転がっていたスクリプトを何も修正せずにGrowlする事が出来る様になれば、素敵な事だなと思います。

ちなみに上記のスクリプトですが、初回に実行するとconsumer_key/consumer_secretを聞かれます。ブラウザでPINを貰って入力するとConfig::Pitで設定を保存して終了するので、再度起動すると動き出します。
streamなAPIでもやってみたのですが、更新が多くGrowlの表示数が激しすぎたので一定間隔で取得する様にしてあります。お好みで修正してみて下さい。
Posted at by



2010/09/14


えーっと、IDE使ってません。ごめんなさい。
PerlをIDEで書いてる人、どれくらいいるんだろう。何使ってるんだろ。
私はLinuxでもWindowsでもVimを使ってます。Perlってサーバで使われる事が多いし、サーバだとsshで入る事が多いだろうし、まぁXの転送やVNCやそれっぽいのでグラフィカルに操作しても良いけど、正直PerlのドキュメントもテキストベースだしPerlで入力補完出来るグラフィカルエディタもまず無いと思ってる。

で、私がVim上で使ってるPerlの開発を「200倍便利にするスクリプト」を紹介。

perldoc.vim

perldocはPerlのオンラインマニュアルなんだけど、これを一々ターミナルに戻ったりして引いてると時間のロス。screenで分割して開いてもいいんだけどscreenの画面間移動よりもVim内でのウィンドウ間移動の方が操作感が統一出来てて良い。
そこで使ってるのがid:secondlifeさんが書いたperldoc.vim

hotchpotch's perldoc-vim at master - GitHub

Perldoc plugin for vim

http://github.com/hotchpotch/perldoc-vim
ドキュメントを引くにはPerldocコマンドを使います。関数openのドキュメントを引くには :Perldoc -f open とします。またモジュールAcme::Oppaiのドキュメントを引くには :Perldoc Acme::Oppai と実行します。 このスクリプトの良いと思う点は、補完が効くことです。「:Perldoc Acm」くらいまでタイプしてtabを押すと、「Acme::Oppai」や「Acme::Hidek」が補完されます。

ref.vim

もう一つ、同じ様にperldocが引けるプラグインとしてthincaさんが書いたrefがあります。
thinca's vim-ref at master - GitHub

Integrated reference viewer.

http://github.com/thinca/vim-ref
こちらはperldocに限らず
  • alc
  • erlang
  • man
  • perldoc
  • phpmanual
  • pydoc
  • refe
といったドキュメントを開く事が出来ます。ドキュメントを引くには :Ref perldoc -f open :Ref perldoc Acme::Oppai 等と実行します。
perldoc-vim
こちらも補完が効きますが、refはモジュール検索の際、あり得るモジュール全てが補完されてしまう為、ネームスペースにわんさかモジュールがあると少しモタつきます。なので私はperldoc.vimを使ってます。

追記
thincaさんから某所でコメント貰った。
let g:ref_perldoc_complete_head = 1 でそれっぽい事出来るらしい。

perlomni.vim

Perlは結構(かなり?)文法制限の緩い言語だと思ってますが、これが災いしてかIDE等でPerlの入力補完が出来る物は殆どありません。そんな中c9sさんがVimのomni補完でPerlの入力補完をしてくれるスクリプトを書いてくれています。
c9s's perlomni.vim at master - GitHub

perl omnicompletion for vim (including base class function compleltions .. etc)

http://github.com/c9s/perlomni.vim
これ、ぜひ実際に試して欲しいのですが use Acme::O までタイプして<c-x><c-o>(CTRL-x CTRL-o)をタイプすると
perl-completion-vim1
どどーーーん!
さらに
perl-completion-vim2
Moooooooooooooooooooseな補完や
perl-completion-vim3
変数の補完。そして
perl-completion-vim4
モジュールのメンバまで補完してくれます。キャーすてきー!
すばらし過ぎます。ちなみにこのperlomni.vimのwin32対応と、上で紹介したperldoc.vimの補完部分を担当させて頂きました。

2010年はPerlが熱い!そう思ってます。ぜひインストールして活用して見てください。200倍とは言い過ぎかもしれませんが、間違いなく2倍にはなりますよ。

初めてのPerl 第5版 初めてのPerl 第5版
Randal L. Schwartz, Tom Phoenix, brian d foy, 近藤 嘉雪
オライリージャパン 大型本 / ¥554 (2009年10月26日)
 
発送可能時間:


モダンPerl入門: 業務で使う実用的なPerlの“すべて” モダンPerl入門: 業務で使う実用的なPerlの“すべて”
牧 大輔
翔泳社 単行本 / ¥932 (2009年02月01日)
 
発送可能時間:

Posted at by



2010/09/11


以前、twitter上で「STL以上、Boost未満な使い心地のライブラリが欲しい。ネットワーク関連込みの。」とつぶやいた所、id:faith_and_braveさんから返事貰った。
Faith and Brave Twitter / Faith and Brave

CLX or Message-Pack or Pocoあたりでしょうか RT @mattn_jp: STL以上、Boost未満な使い心地のライブラリが欲しい。ネットワーク関連込みの。

http://twitter.com/cpp_akira/status/21234425525
CLXは実は前から知っていて何度か捨てコードに使わせて頂いていたのですが、SSL越しにproxyが使えなかったので、ネットワーク関連のコードには使ってませんでした。
まぁでも一応書いて判断すべきかなと思って、僕的な答えとして、さらーーーっと書いた。
CONSUMER_KEY と CONSUMER_SECRET を書き換えて実行して下さい。xAuth認証を使ってtwitterにステータスをポストします。

#include <iostream>
#include <sstream>
#include <clx/base64.h>
#include <clx/hexdump.h>
#include <clx/date_time.h>
#include <clx/format.h>
#include <clx/hmac.h>
#include <clx/https.h>
#include <clx/random.h>
#include <clx/salgorithm.h>
#include <clx/sha1.h>
#include <clx/uri.h>

std::string url_encode(const std::string& url) {
    std::ostringstream rets;
    for(size_t n = 0; n < url.size(); n++) {
        unsigned char c = (unsigned char)url[n];
        if (isalnum(c) || c == '_' || c == '.' || c == '-')
            rets << c;
        else {
            char buf[8];
            sprintf(buf, "%02X", (int)c);
            rets << '%' << buf[0] << buf[1];
        }
    }
    return rets.str();
}


int main(int argc, char* argv[]) {

    if (argc != 4) {
        std::cerr << "usage: " << argv[0]
            << " [username] [password] [message]" << std::endl;
        return -1;
    }

    clx::date_time now;
    clx::random<unsigned int> randgen;
    randgen.engine().seed(time(0));

    std::string consumer_key = "CONSUMER_KEY";
    std::string consumer_secret = "CONSUMER_SECRET";
    std::string username = argv[1];
    std::string password = argv[2];
    std::string message = argv[3];

    std::stringstream ss;
    std::string times = clx::str(clx::format("%s") % now.c_time());
    std::string nonce = clx::hexdump(times);

    ss << "oauth_consumer_key=" << consumer_key
       << "&oauth_nonce=" << nonce
       << "&oauth_signature_method=" << "HMAC-SHA1"
       << "&oauth_timestamp=" << times
       << "&oauth_version=" << "1.0"
       << "&x_auth_mode=" << "client_auth"
       << "&x_auth_password=" << password
       << "&x_auth_username=" << username;

    std::string key = consumer_secret + "&";
    std::string val = "POST&";

    val += url_encode("https://api.twitter.com/oauth/access_token");
    val += "&";
    val += url_encode(ss.str());
    char* hmac = (char*)clx::hmac<clx::sha1>(
            key.c_str(), key.size(), val.c_str(), val.size()).code();
    std::string hm = clx::base64::encode(hmac, 20);

    ss << "&oauth_signature=" << url_encode(hm);

    clx::https session(clx::uri::encode("api.twitter.com"), 443);
    session.post(clx::uri::encode("/oauth/access_token"), ss.str());

    std::vector<std::string> item;
    std::map<std::string, std::string> params;
    clx::split_if(session.body(), item, clx::is_any_of(LITERAL("&")));
    for (unsigned int i = 0; i < item.size(); i++) {
        std::string tok = item.at(i);
        size_t pos = tok.find_first_of("=");
        params[tok.substr(0, pos)] = clx::uri::decode(tok.substr(pos+1));
    }


    ss.str("");
    ss.clear(std::stringstream::goodbit);

    ss << "oauth_consumer_key=" << consumer_key
       << "&oauth_nonce=" << nonce
       << "&oauth_signature_method=" << "HMAC-SHA1"
       << "&oauth_timestamp=" << times
       << "&oauth_token=" << params["oauth_token"]
       << "&oauth_version=" << "1.0"
       << "&status=" << url_encode(message);

    key = consumer_secret + "&" + params["oauth_token_secret"];
    val = "POST&";

    val += url_encode("https://api.twitter.com/1/statuses/update.json");
    val += "&";
    val += url_encode(ss.str());
    hmac = (char*)clx::hmac<clx::sha1>(
            key.c_str(), key.size(), val.c_str(), val.size()).code();
    hm= clx::base64::encode(hmac, 20);

    ss << "&oauth_signature=" << url_encode(hm);

    session.post(clx::uri::encode("/1/statuses/update.json"), ss.str());
    std::cout << session.body() << std::endl;

    return 0;
}

/* vim:set et sw=4: */
URLまわりと、POSTパラメータ構築に便利なのがあっても良いかなと思った。あーそれboost::asioでも言えるか。
Posted at by