上記の広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書く事で広告が消せます。

2ちゃんねる入門にようこそ

このブログでは、2ちゃんねると関連Web技術の紹介をしています。
初級編では、2ちゃんねるにとりあえず参加できる状態の構築。
中級編では、2ちゃんねるをより一層楽しむための知識。
仕様偏では、プログラミングなどで2ちゃんねるを利用する上で必要な知識。
をそれぞれ紹介しています。

2ちゃんねる入門偏
2ちゃんねる中級編
2ちゃんねるの仕様

Mopechan 0.33 リリース

VivaSamba24に急いで対応させたもの
239行目付近の処理がかなり適当

http://ux.getuploader.com/2chbeginner/download/12/2chbeginner_12.zip

Mopechan 0.2 リリース

MopeSample.plはモジュールの使い方のサンプルです。

MopeSample.pl

use strict;
use warnings;
#プロキシを扱うモジュール
use Mopexy;
#書き込みを扱うモジュール
use Mopechan;
#●を扱うモジュール
use Mopemaru;
#投稿先を扱うモジュール
use Mopetarget;

use constant {
COOKIE => 'tepo=don; ', #Set-Cookieに現れないが必須なクッキー
};

###プロキシ関連の処理###
my $mopexy = Mopexy->new;
#ファイルからプロキシを読み込む
#形式は IP又はホスト:ポート:プロトコル<>Cookie (Cookieはなければ空でよい)
#例 localhost:8118:http<>path="/"; domain=hideyoshi.2ch.net; port=80; expires="2010-**-** **:**:**Z"; version=0
$mopexy->loadProxy('設定/Proxy.txt');
#2ちゃんねるに書き込めそうか導通試験をする
# ・串特有のヘッダなどがあるせいで吸い込まれる
#  ・バーボン行き
#  ・そもそも折れてる
#串を除外し、IP又はホスト:ポート:プロトコル<>Cookieの形式で記録される
#スレッド数、チェック先スレッド、いくつ見つけるか、出力先ファイル名、を引数にして呼び出す
$mopexy->try2ch_thread(5, 'http://hideyoshi.2ch.net/test/read.cgi/kitchen/1269522830/', 2, 'test.txt');

###●関連の処理###
#IDとパスワードを指定
my $mopemaru = Mopemaru->new('mail@addr<>pass');
#ログインに使う串をセット(オプションなので生で認証する場合はコメントアウト)
$mopemaru->setProxy('localhost:8118:http');
#ログイン処理
$mopemaru->login;
#$mopemaru->getSidで認証したセッションIDを得る
print $mopemaru->getSid."\n";

my $mopetarget = Mopetarget->new;
#URLから板全体の書き込み先を取得
$mopetarget->addTargetFromURL('http://tsushima.2ch.net/news/');
#取得した書き込み先を連想配列で得る
#なんで連想配列なのかというと重複チェックが面倒だから
my %target = $mopetarget->getTarget;

my $mopechan = Mopechan->new;
#書き込み先を指定
$mopechan->setTarget($target{test});
#本文とメールと名前を指定
$mopechan->setMessageMailFrom( 'sagetest', 'sage', "" );
#●をセット(オプション)
$mopechan->setMaruSid( $mopemaru->getSid );
#串をセット(オプション)
$mopechan->setProxy("localhost:8118:http");
#書き込み処理。クッキーをセットしていないと書き込みは完了しない
$mopechan->write;
#クッキーをセット(一度$mopechan->writeをするとクッキーがセットされる)
#tepo=donとdomain=***.2ch.netを指定し、先程のクッキーを指定
$mopechan->setCookie(COOKIE."domain=".$mopechan->getServer.'; '.$mopechan->getCookie);
#もう一度書き込み処理
$mopechan->write;
#レスポンスを表示
print $mopechan->getResponse, "\n";


Mopechan.pm

package Mopechan;
use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request::Common qw(POST);
use HTTP::Cookies;

