Help us understand the problem. What is going on with this article?

TwitterでTILしたらGitHubに草が生える

はじめに

TIL(Today I Learned): 今日学んだこと

をtwitterのつぶやきで行おうと思いました

  1. #til というハッシュタグで学びをつぶやく
  2. 1日1回自分の投稿の #til ハッシュタグを拾いに行く
  3. GitHubにコミット

というのがいけないかな〜と思ったのがきっかけです

AWSで CloudWatch Events + Lambda Functions あたりでできそうな気がしたのでやってみます

参考

準備

  • AWSアカウント
  • Twitter開発者アカウント
  • TILをコミットしていくGitHubリポジトリ

Twitter APIを利用するためには開発者アカウントの登録が必要です

※ 利用目的とか審査とかあってやや面倒です…

手順

til-twitter.png

  1. Amazon CloudWatch Eventsで定期的にAWS Lambdaを起動
  2. LambdaでTwitter APIを叩いて #til ツイートを取得
  3. #til ツイートがあればGitHub APIを叩いてコミットする

アプリケーションの動きとしては上記の流れを想定します

なのでやらないといけないことは、

  1. Lambda Functionsの作成
    1. Twitter APIを叩いて #til ツイートを取得するjsを作る
    2. GitHub APIを叩いてコミットするjsを作る
    3. Lambdaに登録する
  2. 定期的にLambda Functionsを実行するCloudWatch Eventsの作成

という感じになります

1. Lambda Functionsの作成

最終的にはこんなjsができあがりました

https://github.com/halnique/til-twitter/blob/master/index.js

色々詰め込み過ぎだし改善の余地は大いにありそうですが、中身を見ていきます

1-1. Twitter APIを叩いて #til ツイートを取得する

Twitter開発者アカウントを登録してアプリ作成を行うと、以下の値が得られます

  • Consumer API key
  • Consumer API secret key
  • Access token
  • Access token secret

これらを環境変数から設定し、twitterモジュールを利用します

const Twitter = require('twitter');

const twitterClient = new Twitter({
  consumer_key: process.env.API_KEY,
  consumer_secret: process.env.API_SECRET,
  access_token_key: process.env.ACCESS_TOKEN,
  access_token_secret: process.env.ACCESS_TOKEN_SECRET,
});

あとはTwitter APIのドキュメントを見ながら、特定のツイートを取得するように実装します

  const nowString = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate() - 1}`;
  const params = {
    q: `(from:${process.env.ACCOUNT_NAME}) since:${nowString} ${TARGET_HASHTAG}`,
    count: process.env.MAX_COUNT || 5,
  };

  const tweets = await twitterClient.get('search/tweets', params).catch(() => []);

ACCOUNT_NAME は自分のTwitterアカウントを環境変数で設定します

最終的には毎日日付が変わったタイミングぐらいで実行させるので、前日以降のツイートに絞っています

※ jsの日付処理貧弱すぎない?みんなmoment.jsとか使うの?

実際はこのあとに各ツイートのハッシュタグを厳密にチェックしてますが、だいたい↑ぐらいでお目当てのツイートは取得できるはずです

1-2. GitHub APIを叩いてPRを作成する

これが地味に大変だった…

CLIなら git add して git commit して git push するだけのかんたんなお仕事ですが、APIでやろうとするとある程度踏み入った理解が必要になります

流れとしては blob を作って tree を作って commit を作って HEADのSHAを書き換える ということになります

    const prevRefsHead = await getRefsHead();
    const commitSha = prevRefsHead.object.sha;
    const prevCommit = await getCommit(commitSha);
    const blob = await postBlob(data[i]);
    const tree = await postTree(prevCommit.tree.sha, blob.sha, i + 1);
    const commit = await postCommit(commitSha, tree.sha, i + 1);
    await patchRefsHead(commit.sha);
    await sleep(1);

中身はそれぞれ対応したAPIを実行しているだけですが、こちらもドキュメントとにらめっこしながらパラメータと流れを調節しました

※ 連続して実行したときにコミットがうまくいかないことがあったので、1件ずつsleepするようにしてます

1-3. Lambdaに登録する

jsは何度もお試し実行すると思うので、ローカルでDockerとかで書くのがよいです

jsができあがったらLambdaに登録していきます

注意点として、Lambda上で外部モジュールは基本的にそのまま require することはできません

今回でいうと

const Twitter = require('twitter');

ですね

これは事前に Lambda Layers として作成しておくことで解決できます

image-lambda-layers1.png

zipファイルを直接アップロードするか、 S3 に上げておいてそれを利用することができます

zipファイルの中身は注意が必要で、例えば modules.zip を解凍したときに以下の構成になっている必要があります

$ ls -1 modules/
node_modules/

node_modules の中に利用したいモジュールが入っているイメージです

この構成になっていないと、Lambda Functionsの方で利用するのにうまくいきません

先にLambda Layersを作ったら、Lambda Functionsを作成していきます

image-lambda1.png

ランタイムはLambda Layersと同じになるようにします

Lambda FunctionsもzipファイルをアップロードしたりS3のファイルを利用することができますが、今回はそのまま作成したコードを貼り付けます

image-lambda2.png

環境変数をたくさん使うので、ぽちぽち登録します

image-lambda3.png

タイムアウト設定をデフォルトの3秒 -> 30秒に変更しておきます

image-lambda4.png

最後にLambda FunctionsにLambda Layersを追加すればOKです

image-lambda5.png

右上から適当なテストイベントを作ってテストしてみて、無事に動けばLambdaとしては完成です

2. 定期的にLambda Functionsを実行するCloudWatch Eventsの作成

image-cloudwatch-events3.png

image-cloudwatch-events2.png

以上

簡単に設定できました

Cron式については、日付が変わったころに前日分のツイートを取得するような感じで実行されるようにします

CloudWatch EventsのCron式で実行されるイベントは、UTCで誤差1分以内だそうです

前日に #til ツイートをしていれば、日本時間でだいたい翌日の朝9時頃にコミットができあがる想定ですね

実行結果

image-tweet1.png

image-commit1.png

コミットされました

これで無事にツイートするだけで草が生える環境ができあがりました

学び

  • node_modulesを Lambda Layers に追加しておくことで、Lambda Functionsで使えるようになって便利
  • Gitのコミットができるまでの流れ 10.2 Git Internals - Git Objects
  • CloudWatch Events のスケジュールでCron式を使う場合、日と曜日のどちらかは ? にする必要がある

Todo

  • Lambda FunctionsとLambda Layersへのデプロイを GitHub Actions で自動化したい人生だった…
  • GCPで Cloud FunctionsCloud Scheduler でも似たようなことができそうなのでやってみたい

まとめ

GitHubの草が生えているからといって活発に開発をしているとは限らないぞ

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした