18
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Firebaseを駆使してTwitterをbot化してみた

Last updated at Posted at 2018-09-23

#きっかけ・やったこと
Twitter apiの制限が厳しくなって、1アプリあたり300ツイートしかできなくなる(なった?)そうなので、
自分専用のbotアプリをひとつ作った。

#実装方針
Firebaseとapp engineでcloud functionを定期実行し、その関数からtwitter apiを叩く

#Firebaseプロジェクトの作成
まずはFirebaseでプロジェクトを作成するところから。Firebaseにアクセス。
以下の画面から、「プロジェクトを追加」を選択。(お仕事で使ってるプロジェクトも写ってるのでそこは隠してます)スクリーンショット 2018-09-15 19.12.00.png

すると次のようなポップアップが出るので赤丸のとこにチェックを入れてプロジェクトを作成。プロジェクトIDはあとで使うのでメモっておく。
スクリーンショット 2018-09-15 19.15.41.png

プロジェクトを作成できたら料金プランを従量制のBlazeにアップグレードする。(外部のAPIにアクセスできないので)

#定期実行のためのcronjobの作成
cloud functionを定期実行するサンプルが以下のページに掲載されていたので、そこを参考にする。
Cloud Functions for Firebase でジョブをスケジューリング(cron)する

##初期設定
上記のリンク先と内容はほぼ同じであるが、こちらでも改めて書いておく。
上記を参考にリポジトリを twitter-bot ディレクトリにクローンし
git clone https://github.com/firebase/functions-cron twitter-bot

そのディレクトリに移動
cd twitter-bot

そして初期設定色々
gcloud config set project your-project-id
cd appengine
npm install

これで初期設定は完了。
(ちなみに、チュートリアルの方にはgcloud app createも実行せよと書いてあるが、firebaseプロジェクトを作った段階ですでにプロジェクトができているため今回の場合は実行する必要がない、)

##スケジューリングの設定
cron.yaml を編集して実行スケジュールを設定する。以下のようにすれば毎日指定の時間にfunctionを実行してくれる

cron.yaml
cron:
- description: Push a "tick" onto pubsub every day
  url: /publish/daily-tick
  timezone: Asia/Tpkyo
  schedule: every day 12:00

そしてデプロイ
gcloud app deploy --project your-project-id

##pubsubトピックの発行
リポジトリのソースのままだと動かないので、appengine/app.jsに以下のエンドポイントを追加

app.js
// そもそもトピックが作られていなかったりすのでトピックを作成するエンドポイントを作成
app.post('/create', async (req, res) => {
  try {
    await pubsubClient.createTopic(`${req.body.name}`);
    res.status(200).send(`Published to ${req.body.name}`).end();
  } catch (error) {
    res.status(500).send('' + e).end();
  }
});

さらにトピックメッセージが空だとエラーになるので、そこも修正する。

app.js
app.get('/publish/:topic', async (req, res) => {
  const topic = req.params['topic'];

  try {
    await pubsubClient.topic(topic)
        .publisher()
        .publish(Buffer.from('{"hoge":"huga"}'));//なんでもいいから jsonStringをメッセージに乗せる

    res.status(200).send('Published to ' + topic).end();
  } catch (e) {
    res.status(500).send('' + e).end();
  }
});

これをデプロイする
gcloud app deploy app.yaml \cron.yaml

そしてさっき追加したエンドポイントに以下のbodyを乗せてPOSTリクエスト

post-body
{
  "name":"topicName"
}

これでトピックが新しく作られる。

#twitter開発者登録
Qiitaのこの記事を参考に、開発者登録を行う。
途中で使用目的を英語で求められてしまうため非常にめんどくさい。(300文字以上)

#Typescriptを使えるようにする
非同期処理を書きやすいのでTypeScriptを利用する。
functions 直下に src ディレクトリを作成、index.jsをこの中に移動する。
functions 直下に以下の内容の package.json を作成。パッケージ以外にも色々エイリアスを作成。

package.json
{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "lint": "tslint --project tsconfig.json",
    "build": "tsc",
    "deploy": "firebase deploy --only functions --project your-project-id",
    "logs": "firebase functions:log --project your-project-id"
  },
  "main": "lib/index.js",
  "dependencies": {
    "es6-promise": "^4.2.4",
    "eslint": "^5.2.0",
    "firebase-admin": "~5.13.0",
    "firebase-functions": "^2.0.5",
    "google-auth-library": "^2.0.0",
    "googleapis": "^33.0.0",
    "prettier": "^1.14.2",
    "tslint-plugin-prettier": "^1.3.0",
    "twitter": "^1.7.1"
  },
  "devDependencies": {
    "tslint": "~5.8.0",
    "typescript": "~2.8.3"
  },
  "private": true
}

ビルド用のコンフィグファイルも作成

tsconfig.json
{
  "compilerOptions": {
    "lib": ["es6"],
    "module": "commonjs",
    "noImplicitReturns": true,
    "outDir": "lib",
    "sourceMap": true,
    "target": "es6"
  },
  "compileOnSave": true,
  "include": [
    "src"
  ]
}

#実際に実行されるcloud functionの作成
以下のように2ファイルを作成

index.ts
import * as functions from 'firebase-functions';
import { twitter_credentials } from './twitter_credentials';
const Twitter = require('twitter');

export const tweet = functions.pubsub
  .topic('daily-tick')
  .onPublish(async event => {
    try {
      const client = new Twitter(twitter_credentials);
      await client.post('statuses/update', {status: 'test'});
    } catch (error) {
      throw error;
    }
  });
twitter_credentials.ts
export const twitter_credentials = {
  consumer_key: 'your_api_key',
  consumer_secret: 'your_api_secret_key',
  access_token_key: 'your_access_token',
  access_token_secret: 'your_access_token_secret'
}

credentialsはちゃんとgitignoreに入れておくように。
これを npm run build でビルドして、 npm run deploy デプロイ。

#動作結果
スクリーンショット 2018-09-23 14.24.09.png
つぶやけた。
あとは呟く内容をそれっぽくするだけなので省略。

#感想
記事書くのってすっごい時間かかるんだなぁと実感。もうちょっとスムーズに作れるようにしたい。
(コーディング1時間、記事執筆は数えることをやめた)

#宣伝
週末フリーランスやってます。お問い合わせはこちら
本当にこんなこと頼んでもいいのかな〜ということでも構わないので、「エンジニアにたのめばいいんじゃない?」と思ったあなたはぜひご連絡ください。

18
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?