【24卒】DiscordBotを作っていたらWeb系上場ベンチャーのサーバーサイドエンジニアとして内定もらった話

# 挨拶
本項は[Nihon University Advent Calendar 2023](https://qiita.com/advent-calendar/2023/nihon-u)の4日目の記事になります。

皆さん、こんにちは！マグロです。
DiscordBotを趣味で作っています。

元々趣味程度で作成していて、就職はB3まで**地元のIT企業**と考えていましたが、苦節あって**都内のミドルベンチャー**に新卒でサーバーサイドエンジニアとして内定をいただくことができました。

今回は内定をいただくまでしたことを、成果物であるDiscordBotと共に説明します。
なかなか長いので、飛ばし飛ばし読むことをお勧めします。

# 目次
- [簡易自己紹介](#簡易自己紹介)
- [Discordサーバーを立てたきっかけ(21年3月)](#discordサーバーを立てたきっかけ21年3月)
- [DiscordBotの導入と活用の背景(21年5月)](#discordBotの導入と活用の背景21年5月)
- [初期のBot(21年6月)](#初期のbot21年6月)
- [2代目Bot(21年7月)](#2代目bot21年7月)
- [3代目Bot(22年4月)](#3代目bot22年4月)
- [4代目Bot(23年3月現役)](#4代目bot23年3月現役)
- [作る上で大切にしていたこと](#作る上で大切にしていたこと)
- [Web系ミドルベンチャーへ内定(23年4月)](#web系ミドルベンチャーへ内定23年4月)
- [Bot作ってて得たもの](#Bot作ってて得たもの)
- [やっておけばよかったこと](#やっておけばよかったこと)


# 簡易自己紹介
- 日本大学の学部4年生
- プログラミングは中学から(今年で7年目)
- プログラミングは好きだったが、個人で何か作ることには興味がなかった
- Botづくりは大学2年から
- **ハッカソン、コンテストなどの受賞経験なし**
- **内定者インターンを除くインターン経験なし**
- Bot作る前までの技術stack
  - C
  - Java
  - Google Apps Script

# Discordサーバーを立てたきっかけ(21年3月)
春休み中に幼稚園からの付き合いの友人たちと遊ぶ機会がありました。
丁度その日に某狩りゲーが発売され、みんなでやろうかという話になりました。
LINEグループを立ち上げ、通話しながらプレイしていました。

同時期に宇宙人狼も流行り、上記の友達に加えて他の小中の友人たちともプレイすることになりました。
しかしゲームの趣向が異なるため、同じLINEグループに入れるのも気が引けると思い、住み分け可能なDiscordサーバーを立ち上げました。

# DiscordBotの導入と活用の背景(21年5月)
住み分けはうまくいきました。
想定以上に人が入ってきたので、通知設定をメンションのみにしていました。

毎日ワイワイゲームをしていたのですが、通話開始のアナウンスをデフォルトでしてくれないので、集まりが悪く悩まされていました。
何か方法はないかと調べてみると、DiscordBotで実装する事例が多かったので、参考にしながら作りました。

# 初期のBot(21年6月)
- ライブラリ
  - Discord.py 1.7.3
- ホスト先
  - Replit

https://replit.com/


## 入退室通知
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/b664036c-dfff-4697-a71b-db8fce1c98c5.png)

ボイスチャンネルに入室するたびにeveryoneメンションで通知を飛ばします。

埋め込みメッセージはボイスチャンネルのステータスに応じて変化したら面白いなーと考え実装しました。
労力と複雑化を招くだけでしたが、、、

とはいえ通知が来るようになったので、集まりはよくなりました。

## 問題点
しかし運用していくうえで問題も出始めました。
- **返信のレスポンスが遅い**
通知設定をメンションのみにしていたので、メッセージが送られてきてもメンションがなければ通知されません。
この影響で人数が集まらず、ゲームをする約束が頓挫する状況が徐々に出始めました。
おとなしくすべてのメッセージに変更するかとも考えましたが、
  - 深夜帯に集まることが多く、その時間帯に多くの通知を送ることは迷惑
  - 参加の頻度、ゲームの趣向の違いで、興味のない通知をうるさく思う人もいる
  
  と考え、変更しませんでした。
  あとDiscordに慣れていない人も多かったのもあります。
- **Botが常時稼働してくれない(落ちることがある)**
当時ホスティングをreplitで行っていたため、仮想マシンに稼働率を大きく左右されていました。
DiscordBotのホスティング先として有名で429エラーを頻繫に起こしていました。
稼働から期間がたてばたつほど落ちやすくなり、集まりに影響が出始めました。

# 2代目Bot(21年7月)
- ライブラリ
  - Discord.js v13
- ホスト先
  - Glitch

https://glitch.com/

## LINEとのトークルーム連携
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/8449baf5-fd97-cf12-f5bd-df01266f4288.png)

テキスト上でのやり取りを活発にさせるため、LINEとの連携を始めました。
はじめはテキストのみでしたが、のちに画像も対応させました。
LINEとの連携を導入することで
- Discordに慣れていなくてもLINE上でチャットができる
- 通知が欲しい人だけトークルームに参加すればいいので、棲み分けができる

と通知関連の問題を一気に解決できました。
また細かいところでは```Discord.py```から```Discord.js```に変更し、ホスティング先も```Glitch```へ変更しました。
稼働率は(replitと比べ)安定するようになりました。

## 問題点(2代目)
- **LINEBotのメッセージ送信上限**
運用していて気づいたのですが、LINE公式アカウントには月1000件(現在200件)の送信上限がありました。
加えて1ユーザーへの送信につき1件なので、月に送信できるメッセージは100件未満と**活発になればなるほど機能しなくなる**変なジレンマが生まれました。
- **Botが落ちるときは落ちる**
replitよりは安定するようになりましたが、結局は同じオンライン統合開発環境なので、落ちるときは落ちます。
運用を続けていくとreplitと同様に落ちる頻度も増えてきたため、更に頭を抱える羽目になりました。

## 応急処置(21年11月)
### LINEに1日の送信上限追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/cc8f0333-7915-7a95-4964-19d2da720497.png)
LINEに送れるメッセージに1日ごとに上限を設けました。
抜本的な解決にはなりませんが、月ごとの稼働率は上昇しました。

大体ゲームをするかどうかのチャットなので1日1回やり取りできればいいかと考えていましたが、、、

## 大規模アプデを決意(22年1月)
そんなこんなで成人式の時期がやってきました。
成人式の日に全員で遊ぶことになり、**1日で500件以上**のメッセージのやり取りが発生しました。
Discord上の会話ではレスが付きづらいので、LINEグループのみでの会話になりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/e647ffab-43b7-821e-0523-79dfe02d7561.png)

加えて、当時はLINEスタンプと動画に非対応だったので下記のような悲しいログが残りました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/74b59a96-df6e-6bfc-662a-07364ae72a1f.png)

成人式自体滅茶苦茶楽しめましたが、
- LINE連携がほぼほぼ機能しなかったこと
- LINEグループ側にDiscord上の会話の抜け、Discord上に動画やスタンプが残らず、ログとして不完全になったこと

ことを悔しく思いました。
会話のログを見て、当時を思い出すのも楽しみの一つと考えていたのでログが不完全なのはかなり悔やんでいます。

これを機に、**大規模アップデートを決意します。**

# 3代目Bot(22年4月)
- ライブラリ
  - Pycord 2.0.0
  - Flask 2.0.3
  - line-bot-sdk 2.2.1
- ホスト先
  - Railway

- リポジトリ

https://github.com/maguro-alternative/discordbot_taityo

## ホスティング先変更
オンライン統合開発環境からPaaSにホスティングを変更しました。
PaaSにはRailwayを採用しました。

https://railway.app/

環境構築を行うため、```Docker```の使い方を勉強しました。
若干規模を大きくしたため、```cog```が使える```Pycord```へ移行しました。
またコードは```GitHub```のものを反映させるため、```git```の勉強も使いながらしました。

## LINE連携にLINE Notify採用(22年12月)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/8f1354be-1052-ce19-e6ed-9e067d8745f1.png)

テキスト、画像の送信を```LINE Notify```に移行しました。
この辺は記事にしているので、興味があればどうぞ。

https://qiita.com/maguro-alternative/items/6f57d4cc6c9923ba6a1d

## LINE連携に動画とスタンプを対応(22年7月)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/c4a53b11-a74c-368c-2978-452e8ed66a04.png)
成人式で悔しい思いをしたので動画にも対応させました。
LINEに動画を送るとYouTubeに動画がアップロードされます。
限定公開なのでプライバシー保護もされています。

こちらも記事にしてます。

https://qiita.com/maguro-alternative/items/618b140c1c034233ed36

## カラオケもどき
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/cc6c0166-7c38-487f-e05a-bbb0215dfe29.png)

```Python```を勉強していた時期に、ファイルの中身をいじって遊んでいた時期がありました。
```wav```ファイルをいじっていた際に、ふと

**discordのボイスチャンネルを録音して、原曲と比較すればカラオケみたいなことできんじゃね？？？**

と思い、作成しました。
```DTW```で比較していたので、ある程度音程があっていれば点数は高くなるようにしていましたが、精度はあまり高くないです。

友人からは

発想はいいけど、**そもそも夜中に歌うことが近所迷惑**

とのことで使われなくなりました。
残念、、、

## Web版VoiceVoxによる読み上げ機能(22年10月)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/42da368a-7607-5668-8d01-ddfbf3b538cb.png)

外出中や家族が近くで寝ていてしゃべれない、でも会話したいという要望がありました。
当初は公開Botを使用していましたが、制限にすぐに引っかかるため、自作した方がいいと考え作成しました。

受けがよさそうと考え、何かと話題だったずんだもんを使用できるVoiceVoxを採用しました。
Railway内での実装ではなく、処理負荷を考慮してWeb版にしました。

余談ですが、読み上げを通して皆タカが外れるのか、ずんだもんのログを見ると怪文書まみれになってます。

# 4代目Bot(23年3月現役)
安定してBotは稼働し、LINE連携は機能する。おまけに読み上げとカラオケがついているならもういいだろう。
ですが変な欲望が生まれます。

**DiscordログインつけてWeb上でカスタマイズ機能つけたら最強じゃね？？？**

いらん事のようですが、
- いちいちプログラム側の変更を施さずに済む
- 管理人の自分以外でも指定したユーザーが設定をいじれるようになる
- 設定が可視化されるので、プログラミングの知識がなくてもBotの動作がわかるようになる

というメリットがあります。

**ここからWebアプリへ進化を遂げます。**

## 技術選定
- サーバー
  - FastAPI
- Bot
  - Pycord
- フロント
  - React

## APIサーバー実装
LINEBotを実装する際に一応APIサーバーは立てていましたが、フロントに応じてほかのAPIも実装します。
```Flask```と```line-bot-sdk```を使用していましたが、型安全と高速性、クラスをカスタムしたかったので```FastAPI```を採用し、LINEBotのAPIサーバーもFastAPIに合わせる形でラッパーを作りました。

## PostgreSQL採用
設定情報を保存するため、データベースを使うことを決めました。
```asyncpg```のラッパーを作成し、BotとFastAPI双方で操作できるようにしました。

速度を重視していたので```asyncpg```を採用しましたが、今考えると```SQLAlchemy```でもよかったなーと思います。

## フロントエンド実装
FastAPI側のJinja2
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/073d5a16-e6fa-b482-ad93-79b71da653c5.png)
React
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/bc0c39b1-47eb-04e1-6a01-e766a77f2a7b.png)

フロントはサーバー側のFastAPI側のJinja2と、Reactの2種を用意しています。
もともとJinja2でのみ実装していましたが、
- form処理が煩雑になりすぎて保守しずらい
- Jinja2によってFastAPIの**静的な型宣言という強みを殺してしまう**

という点でReactをフロントとして新たに用意しました。
Next.jsにするか悩んだのですが
- Reactに比べ処理速度が遅い
- Nextの機能に甘えてしまい、**フロントを用意した意味がなくなりそう**

と考えReactにしました。

どちらも同じことができますが、React側はドメインが異なるため、サードパーティーcookieを許可しないとログインできません。
なので基本Reactで、動作しない場合はJinja2のフロントを使用するといった形になっています。

以下は動作してる動画です。

https://twitter.com/sigumataityouda/status/1714172519463485722

## 各種設定ページ追加
画像がデカいので折り畳みます。

<details>
<summary>ボイスチャンネルの入退出通知</summary>
ボイスチャンネルの入退室の際、通知するかどうかと、メッセージに追加するロールを選択できるようにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/62ba8a20-9102-04e6-fb34-4b9d8edea891.png)
以下のように適応されます。(途中で```@人間```を削除しています。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/83504553-a393-88bd-f87a-f56fa7ef552d.png)


</details>
<details>
<summary>LINEとのメッセージ共有</summary>
LINEへ送信しないメッセージを選べるようにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/aa06109a-7dfc-29f9-dc38-2784dc0b9f22.png)

データベース側にトークンを保存させることも可能です。
(暗号化も行っています。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/53734a93-7014-c520-25c3-c36df5a1713d.png)

</details>

<details>
<summary>Webhook投稿設定</summary>

新たに付け加えたものです。
Webhookを選択し、各種設定を行うことで最新情報をWebhookが投稿してくれるようになります。
Twitter,YouTube,niconicoに対応させていましたが、TwitterはAPIが有料化したため利用不可になりました。
~~(許さんぞイーロン)~~

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/f584cf20-4f1b-6836-0ce9-a9bdc41f6122.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/0151e2be-60f9-869c-cbed-5760142c1695.png)


</details>

<details>
<summary>管理者ページ</summary>

サーバー管理者のみがアクセスできるページです。
ここにユーザーやロールを追加することで、条件を満たすユーザーが設定を変更できるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2853914/2c819cc6-8f1e-d92d-a4c7-14251c408fda.png)

</details>

# 作る上で大切にしていたこと
## 稼働率
**まず機能しなければ意味がないので、第一に考えました。**
規模が大きくなるにつれ、Botの稼働率がサーバーのアクティブ率に直結するようになったため、落ちていないか日々心配しています。

GlitchからRailwayに移行した理由です。
また、Railwayに移行した際にエラーが起きないかどうかテスト環境も構築するようになりました。

## ユーザーファースト
上記と同様に、機能するためには使ってもらわなければいけません。
そのため、作ることより利用してもらうことを重視しました。
自分も利用者であるため、どういった機能があれば便利か、もっと利便性を高められないか、など使う側に立って考えました。

## 実装方針の定義
簡単に言うと**これだけは絶対に守る**という点です。
例：
- LINEBotのトークン類はすべて必ず暗号化を施して保存する。フロントには絶対に全文を送らない。
- LINEからDiscordに動画を送るのにYouTubeを採用したのは
  - 容量無制限
  - プライバシー保護ができる
  - APIあり
  - 安全なプラットフォーム

  という点が守れるから。

このように定義しておくことで、実装時のコーデイング時にある程度一貫性を保てるようになりました。

# 学習方法
## サンプルコードを試して読み取る
PythonやJavaScriptはBot作成に当たって初めて書きました。
言語の基礎を学ぶ前に、とりあえず動くコードを実行していました。
そこから
- どのように動作するのか
- どう変更すれば要件を満たせるか

といったことをしていました。
基礎をすっ飛ばすため、学習効率は悪いですが、
- **実例を交えて基礎を学べる**
- **アンチパターンを踏むこともあるが、あえて踏むことでデメリットを肌で感じることができる**

といったようにメリットデメリットを肌で感じることで理解が大きく進みました。

## 応用→基礎→応用の反復
上記のように応用から入るため、わからないことはたくさん出てきます。
もし学習が手詰まりしたら、逆に基礎へ戻ります。
ある程度つかんだら応用に戻り、また手詰まったら基礎に戻ります。

こうすることで、基礎と応用が結びつきやすくなりました。

# Web系ミドルベンチャーへ内定(23年4月)
当初は地元のIT関連の就職を考えていたのですが、3年の秋ごろにTwitterのDMで逆求人へのお誘いが来ました。
興味本位で一度面談してみると

**その技術力で地元はもったいなさ過ぎる。**

といわれました。

言っていることは本当なのか確かめるため、逆求人へ参加しました。
**それが内定先との出会いでした。**

場数を踏んで3月ごろに選考に挑みました。
面接も上記の開発で大切にしていたことと得たものをアピールしました。

結果、内定をいただきました。
面接を通して、自身の価値観に合っていると感じ、**オファー面談時に即承諾しました。**

オファー面談時に以下のような評価をいただきました。
- **成長が感じられた**
> 3代目のBotは正直微妙だったが、4代目のBotで、型宣言が行われてたり、動画アップロード時のリトライ処理が実装されていたり、非同期処理が追加されていたりと改良がおこなわれている点が非常によかった。
> 成長が感じられ、**ポテンシャルが高いと評価。**

- **課題に対するアプローチを考え実現する能力**
> 例えばLINEからDiscordに動画を送りたい場合に
> - 容量無制限
> - プライバシー保護ができる
> - APIあり
> - 安全なプラットフォーム
>
> といった非機能要件を定義して開発を行う姿勢がよい。
> **エンジニアは顧客を解決へ導くのが仕事で、ユーザーファーストに置いて最も重要な点。**

- **開発に終わりがないこと**
> 要望に合わせて機能の追加、改善を続ける点もいいが、利用され続ける限り開発に終わりがない点がよい。**ユーザーに真摯に向き合う真のユーザーファーストといえる。**

自身が大切にしていた点が評価されて内定が出ました。
フィードバックをいただいたときは、積み重ねてきたことが点と線としてつながったような感覚になって滅茶苦茶嬉しかったです。

# 今後の展望
## Golangへリプレイス
実は先日、discord.goの多機能Botのテンプレートを完成させました。

https://zenn.dev/maguro_alterna/articles/66c388d6429d63

Goの勉強がてらに作成してみたのですが、意外にも書きやすく読みやすいで、リプレイスすることを考えています。
移行の決め手に欠けているのが現状ですが、前向きに検討してます。

## テーブルの再設計
テーブルはsqlに起こすと以下のようになっています。
編集権限を示すテーブルになります。
```sql
CREATE TABLE IF NOT EXISTS guild_set_permissions (
    guild_id NUMERIC PRIMARY KEY,
    line_permission NUMERIC NOT NULL DEFAULT 8,
    line_user_id_permission NUMERIC[] NOT NULL DEFAULT '{}',
    line_role_id_permission NUMERIC[] NOT NULL DEFAULT '{}',
    line_bot_permission NUMERIC NOT NULL DEFAULT 8,
    line_bot_user_id_permission NUMERIC[] NOT NULL DEFAULT '{}',
    line_bot_role_id_permission NUMERIC[] NOT NULL DEFAULT '{}',
    vc_permission NUMERIC NOT NULL DEFAULT 8,
    vc_user_id_permission NUMERIC[] NOT NULL DEFAULT '{}',
    vc_role_id_permission NUMERIC[] NOT NULL DEFAULT '{}',
    webhook_permission NUMERIC NOT NULL DEFAULT 8,
    webhook_user_id_permission NUMERIC[] NOT NULL DEFAULT '{}',
    webhook_role_id_permission NUMERIC[] NOT NULL DEFAULT '{}'
);
```
一つにまとめているせいで、新規での追加の対応がしづらいという問題があります。
toxi法などを参考にして、再設計を考えています。

# Bot作ってて得たもの
## 友人関係
**Botを作る上での一番のモチベーションです。**
コロナ禍でも寂しい思いをせず、毎日楽しくゲームができたり、長期休みはみんなで集まって遊んだり、成人式を一緒に過ごしたりと、大学生活を確実に彩ってくれました。

## 開発物ができた
**学生エンジニアとして強力な武器を手に入れました。**
イベントでネタとして発表ができて、交流関係が一気に広がりました。

## サーバーサイドの知識
```Docker```による環境構築、```git```の使い方、SQLの構文、型宣言、OAuth2などなど、、、
**実践を通して使いどころやメリットを学んでいったので、理解が大きく進みました。**

# やっておけばよかったこと
## 長期インターン
技術を磨ける以外にも、エンジニアとして働くイメージを浮かべられるのは非常に貴重です。

周りはインターンを通してキャリアプランを形成している人が多く、得られるものは本当に多いのだなと思いました。
就活ではインターン経験がないためか具体的なキャリアプランが思い浮かばず、結構苦戦しました。

## サマーインターン選考
自分には高い壁と思い、サマーインターン選考はやりませんでした。
ですが、書類選考や面接があるので**就活の予行演習にもなる**という点でやっておくべきだったなと思います。

# Botに関するよくある質問
## 公開Botにしないの？
**しません。**
理由は
- 大規模トラフィックを想定していない
- LINEBotのトークン類など、機密情報の管理に責任を持てない

からです。
使いたい場合はGitHubにコードがあるのでそれで各自ホストしてください。
READMEに動かし方を記載しています。

## チーム開発じゃないの？
**同じ志を持つ人がいません。**
また仮にいたとしても、縛られるような感覚がして嫌なので個人でやります。

# 終わりに
DiscordBotしか作っていない大学生活でしたが、作っていくうえで大きく成長できたと思います。
ポートフォリオもそこまで充実していませんでしたが、大切なのは**結果ではなくそれに至るまでの過程**なのだと思います。

ポートフォリオに自信のない学生をよく目にしますが、どういった動機で、どういった選定で作ったのか、しっかりと筋が通っていれば必ず評価されます。

自身が作ったものに自信を持つことが大切です。

# Botのコード

- フロント

https://github.com/maguro-alternative/discordfast_front

- Bot+サーバー

https://github.com/maguro-alternative/discordfast