use constant {
WRITE_TIMEOUT => 10, #書き込みタイムアウト
WRITE_USERAGENT => 'Mozilla/4.0', #デフォルトのユーザーエージェント
COOKIE_NAME => 'Cookie.txt',
};

sub new {
my ($class) = @_;
my $self = {
Target => undef,
Message => undef,
Mail => undef,
From => undef,
WriteTimeout => WRITE_TIMEOUT,
WriteUserAgent => WRITE_USERAGENT,
Proxy => undef,
MaruSid => undef,
CookieName => COOKIE_NAME,
Cookie => undef,
BE => undef,
Response => undef,
Bbs => undef,
Server => undef,
Key => undef,
};
bless $self, $class;
return $self;
}

sub write {
my ($self) = @_;

my $ua = LWP::UserAgent->new;
$ua->agent( $self->{WriteUserAgent} );
$ua->timeout( $self->{WriteTimeout} );

my ( $server, $bbs, $key ) = split( '<>', $self->{Target} );
my %params = (
time => '111111111',
key => $key,
bbs => $bbs,
MESSAGE => $self->{Message},
mail => $self->{Mail},
FROM => $self->{From},
submit => "",
);
if ( defined( $self->{Proxy} ) ) {
my ( $proxy_host, $proxy_port, $proxy_protocol ) =
split( ':', $self->{Proxy} );
$ua->proxy( [ 'http', 'tcp' ],
"$proxy_protocol://$proxy_host:$proxy_port" );
}
if ( defined( $self->{MaruSid} ) ) {
$params{sid} = $self->{MaruSid};
}

my $req = POST( "http://$server/test/bbs.cgi", [%params] );
$req->referer("http://$server/$bbs/");
$req->header( 'Accept' => 'text/html' );
if ( defined( $self->{Cookie} ) ) {
$req->header( 'Cookie' => $self->{Cookie} );
}

#print $req->as_string;
my $res = $ua->request($req);
if ( $res->is_success ) {
my $cookie = $res->header("Set-Cookie");
if ( defined($cookie) && length($cookie) >= 5 ) {
$cookie =~ s/domain=.*[;\n]//g;
}
else {
$cookie = 0;
}
$self->{Cookie} = $cookie;
$self->{Response} = $res->content;
}
else {
$self->{Response} = $res->status_line;
}
}

sub setTarget {
my ( $self, $target ) = @_;
if ( $target =~ /^http:\/\//g ) {
$target =~
s/.*http:\/\/(.*?)\/test\/read.cgi\/(.*?)\/(\d*).*/$1<>$2<>$3/g;
my ( $server, $bbs, $key ) = split( '<>', $target );
$self->{Server} = $server;
$self->{Bbs} = $bbs;
$self->{Key} = $key;
}
$self->{Target} = $target;
}

sub setMessageMailFrom {
my ( $self, $message, $mail, $from ) = @_;
$self->{Message} = $message;
$self->{Mail} = $mail;
$self->{From} = $from;
}

sub setProxy {
my ( $self, $proxy ) = @_;
$self->{Proxy} = $proxy;
}

sub setMaruSid {
my ( $self, $marusid ) = @_;
$self->{MaruSid} = $marusid;
}

sub setCookieName {
my ( $self, $cookiename ) = @_;
$self->{CookieName} = $cookiename;
}

sub setCookie {
my ( $self, $cookie ) = @_;
$self->{Cookie} = $cookie;
}

sub setWriteTimeout {
my ( $self, $writetimeout ) = @_;
$self->{WriteTimeout} = $writetimeout;
}

sub setBE {
my ( $self, $be ) = @_;
$self->{BE} = $be;
}

sub getResponse {
my ($self) = @_;
return $self->{Response};
}

sub getCookie {
my ($self) = @_;
return $self->{Cookie};
}

sub getServer {
my ($self) = @_;
return $self->{Server};
}
1;


