トップ画像
Minecraftでプレイヤーの参加をツイートするbotの作り方

執筆者: sasigume

最終更新: 7/23/2021

こんにちはさしぐめです。初投稿の今回はマインクラフトとTypeScriptを使ったbotについて書こうと思います。

Twitterアプリを申請する



Developer PortalでTwitterアプリの申請をしましょう。この時、botとして使いたいアカウントで申請するのがベストです。
間違えてメインアカで申請しちゃった場合は、OAuth1.0aを使ってサブアカのトークンを発行しましょう。(手順は割愛)

また、アプリの権限はRead & Write にします(DM権限は使いません)。

APIを用意する


Webhookの受け皿にはCloud Functions for Firebase (実質GCP)を使います。

Herokuでもいいんですが、個人的に

  • ログが見やすい
  • サーバーレスかつ応答が早い
  • Firestore等と連携できる
  • 外出先で確認できるアプリがある


という理由で選びました。

https://firebase.google.com/pricing/

なお、従量制プランを選択する必要があり、処理時間と回数が膨大になると請求される可能性もありますのでご注意ください。

ステップ1: 認証情報を用意


Firebaseプロジェクトを用意して、開発環境を用意します。ここの手順は日々変わるので割愛します。

また、Cloud Functions for Firebaseでは.env ではなくCLIで一つづつ環境変数を設定していきます。

後述するWebhookプラグインはリクエストのヘッダーをカスタマイズできません。仕方なくbodyでパスフレーズを送って認証します。

まずはオレオレパスフレーズを作ります。OAuthでもなんでもないただのパスワードなので、個人用途以外で使わないでください。

$ cd functions/

# 32桁のオレオレパスフレーズを生成
$ openssl rand -base64 32

# configに登録
$ firebase functions:config:set minecraft.auth="(オレオレパスフレーズ)"


もっといい生成方法があったら教えてください><

アプリの申請が通ると以下のキーが手に入ります。これらを全部コンフィグに登録します。

  • コンシューマキー
  • コンシューマシークレット
  • ユーザのトークン
  • ユーザのシークレット


$ firebase functions:config:set twitter.key="(コンシューマキー)"
$ firebase functions:config:set twitter.secret="(コンシューマシークレット)"
$ firebase functions:config:set minecraft.auth=(ユーザのトークン)"
$ firebase functions:config:set minecraft.auth="(ユーザのシークレット)"

# エミュレータ用ファイルを生成
$ firebase functions:config:get > .runtimeconfig.json


{
  "twitter": {
    "user_secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "user_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  },
  "minecraft": {
    "auth": "(オレオレパスフレーズ)"
  }
}

.runtimeconfig.json がこんな感じになります。これがローカルでテストする時の環境変数代わりになります。

functions/models/AdminConfig.d.ts

export interface AdminConfig {
  twitter: {
    key: string;
    secret: string;
    user_token: string;
    user_secret: string;
  };
  minecraft: {
    auth: string;
  };
}

TypeScriptの場合、こんな感じで型を作っておくと自動補完が楽です。

ステップ3: APIのエンドポイントを用意


さあいよいよAPIを作ります。

$ npm i twitter-api-client

twitter-api-client というライブラリを使います。

FirebaseなのでFirebaseが用意しているfunctions.https.onRequest を使いますが、別にexpressでもいいです。


functions/src/twitter/post.ts

import * as functions from 'firebase-functions';
import { AdminConfig } from '../models/adminConfig';
const config = functions.config() as AdminConfig;
import { TwitterClient } from 'twitter-api-client';

const client = new TwitterClient({
  apiKey: config.twitter.key,
  apiSecret: config.twitter.secret,
  accessToken: config.twitter.user_token,
  accessTokenSecret: config.twitter.user_secret,
});

