以前Weakened Fun Timeで作っていたスクリーンショットをDiscordに投稿するボットは、大分前にメンテナンスを放棄してしまった。というのも、以前のバージョンはコマンドライン上で動かすので使い勝手が悪く、かといってElectronで作るのも大変だしメモリを食うし微妙になってしまったためだ。
なので最近のWebアプリ風に、ブラウザから設定できるようにしようと思う。こうすれば、スマホやタブレット上で操作できるようになり、ゲーム中でも操作できるようになる。
やろうとしてること
- スクリーンショットフォルダに追加された画像をDiscordに投稿する
- 画像はPNG→JPGに変換する(FullHDだと5MBくらいなのでDiscordのレートリミットにすぐ引っかかる)
- 投稿先のサーバ・チャネルを設定できる
- 投稿のON/OFFを切り替えられる
- ゲーム中でも設定できる(スマホやタブレットから設定できる)
準備
最初に空のプロジェクトを立ち上げることから始める。設定画面を作るAngularのプロジェクトをclient
に作成し、設定画面をブラウザに送るサNestJSサーバのプロジェクトをserver
に作成する。
npm install -g @angular/cli
ng new client
npm install -g @nestjs/cli
nest new server
ブラウザでAngularの画面を表示する
まず、クライアント側の設定を変更する。client/angular.json
のoutputPath
を変更し、Angularプロジェクトのビルド結果を、server/public
以下に置くように設定変更する。
これをサーバで表示できるように、server/main.ts
で静的ファイルのパスを設定する。
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { join } from 'path';
async function bootstrap() {
const app = await NestFactory.create( AppModule );
app.useStaticAssets( join( __dirname, '..', 'public') ); // server/public にバンドルしたファイルを置く
await app.listen( 3000 ); // TODO: コマンドライン引数でポートを変えられるようにする
}
bootstrap();
この変更後、以下コマンドでサーバを立ち上げる。
npm run start
または
npm run start:dev
そして自PCからlocalhost:3000
を開くか、LAN内のPCから<IP>:3000
を開くかすると、以下のような画面が現れるはずである。これで、基本的な準備が完了する。
ブラウザからDiscordのBOTを操作する
試しに、DiscordのBotを操作する簡単なREST APIを設けてみる。ここまでに用意したAngular画面にボタンやテキストボックスを配置し、そこからNestJSで用意したREST APIを操作できるようにしてみる。
バックエンド抜粋: コントローラ
とりあえず簡易REST APIで操作できるよう、server
とmessage
という2つのエンドポイントを用意する。server
では、リクエストボディのactive
プロパティの値(true
かfalse
)で、接続・切断を切り替えられるようにする。message
では、message
プロパティの値をそのままDiscordのボットに送信する。ボットや送信先のチャネルはconfig.ts
内で定義してある。(GitHubには入れていない)
import { Controller, Get, Body, Res, Post, Patch } from '@nestjs/common';
import { ApiModelProperty, ApiResponse } from '@nestjs/swagger';
import { DiscordBotService } from './service'
export class MessageDto {
@ApiModelProperty( { required: true } )
message: string;
}
export class ServerStatusDto {
@ApiModelProperty( { required: true } )
active: boolean;
}
@Controller()
export class DiscordBotController {
constructor( private bot$: DiscordBotService ) {}
@Post('message')
async message( @Body() message: MessageDto ) {
await this.bot$.send( message.message );
}
@Patch('server')
async connect( @Body() serverStatus: ServerStatusDto ) {
if( serverStatus.active ) {
await this.bot$.connect();
} else {
await this.bot$.disconnect();
}
}
}
フロントエンド抜粋:操作画面
上記のバックエンドの2つのエンドポイントを操作するメソッドを用意し、簡易画面から操作できるようにする。
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component( {
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
} )
export class AppComponent {
message: string = '';
constructor( private http$: HttpClient ) {}
async connect() {
await this.http$.patch( '/server', { "active": true } ).toPromise();
}
async disconnect() {
await this.http$.patch( '/server', { "active": false } ).toPromise();
}
async send() {
await this.http$.post( '/message', { "message": this.message } ).toPromise();
this.message = '';
}
}
動作イメージ
コード: https://github.com/WeakenedPlayer/botty/releases/tag/0.0.3