rowbot/rowbot

327 lines
4.6 KiB
Plaintext
Raw Normal View History

2021-06-14 22:38:50 +00:00
#!/usr/bin/env bash
2021-06-14 23:35:23 +00:00
###
# logger
###
declare -A levels=(
[debug]=1 [info]=2
[warn]=3 [error]=4
)
log() {
if [[ -v LEVEL ]] && (( levels[$level] <= levels[$LEVEL] )); then
2021-06-14 23:45:40 +00:00
printf "%s: $1\n" "${LEVEL^^}" "${@:2}" >&"$log"
2021-06-14 23:35:23 +00:00
fi
}
debug() {
LEVEL=debug log "$@"
}
info() {
LEVEL=info log "$@"
}
warn() {
LEVEL=warn log "$@"
}
error() {
LEVEL=error log "$@"
}
2021-06-14 22:38:50 +00:00
###
2021-06-14 23:15:49 +00:00
# argument parser for parsing arguments
2021-06-14 22:38:50 +00:00
##
declare -A opts
while (( $# )); do
case $1 in
--*=*)
key=${1#--} key=${key%%=*}
opts[$key]=${1#--*=}
;;
--no-*)
key=${1#--no-}
2021-06-14 22:41:05 +00:00
opts[$key]=no
2021-06-14 22:38:50 +00:00
;;
--)
shift
break
;;
--*)
key=${1#--}
2021-06-14 22:41:05 +00:00
opts[$key]=yes
2021-06-14 22:38:50 +00:00
;;
*)
break
esac
shift
done
2021-06-14 22:41:05 +00:00
###
# default config
##
server=${opts[server]:-irc.libera.chat}
tls=${opts[tls]:-no}
2021-06-14 23:15:49 +00:00
if [[ $tls = yes ]]; then
if ! hash socat 2>/dev/null; then
printf 'please install socat to use tls with rowbot.\n' >&2
exit 1
fi
port=${opts[port]:-6697}
else
2021-06-14 22:41:05 +00:00
port=${opts[port]:-6667}
2021-06-14 23:15:49 +00:00
fi
2021-06-14 23:45:40 +00:00
level=${opts[log-level]:-info}
if [[ ${opts[log]} ]]; then
exec {log}>"${opts[log]}"
else
log=1
fi
2021-06-15 06:33:30 +00:00
nick=${opts[nick]:-rowbot-dev}
ident=${opts[ident]:-rowbot}
realname=${opts[realname]:-rowbot}
chan=${opts[chan]:-}
2021-06-14 23:15:49 +00:00
###
# net code
###
if [[ $tls = yes ]]; then
coproc sock { socat OPENSSL:"$server":"$port" -; }
2021-06-15 06:33:30 +00:00
exec {in_sock}<&"${sock[0]}" {out_sock}>&"${sock[1]}"
2021-06-14 22:41:05 +00:00
else
2021-06-14 23:15:49 +00:00
exec {sock}<>/dev/tcp/"$server"/"$port"
in_sock=$sock out_sock=$sock
2021-06-14 22:41:05 +00:00
fi
2021-06-14 23:15:49 +00:00
send() {
local fmt
printf -v fmt "$1" "${@:2}"
printf '%s\r\n' "$fmt" >&"$out_sock"
2021-06-15 06:33:30 +00:00
debug "sending line: %s" "$fmt"
2021-06-14 23:15:49 +00:00
}
recv() {
declare -n sock_line=$1
IFS= read -r "$1" <&"$in_sock"
sock_line=${sock_line%$'\r'}
2021-06-15 06:33:30 +00:00
debug "received line: %s" "$sock_line"
}
###
# irc recv code
###
on_ERROR() {
error "${params[0]}"
exit
}
on_MODE() {
if (( ${#params[@]} == 2 )); then
info "%s sets mode(s) %s on %s" "$from" "${params[1]}" "${params[0]}"
else
warn "mode line was not handled: %s" "$orig_line"
fi
}
on_NOTICE() {
info "[%s/%s] %s" "$from" "${params[0]}" "${params[1]}"
}
on_PING() {
pong "${params[1]}"
debug "received ping: %s" "${params[0]}"
}
on_PONG() {
debug "received pong: %s" "${params[1]}"
}
on_QUIT() {
info "%s has disconnected: %s" "$from" "${params[0]}"
}
on_001() {
info %s "${params[1]}"
if [[ $chan ]]; then
join "$chan"
fi
while true; do
ping "row your bot gently down the stream"
sleep 10
done &
trap "kill $!" EXIT
}
on_002() {
info %s "${params[1]}"
}
on_003() {
info %s "${params[1]}"
}
on_004() {
debug "%s " "${params[@]:1}"
}
declare -A isupport
on_005() {
local param key value
for param in "${params[@]:1:${#params[@]}-2}"; do
IFS== read -r key value <<< "$param"
isupport[$key]=$value
debug "isupport: %s = %s" "$key" "$value"
done
}
on_250() {
info %s "${params[1]}"
}
on_251() {
info %s "${params[1]}"
}
on_252() {
info "There are %d operators online" "${params[1]}"
}
on_253() {
info "There are %d unknown connections" "${params[1]}"
}
on_254() {
info "There are %d channels formed" "${params[1]}"
}
on_255() {
info %s "${params[1]}"
}
on_265() {
info %s "${params[3]}"
}
on_266() {
info %s "${params[3]}"
}
on_372() {
info %s "${params[1]}"
}
on_375() {
debug %s "${params[1]}"
}
on_376() {
debug %s "${params[1]}"
}
###
# irc send code
###
join() {
send "JOIN %s" "$1"
}
nick() {
send "NICK %s" "$1"
info "changing nickname to %s" "$1"
}
ping() {
send "PING :%s" "$1"
}
pong() {
send "PONG %s" "$1"
}
privmsg() {
send "PRIVMSG %s :%s" "$1" "$2"
}
user() {
send "USER %s * * :%s" "$ident" "$realname"
}
###
# utility hooks
##
hook_JOIN_greet() {
privmsg "${params[0]}" "Hello, $from!"
2021-06-14 23:15:49 +00:00
}
###
# driver
###
2021-06-15 06:33:30 +00:00
nick "$nick"
user "$ident" "$realname"
2021-06-14 23:15:49 +00:00
while recv line; do
2021-06-14 23:35:23 +00:00
params=( )
has_words=no
orig_line=$line
2021-06-14 23:23:36 +00:00
2021-06-14 23:35:23 +00:00
if [[ ${line:0:1} = : ]]; then
2021-06-14 23:23:36 +00:00
src=${line%% *} src=${src#:}
line=${line#:"$src"} line=${line# }
from=${src%@*} ident=${from#*!}
from=${from%!*} host=${src#*@}
fi
2021-06-14 23:35:23 +00:00
cmd=${line%% *}
line=${line#"$cmd"}
line=${line# }
2021-06-14 23:23:36 +00:00
while [[ $line ]]; do
2021-06-14 23:35:23 +00:00
if [[ ${line:0:1} = : ]]; then
2021-06-14 23:23:36 +00:00
params+=("${line:1}")
line=""
has_words=yes
else
2021-06-14 23:35:23 +00:00
param=${line%% *}
params+=("$param")
2021-06-14 23:23:36 +00:00
line=${line#"$param"} line=${line# }
fi
done
if [[ $has_words = yes ]]; then
read -ra words <<< "${params[@]:(-1)}"
else
words=( )
fi
2021-06-15 06:33:30 +00:00
while IFS= read -r hook; do
"$hook"
done < <(compgen -A function "hook_${cmd^^}_")
2021-06-14 23:23:36 +00:00
if hash "on_${cmd^^}" 2>/dev/null; then
"on_${cmd^^}"
else
2021-06-14 23:35:23 +00:00
warn "unhandled line: %s" "$orig_line"
2021-06-14 23:23:36 +00:00
fi
2021-06-14 23:15:49 +00:00
done