Foltia ANIME LOCKERの録画予約状態をtwitterに通知する

## はじめに
　テレビ録画にfoltiaALを使用していますが、録画状態の通知機能が便利そうなのでバージョン5から6にバージョンアップしたのですが、~~MastodonセットアップがLet's Encryptの仕様変更に対応していないため現状使用できないことと、~~ (22/12/08 のV6.1.17アップデートでMastodonインストールメニューも廃止されました) Mastodonより普段使っているTwitterのほうに通知が上がってくれたほうが正直使いやすいということもあって、ソフトウェアの改変やリバースエンジニアリングを含まない範囲(ソフトウェア利用規約を侵害しない範囲？)で実装を試みてみました。
messagesログの監視のみで実装しています。foltiaALの本体ソースコードには一切手を付けないため、foltiaAL自体の機能に影響を与えたりバージョンアップの影響を受けるようなことも無いかと思います。

##環境
CentOS6.x
foltia ANIMELOCKER 5.x 以上
(オープンソース版のDBの仕様を参考にしているので、オープンソース版や旧Verでも動くかと思います)

##事前準備
ツイートにはbtiを使用するためインストールしておきます
※btiの導入についてはこのページを参考にさせてもらいました
　https://kentai-shiroma.blogspot.com/2014/04/linuxtwitterbtibash.html

```bash
yum -y install bti
```
インストールが済んだらOAuth認証用のファイルを作成します

```bash
vi ~/.bti

# Consumer key
consumer_key=cZy8DdioswAfu3LJYg6E2w

# Consumer secret
consumer_secret=fnIGGU0T12mMWKjmThUdSeKN32NLWfmnwapwubVQ
```
OAuth認証用のファイルを作成したらbtiを実行して、表示されたURLにアクセスして
認証を行うと、7桁のPINコードが表示されるのでコンソールに貼り付けます。

access_token_keyとaccess_secret_key が表示されるので、先ほどの認証ファイルに追記します。


```bash
vi ~/.bti

# Consumer key
consumer_key=cZy8DdioswAfu3LJYg6E2w

# Consumer secret
consumer_secret=fnIGGU0T12mMWKjmThUdSeKN32NLWfmnwapwubVQ

access_token_key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
access_token_secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

これで、コマンドラインからツイートができるようになりました。
続いて、ツイートの際に付与する「局タグ」をDBに追加します。

まずはSELECT文を実行して放送局のID（stationid）を確認します。

```shell-session
#DBにアクセスするため、ユーザー「foltia」でログインします
su foltia
psql -c "select stationid,stationname from foltia_station"
 stationid |            stationname 
-----------+-----------------------------------
       102 | 中国放送
       103 | 広島テレビ
       104 | 岡山放送
        21 | アニマックス
      2079 | FOX bs238
      2080 | 放送大学テレビ
      2081 | 放送大学2
      2082 | 放送大学3
      2083 | グリーンチャンネル


#ユーザー「foltia」をログアウトします
exit
```

追加したい放送局の局タグと、上項で確認したstationidを以下の書式で登録していきます。

書式
　psql -t -c "update foltia_station set ext_stationtag = '局タグ' where stationid=放送局のID;"

下記実行例では東京周辺で受信可能な局のみ記載しています。
お住まいの地域に合わせて変更して下さい。

```bash
#DBにアクセスするため、ユーザー「foltia」でログインします
su foltia
#ここ ↓ のコマンドでは、DBに新規カラムを作成しています
psql -t -c 'select count(ext_stationtag) from foltia_station;' >/dev/null 2>&1 || psql -c 'alter table foltia_station add ext_stationtag text;'

