BIZ

DartでTwitterライブラリを作ってpubに公開しました

Dartの勉強がてらTwitter OAuthライブラリを作成しました。twitter_1userです。同じようなものがいくつかあったのですが、どうもpostがエラーになったり、日本語でエラーになったりするのでつくってみました。

https://github.com/edo1z/twitter_1usergithub.com

pubで公開しました

f:id:edo1z:20190830055109p:plain

https://pub.dev/packages/twitter_1userpub.dev

pubにはスコアがあるみたいで、現状めっちゃ低い。

https://pub.dev/packages/twitter_1user#-analysis-tab-

説明を長くして、exampleを追加したら22点上がるのかな?

f:id:edo1z:20190830053440p:plain

ライブラリの内容

  • 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(', ');
}
}