yaic (10856B)
1 #!/bin/sh 2 3 # yaic: Yet another ii client. 4 # Cem Keylan 5 6 msg() { printf '\033[1m%s\033[m\n' "$@" >/dev/tty ;} 7 die() { printf '\033[1;38;41merr: %s\033[m\n' "$@" >&2; exit 1;} 8 9 10 toggle() { 11 # A hacky way to create and remove variables 12 eval "[ \"\$$1\" ] && unset $1 && return 0 ||:" 13 eval "$1=1" 14 } 15 16 usage() { printf '%s\n' "usage: ${0##*/} [-s server] [-n nick] [-c chan] [-r dir]" \ 17 " [-h size] [-l length] [f fold]" "" \ 18 " -s Sets the server (default: $s)" \ 19 " -n Sets the nick (default: $n)" \ 20 " -c Sets the channel (default: $c)" \ 21 " -r Sets root irc dir (default: $r)" \ 22 " -h Sets hist value (default: $h)" \ 23 " -l Limits nick space (default: $l)" \ 24 " -f Sets the fold value (default: $f)" "" 25 exit 1 26 } 27 28 # Function for bookmarking a location in the chat. 29 mark() { printf '%s --- ---------------------------------------------------\n' \ 30 "$(date +%s)" >> "$outfile" ;} 31 32 gethelp() { 33 # This is the helper function called 34 # by :h|:help 35 msg \ 36 "[1;35mList of Commands" \ 37 ":b Toggles the upper bar" \ 38 ":c|:chan Prints a list of open channels" \ 39 ":c|:chan [channel] Changes the channel to the given channel" \ 40 ":h|:help Prints this help information" \ 41 ":hist Changes the history size" \ 42 ":l [length] Change the limit for nick space" \ 43 ":m Marks the current position" \ 44 ":n [note] Adds a note to the current position" \ 45 ":s Toggles server notifications" \ 46 ":t Toggles outputting your own messages" \ 47 ":q Exits the program (not ii)" \ 48 ":x Marks the current position and exits" \ 49 ":v|:version Prints the version" 50 } 51 52 getchan() { 53 # Gets a list of all active channels in the server 54 # Does not include the server itself 55 msg "[35mChannel list" 56 for chan in "$r/$s/"* ; do 57 # We don't want this to return errors. 58 # shellcheck disable=2015 59 [ -p "$chan/in" ] && printf '%s\n' "${chan##*/}" >/dev/tty ||: 60 done 61 } 62 63 checkchan() { 64 # Checks if the given channel is open. 65 [ -p "$r/$s/$1/in" ] || { 66 msg "Channel '$1' doesn't seem to be open" 67 return 1 68 } 69 } 70 71 changechan() { 72 # Kill the tail from the previous channel 73 kill "$tailpid" ||: 74 75 # Restore the original stty configuration 76 # and reset the colour. 77 stty "$prestty" ; printf '\033[m' 78 79 # unset the CHAN variable so that it doesn't 80 # try to change the channel when the user 81 # is trying to quit. 82 unset CHAN 83 84 # Run the main function with the new channel 85 main -s "$s" -n "$n" -c "$1" -r "$r" -h "$h" -l "$l" -f "$f" 86 } 87 88 statusbar() { 89 # If NOBAR variable is set, doesn't add a statusbar line. 90 [ -z "$NOBAR" ] || return 0 91 92 # Get the channel name to a variable. This is done to remove 93 # '/.' if it's the main server output. 94 chan="${c#.}" 95 96 # This horrible looking printf function does the following, 97 # \033[s - Saves the current position 98 # \033[H - Goes to the top of the screen 99 # \033[K - Removes that line 100 # \033[1;37;40m - Changes colour to black background and white foreground 101 # "$s/$c" - Prints the channel name 102 # \033[u - Returns back to the previous cursor position 103 # \033[m - Restores the colour 104 printf '\033[s\033[H\033[K\033[1;37;40m%s\033[u\033[m' "$ss${chan:+/}$chan" >/dev/tty 105 } 106 107 main() { 108 # Set default values. 109 : "${f:=60}" "${n:=${USER:-user}}" 110 : "${c:=""}" "${r:=$HOME/irc}" 111 : "${h:=30}" "${l:=10}" 112 : "${s:=irc.freenode.net}" 113 114 case "$1" in --help|help) usage ; esac 115 116 # Parse all arguments. 117 while getopts ':s:n:c:r:h:l:f:' opt ; do 118 case "$opt" in 119 s) s="$OPTARG" ;; n) n="$OPTARG" ;; 120 c) c="$OPTARG" ;; r) r="$OPTARG" ;; 121 h) h="$OPTARG" ;; l) l="$OPTARG" ;; 122 f) f="$OPTARG" ;; 123 :) die "'-$OPTARG' requires a value" ;; 124 ?) die "${0##*/}: invalid option '-$OPTARG'" ;; 125 esac 126 done 127 128 # Save in file and out file in a variable. 129 infile="$r/$s/$c/in" 130 outfile="$r/$s/$c/out" 131 132 # Save the initial stty state in a variable 133 # to make sure it doesn't get overriden when 134 # changing channels. 135 prestty="$(stty -g)" 136 137 # Check if we are using sbase date, which parses 138 # Unix epoch without an '@' sign. We don't want 139 # this to return errors. 140 # shellcheck disable=2015 141 date -d @0 >/dev/null 2>&1 && GNUDATE=1 ||: 142 143 # Save a short name of the server. 144 # This will be used for server messages, 145 # announcements, etc. 146 ss=${s%.*} ss=${ss#*.} 147 148 # Check for the existence of in and out files. 149 # Exit if in file doesn't exist. 150 # Create it if out file doesn't exist. 151 [ -p "$infile" ] || die "in file could not be found, is the daemon available?" 152 [ -e "$outfile" ] || touch "$outfile" 153 154 # Clear the screen for prettier output 155 clear 156 157 # Start tail with the history variable. We read the input in 158 # 3 different variables. First word is always the date. The 159 # second word is usually the author's nickname, and the rest 160 # of the line is the message. We will deal with the exceptions 161 # in the function itself. 162 tail -f -n "$h" "$outfile" | while read -r date auth msg; do 163 # Restore colour. 164 printf '\033[m' 165 166 # Get the time in a pretty format. The date is in Epoch 167 # format. We convert it to [12:34] format. 168 cleardate="$(date -d "${GNUDATE:+@}$date" '+[%H:%M]')" 169 170 # Nicknames are in <nick> format and server notifications 171 # are in -!- format. If we are not dealing with them, then 172 # it is a server message (not a server notification). We 173 # need to seperate them in a hacky way. 174 case "$auth" in \<*\>|-!-|---) ;; *) msg="$auth $msg" auth="$ss" ; esac 175 176 # Don't print server messages if NOSERVER is set. 177 case "$auth" in \<*\>|---);; *) [ "$NOSERVER" ] && continue ; esac 178 179 # Remove the <*> from nicks. 180 auth="${auth%>}"; auth="${auth#<}" 181 182 # If the nickname is the user's, print the nick in yellow. 183 # Other nicknames are printed in blue. 184 # Server messages are printed grey to be not distracting. 185 case "$auth" in 186 "$n") printf '%s \033[1;33m%-*.*s \033[m' "$cleardate" "$l" "$l" "$auth" ;; 187 "NOTE")printf '%s \033[1;31m%-*.*s \033[m' "$cleardate" "$l" "$l" "$auth" ;; 188 "-!-") printf '\033[37m%s %-*.*s ' "$cleardate" "$l" "$l" "$auth"; ;; 189 "---") printf '\033[37m%s %-*.*s ' "$cleardate" "$l" "$l" "$auth"; ;; 190 "$ss") printf '%s %-*.*s ' "$cleardate" "$l" "$l" "$auth" ;; 191 *) printf '%s \033[1;36m%-*.*s \033[m' "$cleardate" "$l" "$l" "$auth" 192 highlight=1 193 ;; 194 esac 195 196 # This prints the message and folds it. It doesn't add any 197 # space if it is the first line. But adds space equal to 198 # the space given for the time, nick, and two spaces. If the 199 # user's nick is inside the message string, highlight it, but 200 # only once. We get the highlight value only for messages from 201 # other users (i.e. not server messages or your own messages). 202 firstline=1 203 printf '%s\n' "$msg" | sed "s/$n/${highlight:+[1;33m}$n${highlight:+[m}/" | fold -sw "$f" | while read -r line; do 204 [ "$firstline" ] || printf '%*s' "$(( ${#cleardate} + l + 2 ))" '' 205 printf '%s\n' "$line" ; unset firstline 206 done 207 208 # Unset the highlight variable so that we don't highlight 209 # server messages. 210 unset highlight 211 212 # Refresh the statusbar line. 213 statusbar 214 215 # Restore colour. 216 printf '\033[m' 217 done >/dev/tty & 218 219 # Save the process id of tail so that we don't have any 220 # issues when we are trying to switch channels. 221 tailpid=$! 222 223 # Restore stty to what they were before. We do want this 224 # to expand now, so the shellcheck error can be ignored. 225 trap 'stty $prestty; printf "\033[m" ; kill -TERM 0' EXIT INT QUIT 226 stty -echonl ${NOTEXT:+-}echo 227 228 printf '\033[m' 229 while read -r msg; do 230 # Go up, remove the line, and go back down. 231 # Keeps it clean. If we have 'stty -echo' set, 232 # we don't need to do this. 233 [ "$NOTEXT" ] || printf '\033[A\033[K' >/dev/tty 234 235 # Refresh the statusbar line. 236 statusbar 237 238 # shellcheck disable=2015 239 case "$msg" in 240 '') continue ;; 241 :b) toggle NOBAR ; CHAN="${c:-.}" ; break ;; 242 :c|:chan) getchan ; continue ;; 243 ':c '*|':chan '*) 244 # Check if the channel exists beforehand 245 # so that the program outputs an error 246 # instead of exiting. Set a CHAN variable 247 # and break if the channel exists 248 if checkchan "${msg#:c* }" ; then 249 CHAN="${msg#:c* }" 250 break 251 else continue ; fi 252 ;; 253 :h|:help) gethelp ; continue ;; 254 :hist*) 255 # Word splitting is intentional here. 256 # shellcheck disable=2086 257 [ ${msg#:hist} ] || { msg "Specify a number" ; continue ;} 258 h="${msg#:hist* }" CHAN="${c:-.}" ; break ;; 259 :q) break ;; 260 :t) toggle NOTEXT ; [ "$NOTEXT" ] && stty -echo || stty echo ; continue ;; 261 ":n "*) printf '%s <NOTE> %s\n' "$(date +%s)" "${msg#:n }" >> "$outfile" ; continue ;; 262 ":l "*) l="${msg#:l* }" CHAN="${c:-.}"; break ;; 263 :m) mark; continue ;; 264 :x) mark; break ;; 265 :s) toggle NOSERVER ; CHAN="${c:-.}" ; break ;; 266 :v|:version) msg "${0##*/}-0.1.0" ; continue ;; 267 :*) msg "${0##*/}: Unknown Command '$msg'" "Type :help to get commands" ; continue ;; 268 '\:'*) 269 # User can send a smiley, or anything starting with 270 # a ':' as a message by adding a backslash. 271 msg=${msg#\\} ;; 272 /msg) msg="/j ${msg#/msg}" ;; 273 /names) msg="/names $c" ;; 274 /q) printf '%s\n' "/q" >> "$infile" ; break ;; 275 esac 276 printf '%s\n' "$msg" 277 done >> "$infile" 278 279 [ "$CHAN" ] && changechan "$CHAN" 280 281 } 282 283 # Load a configuration file if it exists, very beneficial if 284 # you don't want to set them on your shellrc. 285 # shellcheck disable=1090 286 [ -r "${XDG_CONFIG_HOME:-$HOME/.config}/yaicrc" ] && 287 . "${XDG_CONFIG_HOME:-$HOME/.config}/yaicrc" 288 289 main "$@"