#ここ ↓ から、放送局ごとの局タグを設定しています。
## 地上波
psql -t -c "update foltia_station set ext_stationtag = 'nhk' where stationid=1;"
psql -t -c "update foltia_station set ext_stationtag = 'etv' where stationid=2;"
psql -t -c "update foltia_station set ext_stationtag = 'fujitv' where stationid=3;"
psql -t -c "update foltia_station set ext_stationtag = 'ntv' where stationid=4;"
psql -t -c "update foltia_station set ext_stationtag = 'tbs' where stationid=5;"
psql -t -c "update foltia_station set ext_stationtag = 'tvasahi' where stationid=6;"
psql -t -c "update foltia_station set ext_stationtag = 'tvtokyo' where stationid=7;"
psql -t -c "update foltia_station set ext_stationtag = 'tvk' where stationid=8;"
psql -t -c "update foltia_station set ext_stationtag = 'chibatv' where stationid=13;"
psql -t -c "update foltia_station set ext_stationtag = 'teletama' where stationid=14;"
## BS
psql -t -c "update foltia_station set ext_stationtag = 'nhkbs1' where stationid=9;"
psql -t -c "update foltia_station set ext_stationtag = 'bstvtokyo' where stationid=15;"
psql -t -c "update foltia_station set ext_stationtag = 'bstbs' where stationid=16;"
psql -t -c "update foltia_station set ext_stationtag = 'bsfuji' where stationid=17;"
psql -t -c "update foltia_station set ext_stationtag = 'bsasahi' where stationid=18;"
psql -t -c "update foltia_station set ext_stationtag = 'tokyomx' where stationid=19;"
psql -t -c "update foltia_station set ext_stationtag = 'bs11' where stationid=128;"
psql -t -c "update foltia_station set ext_stationtag = 'twellv' where stationid=129;"
psql -t -c "update foltia_station set ext_stationtag = 'bspremium' where stationid=179;"
psql -t -c "update foltia_station set ext_stationtag = 'bs260ch' where stationid=271;"
## CS
psql -t -c "update foltia_station set ext_stationtag = 'at_x' where stationid=20;"
psql -t -c "update foltia_station set ext_stationtag = 'animax' where stationid=21;"
psql -t -c "update foltia_station set ext_stationtag = 'kidsstation' where stationid=22;"
psql -t -c "update foltia_station set ext_stationtag = 'wowow' where stationid=12;"
psql -t -c "update foltia_station set ext_stationtag = 'wowow' where stationid=76;"
psql -t -c "update foltia_station set ext_stationtag = 'wowow' where stationid=97;"
## ラジオ
psql -t -c "update foltia_station set ext_stationtag = 'jolf' where stationid=30;"
psql -t -c "update foltia_station set ext_stationtag = 'joqr' where stationid=41;"
psql -t -c "update foltia_station set ext_stationtag = 'tbsradio' where stationid=53;"
psql -t -c "update foltia_station set ext_stationtag = 'agqr' where stationid=156;"

#ユーザー「foltia」をログアウトします
exit
```

##シェルスクリプトの配置
シェルスクリプトを配置します。
シェルスクリプトは大きく分けてmessagesログをフックして出力された情報を元にデータベースから表示値を引っ張りツイートする部分と、データベースに登録された録画予約情報を抽出してツイートする部分に分かれています。

####1-1.ログ監視シェル
messagesログの出力をtailコマンドで監視して、それらしい情報を引っ掛けてきます

```bash
vi /usr/local/bin/foltwi

#!/bin/sh
#===================================
# foltia 録画・変換監視スクリプト
# Rel.230115   io_oj
# (chkconfigに登録して実行して下さい)
#===================================

export LANG=en_US.utf8

