Twitterの1日分のタイムラインをKindleで読む

この記事は2年以上前に書いたものです。
そのため情報が古い可能性があります。ご了承ください。m(_ _)m

時間かけて探せば良い感じのが有りそうだけど、すぐには見つからなかったのでperlで書いてみた。

仕様的なもの。

  • 常駐せずに、10分おきくらいにcronで動かす。
  • 1日分のtweetは、$timeline_fileに溜め込む。
  • 1日分の切り替えは、Log::Dispatch::FileRotateに任せる。
    (微妙にずれる場合もあるけど、細かいことは(゚ε゚)キニシナイ!!)
  • $timeline_fileへの出力は、Log::Log4perl の INFOで。
  • kindleへ送信するタイミングは、$timeline_fileのサイズが小さくなったら。
    (FileRotateで切り替わったらファイルサイズが小さくなるので、それ契機)
  • kindleへの送信するデータは、htmlファイル。
  • 送信先は、@kindle.com or @free.kindle.com。
    (自動的に変換して、端末へ配信してくれる)

gettimeline.pl(gettimeline.zip)


#!/usr/bin/perl

use 5.010;
use strict;
use warnings;
use Encode;
use DateTime;
use DateTime::TimeZone;
use DateTime::Format::DateParse;
use Data::Dumper;
use HTML::Entities;
use MIME::Lite;

use Net::Twitter;
use Log::Log4perl qw(:easy);

# Twitter key and token
my $consumer_key = 'xxxxxxxxxxxxxxxxxxxxx';
my $consumer_secret = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy';
my $token = 'nnnnnnnnnn-zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz';
my $token_secret = 'sssssssssssssssssssssssssssssssssssssssssss';

# log
my $logfile = '/home/user/tool/twitter.log';
my $timeline_file = '/home/user/tool/timeline.log';

# mail
my $mailfrom = 'xxxx@xxx.xxx';
my $mailto = 'yyyyy@free.kindle.com';

# title
my $title_fmt = 'Twitter_Timeline_%Y-%m-%d(%a)';


my $tid_prefix = '__TID__:';
my $tz = DateTime::TimeZone->new(name => 'local');

&loginit;

&main;

exit;

sub main{
    my $filesize = -s $timeline_file;

    &gettl;

    if($filesize > -s $timeline_file){
	say 'send to kindle';
	&send2kindle;
    }
}

sub send2kindle{
    my $dt = DateTime->now(time_zone => $tz);
    $dt->subtract(days => 1);
    my $title = $dt->strftime($title_fmt);
    my $fname = $title . '.html';

    my $timeline_file1 = $timeline_file . '.1';
    open my $fh, '<', "$timeline_file1"
	or die "failed to open file: $!";
    my $rawdata = do { local $/; <$fh> };

    my @rawdata_array = grep(!/^$tid_prefix \d+$/, split(/\n/, $rawdata));


    my $htmldata = <<_HEADER_;
<html>
  <head>
    <title>$title</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
<body>
  <h3>$title</h3>
<pre>
_HEADER_

    $htmldata .= Encode::decode_utf8(join("\n", @rawdata_array));

    $htmldata .= <<_FOOTER_;
</pre>
</body>
</html>
_FOOTER_

    my $subject = $title;

    my $msg = MIME::Lite->new(
	From => "$mailfrom",
	To => "$mailto",
	Subject => "$subject",
	Type => 'multipart/mixed'
    );
    $msg->attach(
	Type => 'text/plain; charset="iso-2022-jp"',
	Data => "no body"
	);
    $msg->attach(
	Type => 'text/html',
	Data => "$htmldata",
	Filename => "$fname",
	Disposition => 'attachment'
	);
    $msg->attr('content-type.charset' => 'UTF-8');

#    WARN Dumper($msg);
#    WARN $htmldata;

    $msg->send();

}

