[PHP] お手軽にTwitter botを作る

定型文をツイートするTwitter botが必要になったので作る。以下覚え書き。

Twitter botとは

ボット(bot)とは

Twitter の機能を使って作られた、機械による自動発言システム。語源はロボットから来ている。特定の時間に自動ツイートする bot、ユーザーの bot 宛の発言にリプライする bot、特定のキーワードに反応する bot 等、様々な bot が存在する。

Twitter「ボット(bot)とは」とは?|ツイッター用語 – ツイナビ
http://twinavi.jp/guide/section/twitter/glossary/%E3%83%9C%E3%83%83%E3%83%88%EF%BC%88bot%EF%BC%89%E3%81%A8%E3%81%AF

今回作るbotは、あらかじめ用意した文章を決まったタイミング(たとえば毎時零分)でツイートするもの。

参考サイト

Twitterは途中からアカウント認証方式がOAuthに変更されている。ネットを検索するとTwitter bot開発の記事がたくさんヒットするけど、旧認証方式を使った古い記事も多い。いくつかの記事を読んでみた結果、以下の記事が簡潔でわかりやすかったので、この記事をお手本にして作業を進める。

PHP + OAuthで Twitter botをつくってみよう – PHP入門 – Webkaru
http://webkaru.net/php/twitter-bot/

Twitterアカウントを作る

まずは当然のこととしてTwitterアカウントが必要なので普通に作る。

開発者サイトにログイン

Twitter開発者サイトに自分のTwitterアカウントでログインする。

Twitter Developers
https://dev.twitter.com/

Twitter Appの作成

以下はTwitter開発者サイトでの作業。

  1. 画面右上の自分のアイコンをクリックして[My applications]を選択する。
  2. [Twitter Apps]ページで[Create New App]ボタンを押下する。
  3. [Create an application]ページで[Application details]に必要事項を入力する。
    [Name*] Twitter Appの名前(必須)
    [Description*] Twitter Appの説明(必須)
    [Website*] ウェブサイト(必須)
    [Callback URL] 認証に成功した後にジャンプするURL。今回作成するBotには必要なさそうなので空白
  4. [Developer Rules of the Road]の規則に目を通して Yes, I agree.にチェックする。
  5. [Create your Twitter application]ボタンを押下する。

すると、以下のメッセージが表示されてアプリケーションが作成される。

アクセスレベルの変更

Botのアクセスレベルを設定する。デフォルトはRead onlyだけど、これはTwitterアカウントが受け取ったツイートを読み取るだけの権限のようなので、Botからツイート(書き込み)できるようにアクセスレベルを変更する必要がある。

  1. Twitter Appの[API Keys]タブをクリック。
  2. [Application settings]エリアの[Access level]を[Read-only]から[Read and Write]に変更する。

Twitterアカウントによっては、アカウントに電話番号が紐付けされていないと書き込み権限が付与できない場合がある。2つのアカウントで試したところ、1年近く運営しているアカウント(フォロワー数は1,000超)だとすんなり書き込み権限を付与できたけど、1週間ほど前に作成したアカウント(フォロワー数は百以下)で書き込み権限を付与しようとしたら、アカウントに電話番号を登録するように言われた。電話番号を登録した後は、問題なく書き込み権限を付与できた。

Twitter Help Center | Adding your mobile number to your account
https://support.twitter.com/articles/110250-adding-your-mobile-number-to-your-account-via-web#

アクセストークンの作成

次はアクセストークンを作成する。アクセストークンは見ず知らずのBotが成りすまして自分のアカウントにツイートするのを防ぐために必要なのだろう。ボタンを押すだけなので難しいことはない。

  1. Twitter Appの[API Keys]タブをクリックする。
  2. ページの下のほうの[Your access toke]エリアにスクロール。
  3. [Create my access token]ボタンを押下する。

これでTwitter Appの設定は完了。画面に表示されている情報のうち、Bot運営に必要なのは以下の4項目。

  • API key
  • API secret
  • Access token
  • Access token secret

Botソフトの作成

参考サイトに紹介されていた以下のPHPコードを使うとメチャ楽だ。

abraham/twitteroauth · GitHub
The first PHP Library to support OAuth for Twitter’s REST API
https://github.com/abraham/twitteroauth

サブディレクトリtwitteroauthを作成して以下のファイルを配置しておく。

  • twitteroauth/OAuth.php
  • twitteroauth/twitteroauth.php

Botは以下のように簡単に書ける。