tweet() {
  while read i
  do
		set -- $i

		## messagesログから録画開始を抽出
		if [ "${6} ${7}" = "recwrap RECSTART" ]; then
			 ## 抽出行から放送局コード、開始時刻、タイトルコード、話数を抽出
				staid=${12}
				stime=$(echo ${14} | sed -e 's/--/-#-/g' | cut -d '-' -f4)
				stim2=$(echo ${14} | sed -e 's/--/-#-/g' | cut -d '-' -f3)
				etime=$(date --date "${stime} ${11} sec" +%H%M)
				ttlid=${15}
				epsde=${16}
				tsfil=0
				## ツイートを有効にする場合は次の行のコメントアウトを外して下さい
				twisub 🔴録画開始 話 0
		fi

		## messagesログから録画完了を抽出
		if [ "${6} ${7}" = "digitaltvrecording RECEND" ]; then
			 ## 抽出行から放送局コード、開始時刻、タイトルコード、話数を抽出
				staid=${11}
				stime=$(echo ${13} | sed -e 's/--/-#-/g' | cut -d '-' -f4)
				stim2=$(echo ${13} | sed -e 's/--/-#-/g' | cut -d '-' -f3)
				etime=$(date --date "${stime} ${10} sec" +%H%M)
				ttlid=${14}
				epsde=${15}
				tsfil=${13}
				## ツイートを有効にする場合は次の行のコメントアウトを外して下さい
				twisub 🟦録画終了 話 1
		fi

		## messagesログからradikoの録音開始を抽出
		if [ "${6} ${11}" = "addpidatq -2" ]; then
			 ## 抽出行から放送局コード、開始時刻、タイトルコード、話数を抽出
				staid=${17}
				stime=$(echo ${8} | cut -c 9-12)
				etime=$(date --date "${stime} ${12} sec" +%H%M)
				ttlid=${14}
				epsde=${15}
				tsfil=0
				## ツイートを有効にする場合は次の行のコメントアウトを外して下さい
				twisub 🔴録音開始 回 0
		fi

		## messagesログからradikoの録音完了を抽出
		if [ "${6} ${7}" = "digitalradiorecording Finish" ]; then
			 ## 抽出行から放送局コード、開始時刻、タイトルコード、話数を抽出
				staid=69
				stime=$(echo ${8} | cut -d '-' -f5)
				etime=$(date --date "${3} 5 min ago" +%H%M)
				ttlid=64
				epsde=0
				tsfil=0
				## ツイートを有効にする場合は次の行のコメントアウトを外して下さい
				twisub 🟦録音終了 回 0
		fi

		## messagesログからエンコード開始を抽出(デフォルトではログ出力のみ)
		if [ "${6} ${7}" = "ipodtranscode BonTsDemux" ]; then
				twiwd=$(echo -e "ℹ️録画ファイルのエンコードを開始しました")
			 ## ツイートを有効にする場合は次の行のコメントアウトを外して下さい
			 ## echo -e "${twiwd:0:128}" | bti --config /root/.bti
				echo -e "$(date "+%Y/%m/%d %H:%M:%S") $i" >>/var/log/foltwi.log
				echo -e "$(date "+%Y/%m/%d %H:%M:%S") ${twiwd:0:128}" >>/var/log/foltwi.log
		fi

		## messagesログからエンコード完了を抽出(デフォルトではログ出力のみ)
		if [ "${6} ${7} ${8}" = "ipodtranscode ALL COMPLETE" ]; then
				twiwd=$(echo -e "ℹ️録画ファイルのエンコードが完了しました")
			 ## ツイートを有効にする場合は次の行のコメントアウトを外して下さい
			 ## echo -e "${twiwd:0:128}" | bti --config /root/.bti
				echo -e "$(date "+%Y/%m/%d %H:%M:%S") $i" >>/var/log/foltwi.log
				echo -e "$(date "+%Y/%m/%d %H:%M:%S") ${twiwd:0:128}" >>/var/log/foltwi.log
		fi

		## messagesログから録画失敗を抽出
		if [ $(echo $i | grep -c "recwrap ERR 2:Device busy;Giving up digital recording.") = 1 ]; then
				twiwd=$(echo -e "⚠️録画開始できませんでした")
			 ## ツイートを有効にする場合は次の行のコメントアウトを外して下さい
			 echo -e "${twiwd:0:128}" | bti --config /root/.bti
				echo -e "$(date "+%Y/%m/%d %H:%M:%S") $i" >>/var/log/foltwi.log
				echo -e "$(date "+%Y/%m/%d %H:%M:%S") ${twiwd:0:128}" >>/var/log/foltwi.log
		fi
  done
}
twisub() {
			 ## 環境設定
				PG=/opt/rh/rh-postgresql95/root/usr/bin

			 ## タイトルコードからの番組名をDBから取得
			     ## title=$(ssh root@127.0.0.1 "$PG/psql -Ufoltia -t -c 'select title from foltia_program where tid='${ttlid}';' | sed -e 's/^.//g'")
				title=$(ssh root@127.0.0.1 "$PG/psql -q -U foltia -d foltia --no-align -t -c 'select title from foltia_program where tid='${ttlid}';'")
			 ## タイトルコードと話数からのサブタイトルをDBから取得
				sbttl=$(ssh root@127.0.0.1 "$PG/psql -q -U foltia -d foltia --no-align -t -c 'select subtitle from foltia_subtitle where tid='${ttlid}' and countno='${epsde}' and stationid='${staid}';' | head -n 1")
			 ## 放送局コードからの放送局名をDBから取得
				stanm=$(ssh root@127.0.0.1 "$PG/psql -q -U foltia -d foltia --no-align -t -c 'select stationname from foltia_station where stationid='${staid}';'")
			 ## 放送局コードからの局タグをDBから取得
				sttag=$(ssh root@127.0.0.1 "$PG/psql -q -U foltia -d foltia --no-align -t -c 'select ext_stationtag from foltia_station where stationid='${staid}';'")
			 ## タイトルコードからの番組名をしょぼいカレンダーから取得
				ohurl=$(wget "http://cal.syoboi.jp/db.php?Command=TitleLookup&TID=${ttlid}&Fields=Comment" -O /dev/stdout 2>&1 | grep "http" | sed -n 2P | sed -e 's/^.*http/http/g' | sed -e 's/].*$//g')
			 ## 引数1受け取り
				wdtmp=$1
			 ## TSファイル解析
				if [ ${tsfil} != 0 ]; then
					tstmp=$(/home/foltia/perl/tool/tsselect /home/foltia/php/tv/${tsfil} | awk -F'[=,]' '{ drop += $6 } { scr += $8} END{print drop","scr}')
					if [ $(echo ${tstmp} | awk -F'[,]' '{print $1 + $2}') = 0 ]; then
						wdtmp=🟦録画終了
						tsqry="[Drop:0 Scr:0]"
					else
						wdtmp=⚠️録画終了
						tsqry="[Drop:$(echo ${tstmp} | cut -d ',' -f 1) Scr:$(echo ${tstmp} | cut -d ',' -f 2)]"
					fi
				else
					tsqry=
				fi
			 ## 取得した内容を表示フォーマットに埋め込んで整形します
				if [ ${ttlid} = 0 ]; then
					## EPG録画の場合の表示フォーマット整形
					intwd=$(echo -e "${wdtmp} ${tsqry} 📔${stim:0:2}:${stim:2:2}-${etime:0:2}:${etime:2:2} [${stanm}] ${title} $([ ${#sbttl} -gt 0 ] && echo "『${sbttl}")")
				else
					## 番組予約モードの場合の表示フォーマット整形
					intwd=$(echo -e "${wdtmp} ${tsqry} 📔${stime:0:2}:${stime:2:2}-${etime:0:2}:${etime:2:2} [${stanm}] ${title} $([ ${epsde} -gt 0 ] && echo "第${epsde}$2" ) $([ ${#sbttl} -gt 0 ] && echo "『${sbttl}")")
				fi
			 ## 文字数溢れした場合の省略処理と番組公式Web、局タグの追加
				if [ $3 = 0 ]; then
					## 番組公式Webと局タグをつける(引数3を 0 にした場合) Summary Cardが埋め込まれます
					twiwd=$(echo -e "${intwd:0:128}$([ ${#intwd} -gt 128 ] && echo '...』' || [ ${#sbttl} -gt 0 ] && echo '』') $([ ! ${#sttag} = 0 ] && echo "#${sttag} ")"'#foltia'" ${ohurl}")
				else
					## 番組公式Webと局タグをつけない(引数3を 1 にした場合) Summary Cardが埋め込まれるとウザい場合
					intwd=$(echo -e "${intwd}")
					twiwd=$(echo -e "${intwd:0:128}$([ ${#intwd} -gt 128 ] && echo '...』' || [ ${#sbttl} -gt 0 ] && echo '』') $([ ! ${#sttag} = 0 ] && echo "#${sttag} ")"'#foltia')
				fi
			 ## ツイート処理とログ出力処理
				echo -e "${twiwd}" | bti --config /root/.bti
				echo -e "$(date "+%Y/%m/%d %H:%M:%S") $i" >>/var/log/foltwi.log
				echo -e "$(date "+%Y/%m/%d %H:%M:%S") ${twiwd}" >>/var/log/foltwi.log
}

start(){
  printf "%s\n" starting:
	 ## 多重起動すると厄介なので実質Restart処理です
		CMD0="tail -n 0 --follow=name --retry /var/log/messages"
		PID0=$(ps -e -o pid,cmd | sed "s/^ //g" | grep -v "grep" | grep -v $$ | grep "$CMD0" | cut -f 1 -d " ")
		if [ -n "$PID0" ];
				then kill $PID0
		fi
		$CMD0 | tweet &
		PID3=$(ps -e -o pid,cmd | sed "s/^ //g" | grep -v "grep" | grep "$CMD0" | cut -f 1 -d " ")
		echo $PID0 >/var/run/foltwi.pid
  return 0
}

stop(){
  printf "%s\n"  stoping:
	 ## プロセスリストからPIDを検索して終了処理をかけます
		CMD1="tail -n 0 --follow=name --retry /var/log/messages"
		PID1=$(ps -e -o pid,cmd | sed "s/^ //g" | grep -v "grep" | grep -v $$ | grep "$CMD1" | cut -f 1 -d " ")
		if [ -n "$PID1" ];
				then kill $PID1
		fi
		CMD2="/bin/sh /usr/local/bin/foltwi.sh"
		PID2=$(ps -e -o pid,cmd | sed "s/^ //g" | grep -v "grep" | grep -v $$ | grep "$CMD2" | cut -f 1 -d " ")
		if [ -n "$PID2" ];
				then kill $PID2
				rm -f /var/run/foltwi.pid
		fi
  return 0
}


restart(){
	start
  return 0
}

case $1 in
  start)
	start
	  ;;
  stop)
	 stop
	  ;;
  *)
	start
	  ;;
