BomuLeagueで実現出来たコト

# はじめに
BomuLeagueというSplatoon2,3のリーグ制大会を運営してきた中の一人として、私が実現出来たコトと出来なかったコトをポートフォリオ的な形で、自己満足的に書き綴ります。殴り書きのような感覚で、思い出しながら書いているので内容は薄いです。また、前提として私は非IT・文系の素人です。大会運営という名目のもと、システム・プログラミングについての勉強をしながら自分がこうしたらいいんじゃないかとできることをやってきただけなので、大したことはないし、間違った知識等あると思います。温かい目で見ていただけるとありがたいです

# BomuLeagueで実現したかったコト
## BomuLeagueとは
BomuLeagueは毎月開催のリーグ戦という大会フォーマットで、Splatoonを熱心にプレイするアクティブユーザーの中でも特にボリューム層(XP21~25?)に向けて、上位層でなくとも同等程度の実力(以降、ウデマエ)の人と熱く対抗戦や試合等を遊べる環境を提供することと、そういったユーザー方のウデマエの向上を目的に運営されてきました(※あくまでも私の見解です)。

## 問題点
BomuLeagueには運営していく上で以下の問題があると考えていました
1. スマーフ行為(格下狩り)
BomuLeagueのような同等程度のウデマエの人達で集めて試合を行う、いわゆる「制限大会」にはどうしてもスマーフ行為の問題が付き物です。スマーフ行為が横行すると、大会運営も被害を受けたユーザーも負担を抱えるのは必至で、大会離れやSplatoonというゲームそのものからのユーザ離れに繋がるので極めて重大な問題です
2. 運営の負担
BomuLeagueの特性上、毎月参加者を募り、その受付をし、参加者名簿を作成し、リーグ分けをし・・・と膨大な仕事量をこなす必要がありました。また、人が集まる一種のコミュニティですから、当然トラブルは起きます。このような対応までも含めると非常に激務となり、大会運営に支障をきたす問題となります
3. 大会参加ハードル
BomuLeagueやほかの大会も含めてですが、ある程度成熟したコミュニティとして運営されている大会等は、参加方法等が煩雑になる傾向があり、新規のハードルは高くなってしまいがちだと思います。そのような新規の流入が途絶えれば、コミュニティとしての維持・発展はとても難しくなります

## 解決策及びしたいコト
以上の問題を踏まえ、解決策と、解決策以上にBomuLeagueを発展させるために「したいコト」を考えました
1. 自動受付システム
毎月行う、募集→受付→大会準備→大会開催までの一連の流れのうち、人がやらなくてもいいことは自動化し、負担を軽減する目的でシステムを確立しようと考えました。また、直感的に使える設計や使い方を視覚的に分かりやすく伝えるために動画等を使用して、参加ハードルを低くする目的も兼ねています
2. 適正なウデマエ確認方法
上記の自動受付システムを使ったものになりますが、適正なウデマエであるかを確認できるシステムがあれば、安心してユーザーがSplatoonを遊べる環境が提供できると考えました。
3. 対抗戦サーバー
BomuLeagueでは基本的にBomuLeagueが開催するリーグの試合に合わせて、練習として近いウデマエのチームと対抗戦をする目的で大会用Discordサーバーに対抗戦をできるチャンネル・スレッドを用意していましたが、大会に参加する・しないに関係なく、広く色々な人に、いつでもアツく楽しく、そして安心して遊べる環境として対抗戦サーバーを提供できればと考えました。