sub gettl{
    my $nt = Net::Twitter->new(
	traits => [qw/OAuth API::REST/],
	consumer_key => $consumer_key,
	consumer_secret => $consumer_secret,
	access_token => $token,
	access_token_secret => $token_secret,
	);

#    WARN "TWEET: " . $tweet;

    my @tl = $nt->home_timeline({count => 200});

    my @rtl = reverse(@{$tl[0]});

    my $tail_cmd = "tail -2 $timeline_file";
    my $grep_cmd = "grep -e \"$tid_prefix [0-9]\\+\"";
    my $sed_cmd = "sed -r \"s/[^0-9]+([0-9]+)/\\1/\"";
    my $lastid = `$tail_cmd | $grep_cmd | $sed_cmd`;
    chomp $lastid;

    if($lastid){
	for my $i (0 .. $#rtl) {
	    if($lastid eq $rtl[$i]->{id}){
#say "0 -> " . $i . " ($#rtl)";
		splice(@rtl, 0, $i+1);
#say "  -> " . $#rtl;
		last;
	    }
	}
    }

    my $id = '';
    foreach my $t(@rtl){
	WARN $t->{id};
	$id = $t->{id};
	my $s = &mklinktag($t->{text});
	my $dt = DateTime::Format::DateParse->parse_datetime($t->{created_at});
	$dt->set_time_zone($tz);

	INFO encode_utf8($t->{user}{name}) .
	    ' (<a href="https://twitter.com/' . $t->{user}{screen_name} . '">' .
	    '@' . $t->{user}{screen_name} . '</a>)' . "\n" .
	    encode_utf8($s) . "\n" .
	    $dt->strftime('(%Y/%m/%d %H:%M:%S)') . "\n";
#	WARN Dumper($t);
	WARN Dumper($s);
    }
    INFO "$tid_prefix " . $id . "\n" if $id;
}

sub mklinktag{
    my ($s) = @_;

    $s = encode_entities($s, q{&<>"'});

    $s =~ s!(https?://)([0-9a-zA-Z/\.]+)!<a href="$1$2">$2</a>!go;

    $s =~ s!@([a-zA-Z0-9_]+)!<a href="https://twitter.com/$1">\@$1</a>!go;

    $s;
}

sub loginit{
  my $logsize = 10*1024*1024;
  Log::Log4perl->init(\ qq{
    log4perl.logger = INFO, AppError, Tweet

    # filter range
    log4perl.filter.MatchRange               = Log::Log4perl::Filter::LevelRange
    log4perl.filter.MatchRange.LevelMin      = WARN
    log4perl.filter.MatchRange.LevelMax      = ERROR
    log4perl.filter.MatchRange.AcceptOnMatch = true

    # Error appender
    log4perl.appender.AppError = Log::Dispatch::FileRotate
    log4perl.appender.AppError.filename   = $logfile
    log4perl.appender.AppError.max   = 10
    log4perl.appender.AppError.autoflush   = 1
    log4perl.appender.AppError.size   = $logsize
    log4perl.appender.AppError.mode = append
    log4perl.appender.AppError.layout   = Log::Log4perl::Layout::PatternLayout
    log4perl.appender.AppError.layout.ConversionPattern = %d %02X{client} %p %F{1} %L: %m %n
    log4perl.appender.AppError.Filter   = MatchRange

    # Filter to match level WARN
    log4perl.filter.MatchInfo  = Log::Log4perl::Filter::LevelMatch
    log4perl.filter.MatchInfo.LevelToMatch  = INFO
    log4perl.filter.MatchInfo.AcceptOnMatch = true

    # Error appender
    log4perl.appender.Tweet = Log::Dispatch::FileRotate
    log4perl.appender.Tweet.filename   = $timeline_file
    log4perl.appender.Tweet.max   = 10
    log4perl.appender.Tweet.autoflush   = 1
    log4perl.appender.Tweet.DatePattern = yyyy-MM-dd
    log4perl.appender.Tweet.TZ = JST
    log4perl.appender.Tweet.mode = append
    log4perl.appender.Tweet.layout   = Log::Log4perl::Layout::PatternLayout
    log4perl.appender.Tweet.layout.ConversionPattern = %m %n
    log4perl.appender.Tweet.Filter   = MatchInfo
 });
}

↑のコードで送信されたファイルのスクリーンショットが↓。

screenshot_2013_05_19T09_28_12 0900

アイコンとかも持って来たほうが見栄えが良いんだろうけど、とりあえずシンプルなこれで使ってみる。

カテゴリー: ソフトウェア, プログラム タグ: , , , , パーマリンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です