esac
```

/usr/local/bin/foltwi を配置したら、実行権限を付与しておきます。

```bash
chmod +x /usr/local/bin/foltwi
```
　
####1-2.ログ監視シェルのデーモン化
常駐してmessagesを監視するため、デーモンとして登録します。
※コンソールで実行すると問題無いが、デーモン起動するとなぜかpsqlが正しく動いてくれないので自分自身にsshで接続するという力技で切り抜けています。

```bash
vi /etc/init.d/foltwi

#!/bin/sh
# chkconfig: 2345 85 15
# description: foltwi.

start(){
        ssh root@127.0.0.1 /usr/local/bin/foltwi start &
  return 0
}
stop(){
        ssh root@127.0.0.1 /usr/local/bin/foltwi stop
  return 0
}
case $1 in
  start)
    start
      ;;
  stop)
     stop
      ;;
  *)
    start
      ;;
esac
```

こちらも実行権限を付与しておきます。

```bash
chmod +x /etc/init.d/foltwi
```
スクリプトを実行します

```bash
service foltwi start
```
システム起動時に自動実行するようにchkconfigに登録します

```bash
chkconfig foltwi on
```
これで録画状態の監視設定は完了です、
つづけて番組予約の監視設定を行います。


####2.予約監視シェル
番組予約をPOSTする際のデータを拾ったほうが正直スマートだと思いますが、あくまでソフトウェアの改変を行わない形で実装するため、データベース予約テーブルの追加差分を取ることで予約が追加されたことを検出しています。差分取得もSQL文で処理したほうがスマートですが、あとで読んだ時に訳が判らなくなりそうなので、あえてdiffを取って差分ファイルを生成しています。

```bash
vi /usr/local/bin/resvchk.sh

