mixiの最新情報をチェックしたり、書き込んだりするスクリプト。

衝動的に作ってみた。

mixi

とかやると、最新情報が表示されて、

mixi ファイル名

とかやると、そのファイルの中身をmixiに書き込みますよ。

#!/usr/bin/perl
use strict;
use warnings;

use Jcode;
use LWP::UserAgent;
use HTTP::Cookies;
use URI;

$| = 1;

# formを含んだhtmlをgetするためのurl。
my $login_url = 'https://mixi.jp';
# ホームのURL。
my $home_url = 'http://mixi.jp/home.pl';
# 日記入力フォームのurl
my $add_form_url = 'http://mixi.jp/add_diary.pl';

# データの保存先ディレクトリ。
my $data_dir = './';

# cookieの保存先。
my $cookie_file = $data_dir . 'cookies.txt';

# 待ち時間。
my $sleep_time = 1;

# 文字コード。
my $mixi_code = 'euc';  # あっち側の文字コード。
my($term_code)= getcode( 'こっち側' ); # こっち側の文字コード。

# w3mを使うか。
my $use_w3m = 1;
my $w3m_command = "w3m -I $mixi_code -O $term_code -dump -T text/html";

# idとパス。
my $id;
my $pass;

unless( $id ){
    print "id : ";
    $id = <STDIN>;
}
unless( $pass ){
    print "pass : ";
    $pass = <STDIN>;
}

# mixiの文字コードに変更。
my ${oshirase} = jcode('お知らせ')->$mixi_code;
my ${kanryoushimashita} = jcode('作成が完了しました。')->$mixi_code;
my ${ikanonaiyou} = jcode('以下の内容で作成します。よろしいですか?')->$mixi_code;
my ${sakuseigakannryou} = jcode('作成が完了しました。')->$mixi_code;

##################################################

# UA準備。
my $ua = new LWP::UserAgent;
# $ua->cookie_jar( new HTTP::Cookies );
$ua->cookie_jar( HTTP::Cookies->new( file => $cookie_file,
				     autosave => 1,
				     ignore_discard => 1,
				     ) );

# LWP::UserAgentのProxyを使うか、Crypt::SSLeayのProxyを使うか。
# JEDのproxyは、LWP::UserAgentのプロキシサポートだとうまくいかないようだ。
my $use_LWP_proxy_http = 1; # よく考えたら、Crypt::SSLeayは、httpsだけだ。
my $use_LWP_proxy_https = 0;
if( $use_LWP_proxy_http ){
    $ua->proxy( 'http', $ENV{http_proxy} ) if $ENV{http_proxy};
}else{
}
if( $use_LWP_proxy_https ){
    $ua->proxy( 'https', $ENV{http_proxy} ) if $ENV{http_proxy};
}else{
    $ENV{https_proxy} = $ENV{http_proxy} if $ENV{http_proxy};
}
# $ua->env_proxy;
# LWP::UserAgentのproxyの設定を表示。
# print 'http  => ', $ua->proxy( 'http' ), "\n";
# print 'https => ', $ua->proxy( 'https' ), "\n";

if( @ARGV == 0 ){
    &main_get_login_page( $ua );
}elsif( $ARGV[0] =~ /home/i ){
    &main_home( $ua );
}elsif( $ARGV[0] =~ /add/i  and  2 == @ARGV ){
    &main_get_add_form( $ua, $ARGV[1] );
}elsif( 1 == @ARGV and -e $ARGV[0] ){ # 引数1個の最後に。
    &main_get_add_form( $ua, $ARGV[0] );
}

