-
-
Save DanTup/2fbdde2b5c5a69520c56ba072ca28b7e to your computer and use it in GitHub Desktop.
Dart formatting sample
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'dart:convert'; | |
import 'dart:io'; | |
import 'package:convert/convert.dart'; | |
import 'package:crypto/crypto.dart'; | |
const String twitterApiBaseUrl = "https://api.twitter.com/1.1/"; | |
class TwitterApi { | |
final String consumerKey, consumerKeySecret, accessToken, accessTokenSecret; | |
Hmac _sigHasher; | |
final DateTime _epochUtc = new DateTime(1970, 1, 1); | |
TwitterApi(this.consumerKey, this.consumerKeySecret, this.accessToken, this.accessTokenSecret) { | |
_sigHasher = new Hmac(sha1, UTF8.encode("$consumerKeySecret&$accessTokenSecret")); | |
} | |
/// Sends a tweet with the supplied text and returns the response from the Twitter API. | |
Future<String> tweet(String text) { | |
var data = {"status": text, "trim_user": "1"}; | |
return _callApi("statuses/update.json", data); | |
} | |
Future<String> _callApi(String url, Map<String, String> data) { | |
var fullUrl = Uri.parse(twitterApiBaseUrl + url); | |
// Timestamps are in seconds since 1/1/1970. | |
var timestamp = new DateTime.now().toUtc().difference(_epochUtc).inSeconds; | |
// Add all the OAuth headers we'll need to use when constructing the hash. | |
data["oauth_consumer_key"] = consumerKey; | |
data["oauth_signature_method"] = "HMAC-SHA1"; | |
data["oauth_timestamp"] = timestamp.toString(); | |
data["oauth_nonce"] = "a"; // Required, but Twitter doesn't appear to use it, so "a" will do. | |
data["oauth_token"] = accessToken; | |
data["oauth_version"] = "1.0"; | |
// Generate the OAuth signature and add it to our payload. | |
data["oauth_signature"] = _generateSignature(fullUrl, data); | |
// Build the OAuth HTTP Header from the data. | |
var oAuthHeader = _generateOAuthHeader(data); | |
// Build the form data (exclude OAuth stuff that's already in the header). | |
var formData = filterMap(data, (k) => !k.startsWith("oauth_")); | |
return _sendRequest(fullUrl, oAuthHeader, toQueryString(formData)); | |
} | |
/// Generate an OAuth signature from OAuth header values. | |
String _generateSignature(Uri url, Map<String, String> data) { | |
var sigString = toQueryString(data); | |
var fullSigData = "POST&${encode(url.toString())}&${encode(sigString)}"; | |
return BASE64.encode(hash(fullSigData)); | |
} | |
/// Generate the raw OAuth HTML header from the values (including signature). | |
String _generateOAuthHeader(Map<String, String> data) { | |
var oauthHeaderValues = filterMap(data, (k) => k.startsWith("oauth_")); | |
return "OAuth " + toOAuthHeader(oauthHeaderValues); | |
} | |
/// Send HTTP Request and return the response. | |
Future<String> _sendRequest(Uri fullUrl, String oAuthHeader, String body) async { | |
var http = new HttpClient(); | |
return http.postUrl(fullUrl).then((HttpClientRequest request) { | |
request.headers.contentType = new ContentType("application", "x-www-form-urlencoded", charset: "utf-8"); | |
request.headers.add("Authorization", oAuthHeader); | |
request.write(body); | |
return request.close(); | |
}).then((HttpClientResponse response) { | |
return response.transform(UTF8.decoder).join(""); | |
}); | |
} | |
Map<String, String> filterMap(Map<String, String> map, bool test(String key)) { | |
return new Map.fromIterable(map.keys.where(test), value: (k) => map[k]); | |
} | |
String toQueryString(Map<String, String> data) { | |
var items = data | |
.keys | |
.map((k) => "$k=${encode(data[k])}") | |
.toList() | |
..sort(); | |
return items.join("&"); | |
} | |
String toOAuthHeader(Map<String, String> data) { | |
var items = data | |
.keys | |
.map((k) => "$k=\"${encode(data[k])}\"") | |
.toList() | |
..sort(); | |
return items.join(", "); | |
} | |
List<int> hash(String data) => _sigHasher.convert(data.codeUnits).bytes; | |
// TODO: Remove this hacky crap when percentEncode is fixed. | |
String encode(String data) { | |
return percent.encode(data.codeUnits) | |
.replaceAll('%30', '0') | |
.replaceAll('%31', '1') | |
.replaceAll('%32', '2') | |
.replaceAll('%33', '3') | |
.replaceAll('%34', '4') | |
.replaceAll('%35', '5') | |
.replaceAll('%36', '6') | |
.replaceAll('%37', '7') | |
.replaceAll('%38', '8') | |
.replaceAll('%39', '9') | |
.replaceAll('=', '%3D'); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment