はじめに
いらっしゃいませ。わたしは@shnskfjwrです。
わたしは毎日すごい量のserverless.ymlを書いていますが、だれにも読ませるつもりはありません。
ところで、わたしの趣味はおバイクとTwitterです。
基本はツーリングの休憩中にツイするのですが、運転中に面白いものを見つけたらその場でツイしたくなることもありますよね?
ですが、おバイクは車と違って基本的に両手が塞がってる上にグローブを装着してるので、その場ツイは難しいですね?
だからといって、わざわざ停車してツイする? そのネタ、そこまでするほど面白い?
というわけで、それをどうにかしました。
何をするか
Siriの音声入力を利用したTwitter投稿
必要なもの
- iOS12以上がインストールされたiPhone
- ヘルメット内でsiriを起動できる環境(airpods2がおすすめ)
- AWSアカウント
- Twitterアカウント
TwitterAPI
Twitterへの投稿はTwitterAPIを利用します。
TwitterAPIをコールするにはキー&トークンが必要となるため、下記サイトを参考に開発者登録を行います。
2020年度版 Twitter API利用申請の例文からAPIキーの取得まで詳しく解説
利用目的などは正直どう書いたか忘れてしまいましたが、正直に書いたら通った記憶があります。
なんやかんやで以下のキー情報を入手します。
- API key
- API key secret
- Access Token
- Access Token Secret
これらのキー&トークンを使って認証を行い、TwitterAPIをコールします。
AWS
Secrets Manager
Twitterキー情報はSecrets Managerに登録しておきます。今回は以下のように格納しました。
- twitter_api_keys
- api_key
- api_secret_key
- access_token
- access_token_secret
Lambdaのコード
認証情報をSecrets Managerから取得し、
リクエストに記載されたテキストをTwitterに投稿するシンプルな例です。
import json
import os
import boto3
import base64
import requests
from botocore.exceptions import ClientError
from requests_oauthlib import OAuth1Session
SECRET_NAME = os.environ.get('SECRET_NAME')
STATUS_URL = "https://api.twitter.com/1.1/statuses/update.json"
def get_secret():
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager'
)
try:
get_secret_value_response = client.get_secret_value(
SecretId=SECRET_NAME
)
except ClientError as e:
raise e
else:
if 'SecretString' in get_secret_value_response:
secret = get_secret_value_response['SecretString']
else:
secret = base64.b64decode(get_secret_value_response['SecretBinary'])
return secret
def handler(event, context):
try:
secret = json.loads(get_secret())
twitter = OAuth1Session(
secret['api_key'],
secret['api_secret_key'],
secret['access_token'],
secret['access_token_secret']
)
body = json.loads(event['body'])
tweet = body['tweet']
params = {
'status': tweet[:140]
}
res = twitter.post(STATUS_URL, params = params)
return {
'statusCode': res.status_code
}
except Exception as e:
print (e)
raise e
公開されているコードを参考にさせていただきました。
API Gateway & Lambda
使い慣れてる Serverless Framework を使ってサクッとデプロイします。
service: tourin_tweeting
frameworkVersion: '>=2.0.0 <3.0.0'
plugins:
- serverless-python-requirements
custom:
defaultStage: alpha
pythonRequirements:
layer: true
provider:
name: aws
runtime: python3.7
region: ap-northeast-1
profile: ${opt:profile, ''}
stage: ${opt:stage, self:custom.defaultStage}
environment:
TZ: Asia/Tokyo
STAGE: ${self:provider.stage}
SECRET_NAME: twitter_api_keys
logRetentionInDays: 7
iamRoleStatements:
- Effect: Allow
Action:
- 'secretsmanager:GetSecretValue'
Resource:
- '*'
apiKeys:
- defaultApiKey
usagePlan:
quota:
limit: 1000
period: DAY
throttle:
burstLimit: 500
rateLimit: 250
functions:
tweet:
handler: src/tweet.handler
layers:
- !Ref PythonRequirementsLambdaLayer
events:
- http:
path: /v1/tweet
method: post
cors: true
integration: lambda-proxy
private: true
デプロイ完了後、apiKeyがログに表示されますので控えておきます。
他の人に勝手にTwitterAPIを叩かれてしまうと 面白い 困るので、apiKeyを有効にして最低限のセキュリティは担保しています。
ここまでの工程でTwitterAPIをwrapするAPIが完成しましたが、これだけではなんの意味もありません。
音声投稿を行うためには、iPhoneのパワーを借りる必要があります。
ショートカット(ios)について
音声入力にはiOSに搭載されているSiriとショートカットを使います。
ショートカットは、iOS上で動作させられる簡易スクリプトのようなものです。
ショートカットはsiriから起動できるため、「HeySiri! {ショートカット名}!!」と話しかければ起動できます。
つまり、Twitterに投稿するためのショートカットを用意し、それをSiri経由で起動することで
完全音声制御のツイが実現するというわけです。
そしてネタばらしですが、今回の企画は以下の記事の内容を真似してみることから始まりました。
AirPodsだけでTwitter投稿も!SiriからTwitterにバックグラウンドで投稿する
こちらの記事を読んでええやん!!と真似してみたところ、ちょっとした問題が発生しました。
それは、IFFFTからのTwitter投稿は24時間で25件までに制限されていること!!(2020/9時点)
そんなんじゃぜーんぜん足りないYO!!というわけで、わたし専用のTwitterAPI基盤を構築する必要があったのです。
ショートカットのインストール
ショートカットはicloudを経由して自由に公開・配布が可能になっています。
先述の記事で配布されていて、わたしがカスタムした「バックグラウンドでツイート」と「ツイート」をiPhoneにインストールします。
うまく行かない場合は先述記事を参考に、「信頼されていないショートカットを許可」してください。
インストール後、「バックグラウンドでツイート」の最上部のテキスト(変数URL)をAPI Gatewayの実行URLに書き換えてください。
これで準備完了です。
動作確認
HeySiriに対応しているイヤホンをヘルメットに装着し、心の赴くままに叫びます。
「HeySiri! ツイート!!」
※iOS14以降、「StringAnswerは何にしますか?」と聞いてくるようになったので若干テンポが悪くなった。早く修正してほしい。
「テスト投稿!!」
「はい!!」
結果
実際におバイクで使ってみた感想
ゆっくり流してるときはきちんと聞き取ってくれます。
しかし、60km/h超えたあたりからは風切り音のせいでマイクの精度が低下してしまい微妙、
高速道路は絶望的という結果でした。それでも結構楽しい。
実は、写真を音声シャッターで撮影しそれを添付してツイする機能も開発済なのですが、それはまたの機会に……。