この例では固定テキストメッセージをツイートしているが、ファイルやデータベースにあらかじめメッセージを用意しておくことで、都度ロードしてバラエティに富んだ内容をツイートすることも可能だろう。動作確認したらcronに登録して自動運転させれば完成。

さらに参考サイト

以下のサイトは説明が詳細で参考になる。

Twitterを100倍楽しむためのbot開発基礎講座:連載|gihyo.jp … 技術評論社
http://gihyo.jp/dev/serial/01/twitterbot

PHP+OAuthでTwitter – SDN Project
http://www.sdn-project.net/labo/oauth.html

PHP+OAuthでTwitterのBotを作ってみる – SDN Project
http://www.sdn-project.net/labo/twitter_bot.html

[PHP] BitlyのWebAPIを使ってURLを短縮する

先日作成したTwitter botのツイートにサイトURLを短縮して含めたいというニーズあり。調べたところ、短縮URLを生成する機能は各社からWeb APIとして提供されている模様。参考になる記事を探したら以下が非常にわかりやすかったので、この記事に倣ってbitlyのWeb APIを使って短縮URL生成機能を実装してみる。

bit.ly API|短縮URL|Web関連特集|PHP & JavaScript Room
http://phpjavascriptroom.com/?t=topic&p=bitlyapi

OAuth2認証は必要なし、API Keyによる認証でOK

bitlyのオンラインドキュメントを読むと、APIを使うにはユーザ認証が必要。認証方式はOAuth2。だけど、短縮URLの生成に限っては、OAuth2に加えてAPI Keyによるシンプルな認証も可能みたい。実装が少しでも楽になるのはうれしいので、API Keyで認証することにする。

bitly API Documentation
http://dev.bitly.com/links.html#v3_shorten

bitly API Documentation
http://dev.bitly.com/

bitlyのウェブサイトでAPI Keyを取得する

以下のページからサインアップしてAPI Keyを取得する。

Bitly. The power of the link.
https://bitly.com/a/your_api_key

サンプルコード

[PHP] 画像をダウンロードして保存する

画像をダウンロードするにはいろんな方法がありそうだけど、普通にPHPの組み込み関数で出来たので覚え書き。

PHP: file_get_contents – Manual
http://www.php.net//manual/ja/function.file-get-contents.php

タイムアウトとかリダイレクトとか細かい制御をしたければPearのHTTP_Requestでも使うかな。

HTTP_Request
http://pear.php.net/package/HTTP_Request/

[PHP] SimpleXML関数を使ってXMLデータをパースする

結果をXML形式で返すウェブサービス (Web API) を使うことがある。そこで、PHPでXMLデータをパースする方法を覚え書き。

PHP5のSimpleXMLエクステンションを使うと簡単

PHP5にはSimpleXMLと呼ばれるXML操作のための拡張モジュールが含まれている。SimpleXMLの関数を使うと、XMLを簡単にオブジェクトに変換可能。

PHP: SimpleXML – Manual
https://php.net/manual/ja/book.simplexml.php

PHP: XML 操作 – Manual
https://php.net/manual/ja/refs.xml.php

サンプルコード

XMLデータにCDATAセクションが含まれていると上のコードでは読めない。その場合、以下のように変更する。

XML用語事典 [CDATAセクション]
http://www.atmarkit.co.jp/aig/01xml/cdata.html

パースしたXML要素はオブジェクトのプロパティとして参照できる。たとえば $data->section->item という感じ。開発途中でprint_r()関数などを使ってダンプしておくとXML要素の特定は楽になる。

参考サイト

SimpleXML | PHP プログラミング解説
http://www.crystal-creation.com/web-app/tech/programming/php/xml/simple-xml.htm

PHP SimpleXMLでXMLパースをするときの注意点まとめ – ウッディーの日記
http://blog.livedoor.jp/gnoot/archives/51371942.html

[PHP] Webリクエストをハッシュを使って認証する

Webリクエストをハッシュを使って認証する必要があったので覚え書き。条件はクライアントとサーバーの両者が共通の秘密鍵を保持していること。

例1: ユーザ名をハッシュ化してリクエストに付加する

ハッシュ関数としてsha1を使うと仮定する。ユーザ名をusername、秘密鍵をsecret_codeとすると、ハッシュは以下のように計算できる。

そうして得られたこのハッシュ値を以下のようにリクエストに付加する。

リクエストを受け取ったサーバー側では、リクエスト中のusernameと独自に保持する秘密鍵からハッシュ値を計算し、リクエストに付加されているハッシュ値と一致したら認証OKとする。