sub main_get_login_page{  # ログインページを取ってくる。
    my( $ua ) = @_;

    my $res = &get( $ua, $login_url );

    # ほんとにmixiのログインページか、軽くチェック。
    if( not &is_mixi_login( $res->content ) ){
	if( &is_mixi_home( $res->content ) ){
	    print STDERR "ログイン済み。\n";
	    &main_home( $ua, $res );
	    exit;
	}
	print STDERR $res->content;
	die "ログインページじゃない。";
    }

    &main_login( $ua, $res );
}
sub main_login{  # ログインする。
    my( $ua, $res ) = @_;

    my %form = &get_form_values( $res->content );

    die 'postじゃないよ。' unless $form{method} =~ /post/i;

    my $action_url = URI->new_abs( $form{action}, $res->base );
    delete $form{method};
    delete $form{action};

    # IDとパスワードの挿入。
    $form{email} = $id;
    $form{password} = $pass;

    $res = &get( $ua, $action_url, %form );

    if( not &is_mixi_login_ok( $res->content ) ){
	die "ログイン失敗。\n";
    }

    &main_home( $ua, $res );
}
sub main_home{
    my( $ua, $res ) = @_; # $uaは必須。

    unless( defined $res and &is_mixi_home( $res->content ) ){
	$res = &get( $ua, $home_url );
	die 'ホームが表示できない?' unless &is_mixi_home( $res->content );
    }
    my( $info ) = ( $res->content =~ m{<!-- start: ${oshirase} -->(.*)<!-- end: ${oshirase} -->}s );
    die 'お知らせが切り取れんかった。' unless $info;
    &print_html( $info );

    my( $update ) = ( $res->content =~ m{<!-- start: update information -->(.*)<!-- end: update information -->}s );
    die '最新情報が切り取れんかった。' unless $update;
    &print_html( $update );
}

sub main_get_add_form{
    my( $ua, $inputfile ) = @_;

    my $res = &get( $ua, $add_form_url );

    # ほんとに日記の入力ページか、軽くチェック。
    if( not &is_mixi_add_form( $res->content ) ){
	if( &is_mixi_login( $res->content ) ){
	    print STDERR "未ログインです。\n";
	    exit;
	}
	print STDERR $res->content;
	die "入力ページじゃない。";
    }

    &main_add_diary( $ua, $res, $inputfile );
}
sub main_add_diary{
    my( $ua, $res, $inputfile ) = @_;

    my %form = &get_form_values( $res->content );

    die 'postじゃないよ。' unless $form{method} =~ /post/i;

    my $action_url = URI->new_abs( $form{action}, $res->base );
    delete $form{method};
    delete $form{action};
    delete $form{photo1};
    delete $form{photo2};
    delete $form{photo3};

    open DATA, $inputfile
	or die $inputfile . 'が開けない。';
    $form{diary_title} = <DATA>;
    die '2行目は、空行で。' unless <DATA> =~ /^$/;
    $form{diary_body} = '';
    while( <DATA> ){
	$form{diary_body} .= $_;
    }
    close DATA;

    # プレビュー。
    $res = &get( $ua, $action_url, %form );
    # プレビューページに行けてるかチェック。
    if( not &is_mixi_preview( $res->content ) ){
	if( &is_mixi_alert( $res->content ) ){
	    my( $alert ) = ( $res->content =~ m{<!-- start: alert -->(.*)<!-- end: alert -->}s );
	    &print_html( $alert );
	    die '入力失敗。';
	}
	die '入力失敗。';
    }

    %form = &get_form_values( $res->content );

    die 'postじゃないよ。' unless $form{method} =~ /post/i;

    $action_url = URI->new_abs( $form{action}, $res->base );
    delete $form{method};
    delete $form{action};

    # 書き込み。
    $res = &get( $ua, $action_url, %form );
    # 書き込みチェック。
    if( &is_mixi_add_success( $res->content ) ){
	my( $message ) = ( $res->content =~ m|(<b>${kanryoushimashita}.*?</b>)| );
	&print_html( $message );
	exit;
    }else{
	die '書き込み失敗?';
    }
}

