Node.js で Discord の bot を動かしてみた

前回の記事で、Node.js による簡単な HTTP サーバを構築しました。
今回はそのサーバに付随するかたちで、Discord の bot を動かしてみました。

乗せる bot のソースはこちら。がんばって github にアップできるレベルにしました。
RiinaKw/discord_bot

ざっくりした動作説明

起動すると、一定時間毎に現在時刻を自動投稿します。
リマインダー機能があり、リマインダーの名前と予定日時を入力。サーバに参加している各ユーザごとに登録でき、予定日時を過ぎると自動投稿のタイミングでリプライが飛んできます。
またリプライでコマンドを送ると、自動投稿の間隔を分単位で設定できます。

bot が動作しているサンプル

実際に動作させている様子です。R18国道18号アイコンは私の別アカウント。テストのために複数アカウントでリマインダーを登録し、リプライが来るかチェックしてました。

ソースコードの説明

bot の仕組み

bot.js

最下層レイヤー、Discord との直接通信を行なっています。

behavior.js

中間レイヤー、自動投稿の時刻算出やコマンドの解析を行なっています。

app.js

最上位レイヤー、エントリポイント。具体的なリプライの内容、リマインダーの参照・登録・削除を行なっています。

model/reminder.js

MariaDB との通信・SQL の生成を行なっています。


そして今回ハマったのが、Dockerfile の記述方法。Web サーバと bot の両方を同時に動かすための設定で手間取りました。

version: "3"

services:

  node.discord.riina-k.dev:
    build: .
    restart: on-failure
    container_name: node.discord.riina-k.dev
    environment:
      VIRTUAL_HOST: discord.riina-k.dev
      LETSENCRYPT_HOST: discord.riina-k.dev
      LETSENCRYPT_EMAIL: riinak.tv@gmail.com
    volumes:
      - ./app:/usr/src/app
      - ./bot:/usr/src/bot
    networks:
      - front_bridge
      - backend_mysql_bridge
      - admin_bridge

networks:
  front_bridge:
    external: true
  backend_mysql_bridge:
    external: true
  admin_bridge:
    external: true

こんな感じで docker-compose にファイルのマウントを任せたのですが、どうやらうまく行ってない。(よくよく調べたら、前回の Web サーバの時点でうまく行ってなかった様子)
npm install がうまく動作していなかったので、ファイルのコピー自体を Dockerfile に任せました。

FROM node:13

# modify timezone
RUN unlink /etc/localtime
RUN ln -s /usr/share/zoneinfo/Japan /etc/localtime

# file copy
WORKDIR /usr/src
COPY ./app ./
COPY ./bot ./

# discord bot
WORKDIR /usr/src/bot
RUN npm install

# application dir
WORKDIR /usr/src/app
RUN npm install

# execute
RUN npm install -g forever
EXPOSE 8080
CMD [ "forever", "server.js" ]

そして、forever で呼んでいる server.js に Web サーバの機能を残したまま bot のエントリポイントを呼び出すよう修正。

'use strict';

const server1 = require('./http');
const server2 = require('../bot/src/app');

2つのサーバのインスタンスを作成、require だけで使えるようにしました。(./http は前回作った Web サーバ)
その結果、Web サーバと bot のログが混在する状況になってしまいましたが、今後の課題ということで。


ところで今回の bot は3層レイヤー構造となっているのですが、下のレイヤーを呼ぶときに、上のレイヤーのインスタンスを渡すという方式を取りました。

class App {
  // 略
}

const app = new App;
require('./behavior')(app);

module.exports = app;
let app;

module.exports = a => {
  app = a;
};

class Behavior {
  // 略
}

require('./bot')(new Behavior);
let behavior;

module.exports = b => {
  behavior = b;
};

// 略

const token = require('../config/token');
client.login(token);

下層レイヤーで定義された関数を上層レイヤーで require し、その引数に上層レイヤーのインスタンスを渡すことで、下層レイヤーは上層レイヤーの構造を意識せずに実行できる。
これが「依存性注入」の概念で合ってますかね?

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です