例2: パラメータを組み合わせてハッシュ化してリクエストに付加する

以下のようなパラメータ付きリクエストを想定する。

それぞれのパラメータはユーザ固有とし、リクエストにはユーザ名は付加しないものとする。すると、ハッシュは以下のように計算できる。

そうして得られたこのハッシュ値を以下のようにリクエストに付加する。

最初の例と同様に、リクエストを受け取ったサーバーは独自に保持する秘密鍵からハッシュ値を計算し、リクエストに付加されているハッシュ値と一致するかどうか確認する。

補足事項

クライアント側とサーバー側でハッシュ計算方法が共通になりさえすれば、ハッシュ計算方法はアレンジしても問題ない。上の例ではリクエストに付加する値だけをハッシュの元値として使ったけど、URLの部分文字列(たとえば “param1=<param1値>&param2=<param2値>”) をそのままハッシュの元値として使ってもかまわない。

sha1でバイナリのダイジェストを使いたい場合はbase64でエンコードしてからリクエストに追加すれば良い。

参考サイト

PHP: sha1 – Manual
http://php.net/manual/ja/function.sha1.php
sha1 — 文字列の sha1 ハッシュを計算する

PHP: base64_encode – Manual
http://php.net/manual/ja/function.base64-encode.php
base64_encode — MIME base64 方式でデータをエンコードする

[PHP] レスポンスタイムを計測する監視スクリプト

仕事先が契約しているデータセンターのウェブサーバーがもう笑うしかないぐらい不安定。僅かなアクセスしか無いにも関わらず、気が付くとサーバーは落ちてるし、502、503、504のエラーが返るのも日常茶飯事。24時間通算してどれぐらい不具合が起きているのだろうか?と気になったので監視スクリプトを作ってみた。

監視スクリプトの仕様

  • 監視スクリプトはターゲットのサーバーとは別の安定したサーバーに置く。
  • 監視スクリプトはPHPで書いてcronで5分ごとに実行させる。
  • ターゲットのサーバーから特定URLをGETするのに要した時間をログに書き出す。

ネットを調べるとpingコマンド利用のサーバー死活監視スクリプトが多数ヒットしたけど、僕が必要なのはブラウザにページが表示されるまでの体感時間なので、HTTPのGETのレスポンスタイムを計測するのが現実的かと。サーバーが完全に死んでいたらタイムアウトになるだろうし。

監視スクリプト

以下のようなスクリプトを作成した。HTTPリクエストの実行にはPEARのHTTP_Requestを利用。logsというサブディレクトリを作成しておくと、日付を名前にしたCSVファイルが自動生成されて、スクリプトが実行されるたびにログが追記される仕組み。監視するURLは複数指定可能にした。

実行例

以下にログファイルの抜粋を例として示す。各行先頭から処理時間(秒)、実行日時、ステータスコード、URLの順にカンマ区切りで記録される。ステータスが200以外のときはレスポンスヘッダの配列ダンプも追記される。

[PHP] アクセスログを記録するクラスを書いてみた

Apacheの生ログとは別に、特定のPHPファイルのアクセスログだけを記録したいことがある。そんなことが簡単に出来るように、いろいろ使い回しが利きそうな汎用のアクセスログクラスを書いてみた。

PHPソース

使い方

アクセスログを記録したいPHPファイルの冒頭にインクルードするだけ。

ログ出力例

[PHP] トラブルに強い問い合わせフォームを作る

前回のエントリで問い合わせフォームのトラブルについて書いた。トラブルフリーな問い合わせフォームは無いけれど、トラブルの影響を最小限に食い止めるための方策はいろいろある。すなわち「問い合わせをもらっていたのに知らなかった」という状況を避ける対処法。僕が気をつけているのは以下のこと。

1. mb_send_mail()の戻り値は必ずチェック

mb_send_mail()はmail()のラッパクラスで、mail()はphp.iniでパス指定されたsendmailコマンドを呼んでいる。sendmailコマンドがリクエストを正しく受け入れると正常ステータスを返すのだけど、実際にメールを送信するのはMTAの役割なので、メールが送られたかどうかをステータスから判断することはできない。だけど、少なくともリクエストは正しく受け入れられないとメールは送られないので、関数の戻り値は必ずチェックしてエラーはユーザに通知する。ユーザに「エラーで送られなかったんだ」と知らしめるために。

2. メール送り先は複数メールアドレスで、異なるドメインも含めて。

