LoginSignup
0
0

シェルスクリプトと SQLite3 で Nostr のリレーサーバ(っぽいの) (3-2) in.sh の記述2

Posted at

リアルタイムでの REQ へのレスポンス

前回までで EOSE(end of stocked event) までのリクエストのレスポンスを返せるようになった。次に REQ が来た後に、そのフィルタに引っ掛かるデータが来たらそれを返せるようにする。
条件(フィルタ)は同じなので、単に REQ が来た時刻以降の、同条件のデータを返せばいい。
名前付きパイプなどを使えば Bash スクリプトでも標準入力があった時点でスクリプトが作動するように出来るはずだが、ここでは簡単に while sleep を使って一定時間間隔でデータベースをチェックすることにする。何故なら面倒だから。
あと、リアルタイムで REQ に応じるとき limit は外す。
まず同時刻を

now=$(date "+%s")

で取得し、この値(now)をデータベース内のデータの created_at と比べる。これによって REQ が来た時刻以降かどうかを判断することにする。
なので条件に
created_at >= $now
を加える。後は前回と同じだが、最後に EOSE を加える必要は無い。ただ CLOSE が来るまで同じことを繰り返し続ける。CLOSE を送ることなくクライアントが切れた場合、websocketd が out.sh のプロセスを切るようである、多分。その場合、out.sh の最初に書いた呪文で in.sh も切れる。
以下、in.sh の全文を記す。

in.sh
#!/bin/bash


session_id=$(tr -d '"' <<< $1)
jsons=${@:2} #複数の(1つかもしれないが)フィルタを全部 jsons に空白区切りで入れる

limit=100 # デフォルトで limit を 100 に設定

function add_and_in(){
	read -r line
	if [ "$line" == "null" ]; then
		echo ""
	else
		echo "and $1 in $line"
	fi
}

function add_and_compa(){
	read -r line
	if [ "$line" == "null" ]; then
		echo ""
	else
		echo "and $1 $2 $line"
	fi
}

function add_and_or(){
read -r line
tmp=""
if [ "$line" = "null" ]; then
	echo "$tmp"
else
	while read -r element; do
		tmp="${tmp}${element} in (tag1,tag2,tag3,tag4) or "
	done < <(echo $line | jq .[])
	echo "and ($(head -c -4 <<< "${tmp}"))" # 無理やり最後の " or " の四文字を削る
fi
}

function make_limit(){
	read -r line
	if [ "$line" == "null" ]; then
		echo ""
	else
		echo " limit $line"
	fi
}

function make_dbs(){
	json=$1

	created_at=$(echo $json | jq -c .created_at | add_and_in created_at)
	ids=$(echo $json | jq -c .ids  | sed 's/"/'\''/g' | add_and_in id | tr "[]" "()")
	authors=$(echo $json | jq -c .authors | sed 's/"/'\''/g'  | add_and_in author | tr "[]" "()")
	kinds=$(echo $json | jq -c .kinds | add_and_in kind | tr "[]" "()") 
	hash_e=$(echo $json | jq -c '."#e"' | add_and_or)
	hash_p=$(echo $json | jq -c '."#p"' | add_and_or) 
	since=$(echo $json | jq -c .since | add_and_compa 'created_at' '>')
	_until=$(echo $json | jq -c .until | add_and_compa 'created_at' '<')
	limit=$(echo $json | jq -c .limit | make_limit)

	condition=$(echo \(1=1 $kinds $authors $ids $hash_e $hash_p $since $_until\))
}


DB_arr_tmp=() # フィルタを created_at に変換したものを1つずつ要素とする一時的な配列
for json in $jsons; do
	make_dbs $json # フィルタを condition に変換
	DB_arr_tmp+=( $(sqlite3 test.db "select created_at from forRelay where ${condition} order by created_at desc$limit") ) # limit はここで条件に加える
done

IFS=$'\n'; DB_arr=( $(sort <<<"${DB_arr_tmp[*]}") ); unset IFS # 一時的な配列を sort で並べ直す
unset DB_arr_tmp

# DB_arr 配列の値(created_at)に応じて順にファイルを取ってきて整形し、クライアントに返す
for line in ${DB_arr[@]}; do
	json=$(cat ./jsons/$line)
	echo "[\"EVENT\",\"$session_id\",$json]"
done
unset DB_arr

echo "[\"EOSE\",\"$session_id\"]"

# リアルタイムで REQ のフィルタに応じる
while :; do
	now=$(date "+%s")
	sleep 5 # 5秒おきに拾いにいく。都合に応じて変える

	DB_arr_tmp=()
	for json in $jsons; do
		make_dbs $json
		DB_arr_tmp+=( $(sqlite3 test.db "select created_at from forRelay where ${condition} and (created_at >= $now)") ) # "以上" でないと同時刻を拾えない
	done

	IFS=$'\n'; DB_arr=( $(sort <<<"${DB_arr_tmp[*]}") ); unset IFS
	unset DB_arr_tmp

	for line in ${DB_arr[@]}; do
		json=$(cat ./jsons/$line)
		echo "[\"EVENT\",\"$session_id\",$json]"
	done
	unset DB_arr

done
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0