twitterの最新のツイートをfacebookに投稿する。

twitterで指定したユーザーの最新のツイートをfacebookのウォールに投稿するPHPスクリプトを作りました。

このアプリのために作りました。

ただーし、実際にこれを動かすには、

  • facebook にアプリケーション登録をする。
  • アクセストークンを取得する。

という一見簡単そうだけど、めっちゃめんどくさい手続きが必要です。

以上が完了したら、以下のスクリプトの”YOUR …”の部分を修正して Cron でぶん回してください。

twitterのユーザー名は、コマンドライン引数で渡すようになっています。

あと、require してるファイルは facebook のPHP 用の SDK です。

Cronには、以下のように渡したほうが幸せになれると思います。

*/10 * * * * export LANG=ja_JP.UTF-8; /usr/bin/php /path/to/cron.php twitter_user

oEmbed Tweet 0.22

以下のプラグインをアップデートしました。

http://firegoby.theta.ne.jp/wp/oembedtweet

更新内容

  • Twitter APIから取得したデータをpost_meta(カスタムフィールドと同じ場所)に保存するように変更しました。(既存のデータは自動的に引っ越します。)

おまけ

本文とは関係ありませんが(笑)

日本終了のお知らせ笑 QT @exitelecaster: 【緊急速報】 http://twitpic.com/46rgau

oEmbed Tweet 0.1

ツイートをURLをコピペするだけで引用できるプラグインを作りました。

firegoby » oEmbed Tweet – 記事内にURLをコピペするだけでツイートを挿入

以下は、つくろうと思った時から、完成するまでの私のハマリっぷりです。

あっ、ナイスアイディア思いついた!
無理か。。。意外。

記事のID等を指定して、ツイートを取得するAPIがドキュメントの中に見つからなかったので、一度あきらめかけました。

やっぱできるじゃん。しつこくしらべてよかった。

が、oembed tweet wordpressってGoogle先生に聞いたら、海外のサイトで参考になりそうなものを見つけました。

これをやるWordPressプラグインを作る。今日作る。 ”How to Embed a Tweet in WordPress: a Complete oEmbed Tutorial « planetOzh” http://bit.ly/fDSr7h

できるじゃん、と確信。

脳ミソがJavaScriptな頭から抜け出せない

しばらくJavaScripterだったので、PHPが途中からJavaScriptになったりして。。。

しんど!ロジックはすぐだったんだけど。。。

このあたりで、URLを取得してAPIたたいてJSON取るとこまで出来ましたが、CSSとかHTMLが意外とめんどくさい。すぐってかいてあるけど、実際には全然すぐじゃないし。。。

うー、wp_autopめ。。。

なぜか変なスペースが入ったりするので?と思ったところ、WordPressの悪名高きwp_autopフィルターに引っかかってHTMLが壊れていることを発見。

いったん、ショートコードをかませばwp_autopは怖くないというハックを発見した。

URLを一度ショートコードに変換すれば、wp_autopに引っかからないことに気づいて、解決。

WordPressの本家のプラグインレポジトリのディレクトリ名にハイフォンが入るのは、なんか気持ち悪い。

WordPress本家のプラグインレポジトリからサブバージョンのディレクトリがもらえました。

新作のWordPressプラグインをコミットした。英語はダメダメなのでツッコミ歓迎。 twitter謹製のblackbird pieみたいなプラグインだけど、こちらはスタイルやHTMLをテーマファイル内でカスタマイズできる。 http://t.co/Ka56nqh

めでたく公開。

って感じです。

Backtweets Notifier for Chrome 0.2

閲覧中のサイトのTwitterのつぶやき件数をツールバー上に表示するChrome用拡張機能「Backtweets Notifier」をアップデートしました。

更新内容

  • アイコンをクリックしてbacktweets.comを開く際に新しいタブで開くかどうかを選択できるようにしました。
  • SSLページやクエリー付きのページなどのツイート件数を自動的にチェックするかどうかを、選択できるようになりました。

PerlでTwitterのキーワード検索&リツイート(oAuth編)

先日、twitter API の仕様が変更になって、基本認証が使えなくなった。

そのため、以下の記事のスクリプトが動かなくなった。

そこで、oAuthに対応したものをご紹介。

目的

Twitterの検索メモに登録されたキーワードのリストを取得して、タイムラインを検索し、マッチしたものを公式リツイートする。

  • 自分自身の投稿はリツイートしない。
  • RTや@が含まれる投稿はリツイートしない。
  • すでにリツイートした投稿はリツイートしない。
  • ログに保存されたIDより古い投稿はリツイートしない。