Mopexy.pm

package Mopexy;

use strict;
use warnings;
use Net::Ping;
use Mopechan;
use threads;
use threads::shared;

use constant {
PING_TIMEOUT => 5, #pingチェックのデフォルトのタイムアウト値
TRY2CH_TIMEOUT => 5, #2ch書き込みチェックのデフォルトのタイムアウト値
};

sub new {
my ($class) = @_;
my %proxy;
my %checked_proxy;
my $self = {
Proxy => \%proxy,
Checked_Proxy => \%checked_proxy,
CookiePath => './',
Cookie => undef,
try2ch_timeout => TRY2CH_TIMEOUT,
};
bless $self, $class;
return $self;
}

sub ping {
my ( $self, $timeout ) = @_;

#タイムアウト指定がなかったら標準のタイムアウト
$timeout = PING_TIMEOUT unless defined($timeout);

#pingが届かないものを除去
foreach my $proxy ( @{ $self->{Proxy} } ) {
my ( $host, $port ) = split( ':', $proxy );
my $pObj = Net::Ping->new("icmp");
$proxy = undef unless $pObj->ping( $host, $timeout );
$pObj->close();
}
}

sub try2ch_thread {
my ( $self, $thread, $target, $limit, $file) = @_;
unlink($file);
my %proxy;
my @proxy;
my %checked_proxy;
my @thread;
share(@proxy);
share(%checked_proxy);
%proxy = %{ $self->{Proxy} };
@proxy = keys (%proxy);
my $ref = sub {
while ( my $proxy = shift(@proxy)) {
return if scalar( keys(%checked_proxy) ) >= $limit;
return if scalar( @proxy ) <= 0;
next if length($proxy) <= 4;
my $mopechan = Mopechan->new;
$mopechan->setTarget($target);
$mopechan->setMessageMailFrom( 'test', 'sage', '' );
$mopechan->setProxy($proxy);
$mopechan->setWriteTimeout( $self->{try2ch_timeout} );
$mopechan->setCookie( $self->{Cookie} );
$mopechan->write;

#print $mopechan->getResponse . "\n";

#レスポンスに'>input TYPE=hidden NAME=FROM'があった場合、つまりレスポンスが正常に返ってきた場合
#バーボン行きや折れた串はここで弾かれる
if (
index( $mopechan->getResponse, '>input TYPE=hidden NAME=FROM' )
!= -1 )
{

#さらにレスポンスにCookieがあった場合
if ( index( $mopechan->getCookie, 'PON=' ) != -1 ) {

#使用可能なプロキシとして追加
#print "$proxy\n";
open (WRITE, ">>$file");
print WRITE $proxy.'<>'.$mopechan->getCookie."\n";
close WRITE;
$checked_proxy{$proxy} = $mopechan->getCookie;
}
}

print "残り" . scalar(@proxy) . "現在" . scalar(keys (%checked_proxy))."\n";
threads->yield();
}
};

for ( my $i = 0 ; $i < $thread ; ++$i ) {
$thread[$i] = threads->new($ref);
}
for ( my $i = 0 ; $i < $thread; ++$i ) {
$thread[$i]->join;
}
%{ $self->{Checked_Proxy} } = %checked_proxy;
}

sub getChecked_Proxy {
my ($self) = @_;
return %{ $self->{Checked_Proxy} };
}


sub getProxy {
my ($self) = @_;
return %{ $self->{Proxy} };
}

sub extract {
my ( $self, $proxys ) = @_;
my @proxy = $proxys =~ /\d*\.\d*:\d{1,5}/g;
@proxy = $proxys =~ /[a-zA-Z0-9.]*:\d{1,5}/g;
return @proxy;
}

sub writeCheckedProxy {
my ( $self, $file ) = @_;
open( WRITE, ">$file" ) or return;
foreach ( keys( %{ $self->{Checked_Proxy} } ) ) {
print WRITE $_ . '<>' . ${ $self->{Checked_Proxy} }{$_} . "\n";
}
close WRITE;
}

