Twitterブログ: #日本語ハッシュタグ
こちらの対応。
以前はハッシュタグの対応に正規表現を利用していたかと思いますが、最近はstatusからハッシュタグ情報がとれます。
GET statuses/show/:id | Twitter Developers
include_entities=trueでhashtagsがとれてその中にハッシュタグ情報が。
APIのバグかと思われますが、「ー」で終了しているタグがhashtagsにはいってきません。
api.twitter.com/1/statuses/show/90961863306772481.xml?include_entities=true
バグレポートすればそのうち直るのかな。
searchAPIにはそもそもハッシュタグ情報が含まれないので現状では解決策なし。
GET search | Twitter Developers
他のIntentから遷移し、URL表示しないブラウザを立ち上げ認証したらhogehoge。
public class OAuthActivity extends Activity {
final private String CONSUMER_KEY = "xxxxxx";
final private String CONSUMER_SECRET = "xxxxxx";
private String CALLBACK_URL = "myapp://oauth";
private OAuthAuthorization oauth;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WebView webview = new WebView(this);
setContentView(webview);
Configuration config = new ConfigurationBuilder().build();
oauth = new OAuthAuthorization(config);
oauth.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
oauth.setOAuthAccessToken(null);
try {
String url = oauth.getOAuthRequestToken(CALLBACK_URL).getAuthorizeURL();
webview.loadUrl(url);
} catch (TwitterException e) {
Toast.makeText(this, R.string.twitter_failed, Toast.LENGTH_LONG).show();
finish();
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Uri uri = intent.getData();
if (uri != null && uri.toString().startsWith(CALLBACK_URL)) {
String verifier = uri.getQueryParameter("oauth_verifier");
try {
AccessToken at = oauth.getOAuthAccessToken(verifier);
Log.d(getLocalClassName(), "screen_name = " + at.getScreenName());
Log.d(getLocalClassName(), "access_token = " + at.getToken());
Log.d(getLocalClassName(), "access_token_secret = " + at.getTokenSecret());
// hogehoge
Toast.makeText(this, R.string.twitter_login_success, Toast.LENGTH_LONG).show();
finish();
} catch (Exception e) {
Log.w(getLocalClassName(), e.getMessage());
Toast.makeText(this, R.string.twitter_login_failed, Toast.LENGTH_LONG).show();
finish();
}
}
}
}
AndroidManifest.xml該当箇所。
lunchModeで多重起動させない。
あとヒストリー残さない。
ブラウザから「myapp://oauth」でIntentが起動するように。
<activity
android:name=".OAuthActivity"
android:launchMode="singleTask"
android:noHistory="true"
>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="oauth" />
</intent-filter>
</activity>
UTF-8の範囲、BMP外はダメっぽい。
文字数はUTF-8で23文字までっぽい。
日本語
http://twitter.com/cockok/%E6%97%A5%E6%9C%AC%E8%AA%9E
日本語-18
http://twitter.com/cockok/%E6%97%A5%E6%9C%AC%E8%AA%9E-18
☃
http://twitter.com/cockok/%E2%98%83
http://twitter.com/cockok/%EE%98%BE
Snowflake: An update and some very important information – Twitter Development Talk | Google グループ
TwitterのIDが64bitになるとJavaScript等で問題が出るので対策を – F.Ko-Jiの「一秒後は未来」
事前に告知されていましたが、日本時間で11月5日の朝6時にstatus IDが41bitまでの値を利用するようになりました。
29535221410 以前
1036396793962496 以後
桁が大分増えましたね。
10月20日からレスポンスにstatus IDの文字列である「id_str」が含まれるようになりました。
2週間という短い期間での移行、そもそも告知自体知らない人も多かったかも知れませんが、対応せず「id」の値を利用していた場合不具合が生じる場合があります。
言語、OSによって内部で利用するstatus IDである「id」の型が異なると思いますが、32bitOS環境のPHPでJSONのデコードにjson_decode関数を利用している場合、「id」はfloat型としていました。
float型、浮動小数点数はマニュアルにあるように、およそ 10 進数で 14 桁の精度であり、新しいstatus IDは正確には扱えず以下のようになります。
$ php -r 'var_dump(1036396793962496);'
float(1.0363967939625E+15)
この値をMySQLのinteger型に保存すると、1036396793962500となります。
対策として、「id」ではなく「id_str」を利用するのですが、修正箇所が多く容易ではなかったため、一時しのぎとして取得したJSONに手を加え、「id」自体を文字列型として扱うことにしました。
$json = preg_replace('/"(id|in_reply_to_status_id)":(\d+)/', '"$1":"$2"', $json);
ツイート文字列にも影響がありますが、まず無いであろうと思われる文字列と、近いうちに「id_str」を利用するよう修正するということで。
twitter利用時に画像ストレージとして利用されることが多いTwitPic。
以前は、twitterのID、パスワードを投げる仕様でしたが、OAuth対応後は、token、keyを投げる仕様となっていました。
token、keyをまんま投げたら認証キーを削除できる程度で、ID、パスワード投げるのと変わらないんですけどね。
TwitPic Developers – API Documentation – API v2 » upload
v2ではtwitterとOAuth認証で利用するヘッダを「X-Verify-Credentials-Authorization」として、リクエストURIを「X-Auth-Service-Provider」としてヘッダに含めることでAPIを利用できるようになりました。
以下、PEAR::HTTP_Request2とPEAR::HTTP_OAuthを利用したサンプル。
<?php
require_once 'HTTP/OAuth/Consumer.php';
define('OAUTH_CONSUMER_KEY', 'xxxx');
define('OAUTH_CONSUMER_SECRET', 'xxxx');
define('OAUTH_TOKEN', 'xxxx');
define('OAUTH_TOKEN_SECRET', 'xxxx');
define('TWITPIC_KEY', 'xxxx');
try {
$consumer_request = new HTTP_OAuth_Consumer_Request();
$consumer_request->accept(new HTTP_Request2());
$consumer = new HTTP_OAuth_Consumer(OAUTH_CONSUMER_KEY,
OAUTH_CONSUMER_SECRET,
OAUTH_TOKEN,
OAUTH_TOKEN_SECRET);
$consumer->accept($consumer_request);
$resp = $consumer->sendRequest('https://api.twitter.com/1/account/verify_credentials.json', array(), HTTP_Request2::METHOD_GET);
$headers = $consumer->getLastRequest()->getHeaders();
$http_request = new HTTP_Request2();
$http_request->setHeader('X-Auth-Service-Provider', 'https://api.twitter.com/1/account/verify_credentials.json');
$http_request->setHeader('X-Verify-Credentials-Authorization', $headers['authorization']);
$http_request->setUrl('http://api.twitpic.com/2/upload.json');
$http_request->setMethod(HTTP_Request2::METHOD_POST);
$http_request->addPostParameter('key', TWITPIC_KEY);
$http_request->addPostParameter('message', 'test');
$http_request->addUpload('media', '/path/to/image');
$resp = $http_request->send();
$body = $resp->getBody();
$body = json_decode($body);
var_dump($body);
} catch (Exception $e) {
var_dump($e->getMessage());
}
twitterとのOAuth認証に必要なAuthorizationヘッダを生成するだけでよいので、実際はtwitter側にリクエストする必要はないのですが、ソース読んだところいろいろ面倒なのでとりあえずこんな形で。
Services_Twitter_r302262.diff
7904 byte
[md5] 89930ad5cf340af7a101ed74261d4a34
修正
sendOAuthRequest()メソッドでHTTP_OAuth_Exceptionがthrowされる問題
serchメソッドでhttp://search.twitter.comを利用しない問題
blocks/createメソッドでuser_id、screen_nameを指定できない問題
blocks/destroyメソッドでuser_id、screen_nameを指定できない問題
テストでOAuthの場合sourceはアプリケーションになる問題
テストでutf8テストが二重投稿になる問題
テストでOAuthの場合オブジェクトがHTTP_OAuth_Consumer_Responseになる問題
追加
blocks/bloking/idsメソッド (call $t->blocks->blocking_ids())
$ svn export -r 302262 http://svn.php.net/repository/pear/packages/Services_Twitter/trunk Services_Twitter
$ wget http://blog.cheki.net/Services_Twitter_r302262.diff
$ cd Services_Twitter
$ patch -p0 < ../services_twitter.diff
patching file Services/Twitter.php
patching file data/api.xml
patching file tests/900-exceptions.phpt
patching file tests/910-options.phpt
patching file tests/920-utf8.phpt
patching file tests/data/utf8-2.dat
patching file tests/setup.php
Services_Twitter
以前パッチが取り込まれたけど、Basic認証でしか確認してなくて、OAuthだとDELETEメソッドで問題がでてました。
その他なんか迷走してる感じだったので、パッチ送りたいなとPEARアカウントを取得しようと申請したのですがレスポンスがないので、とりあえずgithubにぶち込んだ次第です。
バグレポートもパッチ投げるにもPEARアカウントないと半匿名な感じになるので。
cockok's Services_Twitter at master – GitHub
リリース最新は0.6.2ですが、trunkが変更されてたのでそちらをfork。
あいきゃんとすぴーくいんぐりっしゅ。
もちろん書くのも。
ちなみに、DELETEメソッドででる問題は、RFC2616に以下のようなところ。
The DELETE method requests that the origin server delete the resource identified by the Request-URI.
HTTP/1.1: Method Definitions
こっちはPEAR::HTTP_OAuthの問題。
まさに誰得。
ブラウザベースは情報いっぱいあったんだけど、クライアントベースが見つからなかったので。
取得したaccess_token、access_token_secretを利用の仕方は下記で。
PEAR::HTTP_OAuthでxAuth – ぱんぴーまっしぐら
#!/usr/bin/env php
<?php
require_once 'HTTP/OAuth/Consumer.php';
define('CONSUMER_KEY', 'xxxxxxxxxxxxxxxxxxxx');
define('CONSUMER_SECRET', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
try {
$http_request = new HTTP_Request2();
$http_request->setConfig('ssl_verify_peer', false);
$consumer_request = new HTTP_OAuth_Consumer_Request();
$consumer_request->accept($http_request);
$consumer = new HTTP_OAuth_Consumer(CONSUMER_KEY, CONSUMER_SECRET);
$consumer->accept($consumer_request);
$consumer->getRequestToken('http://twitter.com/oauth/request_token');
$url = $consumer->getAuthorizeUrl('http://twitter.com/oauth/authorize');
if (!$stdin = fopen('php://stdin', 'r')) {
throw new Exception('stdin open failed');
}
echo 'Visit the following URL in your browser to authenticate twitter: ';
echo $url."\n";
echo 'Enter Twitter OAuth PIN: ';
$pin = trim(fgets($stdin, 64));
$consumer->getAccessToken('http://twitter.com/oauth/access_token', $pin);
echo 'access_token: '.$consumer->getToken()."\n";
echo 'access_token_secret: '.$consumer->getTokenSecret()."\n";
} catch (Exception $e) {
echo $e->getMessage().' in '.$e->getFile().' line '.$e->getLine()."\n";
exit(1);
}
Services_Twitter::sendOAuthRequestではHTTP_Request2_Exceptionをcatchしていますが、実際はHTTP_OAuth_Exceptionをcatchしなきゃいけない。
Services_Twitterはベーシック認証周りは大丈夫だと思いますが、OAuth周りはまともにテストされてないので、結構バグあります。