#!/bin/sh

#===================================
# foltia 予約監視スクリプト
# Rel.220115 @io_oj
# (crontabに登録して実行して下さい)
#===================================

listcheck() {
	cat /tmp/resvchk_d | while read i
	do
		ttlid=$(echo $i | cut -d ',' -f1)
		staid=$(echo $i | cut -d ',' -f2)
		title=$(su foltia -c "psql -t -c 'select title from foltia_program where tid='$ttlid';'''" | sed -e 's/^.//g')
		stanm=$(su foltia -c "psql -t -c 'select stationname from foltia_station where stationid='$staid';'''" | sed -e 's/^.//g')
		sttag=$(su foltia -c "psql -t -c 'select ext_stationtag from foltia_station where stationid='$staid';'''" | sed -e 's/^.//g')
		ohurl=$(wget "http://cal.syoboi.jp/db.php?Command=TitleLookup&TID=${ttlid}&Fields=Comment" -O /dev/stdout 2>&1 | grep "http" | sed -n 2P | sed -e 's/^.*http/http/g' | sed -e 's/].*$//g')
		intwd=$(echo -e "🕒録画予約をしました  [${stanm}] ${title} #foltia")
		# tweet処理とログ出力
		echo -e "${intwd:0:128}\n${ohurl} #${sttag}" | bti --config /root/.bti
		echo -e "$(date "+%Y/%m/%d %H:%M:%S") ${intwd:0:128} ${ohurl} #${sttag}" >>/var/log/foltwi.log
		# しょぼいカレンダーに予約情報送信
		/usr/bin/perl /home/foltia/perl/foltia_sch_upload.pl user password
		sleep 30s
	done
	}

	# ファイル存在確認
	if test -e /tmp/resvchk_o; then
		su foltia -c "psql -t -c 'select distinct on(tid)tid,stationid from foltia_tvrecord order by tid,stationid;' >/tmp/resvchk_o"
	fi

	# 差分抽出・差分件数カウント
	mv /tmp/resvchk_n /tmp/resvchk_o
	su foltia -c "psql -t -c 'select distinct on(tid)tid,stationid from foltia_tvrecord order by tid,stationid;' >/tmp/resvchk_n"
	diff -B /tmp/resvchk_o /tmp/resvchk_n | grep ">" | sed -e "s/>//g" | sed -e "s/ //g" | sed -e "s/|/,/g" >/tmp/resvchk_d
	count=$(cat /tmp/resvchk_d | wc -l)

	# 10件以上登録データが検出されが場合は、誤動作として一切の投稿をスキップします
	if [ $count -gt 0 ] && [ $count -lt 10 ]; then
		listcheck
	fi