アプリケーション登録申請

oAuth認証を行うには以下の情報が必要なので、アプリケーション登録申請を行う。

  • Consumer key
  • Consumer secret
  • Access Token
  • Access Token Secret

登録申請の手順は以下のサイトが分かりやすかった。

実用! PerlでコマンドラインからTwitter投稿(OAuth対応) – perl-mongers.org

ソース

以下のような感じ。
13行目〜17行目を書き換えること。

あとは、Cron等に登録して使用する。

#!/opt/local/bin/perl -wT

#binmode(STDOUT, ":utf8");

use strict;
use Encode;
use Net::Twitter;
use URI::Escape;
use LWP::Simple;
use XML::DOM;

# Config
my $user    = ''; # Twitter のユーザー名
my $key     = ''; # Consumer key
my $secret  = ''; # Consumer secret
my $token   = ''; # Access Token
my $tsecret = ''; # Access Token Secret

my $lang         = 'ja';
my $api          = 'http://search.twitter.com/search.atom';

# do not need to edit
my $max_length      = 140;
my $home;
if (-d $ENV{'HOME'} && $ENV{'HOME'} =~ /^(\/.+)$/) {
    $home = $1;
}
my $log = $home.'/.twitbot.txt'; # 最新のIDを保存して次回以降はこのID以下は無視
if (!-e $log) {
    open (OUT, ">", $log);
    close(OUT);
}

my $max_id = 0;
open (IN, "<", $log);
my $n = <IN>;
if ($n) {
    chomp $n;
    $max_id = $n;
}
close(IN);

# connect to twitter
my $twt = Net::Twitter->new(
    traits => [qw/API::REST OAuth WrapError/],
    consumer_key    => $key,
    consumer_secret => $secret
);

$twt->access_token       ($token);
$twt->access_token_secret($tsecret);

my $sch = $twt->saved_searches();
my %posted;
my $since = $max_id;
foreach (@$sch) {
    my $k = uri_escape_utf8($_->{query});
    my $url = $api.'?show_user=true&q='.$k.'&lang='.$lang;
    my $atom = get($url);
    my $parser = new XML::DOM::Parser;
    my $doc = $parser->parse ($atom);
    my $nodes = $doc->getElementsByTagName ("entry");
    for (my $i=0; $i<$nodes->getLength; $i++) {
        my $node = $nodes->item($i);
        my $txt = getvalue($node, 'title');
        my $id = getvalue($node, 'id');
        if ($id =~ /([0-9]+)$/) {
            $id = $1;
        } else {
            die ('can not get id');
        }
        if ($max_id < $id) {
            $max_id = $id;
        }
        # 以下に該当するものはRTしない
        # 自分の投稿
        # RT(スペース)が含まれる投稿
        # @が含まれる投稿
        # すでにRTした投稿
        # ログに保存されたIDより古い投稿
        if ($txt !~ /^$twitter_user/ && $txt !~ /RT\s/ && $txt !~ /\@/ && !$posted{$id} && $since < $id) {
            $posted{$id} = 1; # 重複投稿しないためのフラグ
            eval {$twt->retweet($id)};
            if ($@) {
                warn "update failed because: ".$@."\n";
            }
        }
    }
}

open (OUT, ">", $log);
print OUT $max_id;
close(OUT);

exit;

sub getvalue {
    my $node = shift @_;
    my $tag = shift @_;
    my $n = $node->getElementsByTagName($tag)->item(0);
    return $n->getFirstChild->getNodeValue;
}

参考

Perlでキーワード検索&公式リツイートを行うbot

ご注意!

ここでご紹介しているスクリプトは、Twitter API の仕様変更により現在は動作しません!(2010/09/06)

以前に書いた記事「キーワード検索したつぶやきをRTするTwitter用bot」で、特定のキーワードにマッチする記事をRTするスクリプトを紹介したが、その後、みなさん御存知の通りTwitterには公式リツイートという仕組みが導入された。

Twitterブログ: リツイート機能を公開しました

というわけで、以前の記事のスクリプトを以下のように修正することで、公式なリツイートに対応できる。

#!/opt/local/bin/perl -wT

#binmode(STDOUT, ":utf8");

use strict;
use Encode;
use Net::Twitter;
use URI::Escape;
use LWP::Simple;
use XML::DOM;

# Config
my $twitter_user = ''; # twitterユーザー名
my $twitter_pass = ''; # twitterパスワード
my $lang         = 'ja';
my $api          = 'http://search.twitter.com/search.atom';