const post = functions.region('asia-northeast1').https.onRequest(async (request, response) => {
  if (request.method !== 'POST') {
    response.status(405).json({
      message: `POSTメソッドを使ってください`,
    });
    return;
  }

  const body: {
    // マイクラサーバーのplugins/SimpleWebhooks/config.ymlで内容は定義してある
    tweet?: string;

    // コンフィグで定義
    auth?: string;

    // テスト用
    test?: boolean;
  } = request.body;

  functions.logger.debug(`Received body: ${JSON.stringify(body)}`);

  if (Object.keys(body).length == 0) {
    response.status(400).json({
      message: `bodyはJSONオブジェクトにしてください`,
    });
    return;
  }

  if (body.auth !== config.minecraft.auth) {
    response.status(401).json({
      message: `認証できませんでした`,
    });
    return;
  }

  let tweet = body.tweet;
  if (!tweet) {
    response.status(422).json({
      message: `ツイート本文を指定してください`,
    });
    return;
  }

  // testがtrueならツイートはしない
  if (body.test) {
    response.status(200).json({
      message: `テスト成功`,
      ...body,
    });
    return;
  }

  // 以下、Twitter APIと通信する
  try {
    await client.tweets
      .statusesUpdate({
        status: tweet,
      })
      .then(async (apiResponse) => {

        // https://developer.twitter.com/en/docs/twitter-api/v1/tweets/post-and-engage/api-reference/post-statuses-update
        // レスポンスにcreated_atがあればツイート成功
        if (apiResponse.created_at) {
          response.status(200).json({ message: `ツイート成功`, ...apiResponse });
          functions.logger.info(`Successfully tweeted: ${tweet}`);
        } else {
          response.status(500).json(
            apiResponse ?? {
              message: `Twitter APIからの応答がありませんでした`,
            }
          );
        }
      })
      .catch((e) => {
        response.status(500).json({
          message: `Twitter API呼び出し中のエラー: ${JSON.stringify(e)}`,
        });
      });
    return;
  } catch (e) {
    response.status(500).json({
      message: `その他のエラー: ${JSON.stringify(e)}`,
    });
  }
  return;
});

export default post;


ステップ3: フォルダにまとめる


expressならこんなことしなくて良いですが、Firebaseの書き方だとこうやってエンドポイントを整理します。

functions/src/index.ts

const twitter = require('./twitter');
exports.twitter = twitter;


functions/src/twitter/index.ts

export { default as post } from './post';


こうすることで、エンドポイントが /twitter-post になります。

ステップ4: テストとデプロイ


$ npm run serve


Firebaseのエミュレータが立ち上がり、 http://localhost:5001/(プロジェクト名)/asia-northeast1/twitter-post でテスト可能になります。

curl --location --request POST 'http://localhost:5001/(プロジェクト名)/asia-northeast1/twitter-post' \
--header 'Content-Type: application/json' \
--data-raw '{
    "tweet": "テストツイート",
    "auth": "(オレオレパスフレーズ)",
    "test": true
}'


こんな感じのリクエストを送ればテストできます。

$ npm run deploy


デプロイすると https://asia-northeast1-(プロジェクト名).cloudfunctions.net/twitter-post にAPIが公開されます。

マイクラサーバー側の設定


あとはマイクラサーバーからWebhookを送るだけです。

Webhookプラグインを入れる


https://github.com/Akenland/SimpleWebhooks

SimpleWebhooks プラグインを使います。PaperMCの1.16.5で動作を確認しました。

プラグインの設定


$ vi plugins/SimpleWebhooks/config.yml


config.yml

webhooks:
  join:
    twitter-join:
      url: https://asia-northeast1-(プロジェクト名).cloudfunctions.net/twitter-post
      json:
        tweet: "{PLAYER_DISPLAYNAME}が{SERVER_MOTD}に参加しました!"
        auth: "(オレオレパスフレーズ)"


こんな感じで、URLと送信するJSONを設定してください。YAMLマジで苦手。

豆知識: Cyberduckなら、サーバー上のファイルをローカルのように編集できます。宗教的こだわりがない場合は例えばVSCodeで開いて編集しましょう。

動作確認


YAMLを保存したら、マイクラサーバー側でwebhookをリロードします。

> webhooks reload


サーバー再起動の必要はなく、このコマンドを実行するだけです。

プレイヤーが参加すると、Firebaseの管理画面にログが出るはずです。



うちのサーバーではこんな感じでツイートしています。

---

以上、マイクラサーバーと連携するbotの作り方でした。

取得に失敗しました

2021年度 入部

Twitter GitHub YouTube