use URI::Escape;
sub get {
    my( $ua, $url, %form ) = @_;

    my $req;
    if( %form ){ # POST
	$req = HTTP::Request->new( POST => $url );
	$req->content_type( 'application/x-www-form-urlencoded' );
	$req->content( join '&', map { uri_escape( $_ ) . '=' . uri_escape( $form{ $_ } ) } keys %form );
#	$req->content( join '&', map { enc( $_ ) . '=' . enc( $form{ $_ } ) } keys %form );
	sub enc{
	    my( $txt ) = @_;
	    return join '', map { sprintf "%%%02X", $_ } unpack( "C*", $txt );
	}
    }else{ # GET
	$req = HTTP::Request->new( GET => $url );
    }

    print STDERR '受信中(', $req->uri, ')';
    sleep $sleep_time;
    my $res = $ua->request( $req );
    print STDERR "。\n";

    die $res->error_as_HTML if $res->is_error;

    return $res;
}

sub get_form_values{
    my( $content ) = @_;
    my %form_values;

    my $tag;
    ( $tag, $content ) = ( $content =~ m|(<form[^>]*>)(.*)|is );
    $form_values{ action } = &get_value( $tag, 'action' );
    $form_values{ method } = &get_value( $tag, 'method' );
    sub get_value{
	my( $tag, $name ) = @_;
	my $value;
	if( $tag =~ /${name}=([\'\"])/i ){ #"
	    my $quot = $1;
	    ( $value ) = ( $tag =~ /${name}=${quot}(.*?)${quot}/si );
	}else{
	    ( $value ) = ( $tag =~ /${name}=(.*?)[\s>]/si );
	}
	return $value;
    }

    while( $content =~ m{(<(input|textarea|/form)[^>]*>)}gis ){
	$tag = $1;

	last if $tag =~ m{</form>}i;
	my $name  = &get_value( $tag, 'name' );
	next if not defined $name;
	my $value = &get_value( $tag, 'value' );
	$value = '' if not defined $value;
	$form_values{ $name } = $value;
    }

    return %form_values;
}

sub print_html{ # html(の断片)を、見やすく表示。
    my( $html ) = @_;

    if( $use_w3m ){ #w3m使用。
	open my $w3m, "| ${w3m_command}";
	print $w3m $html;
	close $w3m;
    }else{ # w3m不使用。
	$html =~ s/<[^>]*>//g;
	$html =~ s/\n+/<br>/g;
	$html =~ s/\s+/ /g;
	$html =~ s/<br>/\n/g;
	$html =~ s/[\n ]+/\n/g;
	print jcode("\n$html\n")->sjis;
    }
}

sub is_mixi_login{ # ログインページかどうか。
    local( $_ ) = @_;
    return 0 unless /loginbtn.gif/;
    return 1;
}
sub is_mixi_login_ok{ # ログインに成功したかどうか。
    return 0 unless @_;
    return not &is_mixi_login_fail( @_ );
}
sub is_mixi_login_fail{ # ログインに失敗したかどうか。
    local( $_ ) = @_;
    return 0 unless m|remind_password.pl|;
    return 1;
}
sub is_mixi_home{ # homeかどうか。
    local( $_ ) = @_;
    return 0 unless m|http://img.mixi.jp/img/info2.gif|;
    return 1;
}
sub is_mixi_add_form{ # 日記の入力画面かどうか。
    local( $_ ) = @_;
    return 0 unless m|<textarea name="diary_body"|;
    return 1;
}
sub is_mixi_preview{ # プレビューの画面まで行けたかどうか。
    local( $_ ) = @_;
    return 0 unless m|${ikanonaiyou}|;
    return 1;
}
sub is_mixi_alert{ # [!]の画面。
    local( $_ ) = @_;
    return 0 unless m|start: alert|;
    return 1;
}
sub is_mixi_add_success{ # 書き込みに成功したか。
    local( $_ ) = @_;
    return 0 unless m|${sakuseigakannryou}|;
    return 1;
}

http://perldoc.jp/docs/modules/Crypt-SSLeay-0.45/Crypt/SSLeay.pod にproxy関係のことがあった。
JEDだと、httpsは、Crypt::SSLeayのProxyサポートを使う必要があるらしい。httpは、LWP::UserAgentの方でやる。


基本ひきこもりなので、よその人のとこ見に行って、とかはできませんよ。ってか、それならw3mかなんかで見たほうが早いと思うし。


あー、こんなことしてる場合じゃないよなぁ。