# do not need to edit
my $twt_clientname  = 'twitbot';
my $twt_clientver   = '0.1';
my $twt_clienturl   = 'http://www.theta.ne.jp/';
my $max_length      = 140;
my $home;
if (-d $ENV{'HOME'} && $ENV{'HOME'} =~ /^(\/.+)$/) {
    $home = $1;
}
my $log = $home.'/.twitbot.txt'; # 最新のIDを保存して次回以降はこのID以下は無視
if (!-e $log) {
    open (OUT, ">", $log);
    close(OUT);
}

my $max_id = 0;
open (IN, "<", $log);
my $n = <IN>;
if ($n) {
    chomp $n;
    $max_id = $n;
}
close(IN);

# connect to twitter
my $twt = Net::Twitter->new(
    username    => $twitter_user,
    password    => $twitter_pass,
    clientname  => $twt_clientname,
    clienturl   => $twt_clienturl,
    useragent   => $twt_clientname,
    source   => $twt_clientname,
    clientver   => $twt_clientver,
);

my $sch = $twt->saved_searches();
my %posted;
my $since = $max_id;
foreach (@$sch) {
    my $k = uri_escape_utf8($_->{query});
    my $url = $api.'?show_user=true&q='.$k.'&lang='.$lang;
    my $atom = get($url);
    my $parser = new XML::DOM::Parser;
    my $doc = $parser->parse ($atom);
    my $nodes = $doc->getElementsByTagName ("entry");
    for (my $i=0; $i<$nodes->getLength; $i++) {
        my $node = $nodes->item($i);
        my $txt = getvalue($node, 'title');
        my $id = getvalue($node, 'id');
        if ($id =~ /([0-9]+)$/) {
            $id = $1;
        } else {
            die ('can not get id');
        }
        if ($max_id < $id) {
            $max_id = $id;
        }
        # 以下に該当するものはRTしない
        # 自分の投稿
        # RT(スペース)が含まれる投稿
        # @が含まれる投稿
        # すでにRTした投稿
        # ログに保存されたIDより古い投稿
        if ($txt !~ /^$twitter_user/ && $txt !~ /RT\s/ && $txt !~ /\@/ && !$posted{$id} && $since < $id) {
            $posted{$id} = 1; # 重複投稿しないためのフラグ
#            my $post = 'RT @'.$txt;
#            $post = decode_utf8($post);
#            if (length($post) > $max_length) {
#                $post = substr($post, 0, 137)."...";
#            }
#            print $post."\n";
#            print "----\n";
            eval {$twt->retweet($id)};
            if ($@) {
                warn "update failed because: ".$@."\n";
            }
        }
    }
}

open (OUT, ">", $log);
print OUT $max_id;
close(OUT);

exit;

sub getvalue {
    my $node = shift @_;
    my $tag = shift @_;
    my $n = $node->getElementsByTagName($tag)->item(0);
    return $n->getFirstChild->getNodeValue;
}

perlでbit.lyのapiを使ってurlを短縮

GoogleがURLの短縮を始めたとのことで、もしかしたら窮地に陥っているかもしれませんが、個人的にはURLの短縮なんてGoogleさんがやらんでも!って感じでbit.lyさんを応援したい今日この頃。

というわけで、perlを使ったbit.ly APIによるURL短縮。
別のシステムに組み込んだものを、この記事用に書き換えたので、もし動かなかったらごめんなさい!

#!/usr/bin/perl -wT

use strict;
use warnings;
use JSON::XS;
use LWP::Simple;
use URI::Escape;

my $baseurl = shift @ARGV; # 短縮したいURL

my $bituser = ''; # bit.lyのユーザー名
my $bitkey = ''; # bit.lyのapiキー
my $apiurl = 'http://api.bit.ly/shorten?version=2.0.1&login=%s&apiKey=%s&longUrl=%s';
my $bitly = sprintf($apiurl,$bituser, $bitkey, $baseurl);

my $res = decode_json(get(uri_escape($bitly)));
my $shortURL = $res->{results}->{$baseurl}->{shortUrl};

print $shortURL;
exit;

twitterでフォローのお礼&フォロー返し

ご注意!

ここでご紹介しているスクリプトは、Twitter API の仕様変更により現在は動作しません!(2010/09/06)

twitter apiでフォローされたらそのメールをsmtpサーバーでキックして、フォロー返し&お礼を送信するbotを作成した。

当初は、cronでやっていたのだが、フォロワー一覧を取得するためのAPIがとても不安定だったのと、複数アカウントに対応するのに負荷を軽減したかったので、メールでキックする方法に変更した。

