#!/bin/sh # Simple irc-like chat system # All channels are logged (they are actually files) # Only one channel at the time can be joined *sorry* # # Copyleft 2001-2004 (g) strk@keybit.net # # Redistributable under the terms of the GNU General Public License # VERSION='0.8' #--< config >--# PREFIX=@prefix@ if [ -z "$SHIRC_CONF" ]; then SHIRC_CONF="$PREFIX/etc/shirc.conf" fi # source systemwide config if [ -e "$SHIRC_CONF" ]; then . $SHIRC_CONF else echo "Configuration file $SHIRC_CONF does not exist, consider installing one" >&2 fi # source user local config if [ -r "${HOME}/.shircrc" ]; then . ${HOME}/.shircrc; fi if [ -z "$SHIRC_MODPATH" ]; then SHIRC_MODPATH=${PREFIX}/lib/shirc:${HOME}/.shirc/mod fi if [ -z "$SHIRC_CHDIR" ]; then SHIRC_CHDIR=/tmp/shirc; fi #--< init >--# umask 000 mkdir -p $SHIRC_CHDIR JOINNAME= JOINPID= JOINFILE= OFILTER= IFILTER= MODULES=$0 # the ``help'' command will grep in these prompt="> " trap 'CMD_quit' 1 5 2 3 9 #--< core commands >--# #HELP# /list [] - Get list of populated channels (matching ) CMD_list() { owd=$PWD; cd $SHIRC_CHDIR echo -e "Name\tOwner\tUsers\tTopic" for name in `ls *$1*.log 2> /dev/null | cut -d . -f 1 | sort -u`; do owner=`ls -l $name.log | awk '{print $3}'` users=`wc -l $name.users | awk '{print $1}'` topic=`cat $name.topic 2> /dev/null` if [ "$users" -lt 1 ]; then continue fi echo -n "$name " echo -n "$owner " echo -n "$users " echo -n "$topic" echo done cd $owd } #HELP# /motd - Print Message Of The Day CMD_motd() { cat ${PREFIX}/etc/shirc.motd 2> /dev/null } #HELP# /join - join or create channel CMD_join() { ch=$1 if [ -z "$ch" ]; then echo "Join which channel ?" return fi if [ -n "$JOINNAME" ]; then if ! CMD_part; then return 1 fi fi lock=${SHIRC_CHDIR}/${ch}.users.LCK if ! lock $lock; then echo "Could not lock user file for channel $ch, try again later" return 1 fi JOINFILE="$SHIRC_CHDIR/$ch.log" JOINNAME=$ch touch $JOINFILE 2> /dev/null exec 3>>$JOINFILE JOINFD=3 # announce the join notify "$LOGNAME joined channel $ch" if [ -n "$OFILTER" ]; then tail -n 1 -f $JOINFILE | ( $OFILTER ) & else tail -n 1 -f $JOINFILE & fi JOINPID=$! echo "${LOGNAME}:${JOINPID}" >> $SHIRC_CHDIR/$ch.users rm -f $lock prompt="$JOINNAME> " # run onjoin hook if [ -r "${HOME}/.shirc/onjoin" ]; then CMD_source ${HOME}/.shirc/onjoin fi } #HELP# /part - leave current channel (a new join will do) CMD_part() { # stop listening on other channel if [ -n "$JOINNAME" ]; then lock=${SHIRC_CHDIR}/${JOINNAME}.users.LCK if ! lock $lock; then echo "Could not lock user file for channel $JOINNAME, try again later" return 1 fi kill $JOINPID ## will be verbose, no way to shut it up cp $SHIRC_CHDIR/$JOINNAME.users /tmp/$$ grep -v "^${LOGNAME}:${JOINPID}$" /tmp/$$ > ${SHIRC_CHDIR}/${JOINNAME}.users rm -f $lock rm -f /tmp/$$ # announce the departure notify "$LOGNAME left channel $JOINNAME" echo "You left channel $JOINNAME" unset JOINNAME unset JOINPID unset JOINFILE exec 3<&- unset JOINFD prompt="> " fi } #HELP# - send to the currently joined channel CMD_say() { if [ -z "$JOINFILE" ]; then echo "You must first join a channel" return fi what=$* if [ -n "$IFILTER" ]; then echo "<$LOGNAME> $*" | $IFILTER >&$JOINFD # $JOINFILE else echo "<$LOGNAME> $*" >&$JOINFD # >> $JOINFILE fi } #HELP# /names [] - list users in or current channel CMD_names() { if [ -n "$1" ]; then ch=$1 elif [ -n "$JOINNAME" ]; then ch=$JOINNAME else echo "You must specify a channel name or join one" return fi if [ ! -r "$SHIRC_CHDIR/$ch.log" ]; then echo "Channel '$ch' does not exist (or file is not readable)" return fi owner=`ls -l $SHIRC_CHDIR/$ch.log | awk '{print $3}'` users=`cat "$SHIRC_CHDIR/$ch.users" | cut -d: -f1 | sort -u 2> /dev/null` echo -n "Users on '$ch': " for u in $users; do if [ "$u" = "$owner" ]; then echo -n '@' fi echo -n "$u " done echo } # this is kept for backward compatibility CMD_users() { CMD_names $@; } #HELP# /channels - list joined channels (currently only one) CMD_channels() { if [ -n "$JOINNAME" ]; then echo "$JOINNAME" else echo "You are not joined to any channel" fi } #HELP# /load - load functions from CMD_load() { # Need an argument if [ -z "$1" ]; then echo "Load what ?" return fi file= for dir in `echo $SHIRC_MODPATH | tr ':' ' '`; do if [ -r $dir/$1 ]; then file=$dir/$1 break fi done if [ -z "$file" ]; then echo "Can't find $1 in $SHIRC_MODPATH" return fi if echo "$MODULES" | grep -q " $file" ; then echo "Module $1 already loaded" return fi # Error loading ? if ! . $file 2> /dev/null; then echo "Error loading $1" return fi MODULES="$MODULES $file" echo "Module '$file' loaded" } #HELP# /source - read commands from CMD_source() { if [ -z "$1" ]; then echo "Source what?" return; fi if [ ! -r "$1" ]; then echo "Can't find or read '$1'" return fi op=$prompt; prompt= parsestream < $1 prompt=$op } #HELP# /topic [] - get or set topic for current channel CMD_topic() { arg=$@ if [ -z "$JOINFILE" ]; then echo "You must first join a channel" return fi if [ -z "$arg" ]; then if ! cat $SHIRC_CHDIR/$JOINNAME.topic 2> /dev/null; then echo "No topic for $JOINNAME" fi else echo "$arg" > $SHIRC_CHDIR/$JOINNAME.topic notify "$LOGNAME changed the topic to '$arg'" fi } #HELP# /quit - exit safely CMD_quit() { if [ -n "$JOINNAME" ]; then CMD_part fi echo "Goodbye" exit } #HELP# /help - show this help CMD_help() { #echo "shirc $VERSION" grep '^#HELP#' $MODULES | cut -d '#' -f 3- #echo "Core commands:" #grep '^#HELP#' $0 | cut -d '#' -f 3- #echo "User defined commands:" #grep '^#HELP#' ${HOME}/.shircrc 2>/dev/null | cut -d '#' -f 3- } #--< end of core commands >--# lock() { lock=$1 retries=2 while :; do if [ -e "$lock" ]; then find "$lock" -cmin +1 -exec rm -f {} \; if [ $retries -gt 0 ]; then sleep 1 retries=$(($retries-1)) else return 1 fi else touch $lock return 0 fi done } notify() { if [ -z "$JOINNAME" ]; then return fi echo "*** $@" >> $JOINFILE } parsestream() { echo -n "$prompt" >&2 while read line; do cmd=`expr "$line" : '^/\([^ ]*\) *.*' 2> /dev/null` args=`expr "$line" : '^/[^ ]* *\(.*\)' 2> /dev/null` if [ -n "$cmd" ]; then if grep "^CMD_$cmd" $MODULES >/dev/null 2>&1; then CMD_$cmd $args else echo "Unknown command: $cmd" fi elif [ -n "$line" ]; then CMD_say "$line" fi echo -n "$prompt" >&2 done } #--< main >--# # run onstart hook if [ -r ${HOME}/.shirc/onstart ]; then CMD_source ${HOME}/.shirc/onstart fi echo "Welcome to shirc $VERSION" echo "Type /help for a list of available commands" CMD_motd parsestream CMD_quit