GolangでAWS Lambdaを試して、TwitterのいいねからURLを抽出するアプリを作った話

Tatsuki Koga
11 min readJan 17, 2018

--

背景 & 作ったもの

AWS LambdaのGolangサポートが始まったので使ってみようと思い、最近欲しいなーと思っていた小さなアプリを作ってみました。(アプリというかAPIですね笑)

僕はTwitterを情報収集ツールとしても使っているのですが、「このツイートのリンク、あとで読みたいな。。。」と思ってはいるものの、読む機会を逸してしまう、という経験がよくあります。 これを解決するために、TwitterのAPIを使って、自分が最近いいねしたツイートに含まれるURLを抽出するLambda functionをGolangで実装してAPI Gatewayと組み合わせて簡単ながら公開しました。

これを使うことでAPI Gatewayで公開されたAPIを叩けば一発で見たいリンク一覧を取得できます。 このリンクです。アクセスしすぎないでください…!

手順概要

  1. Twitter APIの使い方をさらう
  2. AWS Lambdaの使い方をさらう
  3. 上二つを結びつける

実際の作業

以下実際のコードを載せますが、全コードはGithubに上げています。

Twitter APIの使い方をさらう

Twitter APIを使うにはここからAppを適当に作成します。

[Keys and Access Token]から

  • Consumer Key
  • Consumer Secret
  • Access Token
  • Access Token Secret

を確認します。

GolangでTwitter APIを使うにはanacondaが便利です。APIの詳細な使い方は公式ドキュメントからみることができます。

今回はTwitter APIを使うこと自体は本筋ではないので、詳細な説明はしませんが、今回実現したいことを簡単に実現したいと思います。

まずanacondaのAPIクライアントの初期化を行う関数を以下のように定義します。

func newTwitterAPI() *anaconda.TwitterApi {
anaconda.SetConsumerKey(os.Getenv("TWITTER_CONSUMER_KEY"))
anaconda.SetConsumerSecret(os.Getenv("TWITTER_CONSUMER_SECRET"))
return anaconda.NewTwitterApi(os.Getenv("TWITTER_ACCESS_TOKEN"), os.Getenv("TWITTER_ACCESS_TOKEN_SECRET"))
}

ここでTwitter APIの情報を環境変数から読み込んでいます。普段は、.envファイルなどを用いてgodotenvで環境変数を管理していますが、今回はLambda内で環境変数を設定できるので、最も単純な方法で環境変数を読み込むように作りました。

次にいいねしたツイートからURLを取得する関数です。引数としてtweets, err := api.GetFavorites(v)tweetsが与えられることを想定しています。

var twitterHost = "twitter.com"func extractURLsFromTweets(tweets []anaconda.Tweet) (urls []string) {
for _, tweet := range tweets {
urls = append(urls, extractURLsFromTweet(tweet)...)
}
return
}
func extractURLsFromTweet(tweet anaconda.Tweet) (urls []string) {
entitiesUrls := tweet.Entities.Urls
for _, entitiesUrl := range entitiesUrls {
u, err := url.Parse(entitiesUrl.Expanded_url)
if err != nil {
continue
}
if u.Host == twitterHost {
continue
}
urls = append(urls, u.String())
}
return
}

簡単ですが、このような感じでURLのリストを取得できます。 ここはこれからも色々と弄れそうですね〜

AWS Lambdaの使い方をさらう

やっと本題のAWS Lambdaの使い方です。基本的にはここを参照してコーディングをしました。 基本的にはgithub.com/aws/aws-lambda-goを使います。今回は単純にgo getしてしまいます。

$ go get github.com/aws/aws-lambda-go/events
$ go get github.com/aws/aws-lambda-go/lambda

ここに載っていたAPI Gatewayを用いる簡単な例を載せます。