ちなみに、昨日の時点ではGoogle app engineを使おうと思ったのだが、あっさり気が変わった。

smtpサーバーの設定

今回はpostfixを使用した。
/etc/postfix/main.cfなどで正規表現を使ったエイリアスを設定するのがポイント。

詳細は省きます。

ソース(Perl)

標準入力から渡されたメールをパースして、ユーザー名、フォロワーなどを取得して、フォロー返し&お礼を送信する。

今回はフォローメール以外は何もしていないが、標準入力で渡されたメールをsendmailにパイプで渡せば転送できるような気がする。(スパムフィルターには引っかかるかも。)

複数アカウントに対応していますので、user01という記述がある当たりを修正してください。

あと、スパマーをフォローしないようにタイムゾーンでチェックしていますが、これでは不十分だと思いますので、今後工夫が必要ではないかと思います。

#!/usr/bin/perl -wT

binmode(STDOUT, ":utf8");

use strict;
use warnings;
use Encode;
use Net::Twitter;
use MIME::Parser;

# Config
my %user;
$user{'user01'} = 'pass';
$user{'user02'} = 'pass';
$user{'user03'} = 'pass';

my $tmp = '/tmp';
my $msg = '@%s フォローありがとうございます!よろしくお願いいたします。';
my $twt_clientname  = 'thanksbot';
my $twt_clientver   = '0.1';
my $twt_clienturl   = 'http://www.theta.ne.jp/';
# end config

my @mail = <STDIN>;
my $parser = new MIME::Parser;
$parser->output_dir($tmp);
my $entity = $parser->parse_data(join("", @mail));
my $header = $entity->head;

# get the $uid
my $uid = $header->get("X-Twitterrecipientscreenname");
chomp $uid;

# get the mail type
my $type = $header->get('X-TwitterEmailType');
chomp $type;
if ($type ne 'is_following') {
  exit;
}

# get the sender
my $sender = $header->get('X-Twittersenderscreenname');
chomp $sender;

my $twt = Net::Twitter->new(
   username    => $uid,
   password    => $user{$uid},
   clientname  => $twt_clientname,
   clienturl   => $twt_clienturl,
   useragent   => $twt_clientname,
   source   => $twt_clientname,
   clientver   => $twt_clientver,
);

my $stat = $twt->show_user($sender);
my %st = %$stat;
if ($st{'time_zone'} eq 'Tokyo' || $st{'time_zone'} eq 'Osaka') {
   my $exts = $twt->friendship_exists($uid, $sender);
   if (!$exts) {
       my $post = sprintf($msg, $sender);
       $post = decode_utf8($post);
       if ($twt->create_friend($sender)) {
           $twt->update($post);
       }
   }
}

exit;

もう少しエラー処理とかした方がいいんでしょうけどね。ご愛嬌です。

Google app engineの覚え書き

今回は純粋に個人的な覚書です。

やりたいことは、twitterから送信される「フォローされましたよ」メールを受信して、フォローとお礼をかえすというもので、現状はCronを使用しているのだが、あまりにもtwitterのapiが頻繁にエラーを返すので、その対策としてアプローチを変えようと思った次第。

つまり、メールを受信してキックするようにすれば無駄なapiへのアクセスが減るのではないかと。。。

以下は、関連する情報のURLをだらだらとメモっているだけです。

twitterからのメールのメールヘッダ

How should my bot follow users that follow it or inspect direct messages?

By default, Twitter sends an email the first time a user follows you or sends you a direct message.  For your bot-building pleasure, we’ve added the following headers to those emails

  • X-TwitterEmailType - will be ‘is_following’ or ‘direct_message’
  • X-TwitterCreatedAt - ex: Thu Aug 07 15:17:15 -0700 2008
  • X-TwitterSenderScreenName - ex: ‘bob’
  • X-TwitterSenderName - ex: ‘Bob Smith’
  • X-TwitterSenderID - ex: 12345
  • X-TwitterRecipientScreenName - ex: ‘john’
  • X-TwitterRecipientName - ex: ‘John Doe’
  • X-TwitterRecipientID - ex: 67890
  • X-TwitterDirectMessageID - ex: 2346346

キーワード検索したつぶやきをRTするTwitter用bot

ご注意!

ここでご紹介しているスクリプトは、Twitter API の仕様変更により現在は動作しません!(2010/09/06)

2010/06/30
このページの情報はもう古いです。
公式リツイートを使いましょう。

twitterのAPIを使用してキーワードにマッチしたつぶやきをRTするbotをperlでつくりました。

