Skip to content

Instantly share code, notes, and snippets.

@DanTup
Created August 5, 2016 17:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DanTup/2fbdde2b5c5a69520c56ba072ca28b7e to your computer and use it in GitHub Desktop.
Save DanTup/2fbdde2b5c5a69520c56ba072ca28b7e to your computer and use it in GitHub Desktop.
Dart formatting sample
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