376 lines
5.9 KiB
Bash
Executable file
376 lines
5.9 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
###
|
|
# logger
|
|
###
|
|
|
|
declare -A levels=(
|
|
[debug]=1 [info]=2
|
|
[warn]=3 [error]=4
|
|
)
|
|
|
|
log() {
|
|
if [[ -v LEVEL ]] && (( levels[$level] <= levels[$LEVEL] )); then
|
|
printf "%s: $1\n" "${LEVEL^^}" "${@:2}" >&"$log"
|
|
fi
|
|
}
|
|
|
|
debug() {
|
|
LEVEL=debug log "$@"
|
|
}
|
|
|
|
info() {
|
|
LEVEL=info log "$@"
|
|
}
|
|
|
|
warn() {
|
|
LEVEL=warn log "$@"
|
|
}
|
|
|
|
error() {
|
|
LEVEL=error log "$@"
|
|
}
|
|
|
|
###
|
|
# argument parser for parsing arguments
|
|
##
|
|
|
|
declare -A opts
|
|
|
|
while (( $# )); do
|
|
case $1 in
|
|
--*=*)
|
|
key=${1#--} key=${key%%=*}
|
|
opts[$key]=${1#--*=}
|
|
;;
|
|
--no-*)
|
|
key=${1#--no-}
|
|
opts[$key]=no
|
|
;;
|
|
--)
|
|
shift
|
|
break
|
|
;;
|
|
--*)
|
|
key=${1#--}
|
|
opts[$key]=yes
|
|
;;
|
|
*)
|
|
break
|
|
esac
|
|
|
|
shift
|
|
done
|
|
|
|
###
|
|
# default config
|
|
##
|
|
|
|
server=${opts[server]:-irc.libera.chat}
|
|
tls=${opts[tls]:-no}
|
|
|
|
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
|
|
port=${opts[port]:-6667}
|
|
fi
|
|
|
|
level=${opts[log-level]:-info}
|
|
|
|
if [[ ${opts[log]} ]]; then
|
|
exec {log}>"${opts[log]}"
|
|
else
|
|
log=1
|
|
fi
|
|
|
|
nick=${opts[nick]:-rowbot-dev}
|
|
ident=${opts[ident]:-rowbot}
|
|
realname=${opts[realname]:-rowbot}
|
|
chan=${opts[chan]:-}
|
|
trigger=${opts[trigger]:-\`}
|
|
fact_root=${opts[fact-root]:-.}
|
|
|
|
###
|
|
# net code
|
|
###
|
|
|
|
if [[ $tls = yes ]]; then
|
|
coproc sock { socat OPENSSL:"$server":"$port" -; }
|
|
exec {in_sock}<&"${sock[0]}" {out_sock}>&"${sock[1]}"
|
|
else
|
|
exec {sock}<>/dev/tcp/"$server"/"$port"
|
|
in_sock=$sock out_sock=$sock
|
|
fi
|
|
|
|
send() {
|
|
local fmt
|
|
printf -v fmt "$1" "${@:2}"
|
|
printf '%s\r\n' "$fmt" >&"$out_sock"
|
|
debug "sending line: %s" "$fmt"
|
|
}
|
|
|
|
recv() {
|
|
declare -n sock_line=$1
|
|
IFS= read -r "$1" <&"$in_sock"
|
|
sock_line=${sock_line%$'\r'}
|
|
debug "received line: %s" "$sock_line"
|
|
}
|
|
|
|
###
|
|
# irc recv code
|
|
###
|
|
|
|
on_ERROR() {
|
|
error "${params[0]}"
|
|
exit
|
|
}
|
|
|
|
on_JOIN() {
|
|
info "%s has joined %s" "$from" "${params[0]}"
|
|
}
|
|
|
|
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 :\u200b%s" "$1" "$2"
|
|
}
|
|
|
|
user() {
|
|
send "USER %s * * :%s" "$ident" "$realname"
|
|
}
|
|
|
|
###
|
|
# app hooks
|
|
##
|
|
|
|
hook_PRIVMSG_factoids() {
|
|
if [[ ${params[0]:0:1} != \# ]]; then
|
|
return 0
|
|
elif [[ ${words[0]} = "$trigger"* ]]; then
|
|
case ${words[0]:${#trigger}} in
|
|
is)
|
|
if (( ${#words[@]} < 3 )); then
|
|
return 0
|
|
fi
|
|
|
|
local key val
|
|
key=${params[1]#*"$trigger"is} key=${key# }
|
|
val=${key#* } key=${key%% *}
|
|
|
|
info "%s said in %s to remember %s as %s" "$from" "${params[0]}" "$key" "$val"
|
|
privmsg "${params[0]}" "I'm sure I'll remember that."
|
|
mkdir -p "$fact_root"/"${params[0]}"
|
|
printf %s "$val" > "$fact_root"/"${params[0]}"/"$key"
|
|
;;
|
|
isnt)
|
|
if (( ${#words[@]} < 2 )); then
|
|
return 0
|
|
fi
|
|
|
|
local key
|
|
key=${params[1]#*"$trigger"isnt} key=${key# }
|
|
|
|
if [[ -f $fact_root/${params[0]}/$key ]]; then
|
|
info "%s said in %s to delete %s" "$from" "${params[0]}" "$key"
|
|
privmsg "${params[0]}" "I forgot what that was anyways."
|
|
rm -f "$fact_root"/"${params[0]}"/"$key"
|
|
fi
|
|
;;
|
|
ls)
|
|
local facts=( "$fact_root"/"${params[0]}"/* )
|
|
privmsg "${params[0]}" "${facts[*]##*/}"
|
|
;;
|
|
*)
|
|
local key=${params[1]:1}
|
|
|
|
if [[ -f $fact_root/${params[0]}/$key ]]; then
|
|
privmsg "${params[0]}" "$from: $(<"$fact_root"/"${params[0]}"/"$key")"
|
|
fi
|
|
esac
|
|
fi
|
|
}
|
|
|
|
###
|
|
# driver
|
|
###
|
|
|
|
nick "$nick"
|
|
user "$ident" "$realname"
|
|
|
|
while recv line; do
|
|
params=( )
|
|
has_words=no
|
|
orig_line=$line
|
|
|
|
if [[ ${line:0:1} = : ]]; then
|
|
src=${line%% *} src=${src#:}
|
|
line=${line#:"$src"} line=${line# }
|
|
from=${src%@*} ident=${from#*!}
|
|
from=${from%!*} host=${src#*@}
|
|
fi
|
|
|
|
cmd=${line%% *}
|
|
line=${line#"$cmd"}
|
|
line=${line# }
|
|
|
|
while [[ $line ]]; do
|
|
if [[ ${line:0:1} = : ]]; then
|
|
params+=("${line:1}")
|
|
line=""
|
|
has_words=yes
|
|
else
|
|
param=${line%% *}
|
|
params+=("$param")
|
|
line=${line#"$param"} line=${line# }
|
|
fi
|
|
done
|
|
|
|
if [[ $has_words = yes ]]; then
|
|
read -ra words <<< "${params[@]:(-1)}"
|
|
else
|
|
words=( )
|
|
fi
|
|
|
|
while IFS= read -r hook; do
|
|
"$hook"
|
|
done < <(compgen -A function "hook_${cmd^^}_")
|
|
|
|
if hash "on_${cmd^^}" 2>/dev/null; then
|
|
"on_${cmd^^}"
|
|
else
|
|
warn "unhandled line: %s" "$orig_line"
|
|
fi
|
|
done
|