shircbot.sh revision 7c2fbfb345896881c631598ee3852ce9ce33fb07
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#
#
# Make sure all math stuff runs in the "C" locale to avoid problems
# with alternative # radix point representations (e.g. ',' instead of
# '.' in de_DE.*-locales). This needs to be set _before_ any
# floating-point constants are defined in this script).
if [[ "${LC_ALL}" != "" ]] ; then
export \
LC_MONETARY="${LC_ALL}" \
LC_MESSAGES="${LC_ALL}" \
LC_COLLATE="${LC_ALL}" \
LC_CTYPE="${LC_ALL}"
unset LC_ALL
fi
export LC_NUMERIC=C
function fatal_error
{
print -u2 "${progname}: $*"
exit 1
}
# Definition for a IRC session class
typeset -T ircsession_t=(
typeset -C server=(
typeset name
integer port
)
typeset nick="ksh93irc"
typeset running=true
integer fd=-1
function createsession
{
set -o xtrace
_.nick=$3
redirect {_.fd}<>"/dev/tcp/${_.server.name}/${_.server.port}"
return 0
}
function login
{
{
} >&${_.fd}
return 0
}
function join_channel
{
return 0
}
function mainloop
{
typeset line
float -S last_tick=0
# We use the linebuf_t class here since network traffic
# isn't guranteed to fit a single $'\n'-terminated line
# into one TCP package. linebuf_t buffers characters
# until it has one complete line. This avoids the need for
# async I/O normally used by IRC clients
set -o xtrace
while ${_.running} ; do
while serverbuf.readbuf line <&${fd} ; do
done
while clientbuf.readbuf line </dev/stdin ; do
printf "client: %q\n" "${line}"
done
# call mainloop_tick function in intervals to handle
# async events (e.g. automatic /join etc.)
fi
done
return 0
}
function mainloop_tick
{
return 0
}
function dispatch_serverevent
{
typeset line="$1"
typeset -C ping_args=(
)
_.serverevent_ping "ping_args"
;;
typeset -C privmsg_args=(
typeset msguser="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\1}"
typeset msgchannel="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\3}"
typeset msg="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\4}"
)
_.serverevent_privmsg "privmsg_args"
;;
typeset -C invite_args=(
typeset inviteuser="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\1}"
typeset invitenick="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\3}"
typeset invitechannel="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\4}"
)
_.serverevent_invite "invite_args"
;;
*)
printf "server: %q\n" "${line}"
;;
esac
return 0
}
function serverevent_privmsg
{
nameref args=$1
typeset msguser="${args.msguser}"
typeset msgchannel="${args.msgchannel}"
printf "#privms: user=%q, channel=%q, msg=%q\n" "$msguser" "$msgchannel" "$msg"
return 0
}
function serverevent_invite
{
nameref args=$1
return 0
}
function send_privmsg
{
typeset channel="$1"
typeset msg="$2"
# Do we have to escape any characters in "msg" ?
return 0
}
function serverevent_ping
{
nameref args=$1
return 0
}
)
# line buffer class
# The buffer class tries to read characters from the given <fd> until
# it has read a whole line.
typeset -T linebuf_t=(
typeset buf
function reset
{
_.buf=""
return 0
}
function readbuf
{
nameref var=$1
typeset ch
return 0
fi
done
return 1
}
)
function usage
{
OPTIND=0
exit 2
}
# program start
# (be carefull with builtins here - they are unconditionally available
# in the shell's "restricted" mode)
builtin basename
builtin sum
typeset -r shircbot_usage=$'+
[-?\n@(#)\$Id: shircbot (Roland Mainz) 2008-10-31 \$\n]
[-author?Roland Mainz <roland.mainz@sun.com>]
[-author?Roland Mainz <roland.mainz@nrubsig.org>]
[+NAME?shircbot - simple IRC bot demo]
[+DESCRIPTION?\bshircbot\b is a small demo IRC bot which provides
a simple IRC bot with several subcommands.]
[n:nickname?IRC nickname for this bot.]:[nick]
[s:ircserver?IRC servername.]:[servername]
[j:joinchannel?IRC servername.]:[channelname]
[+SEE ALSO?\bksh93\b(1)]
'
typeset -C config=(
typeset nickname="${LOGNAME}bot"
typeset servername="irc.freenode.net"
integer port=6667
typeset -a join_channels
)
while getopts -a "${progname}" "${shircbot_usage}" OPT ; do
# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
n) config.nickname="${OPTARG}" ;;
s) config.servername="${OPTARG}" ;;
*) usage ;;
esac
done
# if no channel was provided we join a predefined set of channels
fi
fi
print "## Start."
# override ircsession_t::serverevent_privmsg with a new method for our bot
function mybot.serverevent_privmsg
{
nameref args=$1
typeset msguser="${args.msguser}"
typeset msgchannel="${args.msgchannel}"
printf "#message: user=%q, channel=%q, msg=%q\n" "$msguser" "$msgchannel" "$msg"
# Check if we get a private message
# ${msgchannel} point to our own nick if we got a private message,
# we need to extract the sender's nickname from ${msguser} and put
# it into msgchannel
msgchannel="${msguser/~(El):(.*)!.*/\1}"
else
# check if this is a command for this bot
fi
# strip beginning (e.g. ":<nick>:" or ":") plus extra spaces
)"
;;
_.send_privmsg "$msgchannel" "${msg#*echo}"
;;
_.running=false
fi
;;
printf "Hello, this is shircbot, written in ksh93 (%s). " "${.sh.version}"
printf "Subcommands are 'say hello', 'math <math-expr>', 'stocks', 'uuid', 'date' and 'echo'."
)"
;;
if [[ "${msg}" == ~(E)[\`\$] ]] ; then
# "restricted" shell mode would prevent any damage but we try to be carefull...
_.send_privmsg "$msgchannel" "Syntax error."
else
typeset mathexpr="${msg#*math}"
printf "Calculating '%s'\n" "${mathexpr}"
( printf 'export PATH=/usr/$RANDOM/foo ; set -o restricted ; printf "%%s = %%.40g\n" "%s" $(( %s ))\n' "${mathexpr}" "${mathexpr}" | source /dev/stdin 2>&1 )
)"
fi
;;
_.send_privmsg "$msgchannel" "Hello, this is a bot."
;;
typeset stockmsg tickersymbol
stockmsg="$( /usr/sfw/bin/wget -q -O /dev/stdout "http://quote.yahoo.com/d/quotes.csv?f=sl1d1t1c1ohgv&e=.csv&s=${tickersymbol}" 2>&1 )"
_.send_privmsg "$msgchannel" "${tickersymbol}: ${stockmsg//,/ }"
done
;;
)"
;;
esac
return 0
}
# Automatically join the list of channels listed in |config.join_channels|
# after the client is connected to the server for some time
function mybot.mainloop_tick
{
integer -S autojoin_done=2
integer i
print "# Autojoin channels..."
mybot.join_channel "${config.join_channels[i]}"
done
fi
return 0
}
# This is a network-facing application - once we've set eveything up
# we set PATH to a random value and switch to the shell's restricted
# mode to make sure noone can escape the jail.
#set -o restricted
print "## End."
exit 0