🐝

Foursquare/Swarmアプリでチェックインしたとき、自動で𝕏(旧Twitter)にポストするWebアプリを作った

2023/10/11に公開

背景

  • 2023/10/2 : Swarmアプリのチェックインが𝕏にポストされていない?
  • 固有の現象かと思ったが、一般的な現象っぽい

https://twitter.com/4sqJPN/status/1684848727981527040

Webアプリの動作全体像

1.認証

  • 𝕏のポスト用token入手
  • Swarmの情報入手用token入手

2.ポスト
①SwarmAppなどでチェックイン
|            ↑
| ②HTTP POST      │ ③画像,共有用
↓ (認証するとpushくる)  ↓ URL入手
Webアプリ

|④投稿

𝕏

仕様

  • Swarmサーバに登録される全てのチェックインがポストされる(アプリ/OS(Android/iOS)には依存していない)
  • 画像アップロードを待つため、1ポストに約15秒かかる(バッファ込み)
  • 画像は、チェックイン時、最後に選択した画像1枚のみが𝕏にポストされる
  • 𝕏へのポストテキストは「I'm at ベニュー名 in 市町村 都道府県」、コメント時は「コメント(@ ベニュー名 in 市町村 都道府県)」
  • Swarmと𝕏のユーザトークンのみアプリ側で保持。アプリから連携切断されると無意味になる。チェックイン情報は保存しない(サーバコスト的にしたくない)

参考ページ/ポイント

各APIの振る舞い、OAuthをなんとなく理解して、サーバ用意して、なにかの言語が使えれば同様のものが作れると思います。

𝕏

FOURSQUARE

ドキュメントにたどり着くまでが大変だった・・

コスト

無料でなんとかしたい

FOURSQUARE

  • 毎月$200のフリークレジットがもらえる
  • Legacy Foursquare API : $1.00 / 1000 calls
  • $200で毎回写真つきのチェックインを何回APIで取得できるか計算: 200/2=200k calls/month
  • 200,000回 APIつかえる。100人がサービス使っていたら、月2000回、1日80回チェックイン

𝕏

  • 1,500ポスト/月/free plan
  • FOURSQUARE APIの200Kにくらべて、たったの1.5K...なんだけど、開発者ポータルでAPI消費量をみると消費していないように見える。謎
  • ↑調べたところ、API消費はユーザごとのようだ(2023/10/18)

Twitter Developer Potal

Webアプリ用サーバー

  • Google App Engine : ポストする数は多いかもしれないが、重い処理とかしてないので無料でいけるはず…

参考コード

GitHubのリポジトリをPublicにすればいいのだが、client IDなどをハードコーディングしているのと、tokenの扱いはセキュリティ上非公開にしたいので。

  • Swarm認証開始部分。$redirect_uriはFOURSQUAREの開発者ページでも指定できるが、コードに書いたURIが優先される。
function start_swarm_cert()
{
	// Foursquare API credentials
	$client_id = SWARM_CLIENT_ID;
	$redirect_uri = REDIRECT_URL;

	// Redirect the user to Foursquare for authentication
	$auth_url = 'https://foursquare.com/oauth2/authenticate' .
		'?client_id=' . $client_id .
		'&response_type=code' .
		'&redirect_uri=' . $redirect_uri;
	header('Location: ' . $auth_url);
	exit;
}
  • Swarm Callback部分。アクセストークンを入手。6割くらいChatGPTに書いてもらった。
function swarm_callback()
{
	// Foursquare API credentials
	$client_id     = SWARM_CLIENT_ID;
	$client_secret = SWARM_CLIENT_SECRET;
	$redirect_uri  = REDIRECT_URI;

	// Get the authorization code from the query string
	$code = $_GET['code'];
	
	// Prepare the POST request to exchange the code for an access token
	$url = 'https://foursquare.com/oauth2/access_token';
	$data = array(
		'client_id' => $client_id,
		'client_secret' => $client_secret,
		'grant_type' => 'authorization_code',
		'redirect_uri' => $redirect_uri,
		'code' => $code
	);
	
	// Initialize cURL session
	$ch = curl_init($url);
	
	// Set cURL options
	curl_setopt($ch, CURLOPT_POST, true);
	curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	
	// Execute cURL session and get the response
	$response = curl_exec($ch);
	
	// Close cURL session
	curl_close($ch);
	
	// Decode the JSON response
	$auth_data = json_decode($response);

	// Check if access token was obtained successfully
	if (isset($auth_data->access_token)) {
		// Access token obtained, you can use it to make authenticated requests to Foursquare API
		$access_token = $auth_data->access_token;
				
		//
		// アクセストークンを使っていろいろするところ
		//

	} else {
		echo 'Error: Unable to obtain access token';
	}
}
  • SwarmのユーザIDを返す(いろいろと必要)
function fetch_swarm_usr_id_by_token( $access_token ){
	
	$client   = new \GuzzleHttp\Client();
	
	$response = $client->request('GET', 'https://api.foursquare.com/v2/users/self?v=20220722&oauth_token='.$access_token);
	
	$json     = $response->getBody();
	$array = json_decode( $json , flags: JSON_OBJECT_AS_ARRAY );
	return $array['response']['user'];
}
  • ユーザの最新写真を返す(チェックインIDを使って、Pushされたときの画像かチェックが必要)
function fetch_latest_photo_array( $swarm_token ){
    
    $client   = new \GuzzleHttp\Client();
    $response = $client->request('GET', 'https://api.foursquare.com/v2/users/self/photos?v=20211111&oauth_token='.$swarm_token.'&limit=1');
    $json     = $response->getBody();
    $array    = json_decode( $json , flags: JSON_OBJECT_AS_ARRAY );

    return $array;
}
  • Swarmの共有用URLを返す
function fetch_swarm_shareurl( $checkin_id , $token )
{
	$client = new \GuzzleHttp\Client();
	
	$response = $client->request('GET', 'https://api.foursquare.com/v2/checkins/'.$checkin_id.'?v=20221011&oauth_token='.$token);
	$json     = $response->getBody();
	$array = json_decode( $json , flags: JSON_OBJECT_AS_ARRAY );

	return $array['response']['checkin']['checkinShortUrl'];
}
  • 𝕏にポストする関数
function tweet( $accessToken , $accessTokenSecret , $t , $i=FALSE ){

	$consumerKey = CONSUMER_KEY;
	$consumerSecret = CONSUMER_SECRET;

	$connection = new TwitterOAuth($consumerKey, $consumerSecret, $accessToken, $accessTokenSecret);
	
	//画像があればアップロード
	if($i){
        $media1 = $connection->upload('media/upload', ['media' => $i]);
        $parameters = [
            'text' => $t,
            'media' => [
                'media_ids' => [
                    $media1->media_id_string
                ]
            ]
        ];
    }else{
        $parameters = ['text' => $t];
    }

    $connection->setApiVersion('2'); # 画像アップロードはv1.1でやるため、connection->uploadのあとに書かないとダメ
    $result = $connection->post( 'tweets' , $parameters , true); # jsonにするため、trueをかかないとダメ
}

その他

Discussion