var (
// ErrNameNotProvided is thrown when a name is not provided
ErrNameNotProvided = errors.New("no name was provided in the HTTP body")
)
// Handler is your Lambda function handler
// It uses Amazon API Gateway request/responses provided by the aws-lambda-go/events package,
// However you could use other event sources (S3, Kinesis etc), or JSON-decoded primitive types such as 'string'.
func Handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
// stdout and stderr are sent to AWS CloudWatch Logs
log.Printf("Processing Lambda request %s\n", request.RequestContext.RequestID)
// If no name is provided in the HTTP request body, throw an error
if len(request.Body) < 1 {
return events.APIGatewayProxyResponse{}, ErrNameNotProvided
}
return events.APIGatewayProxyResponse{
Body: "Hello " + request.Body,
StatusCode: 200,
}, nil
}func main() {
lambda.Start(Handler)
}

基本的にはlambda.Start()に自作のHandlerを与えて完成!ってところでしょうか。

上二つを結びつける

上のコードを雛形として自分のコーディングをしていきます。 自分の書いたコードは以下です。

var api *anaconda.TwitterApifunc init() {
api = newTwitterAPI()
}
// Handler is AWS lambda handler
func Handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error)
// APIのparameterの設定
v := url.Values{}
v.Set("count", "20")
tweets, err := api.GetFavorites(v)
if err != nil {
return events.APIGatewayProxyResponse{}, err
}
// 前述の関数からURL一覧を取得
urls := extractURLsFromTweets(tweets)
if len(urls) == 0 {
return events.APIGatewayProxyResponse{}, errors.New("no new urls")
}
// JSON形式で結果を返すためにMarshal
bytes, err := json.Marshal(urls)
if err != nil {
return events.APIGatewayProxyResponse{}, err
}
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Body: string(bytes[:]),
IsBase64Encoded: true,
Headers: map[string]string{
"Content-Type": "application/json",
},
}, nil
}
func main() {
lambda.Start(Handler)
}

まずfunc init()内でapiを初期化します。 JSON形式で結果を返すためにstringのリストをencoding/jsonパッケージを用いてMarshalしています。 参照したブログにも

The github.com/aws/aws-lambda-go library automatically unmarshals the Lambda event JSON to the argument type used by your handler function. To do this, it uses Go’s standard encoding/json package, so your handler function can use any of the standard types supported for unmarshalling (or custom types containing those):

  • bool, for JSON booleans
  • float64, for JSON numbers
  • string, for JSON strings
  • []interface{}, for JSON arrays
  • map[string]interface{}, for JSON objects
  • nil, for JSON null

と記載されていますので、きっとMarshalもこれでいいでしょう。動いたし…笑 (events.APIGatewayProxyResponse.Bodyはstring型だったので少し悩みましたが…)

さあこれでコーディングが終わったので、デプロイです。

まず

$ GOOS=linux go build -o main
$ zip deployment.zip main

mainという名前のバイナリファイルをzip形式に圧縮します。

次に、AWSのコンソールから[サービス] -> [Lambda]を選択し、[関数の作成]を選択し、以下のように関数を作成します。

  • 名前: 適当に
  • ランタイム: Go1.x を選択
  • ロール: 適当に

次に上で作成したdeployment.zipをこの関数のコンソールページの[設定] -> [関数コード]からアップロードします。[ハンドラ]にはmainといれましょう。(バイナリファイルと名前を一致させるため) そして[設定] -> [環境変数]に

  • TWITTER_CONSUMER_KEY
  • TWITTER_CONSUMER_SECRET
  • TWITTER_ACCESS_TOKEN
  • TWITTER_ACCESS_TOKEN_SECRET

を入力します。 保存を忘れずに…!

最後に、[設定] -> [Designer]でAPI Gatewayをトリガーとして追加します。これでAPIとして外部に公開できます。(URLが発行される)

実際にアクセスしてみると期待通りの動きをしてくれていることがわかると思います!

リンク

非常に簡単にAPIのデプロイまですることができました。今後もちょくちょくいじってみたいと思います。最後まで読んでくださりありがとうございました。

GithubにStarでもつけていただけると幸いです!

ツッコミ、シェア等していただけると幸いです。

--

--