sub loadProxy {
my ( $self, $file ) = @_;
open( LOAD, "$file" ) or return;
foreach () {
chomp();
my ( $proxy, $cookie ) = split('<>');
if (defined($cookie) && length ($cookie) >= 5){
${ $self->{Proxy} }{$proxy} = $cookie;
}else{
${ $self->{Proxy} }{$proxy} = 0;
}
}
close LOAD;
}

sub clearProxy {
my ($self) = @_;
%{ $self->{Proxy} } = ();
}

sub clearChecked_Proxy {
my ($self) = @_;
%{ $self->{Checked_Proxy} } = ();
}
1;


Mopemaru.pm

package Mopemaru;

use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request::Common qw(POST);

use constant {
LOGIN_PROXY_TIMEOUT =>
30, #プロキシでログインする際のタイムアウト値
LOGIN_TIMEOUT => 20, #通常ログインする際のタイムアウト値
};

sub new {
my ( $class, $maru_account ) = @_;
my $self = { MaruAccount => $maru_account, Proxy => undef, Sid => undef, };
bless $self, $class;
return $self;
}

sub login {
my ($self) = @_;
my ( $id, $pw ) = split( '<>', $self->{MaruAccount} );
my $proxy = $self->{Proxy};
my $ua = LWP::UserAgent->new;
$ua->agent('DOLIB/1.00');
$ua->default_header( 'X-2ch-UA' => 'Monazilla/1.00' );

#httpsの場合LWPの標準機能ではプロキシが使えない
if ( defined($proxy) ) {
my ( $proxy_host, $proxy_port, $proxy_protocol ) =
split( ':', $self->{Proxy} );
$ENV{HTTPS_PROXY} = "$proxy_protocol://$proxy_host:$proxy_port";
$ua->proxy( [ 'http', 'tcp' ],
"$proxy_protocol://$proxy_host:$proxy_port" );
}
my $url = 'https://2chv.tora3.net/futen.cgi';
my %form = ( 'ID' => $id, 'PW' => $pw );
my $req = POST( $url, Content => "ID=$id&PW=$pw" );
my $res = $ua->request($req);
my $sid = $res->content;
chomp($sid);
$sid =~ s/^SESSION-ID=//g;
$self->{Sid} = $sid;
}

sub setProxy {
my ( $self, $proxy ) = @_;
$self->{Proxy} = $proxy;
}

sub getSid {
my ($self) = @_;
return $self->{Sid};
}
1;


Mopetarget.pm

package Mopetarget;
use strict;
use warnings;

sub new {
my ($class) = @_;
my %target;
my $self = { Target => \%target, };
bless $self, $class;
return $self;
}

sub getTarget {
my ($self) = @_;
return keys (%{ $self->{Target} });
}

sub addTarget {
my ( $self, $target ) = @_;
${ $self->{Target} }{$target} = 0;
}

