これは今年もやるよ!AWS Lambda縛り Advent Calendar 2015の20日目の記事である。
AWS Lambdaからfacebookの自分自身もしくは特定ページのタイムラインへ投稿してみよう。ただ残念なことに、facebookにはincoming webhookの仕組みがない。そのためfacebookのアクセストークンを取得するにはかなり面倒な手順が必要となる。
検証環境
今回の検証は、以下の環境で実施した。
version | |
---|---|
OS | Ubuntu 14.04.3 (3.13.0-71-generic) |
python | 2.7.10 |
pip | 7.1.2 |
aws-cli | 1.9.12 |
lambda-uploader | 0.5.0 |
requests | 2.8.1 |
facebook-sdk | 0.4.0 |
AWS Lambda用IAMユーザとIAMロールを作成
AWS Lambda Developers Guide等を参考に、AWS Lambda用IAM UserとIAM Roleを作成する。
IAM User
今回は検証のため、AdministratorAccess権限を持ったIAM Userを作成した。本来はきっちり権限を制限した方が良い。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}
またこのユーザのAccess Keyを取得し、AWS CLIに設定した。
$ aws configure list
Name Value Type Location
---- ----- ---- --------
profile <not set> None None
access_key ****************J5NA shared-credentials-file
secret_key ****************uUMd shared-credentials-file
region ap-northeast-1 config-file ~/.aws/config
IAM Role
今回のLambda Functionは他のAWSリソースにはアクセスしないため、CloudWatch Logsへ出力できる権限だけ持つ lambda_basic_execution を適用したIAM Roleを作成した。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
後に必要となるため、Role ARN をメモしておく。
Facebookに投稿するアクセストークンの取得
Facebookのアクセストークンを取得するには、結構面倒な手順(facebookアプリの作成 & OAuth コールバックを処理するhttp endpointの作成)が必要だ。これはAWS Lambdaとは関係無い話でもあるため、手順は別のページにまとめている。そのページを参考にしながら、User Access TokenとPage Access Tokenを取得して欲しい。
なお今回は、取得したアクセストークンをeventとしてLambda Functionに与える形を取っているが、リクエスト毎に変更されるものでもないため、S3やDynamoDBなどに保存しても良いだろう。
Lambda Functionの作成
pythonを用いてLambda Functionを作成し、lambda-uploaderを用いてAWS Lambdaに登録する。
Lambda Functionの設定ファイル作成
lambda-uploaderで用いるlambda.json
を次のように作成する。
{
"name": "facebook_publisher",
"description": "Post a message to facebook timeline",
"region": "ap-northeast-1",
"handler": "facebook_publisher.lambda_handler",
"role": "arn:aws:iam::<AccountID>:role/lambda_basic_execution",
"timeout": 300,
"memory": 256
}
メモしておいたIAM RoleのARNをrole
に設定すること。
requirements.txtの作成
今回はFacebookのGraphAPIをラップするFacebook SDK for Pythonを利用する。このFacebook SDK for Pythonはrequestsに依存しているが、これら二つのライブラリをrequirements.txt
に記述しておけば、Lambda Functionと同梱してAWSにアップロードされる。
requests
facebook-sdk
Lambda Functionの作成
トークンとメッセージをeventとして入力されると、自分自身もしくはトークンを取得したfacebookページにメッセージを投稿するLambda Function(facebook_publisher.py)を作成する。
#!/usr/bin/env python
# -*- encode: utf-8 -*-
from __future__ import print_function
import json
import facebook
class Timeline:
def __init__(self, user_token, page_token):
self.user_endpoint = facebook.GraphAPI(user_token)
self.page_endpoint = facebook.GraphAPI(page_token)
def post_me(self, msg):
self.user_endpoint.put_object('me', 'feed', message=msg)
print('posted to my timeline: %s' % msg)
def post_page(self, msg):
self.page_endpoint.put_wall_post(message=msg)
print('posted to page timeline: %s' % msg)
def lambda_handler(event, context):
user_token = event['token']['user_access']
page_token = event['token']['page_access']
target = event['target']
message = event['message']
try:
timeline = Timeline(user_token=user_token, page_token=page_token)
if target == 'me':
timeline.post_me(message)
elif target == 'page':
timeline.post_page(message)
else:
print('%s is invalid' % target)
return False
except Exception as e:
print('[%s] %s' % (type(e).__name__, str(e)))
return False
return True
AWS Lambdaへアップロード
labmda-uploaderを利用して、Lambda Functionと関連ライブラリをAWS Lambdaへアップロードする。
$ tree .
.
├── facebook_publisher.py
├── lambda.json
└── requirements.txt
$
$ lambda-uploader
λ Building Package
λ Uploading Package
λ Fin
AWS LambdaからFacebookのタイムラインへ投稿
では、AWS CLIからAWS Lambdaへイベントを発行し、AWS LambdaからFacebookへメッセージを投稿してみる。
イベントファイルの作成
今回の検証では、他のサービスから呼び出されるのではなく、手動でイベントを発行するので、次のイベントファイル(event.json)を作成する。
{
"token": {
"page_access": "<取得したFacebookのPage Access Token>",
"user_access": "<取得したFacebookのUser Access Token>"
},
"target": "<me|page> 自分のタイムラインに投稿する場合はme、facebookページに投稿する場合はpage",
"message": "<投稿するメッセージ>"
}
イベント発火
では、AWS CLIからイベントを発火させよう。
$ aws lambda invoke --invocation-type RequestResponse --function-name facebook_publisher --payload file://event.json output.txt
{
"StatusCode": 200
}
発火時に指定したoutput.txt
を確認すれば、Lambda Functionからの戻り値がわかる。
vagrant@trusty64:~/python/aws_lambda$ cat output.txt
true
投稿結果
上記の手順により、自分自身のタイムラインやfacebookページのタイムラインへAWS Lambdaから投稿できた。
AWS Lambdaの結果は、CloudWatch Logsから確認できる。
まとめ
AWS Lambda以前の部分で苦労はあるものの、facebookの自分のタイムラインやページのタイムラインへAWS Lambdaからメッセージを投稿することができた。例えばreleaseタグをgithubへpushしたらfacebookページへ告知するなど、なんかイイカンジに使えないかと考えている。