出来上がってみれば100行ほどなのですが、半日近くかかってしまいました。

動作環境

以下のPerlモジュールが必要です。

  • Net::Twitter
  • URI::Escape
  • LWP::Simple
  • XML::DOM

Net::Twitterをインストールすれば後は勝手についてくる気もします。
私自身はMacOS 10.6 Snow Leopardで開発して動かしています。

仕様

Twitterサイト上で保存した「検索メモ」を取得して、記事を検索し一致した記事の先頭に「RT @ユーザー名: 」をつけて投稿し直します。

検索はNet::Twiterでもできるのですが、デバッグがやりにくかったので、LWPとDOMを使用してAtomフィードをパースしてます。

記事のIDの最大値をログに保存するようになっており、保存されたIDより古い記事はRTしません。
このログはホームディレクトリ内に「.twitbot.txt」というファイル名で保存されます。

使い方

Cronに登録してご利用ください。

なぜかlang=jaをつけると一定時間以上前の古い投稿が検索されないようです。
また、キーワードごとに15件までしか取得しておりませんので、キーワードによっては、Cronのインターバルを短かくしないと取りこぼしが生じると思います。

ソース

以下に直接ソースを貼りつけておきます。

#!/opt/local/bin/perl -wT

#binmode(STDOUT, ":utf8");

use strict;
use Encode;
use Net::Twitter;
use URI::Escape;
use LWP::Simple;
use XML::DOM;

# Config
my $twitter_user = ''; # twitterユーザー名
my $twitter_pass = ''; # twitterパスワード
my $lang         = 'ja';
my $api          = 'http://search.twitter.com/search.atom';

# do not need to edit
my $twt_clientname  = 'twitbot';
my $twt_clientver   = '0.1';
my $twt_clienturl   = 'http://www.theta.ne.jp/';
my $max_length      = 140;
my $home;
if (-d $ENV{'HOME'} && $ENV{'HOME'} =~ /^(\/.+)$/) {
    $home = $1;
}
my $log = $home.'/.twitbot.txt'; # 最新のIDを保存して次回以降はこのID以下は無視
if (!-e $log) {
    open (OUT, ">$log");
    close(OUT);
}

my $max_id = 0;
open (IN, $log);
my $n = <IN>;
if ($n) {
    chomp $n;
    $max_id = $n;
}
close(IN);

# connect to twitter
my $twt = Net::Twitter->new(
    username    => $twitter_user,
    password    => $twitter_pass,
    clientname  => $twt_clientname,
    clienturl   => $twt_clienturl,
    useragent   => $twt_clientname,
    source   => $twt_clientname,
    clientver   => $twt_clientver,
);

my $sch = $twt->saved_searches();
my %posted;
my $since = $max_id;
foreach (@$sch) {
    my $k = uri_escape_utf8($_->{query});
    my $url = $api.'?show_user=true&q='.$k.'&lang='.$lang;
    my $atom = get($url);
    my $parser = new XML::DOM::Parser;
    my $doc = $parser->parse ($atom);
    my $nodes = $doc->getElementsByTagName ("entry");
    for (my $i=0; $i<$nodes->getLength; $i++) {
        my $node = $nodes->item($i);
        my $txt = getvalue($node, 'title');
        my $id = getvalue($node, 'id');
        if ($id =~ /([0-9]+)$/) {
            $id = $1;
        } else {
            die ('can not get id');
        }
        if ($max_id < $id) {
            $max_id = $id;
        }
        # 以下に該当するものはRTしない
        # 自分の投稿
        # RT(スペース)が含まれる投稿
        # @が含まれる投稿
        # すでにRTした投稿
        # ログに保存されたIDより古い投稿
        if ($txt !~ /^$twitter_user/ && $txt !~ /RT\s/ && $txt !~ /\@/ && !$posted{$id} && $since < $id) {
            $posted{$id} = 1; # 重複投稿しないためのフラグ
            my $post = 'RT @'.$txt;
            $post = decode_utf8($post);
            if (length($post) > $max_length) {
                $post = substr($post, 0, 137)."...";
            }
#            print $post."\n";
#            print "----\n";
            eval {$twt->update($post)};
            if ($@) {
                warn "update failed because: ".$@."\n";
            }
        }
    }
}

open (OUT, "> $log");
print OUT $max_id;
close(OUT);

exit;

sub getvalue {
    my $node = shift @_;
    my $tag = shift @_;
    my $n = $node->getElementsByTagName($tag)->item(0);
    return $n->getFirstChild->getNodeValue;
}