AWS Lambdaを使ってmicroCMSのコンテンツ更新通知をX(Twitter)に投稿したい
0.microCMS使ってる
自前のサイトでmicroCMSを使って(ほとんど)毎日日記を書いています。ちまちまと。
microCMSは国産のヘッドレスCMS、、、?らしいです、、、?もうよく分かんないけどなんか書いてます!調べて!各々!
Webサイト自体はお金をかけたくないのでAWSのS3の静的ホスティング機能とか使って構築してます。「S3 静的ホスティング 独自ドメイン」とかでググったらやり方は出てきます。たぶん…
フロントエンドのことはマジでさっぱり分からんなので、日記ページの構築については一切触れません。本当にずぶの素人。
1.IFTTTを用いた更新通知(旧)
さて。microCMSにはコンテンツ更新時にwebhook通知をしてくれる機能があります。ありがたいですね。
microCMS側で用意されている通知先としては、Slack,Chatworkとかとか。
僕は、コンテンツ更新のお知らせ先としてDiscordを使ってました。
Discordってwebhookを使った投稿が簡単にできて。つってもmicroCMS側で送信するwebhookのフォーマットを(Discordの仕様に合ったものに)変えられるわけではないので、
microCMS->IFTTT->Discordって感じでお知らせを送っていました。
「IFTTTって何ぞや」とか、IFTTTでwebhookを受信する方法とか、IFTTTからDiscordへwebhook送信する方法とかは割愛です。ググったら死ぬほど情報が出てきます。
ここで問題点がいくつか。
IFTTT -> Discordの連携がときたまうまくいかない
IFTTTからDiscordにwebhook投げるとき謎にratelimitに引っかかっているようでたまにDiscordに投稿されません。症状で調べると過去に解決された問題の様ですが、ほんとか?って感じです。
通知内容が毎日同じ
「IFTTT側でmicroCMSから受け取ったwebhookの中身(json)を読み取って、Discordの投稿内容に反映させる」みたいなことができない(課金したらできる?)ので、投稿内容が毎回同じになってしまいます。いつの何のお知らせか全くわからないし、日記の個別ページにリンクが張れない。
更新したときも通知が出る
microCMS側で設定できるのは「コンテンツの公開・更新時」のwebhookなので、誤字を直しただけの更新時にも通知が飛んでしまいます。消すのめんどっちいですね。
webhookだけではX(Twitter)でポスト(ツイート)できない
Discordなんて少数の人間しか見てないのでXでも告知しちゃいたい。承認欲求の塊くん
2.そうだ、Lambdaを使おう
AWSの認定資格を何個か取っていた(expireした)くせにAWS Lambdaを使ったことがありません。無料で構築できそうだしやってみましょう。
したいことは以下。
要件
webhookをトリガーにして日記更新通知をDiscordとX(Twitter)で行う
新規作成時のみ通知し、更新時には通知をしない
投稿内容にタイトルと個別ページへのリンクを含める
「API Gatewayは?」とお思いの方いませんか?特に認証とかしないなら、なくても作れるらしいです。
microCMSから送られるwebhookの中身(json)を知る
まずはmicroCMSから送信されるwebhookの中身を見ておきましょう。
microCMSの公式ドキュメントにも書いてある通り、devhookを使うとよさそうです。使い方は公式ドキュメントを見てくださいね
devhookで調べると、コンテンツ新規作成時に送られてくるjsonが以下の感じ。
{
"service": "hoge",
"api": "diary",
"id": "1234abdcef",
"type": "new",
"contents": {
"old": null,
"new": {
"id": "1234abdcef",
"status": [
"PUBLISH"
],
"draftKey": null,
"publishValue": {
"id": "1234abdcef",
"createdAt": "2023-12-23T14:21:47.985Z",
"updatedAt": "2023-12-23T14:21:47.985Z",
"publishedAt": "2023-12-23T14:21:47.985Z",
"revisedAt": "2023-12-23T14:21:47.985Z",
"title": "タイトル",
"content": "<p>投稿内容</p>"
},
"draftValue": null
}
}
}
サービス・API名、公開されたコンテンツID、投稿の種類とかとか。投稿とか場合分けに必要そうなものがパラメータにそろっています。たぶん。(各パラメータの意味については公式ドキュメント参照)
Lambda関数の作成
とにもかくにもAWS Lambdaの関数をつくっちゃいましょう。細かい調整はあとからしたらええねん。
AWSコンソールのLambdaから、「関数」>「関数の作成」ポチ
「一から作成」、テキトーな名前、「Python 3.11(このあとのレイヤーの都合です)」、「x86_64」にして「関数の作成」ポチ
はい、とりあえず箱はできました
関数URLの作成
設定タブを選択してもらって、「関数URL」っていうのを選択してください
そして真ん中の「関数URLを作成」をポチ
んで「NONE」を選択して、表示された内容を確認して「保存」をポチ
そうすると関数URLが作成されます。これがmicroCMSから受信するURLです。絶対に公開しないようにしましょう。とんでもない請求が来ることになると思います。
あとでmicroCMSに登録するURLなので安全なところにURLを保存しときましょう。
レイヤーの追加
このあとPythonでコードを書くわけですが、Discordへ投稿(webhook送信)するためにrequestsモジュール、X(Twitter)へ投稿するためにtweepyモジュールが必要なわけですが、lambdaをそのまま使うとインストールされておらずエラーになっちゃうのでレイヤーの追加で解決します。
1.requestsモジュール
Klayersを追加したらすぐ使えます。以下のサイトを参考に追加してください。(以下のページが参考にならない場合は「Klayers requests」等で調べてください)
KlayersにはPython3.11までしか用意されていなかったので3.11をつかっています。
2.tweepyモジュール
tweepyはKlayersにないので自分で何とかしましょう
↑のページがすごく簡単に手順説明されています。先人の知恵
tweepyのバージョンは最新(202312末時点の)でpython3.11で使えています。
「UNIX環境がなくてzipが作れないよ~」って人はEC2で無料のインスタンスを建てるとか、CloudShellでサクッとやっちゃいましょう
XのAPIを使えるようにする
↑のページが大体現状の申請方法に則しています。
API Key&Secret、Access Token&Secretを大事なところに保存できたらOKです。あとBearer Tokenももらっておきましょう。一応使います。一応。
X(Twitter)、開発者向けのいろいろが(本 当 に)しょっちゅう変わるのでもしかしたら上記のページも古いかもしれません。その際は各自調べてください。
Discord投稿用のwebhookURLを発行する
以下参考にどうぞ。ここで取得したURLも保存しておきましょう
環境変数を追加する
AWSコンソールのLambda関数をいじる画面まで戻りましょう。
「設定」タブから、「環境変数」を選択して、「編集」ポチ
環境変数の編集画面に遷移するので、「環境変数の追加」を押下すると
「キー」と「値」が入力できるようになると思います。ならなかったら壊れてる
以下の表通りにキーと値をぶっこんでいきましょう(「環境変数の追加」を押すとどんどん追加できます)
$$
\begin{array}{l|l}
\textbf{キー} & \textbf{値} \\\hline
\textbf{DISCORD\_URL} & \textbf{discordのwebhookURL} \\
\textbf{X\_API\_KEY} & \textbf{XのAPI Key} \\
\textbf{X\_API\_KEY\_SECRET} & \textbf{↑のSecret} \\
\textbf{X\_BEARER\_TOKEN} & \textbf{XのBearer Token} \\
\textbf{X\_TOKEN} & \textbf{XのAccess Token} \\
\textbf{X\_TOKEN\_SECRET} & \textbf{↑のSecret}
\end{array}
$$
noteで無理やり表作るとキモいな
最後に保存をお忘れなく。
コードを書く
コンソールの「コード」タブを選択するとコードが入力できる画面になると思います。
全然プログラマーじゃないですが、書きました
import json, sys, requests, os, tweepy
from datetime import datetime
from dateutil import tz
def lambda_handler(event, context):
body = json.loads(event.get('body'))
service = body["service"]
api = body["api"]
type = body["type"]
id = body["id"]
time_format = '%H'
time_zone = tz.gettz('Asia/Tokyo')
time_jst = datetime.now(tz=time_zone)
hour_jst = int(time_jst.strftime(time_format))
discord_url = os.environ['DISCORD_URL']
X_API_KEY = os.environ['X_API_KEY']
X_API_KEY_SECRET = os.environ['X_API_KEY_SECRET']
X_BEARER_TOKEN = os.environ['X_BEARER_TOKEN']
X_TOKEN = os.environ['X_TOKEN']
X_TOKEN_SECRET = os.environ['X_TOKEN_SECRET']
if service == "hoge" and api == "diary" and type == "new":
title = body["contents"]["new"]["publishValue"]["title"]
else:
return {
'statusCode': 400,
'body': json.dumps('invalid parameter')
}
sys.exit()
if hour_jst < 9:
diary_date = "昨日"
else:
diary_date = "今日"
diary_url = "https://hogehoge.jp/fuga.html?id=" + id
message = diary_date + "の日記が更新されました。\n" + title + "\n" + diary_url
headers_discord = {
"Content-Type": "application/json",
"User-Agent": "DiscordBot (private use) Python-urllib/3.11",
}
message_discord = {"content": message}
requests.post(
discord_url,
json.dumps(message_discord).encode(),
headers=headers_discord,
)
client = tweepy.Client(bearer_token=X_BEARER_TOKEN, consumer_key=X_API_KEY, consumer_secret=X_API_KEY_SECRET, access_token=X_TOKEN, access_token_secret=X_TOKEN_SECRET)
client.create_tweet(text = message)
return {
'statusCode': 200,
'body': json.dumps('OK')
}
ちょっとずつ説明。
まずはモジュールのインポート
import json, sys, requests, os, tweepy
from datetime import datetime
from dateutil import tz
以下lambda_handler関数内の実行になります。(トリガーされたときに動く関数です)
変数の宣言です。microCMSから受け取ったjsonをbodyとして、そこから必要な情報をどんどん宣言しています。サービス名とか、API名とか。
def lambda_handler(event, context):
body = json.loads(event.get('body'))
service = body["service"]
api = body["api"]
type = body["type"]
id = body["id"]
日本時刻の取得です。「時」だけとるためになんかめんどくさいことしてます。絶対もっとスマートに取得する方法があります。
time_format = '%H'
time_zone = tz.gettz('Asia/Tokyo')
time_jst = datetime.now(tz=time_zone)
hour_jst = int(time_jst.strftime(time_format))
先ほど環境変数に追加したものたちを変数に宣言しています。
os.environ['キー']で呼び出せるらしいです。
discord_url = os.environ['DISCORD_URL']
X_API_KEY = os.environ['X_API_KEY']
X_API_KEY_SECRET = os.environ['X_API_KEY_SECRET']
X_BEARER_TOKEN = os.environ['X_BEARER_TOKEN']
X_TOKEN = os.environ['X_TOKEN']
X_TOKEN_SECRET = os.environ['X_TOKEN_SECRET']
サービス名、API名が想定している(最初の方で取得したjsonと同様の)ものか、投稿内容は「新規作成(new)」かチェックして、OKならタイトルを取得、なんか違うなら400吐いてそのまま終了です。一応変なリクエストは通らないように。一応。
if service == "hoge" and api == "diary" and type == "new":
title = body["contents"]["new"]["publishValue"]["title"]
else:
return {
'statusCode': 400,
'body': json.dumps('invalid parameter')
}
sys.exit()
朝の9時より前だったら一日前の日付で投稿されるので(UTCだから)、先ほど取得した時間が9時より前だったら「昨日」、9時以降なら「今日」を宣言しておきます。(投稿文面に反映します)
if hour_jst < 9:
diary_date = "昨日"
else:
diary_date = "今日"
記事URLと投稿文面の作成です。
僕の場合記事URLはidから生成可能なので生成しています。ここは各々の環境に合わせて実装してください。
投稿文面も良い感じになるように。
今日の日記が更新されました。
[タイトル]
[URL]
↑みたいになります
diary_url = "https://hogehoge.jp/fuga.html?id=" + id
message = diary_date + "の日記が更新されました。\n" + title + "\n" + diary_url
DiscordとX(Twitter)に投稿します。ここはもういろんな人が説明書いてくれてると思います
headers_discord = {
"Content-Type": "application/json",
"User-Agent": "DiscordBot (private use) Python-urllib/3.11",
}
message_discord = {"content": message}
requests.post(
discord_url,
json.dumps(message_discord).encode(),
headers=headers_discord,
)
client = tweepy.Client(bearer_token=X_BEARER_TOKEN, consumer_key=X_API_KEY, consumer_secret=X_API_KEY_SECRET, access_token=X_TOKEN, access_token_secret=X_TOKEN_SECRET)
client.create_tweet(text = message)
最後に200返して終わり
return {
'statusCode': 200,
'body': json.dumps('OK')
}
コードを編集したら「Deploy」押下をお忘れなく!
microCMSにwebhook送信先URLを登録する
公式ドキュメント
↑にほとんど手順書いてあります。
microCMSコンテンツ管理画面の右上「API設定」から、
「webhook」>「追加」>「カスタム通知」でURL入力欄が出てきますのでそこにlambdaの関数URLを入力しましょう。最後に画面下部の「設定する」押下をお忘れなく。
動かしてみる
できちゃったらmicroCMSのコンテンツを追加して動くか確かめてみましょう。
ログはCloudWatch >「ロググループ」から見れると思います。
ロググループ名は「/aws/lambda/[関数名]」みたいな感じかと。
エラー吐いたときなんかはここからログを見て対処法をググりましょう。エラー文でググったらだいたい解決します。
ログ保持期間の設定
CloudWatchもタダではないのでログの保持期間を決めておきましょう。
ロググループを選択して、右上「アクション」から「保持設定を編集」を選択。
保持期間の設定画面が出るので、お好みの期間を選択して「保存」を押下しておいてください。
3.できたもの
↑みたいな感じで通知されるようになりました。みんな見てくれるといいな。
多分Lambdaの無料の範囲で使えるはずです。うれしいね
正直思ったよりは簡単でした。他いろいろできそうだからまたなんか作りたいな~みたいな気持ちもあったりなかったり
4.今後
通知先増やしてみたい
絵文字つけたい
そもそも日記ページのコードがおわってる問題どうにかしたい
スーパー飽き性の僕なので、いろんな人に見てもらうことで続く気がします。たぶん。暇なときにでも読んでってください。それでは…