受信メールサーバーの不具合や、メール受信者のミスなどを想定し、メールの控えを複数のメールアドレスに送信しておく。全てのメールアドレスが同じドメインだとサーバー不具合で全不通になるので、異なるドメインのメールアドレスも混ぜておく。たとえばGmailとか。

3. バウンスメールを喪失しないようにenvelop senderを必ず指定。

受信メールサーバーの不具合などでメールがバウンスしてきたときに確実に通知を受け取れるように、sendmailコマンドの -f オプションを使ってenvelop senderを必ず指定する。もし指定しないのなら、どのメールアドレスがenvelop senderになるのかホスティングプロバイダのデフォルト設定を確認した上で、そのメールアドレスへの着信を常時チェックするようにする。

4. フォーム投稿をサーバー上のログファイルに記録して頻繁に目を通す。

メールは信頼できないものと割り切って、全ての投稿をサーバー上のログファイルに欠かさず記録する。たとえ不具合によりメールが一切機能しなくても、ログを見れば問い合わせがあったことと問い合わせ内容を知ることができる。ログファイルはウェブサーバーから見れないところに置くこと、破損しないように排他制御を行うこと、確実に書き込むために書き込みエラー時にはリトライを行うこと、そして必ず頻繁に管理者が目を通すこと、など基本的な点は遵守すること。僕は管理しているサイトのアクセスログを毎日朝一で閲覧するので、そのときにこのログにも必ず目を通すようにしている。

スクリプト例

[PHP] メールが拒否される原因は発信元ホスト名にあった件

開発中の会員システムにおいて、自動送信した会員向けメールが「Undeliverable」で戻ってくる問題が発覚。会員向けメール全てが戻ってくるわけではなく、特定ドメイン宛てだけが常に戻ってくる模様。試しに、同じメールアドレスへメールソフトから手動でメールを送ったら問題なく届く。サーバーの sendmail 設定の問題か?

戻ってきたエラーメールには以下の記述がある。

アクセス拒否の原因は「不適切なHELOネーム」。RFCによれば、HELOコマンドにはSMTPクライアントの完全修飾ドメイン名(TLDの指定を含めた完全なドメイン名)か、もしそれが利用できない場合にはIPアドレスを指定しなければならないとのこと。

RFC 2821 – I-D Tag:
http://tools.ietf.org/html/rfc2821#section-4.1.1.1

The argument field contains the fully-qualified domain name of the SMTP client if one is available. In situations in which the SMTP client system does not have a meaningful domain name (e.g., when its address is dynamically allocated and no reverse mapping record is available), the client SHOULD send an address literal (see section 4.1.3), optionally followed by information that will help to identify the client system.

参考: HELO/EHLOコマンドとその応答|メール配信技術用語
http://www.cuenote.jp/documents/smtp/000197.html

何か追加情報が無いかと、戻ってきたメールに添付されている「Delivery report」を開くと、以下の記述あり。

すぐ目に付いたのは先頭行の www。これはローカルなサーバー名に過ぎず、完全なドメイン名とは言えない。もしかしてHELOコマンドにwwwをクライアントホスト名として指定しているの?確信は無いものの念のため管理者に問い合わせたらビンゴ。PHPのモジュールPHPMailerの設定に不備があったとのこと。困ったものだ。

[PHP] NuSOAPでWebサービスを使う

ある会員管理のウェブアプリと連携したウェブサイトを構築する必要があり、そのウェブアプリではSOAPによるWebサービスを提供している。これまでWebサービスというと実装が楽という点でJSONを使うことが多かったので、SOAP通信に関してはいまいち不慣れ。どうしたものかとウェブアプリの説明を読み進むと、NuSOAPというツールキットを使ったサンプルが掲載されていた。NuSOAPを使うことで上位の実装はかなり簡単になりそう。というわけでNuSOAPの覚え書き。

公式サイト

SourceForge.net: NuSOAP – SOAP Toolkit for PHP – Project Web Hosting – Open Source Software
http://nusoap.sourceforge.net/

チュートリアル

以下のサイトにNuSOAPを使ったわかりやすいサンプルあり。以下に簡単なサンプルのコードだけ引用しておく。後半にはWSDLを使った実践的なサンプルもあるので、実際のコーディングにはかなり参考になる。

Simple Object Access Protocol
http://www.scottnichol.com/soap/index.html

Hello World サーバー側サンプル

Hello World クライアント側サンプル

一般的なSOAP通信方法

NuSOAPを使わない一般的な実装は以下サイトが参考になる。

【PHP】PHPでSOAP通信を行う方法
http://estpolis.com/2011/08/834.html