プログラミングを始める10の理由 このページをアンテナに追加 RSSフィード

2008 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 |
2011 | 01 | 02 | 05 | 06 | 07 | 08 |
2012 | 01 | 05 | 09 | 10 |
2013 | 08 |

twiiter bot掲示板◎botスクリプトのサポートや疑問、改造や雑談など(閲覧のみ)

[広告]

2009-09-30

[][]phptwitterbotを作る(TLに反応修正版) phpでtwitterのbotを作る(TLに反応修正版)を含むブックマーク

昨日の記事の続きです。

TLの特定語句に反応するが、@付きのpostとRTには反応しない、という修正点。あと、eregからpreg_matchw使うように。

とりあえずソース

<?php
$username = "";
$password = "";
//最新のTLのステータスIDを保存しておくファイルです。パーミッションは書き込み可に。
$filename = "./tmp/last.dat";


//過去の開いて取得
$fp = @fopen($filename,'rb') or die("ファイルが開けません");
flock($fp, LOCK_EX);
$line = fgets($fp, 64);
fclose($fp); 

//自分がフォローしているfriendsのリストです。
$host = "http://twitter.com/statuses/friends_timeline.xml";      //ユーザーの発言一覧

//一番最初の時やファイルが壊れた時、最新1件のみ発言を取得する
if(!empty($line)){
$last_id=$line;
$host .="?since_id=".$last_id;
}
else{
    $host .="?count=1";
    }


$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $host);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
$result = curl_exec($ch);
curl_close($ch);
$XML = simplexml_load_string($result);


$i = count($XML)-1;
$j = 0;
 while($i >= $j) {
$var =  $XML->status[$i]->text;    
$reply_name = $XML->status[$i]->user->screen_name;
$name = $XML->status[$i]->user->name;

if($reply_name != $username){
if( !preg_match ( "/@[a-zA-Z0-9-_]{3,}/", $var ) ){
if( preg_match ( "/おやす|寝る/u", $var )){    
    $message = "@".$reply_name." おやすみ!";
    tweet($message ,$username,$password);
}
if(preg_match ( "/おはよ|むくり/u", $var )){    
    $message = "@".$reply_name." おはよう!";
    tweet($message ,$username,$password);
}
if(preg_match ( "/ただいま/u", $var )){    
    $message = "@".$reply_name." おかえり";
    tweet($message ,$username,$password);
}
}
}
$i--;
}

//ファイルに最新id書き込み
$last_id = $XML->status[0]->id;
$dat = (string)$last_id;
file_put_contents($filename,$dat,LOCK_EX);


//ポスト部分の関数
function tweet($message ,$username,$password)
{
$message =urlencode($message);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://twitter.com/statuses/update.xml');
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "status=$message ");
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
$buffer = curl_exec($ch);
curl_close($ch);
}

?>

解説です。

