ファイルサーバだけあればチャットできる

# はじめに
[jsを使わずリアルタイムチャットを作ってみた](https://qiita.com/s_yamakawa/items/f402b9de6fcf1b06ddfe)に触発されて、昔むかしbatで作ったチャットを公開する気分になりました

# 作ったもの
- つくった当時制約がありました
 1. 社外サービスはつかえない
 1. メッセンジャーアプリを導入できない
 1. IRCサーバなどサービスを導入できない
 1. Java、Rubyなど処理系が動くアプリケーションサーバは用意できない
 1. 利用者の端末にも処理系を用意できない
- 使用言語
 1. bat
- 仕様（らしきもの）
 1. ファイルサーバはある
 1. 発言ごとにファイルを作る
 1. 増えまくるファイルを見張る（無限ループ）
 1. 利用者はファイルサーバにあるbatファイルを直接実行する

# 実装
```sh:filechat.bat
@echo off
setlocal enabledelayedexpansion
set XPWD=%cd%

pushd %~dp0

if "x%1"=="xw" goto modeWrite
if "x%1"=="x" goto modeRead
set Name=%1

:: メインフロー
:modeRead
:: 再読み込み間隔（秒）
set ReloadSecond=2

title ファイルチャット 受信機
color 70

set DirChat=%~dp0filechat

:: チャットフォルダへ移動
set DirChatToday=%DirChat%\%date:/=-%
if not exist %DirChatToday% mkdir %DirChatToday%

call :OnLogon
title ファイルチャット 受信機 %Name%
start "ファイルチャット 発信機 %Name%" %~nx0 w

:: 発言を取得する
set quitRead=
:OnRead
cls
for %%i in ("%DirChatToday%\*.chat") do (set /p FileDate=<nul %%~ti & set /p FileLine=<"%%i" & echo !FileLine:~1,-1!)
timeout %ReloadSecond%  2>&1 1>nul
if exist "%DirChatToday%\%Name%.login" goto OnRead
:: 発言を取得する（無限ループ）
cls
for %%i in ("%DirChatToday%\*.chat") do (set /p FileDate=<nul %%~ti & set /p FileLine=<"%%i" & echo !FileLine:~1,-1!)
popd
pushd %XPWD%
title %cd%
color
endlocal
goto :EOF


:: サブフロー
:modeWrite
set
title ファイルチャット 発信機 %Name%
color 70

:: 発言を発信する
:OnWrite
cls
set FileChatWrite=
set /p FileChatWrite="発言を入力してください(閉じる[q]): "
if /I "x%FileChatWrite%"=="xq" set FileChatWrite=&&goto OnExit
call :SetDateTime
echo>"%DirChatToday%\%Datetime%-%Name%.chat" "=%Name%= %FileChatWrite%"
set FileChatWrite=
goto OnWrite
:: 発言を発信する（無限ループ）
goto OnExit


:: サブルーチン

:SetDateTime
if "x%dateTmp%"=="x" set dateTmp=%date:/=-%
set timeTmp=%time: =0%
set timeTmp=%timeTmp::=-%
set timeTmp=%timeTmp:.=-%
set Datetime=%dateTmp%-%timeTmp%
exit /b

:OnLogon
cls
if not defined Name set /p Name="ユーザ名(半角英数): "
if "x%Name%"=="x" goto OnLogon
echo "%Name%"| findstr /r "^\"[a-zA-Z0-9][a-zA-Z0-9]*\"$" 2>&1 1>nul
if errorlevel 1 (
 set Name=
 goto OnLogon
)
if exist %DirChatToday%\%Name%.login (
 set Name=
 goto OnLogon
)
set MessageWelcome=%Name%さん、いらっしゃい
set MessageExit=%Name%さん、おつかれさまでした

call :SetDateTime
echo>"%DirChatToday%\%Datetime%-SYSTEM.chat" "=SYSTEM= %MessageWelcome%"

:: ログイン情報記録
for /f %%i in ('whoami') do set myid=%%i
echo>"%DirChatToday%\%Name%.login" "%Name% %myid% %myip%"
exit /b

:OnExit
cls
call :SetDateTime
echo>"%DirChatToday%\%Datetime%-SYSTEM.chat" "=SYSTEM= %MessageExit%"
:: ログイン情報削除
del "%DirChatToday%\%Name%.login"
exit 0
```

# 解説

~~おいおい追記編集します~~
以下2018/04/28追記分

`@echo off`とか`setlocal`とかググったり`help <command>`で引けることはさておきchatプログラムとしての工夫点を書きます

#### 【タイマー】
発信と受信を同じ窓で実現しようとすると、
入力がなければ新しい発言を読み込むループとやろうとなり、
- `set /p`に割り込む
- `choice /t n`
- `timeout`
などを考えましたが、窓をバックグラウンドにした時点でタイマーがはまりませんでした。
そこで発信と受信を別窓にして受信を無限ループ、発信を`set /p`に分けました。

```sh:受信を無限ループ、発信をset/p
:modeRead
goto :modeRead

:modeWrite
set /p
goto :eof
```

#### 【プログラムひとつでふた窓ひらく】
発信と受信を別窓にして受信を無限ループ、発信を`set /p`に分けますが、
ユーザが参照するプログラムはひとファイルにしたかったので、パラメータ付きで自己呼び出しをします。

```sh:１ファイルに２プログラム押し込める
if "x%1"=="xw" goto modeWrite
if "x%1"=="x" goto modeRead
start %0 w
```

####【2プログラムの終了を同期する】
２窓開くことにしたので仕組みをつくります。ロックファイルです。

1. まず受信窓でログインファイルを作ります。
1. 受信窓から発信窓を開始します。
1. 発信窓で終了コマンドを叩いたらログインファイルを消します。
1. 発信窓は自分を終了します。
1. 受信窓はログインファイルを監視して終了します。

```sh:２窓の開き方と閉じ方
:: wオプションのとき発信側プログラム
if "x%1"=="xw" goto modeWrite

:: 受信
:modeRead
echo>login 名前
start "発信窓タイトル" 自己bat wオプション
:OnRead
if exist login goto OnRead
goto :eof

:: 発信
:modeWrite
set /p input
if /I "x%input%"=="xq" goto OnExit

:: 終了処理
:OnExit
del login
exit 0
```

つづく