Dartの勉強がてらTwitter OAuthライブラリを作成しました。twitter_1userです。同じようなものがいくつかあったのですが、どうもpostがエラーになったり、日本語でエラーになったりするのでつくってみました。
pubで公開しました
pubにはスコアがあるみたいで、現状めっちゃ低い。
https://pub.dev/packages/twitter_1user#-analysis-tab-
説明を長くして、exampleを追加したら22点上がるのかな?
ライブラリの内容
- Consumer Key, Consumer Secret, Access Token, Access Token Secret の4つを入れると、すぐにTwitter APIに接続できて、つぶやいたり、つぶやき一覧を取得したりできるようになります。
- Access Token, Access Token Secretは、Twitterアプリの詳細ページから取得していただく想定です。
- ですのでシングルユーザー向けアプリに最適です。
このライブラリの仕様
主に下記2つに書いてあるとおりに作ったつもりです。
使い方の例
import 'package:twitter_1user/twitter_1user.dart'; main(List<String> arguments) async { Twitter twitter = new Twitter('CONSUMER KEY', 'CONSUMER SECRET', 'ACCESS TOKEN', 'ACCESS TOKEN SECRET'); String response = await twitter.request( 'get', 'statuses/user_timeline.json', {}); var tweets = jsonDecode(response); print(tweets[2]); response = await twitter.request('post', 'statuses/update.json', {'status': 'Hello!'}); print(response); }
ライブラリのソースコード
lib/twitter_1user.dart
import 'dart:convert'; import 'dart:io'; import 'dart:math' as math; import 'package:convert/convert.dart'; import 'package:crypto/crypto.dart'; class Twitter { static const String _baseUrl = 'https://api.twitter.com/1.1/'; final String api_key, api_secret, access_token, access_secret; Map<String, String> authData; Twitter(this.api_key, this.api_secret, this.access_token, this.access_secret); void initAuthData(api_key, access_token) { authData = {}; authData['oauth_consumer_key'] = api_key; authData['oauth_token'] = access_token; authData['oauth_signature_method'] = 'HMAC-SHA1'; authData['oauth_version'] = '1.0'; } Future<String> request( String method, String path, Map<String, String> requestData) async { initAuthData(this.api_key, this.access_token); final String url = _baseUrl + path; method = method.toUpperCase(); authData['oauth_timestamp'] = _timestamp(); authData['oauth_nonce'] = _nonce(); authData['oauth_signature'] = _signature(method, url, requestData); String authHeader = _headerString(); return await _request(method, url, authHeader, requestData); } Future<String> _request(String method, String url, String authHeader, Map<String, String> data) async { final List<String> list = data.keys.map((key) => "$key=${_per(data[key])}").toList(); String queryString = list.join('&'); if (method == 'GET') url += '?' + queryString; final HttpClient client = new HttpClient(); final HttpClientRequest request = await client.openUrl(method, Uri.parse(url)); request.headers ..contentType = new ContentType('application', 'x-www-form-urlencoded', charset: "utf-8") ..add("Authorization", authHeader) ..add('Connection', 'close'); if (method == 'POST') request.write(queryString); final HttpClientResponse response = await request.close(); String result = await response.transform(utf8.decoder).join(""); client.close(force: true); return result; } String _nonce() { math.Random rnd = new math.Random(); List<int> values = new List<int>.generate(32, (i) => rnd.nextInt(256)); return base64Encode(values).replaceAll(new RegExp('[=/+]'), ''); } String _signature( String method, String url, Map<String, String> requestData) { Map<String, String> data = {...authData, ...requestData}; List<String> list = data.keys .map((key) => "${_per(key)}=${_per(data[key])}") .toList() ..sort(); String parameters = _per(list.join('&')); String signatureBaseString = "$method&${_per(url)}&$parameters"; String signatureKey = "${_per(api_secret)}&${_per(access_secret)}"; Hmac hmacSha1 = new Hmac(sha1, utf8.encode(signatureKey)); List<int> signature = hmacSha1.convert(utf8.encode(signatureBaseString)).bytes; return base64.encode(signature); } String _per(String str) => percent.encode(utf8.encode(str)); String _timestamp() { double sec = new DateTime.now().millisecondsSinceEpoch / 1000; return sec.floor().toString(); } String _headerString() { List<String> list = authData.keys .map((key) => "${_per(key)}=\"${_per(authData[key])}\"") .toList(); return 'OAuth ' + list.join(', '); } }