if( !preg_match ( "/@[a-zA-Z0-9-_]{3,}/", $var ) ){

ここで、@[スクリーンネーム]の入った発言か否か、判断しています。

正規表現でまず先頭に@があって、そのあと中カッコ[]で囲ったa-zA-Z0-9と続きます。

中カッコは範囲なんですね。後、ハイフンとアンダーバーも使われる(ん?ハイフン使えたっけ?

つまり半角英数プラス-_。

その後に来てるのが大カッコ{}、数字の3、カンマ(,)と来てる。これは前の半角英数とかが3回続くって意味ですね。

ユーザーネームでとれる最小の字数が3っぽいので。

これで、文中に@[スクリーンネーム]のがあるか否か判定できますね。

ちなみにRTも考えていたのですが、RTにはたいがいこの@[スクリーンネーム]がついてきますから、RTのはわざわざ記述しなくても大丈夫でしょう。

次に、preg_matchです。

if( preg_match ( "/おやす|寝る/u", $var )){ 

ここで前のeregと違うのはエスケープ/の存在と、末尾のアルファベットu、区切りの|。

エスケープは前も書いたので、今回はその後にあるu。

こちらの記述が詳しいです。

404 Not Found | シーサー株式会社

preg系の正規表現パターン修飾子「u」を指定すると、パターン文字列UTF-8エンコードされた文字列として扱われるようになります

twitter文字コードutf-8、このスクリプトも当然utf-8で書かれているのですが、あってるはずなのにマッチしない状態が長く続いて。

なんでかな?って疑問がずっとあったんです。(それで苦手だったってのもあるかも)

このやり方で氷解しました。

最後の区切り|ですが、勘のいい方ならわかると思いますがorみたいなものですね。

おやす、寝る、どちらが来てもマッチするということです。ちなみにカッコはつけてもつけなくても動作一緒でした。

違いがいまいちわからないんですが、併用する時とかだけですかね。スミマセン。詳しい方指摘してくださると嬉しい…(人任せ)

今までの言葉に反応する系のスクリプトも、書き換えたり自分なりに書いてみるのも一興だと思います、是非そうしてください。

正規表現は本当に奥が深いです。使いこなせばbot精巧になるでしょうし、それ以外にも効率がよくなるでしょうね。

私も勉強していきます!

titania_lovetitania_love 2009/10/01 10:12 ご苦労さまでした!
そして、こんなにも要望通りのスクリプトをありがとうございました。おかげさまで正常に動いております。
私もこの機会に正規表現について調べたのですが、本当に奥が深く感じました。
これからも拝見させていただきます。

bardothodolbardothodol 2009/10/01 17:20 はい!自分でも完成度が高まったようで嬉しいです。
自分のbotにも実装したいです。正規表現は奥が深いですね!
私もphpやプログラムはやりはじめて2年ぐらいなので
作りながら学んでいってる状況です。

実はまだ改良点があるんです。
「ただいま寝る」など、複数の反応語句があるとその数だけメッセージを送ってしまうんです。
これが嫌な場合は書き直さなければなりません。
今後の課題にしたいと思います。

iyuiyu 2009/10/01 22:36 先日はコメント返信有り難うございます。
preg_matchのところ、早速直してみました。
おかげで色んな問いかけにも応えられるようになりました…!

上でお話されている「複数反応語句が含まれる場合」なのですが、自分はelseifを使っています。
phpはまだまだ始めて日が浅く、コピペ頼りな身のため、うまく説明できそうにないのですが…
「一番上の語句に引っかからなければ次の式〜」という形にすれば、リプライ多発しないのではないかと思います。
それでは、これからも更新楽しみにしています!

bardothodolbardothodol 2009/10/01 23:28 >iyuさん
どうも、解決したようで良かったです。
>上でお話されている「複数反応語句が含まれる場合」なのですが、自分はelseifを使っています。
そうですね、以前のソースでは使っていたんですけど反応しなくてこれはelseifが
悪さしているのでは?と上のソースでは全部ifにしてしまったのですね
後から考えれば重複語句だっただけの事でした。
普通に考えればelseifの方が良いですね。優先順位は先に出て来た方ということで…
私も勉強しつつ覚えていきたいと思ってますので
今後も指摘や助言等いただけたら幸いです。

opiiopii 2009/10/04 01:11 はじめまして!こちらの記事を参考にbotを作らせて頂いております。
ド初心者なため丁寧に書かれていて本当に助かっております。
質問があるのですが、たとえば「ただいま」を拾って「おかえり」を言う場合、
おかえりと別の言葉もランダムに表示することは可能なのでしょうか?
分かりにくい質問で申し訳ないのですが、お時間がありましたらお答え頂けると幸いです。

nyanya 2009/10/04 13:11 こんにちは、初めまして!
こちらの記事で、@返し、定期ポストのボットを作る事が出来て、感激しております。
本当にありがとうございます!
上の方に便乗させて頂きたいのですが、私はelse ifで拾った単語を、
$post = $tango[$key];で、複数パターン返しております。

しかし反応させたい単語を増やす時、elseifも増えていくので、
少し管理しにくいと思っていたところ、こちらの記事のコメントで、
preg_matchを知りました。
そして反応させる単語を複数に増やそうとしたのですが、うまく動きません。
数日四苦八苦してみましたが、進展がなく・・・。
何かヒントがあれば、お時間ある時にでもお教え頂けないでしょうか。

realreal 2009/10/04 19:46 こんにちは。はじめまして!
こちらのブログの内容を参考にbot作りをさせて頂いています。
TLに反応するというのをやりたかったのでとても嬉しいです。

ですが、現在上記のソースをまるまるコピーして、
テストとしてパスワード・ID・datだけを書き直して
動かしているのですが反応しません。

botにはフォローしている自分のIDで該当ワードを投稿して
読ませています。

datファイルには書き込みがあるので
取得はうまくいっているとおもうのですが…。

この場合、どのあたりの不具合なのかわからず困っています。
もし良かったらアドバイスを頂ければ幸いです。

ちなみに他のソース(@に返信等)はも使わせていただいているのですが
そちらは問題なく動いています。

bardothodolbardothodol 2009/10/04 22:02 >opiさん
$oyasumi = array("おやすみ!","グッナイ","もう寝るの?");
if(preg_match( "/おやす|寝る/u", $var )){
$key = array_rand($oyasumi);
$post = $oyasumi[$key];
$message = "@".$reply_name." ".$post;
tweet($message ,$username,$password);
}
こんな感じでしょうか。配列を用意して、ランダムに選ばせる…という感じです。
http://d.hatena.ne.jp/bardothodol/20090912#p1
などをごらんいただくとわかると思います(少し違いますが)

>nyaさん
他の方への回答もありがとうございます。
elseifがうまくいかない問題ですが、一番最初のifはelseをつけないようにしないと、きちんと動作しません。以降は全てelseifでOKなので。
elseifだと煩雑になってしまうのは、致し方ないことですので定義関数を作るなりswitchで対処するなりの方法が必要になってくるのかもしれませんね。
preg_matchは、パターンの時"/文字列/u"のように、エスケープの/と最後のuを忘れないようにするのが第一だと思います。

>realさん
>botにはフォローしている自分のIDで該当ワードを投稿して読ませています。
たぶん、このあたりがひっかかってるのではないかと…
一連のスクリプトは全て同一のアカウントで動く事を前提にしていますので、他者のアカウントで投稿したい場合は投稿部分のユーザーネームとパスを変えなければなりません。
追加で
$username_post = "";
$password_post = "";
という変数をつくりtweetの部分で
tweet($message ,$post_username,$post_password);
でどうでしょうか。
私が質問の意図を取り違えてなければこれで動作すると思います。

bardothodolbardothodol 2009/10/04 22:04 間違えた、tweet($message ,$post_username,$post_password);
でなくtweet($message ,$username_post,$password_post);
ですね

realreal 2009/10/04 23:24 レスありがとうございます!
ですが、すみません。私の書き方が悪かったようで…
読ませていると言うのは、
botが読んでいるタイムライン上には反応する単語が存在する
という意味で書きました。
反応する単語(ex/おはよう)自体はTL上にあるはずなのですが
botがそれに対して返信をしない…という意味で書いたつもりでした。
余計な事を書いてしまってすみません。
アカウントはbotのために取ったもので投稿しています。

もしかしてPHPのバージョンがあっていないのでしょうか…
サーバーの方では5.2.10を使っているのですが…。

bardothodolbardothodol 2009/10/05 00:44 >realさん
そうですか、私の意味の取り違えですね汗
他に考えられる原因としてはTLが最大20件しか取れないので、TLの流れがあまりに早いと反応する前に流れてしまうことがある、等ですかね。cronではなく直接アクセスする等して試してみてはいかがでしょう。
phpのバージョンは5以降ですから問題ないと思います。
それでも反応しないようでしたら、お手数ですがメールフォームからソースを送信していただけませんか。

goggog 2009/10/05 14:50 上で質問されていた方とは違うのですが、
$oyasumi = array("おやすみ!","グッナイ","もう寝るの?");
if(preg_match( "/おやす|寝る/u", $var )){
$key = array_rand($oyasumi);
$post = $oyasumi[$key];
$message = "@".$reply_name." ".$post;
tweet($message ,$username,$password);
}
をいれるとエラーが出てしまいます。こちらの記事のソース自体ではエラーが出ないのですが、どこが間違っているようなのですがさっぱりです。
ご迷惑かとは存じますが、其方の都合の宜しいときにフルソースをご教授いただけませんか?

realreal 2009/10/05 18:16 レスありがとうございました。
今日になってもう一度最初からやってみたところ
今度は無事に動きました。
はっきりとした理由が解からないのですが、
レスで指摘いただいたように
最新20件から外れてしまっていたのかもしれません。
最近になって急にフォロワーさんが増えたので…。
お手数おかけして申し訳ありませんでした!
今後も色々参考にさせていただくと思うのですが
どうぞよろしくお願いします!

uoomuoom 2009/10/05 22:55 コメント失礼します。
こちらのブログを参考にさせて頂き、私も無事botを作ることができました…!

そこで質問をしてもよろしいでしょうか?
TL上で「帰りたい」「帰ったら」「寝たい」「寝ようかな」の時に、
間違って「おかえり」「おやすみ」と返さないようにしたいのです。
「たい」「かな」「かも」など特定の単語(終助詞)がついた発言はスルー、など出来たら嬉しいのですが…
そう指定することは可能でしょうか?
もしよければご助言頂きたいです。

ayaaya 2009/10/06 02:55 初めまして。こちらを参考にしてbotを作成させていただきました。ありがとうございました。

そこで一点問題が発生していまして。
botには
?一定間隔でつぶやき続けるよう設定したphp
?@つきに返信(特定の単語に反応)するよう設定したphp
?今回のTLに反応するphp
この三つを動作させようとすると、なぜか一個の発言(@つきのもの)に2回反応してしまいます。
そもそもphpを3つに分けて動作させているのがいけないのかな?と思うのですが原因がわかりません。
よろしければご助言いただきたく思います。
よろしくお願いいたします。

bardothodolbardothodol 2009/10/06 22:31 >gogさん
エラー内容がわかりませんので推測ですが、}の不足(閉じ忘れ)だと思います。
{があると必ず}も存在しなければなりません。
}の適切な位置がわからないのであればまた解説します。

>uoomさん
if( preg_match ( "/寝よう/u", $var ) ){
if(!preg_match ( "/かな/u", $var ){
のように、入れ子でしてみるといいかもしれません。
(「寝よう」の同一文中に「かな」が入らない時だけ投稿する)
その場合、中カッコ}の閉じ忘れにご注意下さい。

>ayaさん
たとえば、aさんがTL上に「おはよう」と発言するとします。
botは反応してaさんに対して「おはよう」と@を飛ばすのですが、
aさんがまたまたbotに@を返すとします。
botのリプライのスクリプトの方で「おはよう」に反応する設定にしていたらbotは2回も言ってしまうことになります。
上記の対策なら、リプライで「おはよう」に反応しない設定にするなどした方がいいかもしれませんね。
あと考えられる原因はTLとリプライの最終発言テキストの共用ですかね。
かならず別ファイルでご使用下さい。

nininini 2009/10/17 22:06 こんにちは、こちらを参考にbotを作らせて頂いております。
ありがとうございます。

自動フォローはうまくいったのですが
こちらがほぼそのままコピペしても動きません


Warning: preg_match() [function.preg-match]: Compilation failed: nothing to repeat at offset 0 in /usr/local/〜〜〜.php on line 46

このようなエラーが46,50,54にでてしまいます

不躾で申し訳ないのですが
どのように修正すればいいのかよろしければご助言いただきたく思い
コメントさせていただきました。
よろしくお願い致します。

abuabu 2009/10/24 18:15 初めまして、こちらを参考にさせて頂き、初めてbotを作ろうと思っているのですが、
本当に初歩的な質問で申し訳ないのですが
この"./tmp/last.dat"というのは自分で用意しなければならないのでしょうか?
空ファイルの.dat(拡張子を変えただけのもの)をサーバにupし、パーミッションを弄ればいいのでしょうか…
お時間のある時にでも、回答頂けると幸いです。

bardothodolbardothodol 2009/10/25 00:35 >niniさん
全てpreg_match関連のようですね。正規表現の書式等があやしいと思います。
一度、こちらのコードのままやってみて下さい。

>abuさん
そうです。ご自身で用意して下さい。
空のファイルに.datの拡張子をつけた、パーミッションは書き込み可のファイルです。
上の例だとtmpディレクトリにlast.datが入っている状態です。

プラムプラム 2009/10/28 03:38 はじめまして!こちらを参考にbot作成でお世話になっています!
上で質問されているniniさんと全く同じなんですが、
こちらのコードのままツイッターIDとパスワード以外は何も書き変えてない状態でも
preg_matchの3行に「Warning: preg_match() 〜〜」というエラーが出てしまいます。
IDとパスしか変えてない状態なので何がいけないのか分からず、どう修正していいかも分かりません…。
対処法などありましたら、教えていただけると幸いです。

プラムプラム 2009/10/28 06:05 上で書き込んだプラムです。書き残した情報があったので再度失礼します。

IDとパスを書き換えた以外に、tmpフォルダとその中にパーミッション666のlast.datファイルは作ってあります。
phpファイルはブラウザで実行してみてエラーが出てる状態です。
また、botと相互フォロー状態の私が発言し、botが反応するかテストした際、反応はなかったですが、datファイルには数字が書かれていました。
使っているサーバーは@pageです。どうぞよろしくお願いいたします。

SS 2009/11/02 18:48 初めまして、こちらのブログ参考にbotを作製している者です。
わかりやすい記事で大変助かっております、ありがとうございます。
どうしてもわからない所があるので質問させて下さい。

TL上の発言に対し、リプライをランダムでpostするように、
上記のコメントに書かれていたソース
$oyasumi = array("おやすみ!","グッナイ","もう寝るの?");
if(preg_match( "/おやす|寝る/u", $var )){
$key = array_rand($oyasumi);
$post = $oyasumi[$key];
$message = "@".$reply_name." ".$post;
tweet($message ,$username,$password);
}
を入れてみてエラーも出なかったのですが、肝心の返信をしてくれません。

何度もおかしい箇所無いががチェックし、思い当たる事を色々やってみたのですがどこがいけないのがさっぱりで困っています…

お忙しい所申し訳ございませんが、宜しければご回答の程お願い申し上げます。

YuliYuli 2009/11/05 05:34 >>Sさん
横レスすみません。こちらのソースに大変お世話になった者です。
管理人様がお忙しいようなので、私なりの解決方法を書かせてください。
私は以下のような構成にしています。

〜略〜
$name = $XML->status[$i]->user->name;

$ohayou=array("xメッセージx","xメッセージx","xメッセージx");

if($reply_name != $username){
if( !preg_match ( "/@[a-zA-Z0-9-_]{3,}/", $var ) ){
if(preg_match( "/おはよ|起床/u", $var )){
$key = array_rand($ohayou);
$post = $ohayou[$key];
$message = "@".$reply_name." ".$post;
tweet($message ,$username,$password,$id);
}

ソースの順番が関係あるのかは分からないのですが… 解決にならなかったらすみません。
このようにしても駄目でしたら、別のところに問題があるかもしれません。
おせっかい失礼いたしました。

YuliYuli 2009/11/06 17:21 すみません、上のソースの
tweet($message ,$username,$password,$id);

tweet($message ,$username,$password);
です。失礼しました。

bardothodolbardothodol 2009/11/06 23:44 >Yuli さん
代わりに返答していただいて、恐縮です。
本当に中々コメントが返せなくて…

この間作った返信掲示板をbotの質疑応答掲示板にでもしようかなあ…

DIZDIZ 2009/11/10 22:50 >>管理人様
質疑応答掲示板、とてもいい考えだと思います。
こちらのようにとても丁寧で親切なソースのおかげで、素人の私でもphpの仕組みを理解することができました。
掲示板があれば、同じように困った人や知識のある人同士で、新しいスクリプトの模索もできるのではと思います。
ぜひご検討ください。失礼いたします。

cecil_romanesquececil_romanesque 2009/11/11 17:58 >>Yuliさん
返信が遅れてしまって大変申し訳ありません。
Yuliさんが書いてくださったソースの通りにやってみたら上手く動いてくれました…!
本当に感謝しています。ありがとうございました!

管理人様も貴重なスペースありがとうございました。
これからも頑張ってください!それでは失礼いたします。

S(cecil_romanesque)S(cecil_romanesque) 2009/11/11 18:01 ↑すみません、名前が違うのですが、以前書き込ませて頂いたSです。
大変失礼いたしました…

空 2009/11/22 11:11 はじめまして。
このファイルを保存するとき、ファイル名はなんと書けばいいでしょうか?