# BomuLeagueで実現出来たコト
結論ですが、[解決策及びしたいコト](##解決策及びしたいコト)の1と2は実現できました。3は作成途中にBomuLeagueの終了等の問題で実現が困難になり、現在は断念しています(ただいつかはやってみたいなと思っています)

短い間でしたが、運営の一員として活動させていただいたこの経験はとても有意義なものでした。何百人とBomuLeagueを利用していただき、拙いシステム運用にお付き合いいただいたユーザー方には感謝しかありません

以下には1と2の内容を乱雑に書いていますが、興味あれば見てください。

# 自動受付システム
## 概要
BomuLeagueに参加する際に回収する情報の自動化及び回収した情報をデータベースに保存し、運営に必要な情報を簡単に引き出せるように作成しました。
ユーザーが操作するフロントエンドは、大会でも連絡手段として用いているDiscord上で動かせるBOTを使用します。
データベースは、お金をあまりかけたくなかった+運営メンバーでも簡単に情報を確認できるようにGoogle SpreadSheetをGASでDB化して使用します。Google SpreadSheetをDBとして利用する方法は以下のものをちょっと改造して使わせてもらいました。

https://pineplanter.moo.jp/non-it-salaryman/2021/12/20/spreadsheet-db2/

## DiscordBOT
BOTはPythonで書きました(discord.pyというライブラリがかなり優秀でそれが理由でPythonを選択)。

https://discordpy.readthedocs.io/en/latest/

ユーザーの情報登録やチーム情報の登録といった情報の回収は、スラッシュコマンドというDiscordの「コマンド」機能を利用し、ユーザーに明示的にどのような情報を入力しなければいけないのかを提示し、直感的で簡単な操作で情報を送信してもらう手段を取りました。こんな感じ↓
![#🖥｜コマンド1 _ BOMU×LEAGUE×受付所 - Discord 2023_08_03 18_58_38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2331914/1688c4ab-6613-53d3-e4c4-fa1ca6d9f25d.png)

## 受付フロー
大会に参加するユーザー方が受付情報を入力する上で、以下のように受付フローを作成しました。
1. ユーザー情報登録・更新
2. チーム情報登録・更新
3. シーズン参加申請

### ユーザー情報登録・更新
大会名簿を作成する上で必要な、ユーザーの情報を登録又は更新してもらうコマンドです。
回収する情報:
| カラム名 | 目的 | check |
|:----:|:-------------:|:----|
| user_name | 登録名・呼称名として使用するためのユーザー名 | -
| friend_code | 試合時の部屋立て用及び不正防止用 | 正規表現
| twitter_id | 不正防止用 | 正規表現
| discord_name | Discord上で識別するための名前 | -
| discord_id | 主キーとして利用するための固有DiscordユーザーID | -
| position | ポジション(名簿確認用) | -
| buki | 使用ブキ(名簿確認用) | -
| image1 | ウデマエ確認用の提出画像URL① | 画像比率/拡張子確認
| image2 | ウデマエ確認用の提出画像URL② | ↑上に同じ↑

ここではチェックが必要な引数を各関数に通して、問題なければそのまま通します。
ちなみに画像ですが、スラッシュコマンド内で送信されたファイルは全てEphemeral(一時的)となり、ある程度の時間が経つと勝手に消えます。

https://discord.com/developers/docs/resources/channel#attachment-object

なので、これをDiscordの同じサーバーもしくは別のサーバーに画像を再送信し、再送信した画像のURLを取得してデータベースに登録します。

再送信関数
```python
imgby = await image.read() #bytes型でファイルを取得
imgbyio = io.BytesIO(imgby) #BytesIO型に変換
file = discord.File(fp=imgbyio, filename=filename) #discord.File型で添付ファイルを作成
message = await channel.send(content=content,file=file) #添付ファイルを送信
url = message.attachments[0].url #discord.Messageのattachmentsから先頭の添付ファイルのURLを取得
return url
```

各引数チェック
```python
#friend_code
re.search(r"^\d{4}-\d{4}-\d{4}$",friend_code)

#twitter_id
re.search(r"^[0-9a-zA-Z_]{1,15}$",twitter_id)

#image
[[img.width, img.height] for img in [image1, image2] if img != None]
```

### チーム情報登録・更新
大会名簿を作成する上で必要な、チームの情報を登録又は更新してもらうコマンドです。
回収する情報:
| カラム名 | 目的 | check |
|:----:|:-------------:|:----|
| team_name | 登録名・呼称名として使用するためのチーム名 | -
| league | どのリーグに参加するか | 適正確認
| leader | チームリーダーのDiscordID兼主キーとして利用する為 | 情報確認
| member1 | ユーザー情報へのアクセス用として記録 | 情報確認
| member2 | ユーザー情報へのアクセス用として記録 | 情報確認
| member3 | ユーザー情報へのアクセス用として記録 | 情報確認
| member4 | ユーザー情報へのアクセス用として記録(任意) | 情報確認

ここではまず、登録したチームが適正なXP帯のリーグに参加しようとしているかの適正確認を行います。この段階で、適正でないリーグに参加しようとするチームを未然に弾きます。

平均XP計算
```python
xpld = sorted(xp, reverse=True) #XP降順リスト
xpavg = int(sum(xpld[:4])/len(xpld[:4])) #平均XP算出
return xpavg
```

適正確認
```python
lgj = json.load(j) #リーグ情報JSONを読み込み
if lgj[league] < xpavg: b = False #規定値より平均XPが高い場合はFalse
else: b = True #規定値より平均XPが低い又は同値の場合はTrue
return b
```

適正確認がTrueだった場合、チームに登録しようとしてメンションされたメンバーのユーザー情報が登録されているか確認後、チーム情報登録・更新は正常に完了します

### シーズン参加申請
シーズンへの参加申請を行うコマンドです。このコマンドには入力が必要な引数はなく、実行者のDiscordIDからチーム情報を検索後、有効なリーダーであることが確認でき次第、以下の項目を確認します
1. 参加申請期間中のシーズンが存在するか
2. ユーザー情報にウデマエ画像が添付されているか
3. ユーザー情報が参加申請期間中に更新されたかどうか

# 適正なウデマエ確認方法
提出してもらった画像をOCRにかけて文字列変換、取得したXPを登録、という形が理想だったのですが、結果から言うと精度の高いプログラムを組むことができませんでした。前提として、提出してもらう画像はイカリング3のものなんですが、端末によって縦横比やコンテンツの位置にバラつきがあり、明らかに経験値が足りない私には満足のいくものはできませんでした。他にもOCRエンジン等の問題に直面し、画像から情報を取得することは断念しました。
代替案として、提出してもらう画像の厳格な基準化と、大会名簿にウデマエ画像のURLを貼り付け、誰でも閲覧できる状態にすることでウデマエ詐欺等への抑止と、仮に誰かが行っていたとしても参加者の誰かしらが報告できる状態にしておくことで、OCRを通して情報取得することと同程度の効果を担保する形で、「相当程度」適正なウデマエ確認方法として確立しました。

## まとめ
重要な部分だけ書きました。自分なりに苦悩して作ったつもりですが、文字に書きおこしてみると大したことないなと痛感させられます。これからも機会があればこういう類でもっと良いのを作ってみたいですね。リポジトリ貼っておきますので全容見たい方はどうぞ

https://github.com/rich-bread/ultw3_bml/tree/master