123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- # Vital bits of bashy2
- # This file is part of bashy2, an irc bot used on irc.sdf.org.
- # Copyright (C) 2015-2016 mlaine@sdfeu.org, grugly@sdf.org
- # Copyright (C) 2017-2018 mlaine@sdfeu.org
- # Permission is hereby granted, free of charge, to any person obtaining
- # a copy of this software and associated documentation files (the
- # "Software"), to deal in the Software without restriction, including
- # without limitation the rights to use, copy, modify, merge, publish,
- # distribute, sublicense, and/or sell copies of the Software, and to
- # permit persons to whom the Software is furnished to do so, subject to
- # the following conditions:
- # The above copyright notice and this permission notice shall be
- # included in all copies or substantial portions of the Software.
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- #-----------------------------------------------------------------------
- function debug {
- # Sends data to #bashy2-debug.
- if [ "${debug}" = 0 ]; then
- printf 'PRIVMSG #bashy2-debug :%s\n' "$*" >&3
- fi
- ## Writes debug data to ~/bashy.log
- if [ "${debug}" = "logfile" ]; then
- echo "$*" >> ${HOME}/bashy.log
- fi
- }
- function nonvital_join {
- # A parent function for nonvital function calls. Called for every JOIN.
- welcome
- }
- function nonvital_pm {
- # A parent function for nonvital function calls. Called for every PRIVMSG.
- seen
- }
- function parse {
- # Parses PRIVMSG formatted information.
- local x="${1%\!*}"
- local y="${1%@*}"
- nick="${x:1}"
- user="${y#*\!}"
- host="${1#*@}"
- chan="$3"
- msg="${4:1}"
- local z="${msg:1}"
- cmd="${z,,}"
- args=("${@:5}")
- prefix="${msg:0:1}"
- }
- function multiply {
- # "$(multiply 3 'foo')" -> 'foo foo foo'
- local x="$(printf "%"$1"s")"
- local y="${x// /"$2" }"
- printf '%s\n' "${y% *}"
- }
- function join {
- # Joins all channels.
- local chans=('./channels/'*)
- local chlist="${chans[*]##*/}"
- send 'join' "${chlist// /,}"
- }
- function chmode {
- # If called with no args, resets channel modes for all channels.
- # Otherwise lists all channels.
- local chans=('./channels/'*)
- if [[ "$1" ]]; then
- send 'pm' "Channels: ${chans[*]##*/}"
- else
- for channel in "${chans[@]##*/}"; do
- local mode="$(awk 'NR==2' "./channels/$channel")"
- local bans="$(awk 'NR==3' "./channels/$channel")"
- send 'mode' "$channel" "$mode"
- send 'mode' "$channel" "$bans"
- done
- fi
- }
- function chtopic {
- # If called with no args, queries the topic of every channel. A query
- # results main() calling chtopic() with args, resulting in the actual
- # setting of topics (should they differ from their default values).
- if (( ! "$#" )); then
- local chans=('./channels/'*)
- for channel in "${chans[@]##*/}"; do
- send 'qtopic' "$channel"
- done
- else
- local ch="$4"
- local foo="${*:5}"
- local topic="${foo:1}"
- local default="$(awk 'NR==1' "./channels/$ch")"
- if [[ -n "$default" && "$topic" != "$default" ]]; then
- send 'topic' "$ch" "$default"
- fi
- fi
- }
- function up {
- # Changes a user's mode based on the output of WHOIS received from
- # the server. WHOIS is issued by the user's request '!up'.
- if [[ "$2" = '311' ]]; then
- nick="$4"
- user="$5"
- host="$6"
- auth
- elif [[ -n "$flag" ]]; then
- local foo=("${@:5}")
- local chans=("${foo[@]//[:~&@%+]/}")
- local name="$(multiply "${#flag}" "$nick")"
- for channel in "${chans[@]}"; do
- if [[ -f "./channels/$channel" ]]; then
- send 'mode' "$channel" "+$flag" "$name"
- fi
- done
- fi
- }
- function upall {
- # Changes user modes based on the output of WHO received from the
- # server. WHO is issued by serv() 'upall'.
- if [[ "$2" = '315' ]]; then
- local ch="$4"
- sendup "$ch" 'o'
- sendup "$ch" 'h'
- sendup "$ch" 'v'
- ops=(); hops=(); voices=()
- else
- user="$5"
- host="$6"
- nick="$8"
- stats="$9"
- ## Test if sixth field of RPL_WHOREPLY indicates chan voice or higher,
- ## and shuck if true. This reduces unneeded shell commands and irc commands
- local reg_shuck="[~&@%+]" ## See E14) of https://tiswww.case.edu/php/chet/bash/FAQ
- if [[ ! "${stats}" =~ ${reg_shuck} ]]; then
- auth
- fi
- case "$flag" in
- 'ohv')
- ops+=("$nick")
- hops+=("$nick")
- voices+=("$nick") ;;
- 'hv')
- hops+=("$nick")
- voices+=("$nick") ;;
- 'v')
- voices+=("$nick") ;;
- esac
- fi
- }
- function sendup {
- # Simplifies upall().
- local ch="$1"
- case "$2" in
- 'o') local nicks=("${ops[@]}") ;;
- 'h') local nicks=("${hops[@]}") ;;
- 'v') local nicks=("${voices[@]}") ;;
- esac
- local f="$(printf 'f%.0s' $(seq 1 "$modes"))"
- while (( "${#nicks[@]}" > "$modes" )); do
- send 'mode' "$ch" "+${f//f/$2}" "${nicks[*]:0:$modes}"
- local nicks=("${nicks[@]:$modes}")
- sleep 3 ## Helps combat hitting sendq limit
- done
- if (( "${#nicks[@]}" > 0 )); then
- local f="$(printf 'f%.0s' $(seq 1 "${#nicks[@]}"))"
- send 'mode' "$ch" "+${f//f/$2}" "${nicks[*]}"
- sleep 3 ## Helps combat hitting sendq limit
- fi
- }
- function send {
- # A wrapper function to simplify sending messages to the server.
- debug 'OUT:' "$@"
- case "$1" in
- 'pong')
- printf 'PONG %s\n' "$2" >&3 ;;
- 'pm')
- printf 'PRIVMSG %s :%s\n' "$chan" "$2" >&3 ;;
- 'mode')
- printf 'MODE %s %s %s\n' "$2" "$3" "$4" >&3 ;;
- 'join')
- printf 'JOIN %s\n' "$2" >&3 ;;
- 'part')
- printf 'PART %s\n' "$2" >&3 ;;
- 'oper')
- printf 'OPER %s %s\n' "$2" "$3" >&3 ;;
- 'user')
- printf 'USER %s 0 * :%s\n' "$2" "$3" >&3 ;;
- 'nick')
- printf 'NICK %s\n' "$2" >&3 ;;
- 'topic')
- printf 'TOPIC %s :%s\n' "$2" "$3" >&3 ;;
- 'qtopic')
- printf 'TOPIC %s\n' "$2" >&3 ;;
- 'whois')
- printf 'WHOIS %s\n' "$2" >&3 ;;
- 'who')
- printf 'WHO %s\n' "$2" >&3 ;;
- 'quit')
- printf "QUIT :I'll be back!\n" >&3 ;;
- esac
- }
- function serv {
- # Maintainer only commands.
- case "$1" in
- 'list')
- chmode 0 ;;
- 'join')
- join
- sleep 5 && chmode &&
- sleep 5 && chtopic &&
- sleep 5 && serv 'upall' & ;;
- 'delete')
- local ch="${args[0]}"
- if [[ -f "./channels/$ch" ]]; then
- rm "./channels/$ch"
- send 'pm' "Channel $ch deleted; remember to push your changes!"
- send 'part' "$ch"
- else
- send 'pm' "Channel ${ch:-null} does not exist!"
- fi ;;
- 'chmode')
- chmode
- send 'pm' 'Channel modes set.' ;;
- 'chtopic')
- chtopic
- send 'pm' 'Channel topics set.' ;;
- 'upall')
- local chans=('./channels/'*)
- for channel in "${chans[@]##*/}"; do
- send 'who' "$channel"
- done ;;
- 'topic')
- local ch="${args[0]}"
- local to="${args[*]:1}"
- if [[ -f "./channels/$ch" ]]; then
- printf '%s\n' 1c "$to" . w | ed -s "./channels/$ch"
- (( "$quiet" == 1 )) && chtopic
- else
- send 'pm' "Channel ${ch:-null} does not exist!"
- fi ;;
- 'head')
- send 'pm' "$(git log -1 --pretty="format:%h: %s [%an, %ci]")" ;;
- 'pull')
- ( git fetch origin master && git reset --hard FETCH_HEAD &&
- git clean -df ) 1>'/dev/null' &&
- send 'pm' 'Pulled!' ;;
- 'reload')
- source './bits/conf.sh'
- source './bits/vitalbits.sh'
- source './bits/nonvitalbits.sh'
- send 'pm' 'Reloaded!' ;;
- 'restart')
- ( sleep 5 && './bashy2' & )
- send 'pm' 'Restarting...'
- send 'quit'
- exit 0 ;;
- 'debug')
- echo "DEBUG: debug toggle [${args[0]}]" >> $HOME/bashy.log
- if [ "${args[0]}" = 0 ]; then
- debug=0
- send 'pm' 'Debug log available at #bashy2-debug.'
- elif [ "${args[0]}" = "logfile" ]; then
- debug="logfile"
- send 'pm' 'Debug log available in ~/bashy.log'
- else
- debug=1
- send 'pm' 'Debug logging off.'
- fi ;;
- 'quiet')
- if (( "$quiet" == 0 )); then
- quiet=1
- send 'pm' 'Volume up!'
- else
- quiet=0
- send 'pm' 'I will shut up.'
- fi ;;
- 'die')
- send 'pm' "What'd I do?"
- send 'quit'
- exit 0 ;;
- esac
- }
- function run {
- # Runs commands.
- if [[ -x "$cmd_dir/$cmd" ]]; then
- ( cd "$cmd_dir" && "./$cmd" "$nick" "$chan" "${args[@]}" ) &
- else
- send 'pm' "'$cmd' not found. See !commands for available commands."
- fi
- }
- function priv {
- # Processes every PRIVMSG.
- parse "$@"
- if [[ "$chan" = "$bot_nick" ]]; then
- chan="$nick"
- else
- nonvital_pm
- fi
- 2botpriv
- if [[ "$msg" = '!up' ]]; then
- send 'whois' "$nick"
- return 0
- elif [[ -n "$cmd" && ("$quiet" -eq 1 || "$chan" = "$nick") &&
- "$prefix" = '!' && ! "$cmd" =~ [^[:alnum:]_] ]]; then
- run
- return 0
- fi
- if [[ "$prefix" = '%' && -f "./maintainers/$nick" ]]; then
- auth; [[ -n "$flag" ]] && serv "$cmd"
- elif [[ "$chan" = "$nick" ]]; then
- send 'pm' "I am a bot. Try '!help' instead."
- fi
- }
- function user_join {
- # Processes every JOIN.
- parse "$@"
- nonvital_join
- 2botjoin
- auth
- if [[ -n "$flag" ]]; then
- local ch="${chan:1}"
- local name="$(multiply "${#flag}" "$nick")"
- send 'mode' "$ch" "+$flag" "$name"
- fi
- }
- function user_quit {
- # Processes every QUIT.
- 2botquit "${1%\!*}"
- }
|