LoginSignup
9
4

More than 3 years have passed since last update.

Salesforce の Apex で Twitter API を叩いてみた

Posted at

この記事は、 Salesforce の Apex から Twitter Developer Platform 上に作成した Twitter Developer App を介して自身の Twitter アカウントにツイートを投稿する方法を紹介します。

Twitter Developer Platform とは

Twitter Developer Platform に登録(審査あり)すると、自身の Twitter アカウントに紐づく Twitter Developer App を作成できるようになります。App を作成すると様々な API にアクセスできるので、BOT を作成したり、マーケティングツールなどと連携しツイートやエンゲージメントの分析、投稿やキュレーションが可能になります。

スクリーンショット 2020-04-06 23.05.37.png

Twitter Developer API のプラン

Twitter Developer の API には、Standard/Premium/Enterprise などの API が存在し、それぞれ目的やできる事、
お値段が異なります。今回は、ツイートを投稿したいだけなので無料の Standard APIs を使わせて頂きます。

スクリーンショット 2020-04-06 21.54.33.png

認証

Standard APIs を利用するには、 HTTP リクエストの Authorization ヘッダーに OAuth 1.0a HMAC-SHA1 signature を作成し設定してあげる必要があります。そのため、 Twitter Developer Platform で App を作成した際に UI 上から以下の情報を発行しておきます。

  • OAuth Consumer Key
  • OAuth Consumer Secret
  • OAuth Token
  • OAuth Token Secret

Apex での OAuth1.0a 認証

通常、Salesforce から Apex で外部サービスの API をコールする際には、指定ログイン情報による認証を行います。
しかし、現在の指定ログイン情報は OAuth1.0a での認証方式をサポートしていません。
そのため、今回は Twitter の公式ドキュメントに従って Apex で記述する事にしました。

public class TwitterUtil {
    static final String ENCODING_SCHEME = 'UTF-8';

    public static HttpRequest signRequest(HttpRequest req, String consumerKey, String consumerSecret, String oauthToken, String oauthTokenSecret) {

        Boolean isMultipartPost = req.getMethod().toUpperCase() == 'POST' && req.getHeader('Content-Type').startsWith('multipart/form-data;');

        // Collecting parameters
        String nonce     = String.valueOf(Crypto.getRandomLong());
        String timestamp = String.valueOf(DateTime.now().getTime() / 1000);
        Map<String,String> parameters = new Map<String,String>();
        parameters.put('oauth_signature_method','HMAC-SHA1');
        parameters.put('oauth_version','1.0');
        parameters.put('oauth_consumer_key', consumerKey);
        parameters.put('oauth_timestamp', timestamp);
        parameters.put('oauth_nonce', nonce);
        parameters.put('oauth_token', oauthToken);
        if (!isMultipartPost) {
            parameters.putAll(getParameterMap(req.getBody()));
            parameters.putAll(getParameterMap(req.getEndpoint().substringAfter('?')));
        }

        // Creating the parameter string
        List<String> keyValues = new List<String>();
        for (String key : parameters.keySet()) {
            keyValues.add(percentEncode(key) + '=' + percentEncode(parameters.get(key)));
        }
        keyValues.sort();
        String parameterString = String.join(keyValues, '&');

        // Creating the signature base string
        String endpoint = req.getEndpoint();
        String baseURL = endpoint.contains('?') ? endpoint.substringBefore('?') : endpoint; 
        String signatureBaseString = req.getMethod().toUpperCase() 
            + '&' + percentEncode(baseURL) 
            + '&' + percentEncode(parameterString);

        // Getting a signing key
        String signingKey = percentEncode(consumerSecret) + '&' + percentEncode(oauthTokenSecret);

        // Calculating the OAuth signature
        Blob sig = Crypto.generateMac('HmacSHA1', Blob.valueOf(signatureBaseString), Blob.valueOf(signingKey));
        String oauthSignature = percentEncode(EncodingUtil.base64encode(sig));

        // Building the header string
        String header = 'OAuth' + 
            + ' oauth_consumer_key="' + consumerKey + '"'
            + ', oauth_nonce="' + nonce + '"'
            + ', oauth_signature="' + oauthSignature + '"'
            + ', oauth_signature_method="HMAC-SHA1"' 
            + ', oauth_timestamp="' + timestamp + '"'
            + ', oauth_token="' + oauthToken + '"'
            + ', oauth_version="1.0"';

        req.setHeader('Authorization', header);
        return req;
    }
    private static Map<String,String> getParameterMap(String parameterString) {
        Map<String,String> parameters = new Map<String,String>();
        if (String.isNotBlank(parameterString)) {
            for (String pair : parameterString.split('&')) {
                List<String> kv = pair.split('=');
                if (kv.size() == 2) parameters.put(kv[0], EncodingUtil.urlDecode(kv[1], ENCODING_SCHEME));
            }
        }
        return parameters;
    }
    private static String percentEncode(String s) {
        return EncodingUtil.urlEncode(s, ENCODING_SCHEME)
            .replace('+', '%20')
            .replace('*', '%2A')    
            .replace('%7E', '~');
    }
}

※ % エンコードは手抜きです。だれか正しく実装する方法を教えて下さい。

ツイートする

ツイートを投稿するには、 POST statuses/update の API を利用します。
一般に「ツイート」と呼ばれるものは、API 上では「status」と呼ばれるので注意が必要ですね。
リクエストが成功すると、自身の Twitter タイムラインにツイート本文が表示されます。

// ツイート本文
String tweetText = 'Hello Twitter!';

// Twitter Developer App で発行した情報
String clientKey = 'XXXXXXXX';
String clientSecret = 'XXXXXXXX';
String oauthToken = 'XXXXXXXX';
String oauthTokenSecret = 'XXXXXXXX';

// HTTP リクエストを作成
HttpRequest req = new HttpRequest();
req.setMethod('POST');
req.setEndpoint('https://api.twitter.com/1.1/statuses/update.json'); 
req.setHeader('Content-Type','application/x-www-form-urlencoded');
req.setBody('status='+ EncodingUtil.urlEncode(tweetText, 'UTF-8'));

// HTTP リクエストから Authorization ヘッダーを作成し設定する
req = TwitterUtil.signRequest(req, clientKey, clientSecret, oauthToken, oauthTokenSecret);

// リクエストを送信
Http http = new Http();
HttpResponse res = http.send(req);

// リクエストが成功したら結果をログに出力
if (res.getStatusCode() == 200) {
  System.debug(res.getBody())
}
9
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
4