sub addTargetFromURL {
my ( $self, $target ) = @_;
if ( $target =~ m{http://(.*?)/(.*?)/} ) {
my ( $server, $bbs ) = ( $1, $2 );
my @dat;
my $body = &get("http://$server/$bbs/subject.txt");
while($body =~ /(\d.*?)\.dat/g){
push (@dat, "http://$server/test/read.cgi/$bbs/$1/");
}
foreach (@dat) {
$self->addTarget($_);
}
}

sub get {
my ($url) = @_;
my $ua = LWP::UserAgent->new;
$ua->agent("Mozilla/4.0");
my $req = HTTP::Request->new( GET => $url );
$req->header( 'Accept' => 'text/html' );
my $res = $ua->request($req);
return $res->content;
}
}
1;

Mopechan 0.1 リリース

2ちゃんねるに書き込むためのCGI。
Mopechan 0.1 ダウンロード
使い方
 Mope.cgiを適宜編集し、実行する。

ソースコード掲載
Mopechan.pmとMopemaru.pmはローカルで動かすことを前提として作られているため、
サーバーの状況によっては一部のメソッドが使えません(特にプロキシ関係)。

Mope.cgi

#!/usr/bin/perl

use strict;
use warnings;
use Mopechan;
use Mopemaru;

###ユーザーがいじる部分ここから###
my $url = 'http://qb5.2ch.net/test/read.cgi/operate/1266376364/'; #投稿先URL
my $message = <<'EOF_EOF'; #上下のEOF_EOFの間に投稿文を書く
>>1

EOF_EOF
my $mail = 'sage'; #メールアドレス
my $from = ''; #名前
my $count = 1; #投稿回数
my $maru_mail = ''; #●メルアド
my $maru_pass = ''; #●パスワード
my $be_mail = ''; #BEメルアド
my $be_pass = ''; #BE認証コード
###ここまで###

my $mopechan = Mopechan->new;
if ($maru_mail ne ""){
my $mopemaru = Mopemaru->new("$maru_mail<>$maru_pass");
$mopemaru->login;
$mopechan->setMaruSid( $mopemaru->getSid );
}

$mopechan->setTarget($url);
$mopechan->setMessageMailFrom( $message, $mail, $from);
unlink("./Cookie.txt");
$mopechan->setCookieName("./Cookie.txt");
if ($be_mail ne ""){
$mopechan->setBE("$be_mail<>$be_pass");
}
$mopechan->setWriteTimeout(5);
$mopechan->write;
print "Content-type: text/html\n";
print "\n";
print $url;

while ($count > 0) {
$mopechan->write;
--$count;
}


Mopechan.pm

package Mopechan;

use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request::Common qw(POST);
use HTTP::Cookies;

use constant {
WRITE_TIMEOUT => 10, #書き込みタイムアウト
WRITE_USERAGENT =>
'Mozilla/4.0', #デフォルトのユーザーエージェント
COOKIE_NAME => 'Cookie.txt',
};

sub new {
my ($class) = @_;
my $self = {
Target => undef,
Message => undef,
Mail => undef,
From => undef,
WriteTimeout => WRITE_TIMEOUT,
WriteUserAgent => WRITE_USERAGENT,
Proxy => undef,
MaruSid => undef,
CookieName => COOKIE_NAME,
BE => undef,
Response => undef,
};
bless $self, $class;
return $self;
}

sub write {
my ($self) = @_;

my $ua = LWP::UserAgent->new;
$ua->agent( $self->{WriteUserAgent} );
$ua->timeout( $self->{WriteTimeout} );

my ( $server, $bbs, $key ) = split( '<>', $self->{Target} );
my %params = (
time => '111111111',
key => $key,
bbs => $bbs,
MESSAGE => $self->{Message},
mail => $self->{Mail},
FROM => $self->{From},
submit => "",
);
if ( defined( $self->{Proxy} ) ) {
my ( $proxy_host, $proxy_port, $proxy_protocol ) =
split( ':', $self->{Proxy} );
$ua->proxy( [ 'http', 'tcp' ],
"$proxy_protocol://$proxy_host:$proxy_port" );
}
if ( defined( $self->{MaruSid} ) ) {
$params{sid} = $self->{MaruSid};
}

my $cookie_jar =
HTTP::Cookies->new( file => $self->{CookieName}, autosave => 1 );
$cookie_jar->set_cookie( 0, 'tepo', 'don', '/', $server, 80, '', '', 99999,
'' );
if ( defined( $self->{BE} ) ) {
my ( $dmdm, $mdmd ) = split( '<>', $self->{BE} );
$cookie_jar->set_cookie( 0, 'DMDM', $dmdm, '/', $server, 80, '', '',
99999, '' );
$cookie_jar->set_cookie( 0, 'MDMD', $mdmd, '/', $server, 80, '', '',
99999, '' );
}
$ua->cookie_jar($cookie_jar);

my $req = POST( "http://$server/test/bbs.cgi", [%params] );
$req->referer("http://$server/$bbs/");
$req->header( 'Accept' => 'text/html' );

#print $req->as_string;
my $res = $ua->request($req);
if ( $res->is_success ) {
$self->{Response} = $res->content;
}
else {
$self->{Response} = $res->status_line;
}
}

sub setTarget {
my ( $self, $target ) = @_;
if ( $target =~ /^http:\/\//g ) {
$target =~
s/.*http:\/\/(.*?)\/test\/read.cgi\/(.*?)\/(\d*).*/$1<>$2<>$3/g;
}
$self->{Target} = $target;
}

sub setMessageMailFrom {
my ( $self, $message, $mail, $from ) = @_;
$self->{Message} = $message;
$self->{Mail} = $mail;
$self->{From} = $from;
}

sub setProxy {
my ( $self, $proxy ) = @_;
$self->{Proxy} = $proxy;
}

sub setMaruSid {
my ( $self, $marusid ) = @_;
$self->{MaruSid} = $marusid;
}

sub setCookieName {
my ( $self, $cookiename ) = @_;
$self->{CookieName} = $cookiename;
}

sub setWriteTimeout {
my ( $self, $writetimeout ) = @_;
$self->{WriteTimeout} = $writetimeout;
}

sub setBE {
my ( $self, $be ) = @_;
$self->{BE} = $be;
}

sub getResponse {
my ($self) = @_;
return $self->{Response};
}
1;


Mopemaru.pm

package Mopemaru;

use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request::Common qw(POST);

use constant {
LOGIN_PROXY_TIMEOUT =>
30, #プロキシでログインする際のタイムアウト値
LOGIN_TIMEOUT => 20, #通常ログインする際のタイムアウト値
};

sub new {
my ( $class, $maru_account ) = @_;
my $self = { MaruAccount => $maru_account, Proxy => undef, Sid => undef, };
bless $self, $class;
return $self;
}

sub login {
my ($self) = @_;
my ( $id, $pw ) = split( '<>', $self->{MaruAccount} );
my $proxy = $self->{Proxy};
my $ua = LWP::UserAgent->new;
$ua->agent('DOLIB/1.00');
$ua->default_header( 'X-2ch-UA' => 'Monazilla/1.00' );

#httpsの場合LWPの標準機能ではプロキシが使えない
if ( defined($proxy) ) {
my ( $proxy_host, $proxy_port, $proxy_protocol ) =
split( ':', $self->{Proxy} );
$ENV{HTTPS_PROXY} = "$proxy_protocol://$proxy_host:$proxy_port";
$ua->proxy( [ 'http', 'tcp' ],
"$proxy_protocol://$proxy_host:$proxy_port" );
}
my $url = 'https://2chv.tora3.net/futen.cgi';
my %form = ( 'ID' => $id, 'PW' => $pw );
my $req = POST( $url, Content => "ID=$id&PW=$pw" );
my $res = $ua->request($req);
my $sid = $res->content;
chomp($sid);
$sid =~ s/^SESSION-ID=//g;
$self->{Sid} = $sid;
}

sub setProxy {
my ( $self, $proxy ) = @_;
$self->{Proxy} = $proxy;
}

sub getSid {
my ($self) = @_;
return $self->{Sid};
}
1;

串くん 0.2 リリース

最低限の機能を搭載しました。
串くん 0.2をダウンロード

現在搭載されている機能
 ・●ログイン
 ・プロキシ選別

将来的に搭載したい機能
 ・マルチスレッド
 ・効率的な串管理方法
  (山田技研「CCB」のような感じ)
 ・一人スレスト
  (タスクを予約するイメージ)
 ・定時スナイプ
 ・複数BE管理
 ・●ログイン時のプロキシ
 ・P2経由