```
実行権限を付与しておきます。

```bash
chmod +x /usr/local/bin/resvchk.sh
```
定時実行して予約の追加が無いかを確認するため、crontabに登録します(毎時00分から10分おき)

```bash
crontab -e

# 番組録画予約チェック
00-50/10 * * * * ssh root@127.0.0.1 /usr/local/bin/resvchk.sh >/dev/null 2>&1
```
以上で番組予約の監視設定も完了です。
btiの部分をmailコマンドに差し換えればメールにも対応できるかもしれませんね。


# 編集履歴：
2019/03/23　epg予約の番組の録画時にサブタイトルが長過ぎてtweetに失敗する問題の修正
2019/03/25　しょぼいカレンダーから取得した番組公式WebのURLを付与するようにしました
2019/03/27　ラジオ放送録音開始完了時の情報取得への対応と冗長になりすぎた部分の集約
2019/04/04　話数やサブタイトルが空の場合の処理を追加
2019/04/12　ラジオ番組は～話よりも～回のほうが自然なので、そのあたりの変更
2019/04/17　局タグ追加の部分のSTATIONIDの確認方法を追加
2020/11/15　録画失敗通知の追加、EPG録画時の開始時刻化け修正・完了時にSummaryCardが
　　　　　　 埋め込まれるのがウザいので対処
2020/12/31　カラム分けをcutからsetに変更、録画開始時間の取得バグがあったので修正
2022/01/15　録画通知を[EDCBNotifier](https://github.com/tsukumijima/EDCBNotifier)っぽく変更、録画ドロップ等の情報を追加。