xref: /titanic_50/usr/src/lib/libshell/common/scripts/shircbot.sh (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
17c2fbfb3SApril Chin#!/usr/bin/ksh93
27c2fbfb3SApril Chin
37c2fbfb3SApril Chin#
47c2fbfb3SApril Chin# CDDL HEADER START
57c2fbfb3SApril Chin#
67c2fbfb3SApril Chin# The contents of this file are subject to the terms of the
77c2fbfb3SApril Chin# Common Development and Distribution License (the "License").
87c2fbfb3SApril Chin# You may not use this file except in compliance with the License.
97c2fbfb3SApril Chin#
107c2fbfb3SApril Chin# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
117c2fbfb3SApril Chin# or http://www.opensolaris.org/os/licensing.
127c2fbfb3SApril Chin# See the License for the specific language governing permissions
137c2fbfb3SApril Chin# and limitations under the License.
147c2fbfb3SApril Chin#
157c2fbfb3SApril Chin# When distributing Covered Code, include this CDDL HEADER in each
167c2fbfb3SApril Chin# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
177c2fbfb3SApril Chin# If applicable, add the following below this CDDL HEADER, with the
187c2fbfb3SApril Chin# fields enclosed by brackets "[]" replaced with your own identifying
197c2fbfb3SApril Chin# information: Portions Copyright [yyyy] [name of copyright owner]
207c2fbfb3SApril Chin#
217c2fbfb3SApril Chin# CDDL HEADER END
227c2fbfb3SApril Chin#
237c2fbfb3SApril Chin
247c2fbfb3SApril Chin#
25*3e14f97fSRoger A. Faulkner# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
267c2fbfb3SApril Chin#
277c2fbfb3SApril Chin
287c2fbfb3SApril Chin#
297c2fbfb3SApril Chin# shircbot - a simple IRC client/bot demo
307c2fbfb3SApril Chin#
317c2fbfb3SApril Chin
327c2fbfb3SApril Chin# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
337c2fbfb3SApril Chinexport PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin
347c2fbfb3SApril Chin
357c2fbfb3SApril Chin# Make sure all math stuff runs in the "C" locale to avoid problems
367c2fbfb3SApril Chin# with alternative # radix point representations (e.g. ',' instead of
377c2fbfb3SApril Chin# '.' in de_DE.*-locales). This needs to be set _before_ any
387c2fbfb3SApril Chin# floating-point constants are defined in this script).
397c2fbfb3SApril Chinif [[ "${LC_ALL}" != "" ]] ; then
407c2fbfb3SApril Chin    export \
417c2fbfb3SApril Chin        LC_MONETARY="${LC_ALL}" \
427c2fbfb3SApril Chin        LC_MESSAGES="${LC_ALL}" \
437c2fbfb3SApril Chin        LC_COLLATE="${LC_ALL}" \
447c2fbfb3SApril Chin        LC_CTYPE="${LC_ALL}"
457c2fbfb3SApril Chin        unset LC_ALL
467c2fbfb3SApril Chinfi
477c2fbfb3SApril Chinexport LC_NUMERIC=C
487c2fbfb3SApril Chin
497c2fbfb3SApril Chinfunction fatal_error
507c2fbfb3SApril Chin{
517c2fbfb3SApril Chin	print -u2 "${progname}: $*"
527c2fbfb3SApril Chin	exit 1
537c2fbfb3SApril Chin}
547c2fbfb3SApril Chin
557c2fbfb3SApril Chin# Definition for a IRC session class
567c2fbfb3SApril Chintypeset -T ircsession_t=(
5734f9b3eeSRoland Mainz	compound server=(
587c2fbfb3SApril Chin		typeset name
597c2fbfb3SApril Chin		integer port
607c2fbfb3SApril Chin	)
617c2fbfb3SApril Chin
627c2fbfb3SApril Chin	typeset nick="ksh93irc"
637c2fbfb3SApril Chin
647c2fbfb3SApril Chin	typeset running=true
657c2fbfb3SApril Chin
667c2fbfb3SApril Chin	integer fd=-1
677c2fbfb3SApril Chin
687c2fbfb3SApril Chin	function createsession
697c2fbfb3SApril Chin	{
707c2fbfb3SApril Chin		set -o xtrace
717c2fbfb3SApril Chin
727c2fbfb3SApril Chin		_.server.name=$1
737c2fbfb3SApril Chin		_.server.port=$2
747c2fbfb3SApril Chin		_.nick=$3
757c2fbfb3SApril Chin
767c2fbfb3SApril Chin		redirect {_.fd}<> "/dev/tcp/${_.server.name}/${_.server.port}"
777c2fbfb3SApril Chin		(( $? == 0 )) || { print -n2 $"Could not open server connection." ; return 1 ; }
787c2fbfb3SApril Chin
797c2fbfb3SApril Chin		printf "fd=%d\n" _.fd
807c2fbfb3SApril Chin
817c2fbfb3SApril Chin		return 0
827c2fbfb3SApril Chin	}
837c2fbfb3SApril Chin
847c2fbfb3SApril Chin	function login
857c2fbfb3SApril Chin	{
867c2fbfb3SApril Chin		{
877c2fbfb3SApril Chin			printf "USER %s %s %s %s\n" "${_.nick}" "${_.nick}" "${_.nick}" "${_.nick}"
887c2fbfb3SApril Chin			printf "NICK %s\n" "${_.nick}"
897c2fbfb3SApril Chin		} >&${_.fd}
907c2fbfb3SApril Chin
917c2fbfb3SApril Chin		return 0
927c2fbfb3SApril Chin	}
937c2fbfb3SApril Chin
947c2fbfb3SApril Chin	function join_channel
957c2fbfb3SApril Chin	{
967c2fbfb3SApril Chin		printf "JOIN %s\n" "$1" >&${_.fd}
977c2fbfb3SApril Chin
987c2fbfb3SApril Chin		return 0
997c2fbfb3SApril Chin	}
1007c2fbfb3SApril Chin
1017c2fbfb3SApril Chin	function mainloop
1027c2fbfb3SApril Chin	{
1037c2fbfb3SApril Chin		typeset line
1047c2fbfb3SApril Chin		float -S last_tick=0
1057c2fbfb3SApril Chin		# We use the linebuf_t class here since network traffic
1067c2fbfb3SApril Chin		# isn't guranteed to fit a single $'\n'-terminated line
1077c2fbfb3SApril Chin		# into one TCP package. linebuf_t buffers characters
1087c2fbfb3SApril Chin		# until it has one complete line. This avoids the need for
1097c2fbfb3SApril Chin		# async I/O normally used by IRC clients
1107c2fbfb3SApril Chin		linebuf_t serverbuf
1117c2fbfb3SApril Chin		linebuf_t clientbuf
1127c2fbfb3SApril Chin		integer fd=${_.fd}
1137c2fbfb3SApril Chin
1147c2fbfb3SApril Chin		_.login
1157c2fbfb3SApril Chin
1167c2fbfb3SApril Chin		while ${_.running} ; do
1177c2fbfb3SApril Chin			while serverbuf.readbuf line <&${fd} ; do
1187c2fbfb3SApril Chin				_.dispatch_serverevent "$line"
1197c2fbfb3SApril Chin			done
1207c2fbfb3SApril Chin
1217c2fbfb3SApril Chin			while clientbuf.readbuf line </dev/stdin ; do
1227c2fbfb3SApril Chin				printf "client: %q\n" "${line}"
1237c2fbfb3SApril Chin				printf "%s\n" "${line}" >&${fd}
1247c2fbfb3SApril Chin			done
1257c2fbfb3SApril Chin
1267c2fbfb3SApril Chin			# call mainloop_tick function in intervals to handle
1277c2fbfb3SApril Chin			# async events (e.g. automatic /join etc.)
1287c2fbfb3SApril Chin			if (( (SECONDS-last_tick) > 5. )) ; then
1297c2fbfb3SApril Chin				(( last_tick=SECONDS ))
1307c2fbfb3SApril Chin				_.mainloop_tick
1317c2fbfb3SApril Chin			fi
1327c2fbfb3SApril Chin		done
1337c2fbfb3SApril Chin
1347c2fbfb3SApril Chin		return 0
1357c2fbfb3SApril Chin	}
1367c2fbfb3SApril Chin
1377c2fbfb3SApril Chin	function mainloop_tick
1387c2fbfb3SApril Chin	{
1397c2fbfb3SApril Chin		return 0
1407c2fbfb3SApril Chin	}
1417c2fbfb3SApril Chin
1427c2fbfb3SApril Chin	function dispatch_serverevent
1437c2fbfb3SApril Chin	{
1447c2fbfb3SApril Chin		typeset line="$1"
1457c2fbfb3SApril Chin
1467c2fbfb3SApril Chin		case "${line}" in
1477c2fbfb3SApril Chin			~(El)PING)
14834f9b3eeSRoland Mainz				compound ping_args=(
1497c2fbfb3SApril Chin					line="$line"
1507c2fbfb3SApril Chin				)
1517c2fbfb3SApril Chin				_.serverevent_ping "ping_args"
1527c2fbfb3SApril Chin				;;
1537c2fbfb3SApril Chin			~(El):.*\ PRIVMSG)
15434f9b3eeSRoland Mainz				compound privmsg_args=(
1557c2fbfb3SApril Chin					typeset line="$line"
1567c2fbfb3SApril Chin					typeset msguser="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\1}"
1577c2fbfb3SApril Chin					typeset msgchannel="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\3}"
1587c2fbfb3SApril Chin					typeset msg="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\4}"
1597c2fbfb3SApril Chin				)
1607c2fbfb3SApril Chin				_.serverevent_privmsg "privmsg_args"
1617c2fbfb3SApril Chin				;;
1627c2fbfb3SApril Chin			~(El):.*\ INVITE)
16334f9b3eeSRoland Mainz				compound invite_args=(
1647c2fbfb3SApril Chin					typeset line="$line"
1657c2fbfb3SApril Chin					typeset inviteuser="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\1}"
1667c2fbfb3SApril Chin					typeset invitenick="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\3}"
1677c2fbfb3SApril Chin					typeset invitechannel="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\4}"
1687c2fbfb3SApril Chin				)
1697c2fbfb3SApril Chin				_.serverevent_invite "invite_args"
1707c2fbfb3SApril Chin				;;
1717c2fbfb3SApril Chin			*)
1727c2fbfb3SApril Chin				printf "server: %q\n" "${line}"
1737c2fbfb3SApril Chin				;;
1747c2fbfb3SApril Chin		esac
1757c2fbfb3SApril Chin
1767c2fbfb3SApril Chin		return 0
1777c2fbfb3SApril Chin	}
1787c2fbfb3SApril Chin
1797c2fbfb3SApril Chin	function serverevent_privmsg
1807c2fbfb3SApril Chin	{
1817c2fbfb3SApril Chin		nameref args=$1
1827c2fbfb3SApril Chin		typeset msguser="${args.msguser}"
1837c2fbfb3SApril Chin		typeset msgchannel="${args.msgchannel}"
1847c2fbfb3SApril Chin		typeset msg="${args.msg}"
1857c2fbfb3SApril Chin
1867c2fbfb3SApril Chin		printf "#privms: user=%q, channel=%q, msg=%q\n" "$msguser" "$msgchannel" "$msg"
1877c2fbfb3SApril Chin
1887c2fbfb3SApril Chin		return 0
1897c2fbfb3SApril Chin	}
1907c2fbfb3SApril Chin
1917c2fbfb3SApril Chin	function serverevent_invite
1927c2fbfb3SApril Chin	{
1937c2fbfb3SApril Chin		nameref args=$1
1947c2fbfb3SApril Chin
1957c2fbfb3SApril Chin		printf "JOIN %s\n" "${args.invitechannel/:/}" >&${_.fd}
1967c2fbfb3SApril Chin
1977c2fbfb3SApril Chin		return 0
1987c2fbfb3SApril Chin	}
1997c2fbfb3SApril Chin
2007c2fbfb3SApril Chin	function send_privmsg
2017c2fbfb3SApril Chin	{
2027c2fbfb3SApril Chin		typeset channel="$1"
2037c2fbfb3SApril Chin		typeset msg="$2"
2047c2fbfb3SApril Chin
2057c2fbfb3SApril Chin		# Do we have to escape any characters in "msg" ?
2067c2fbfb3SApril Chin		printf "PRIVMSG %s :%s\n" "${channel}" "${msg}" >&${_.fd}
2077c2fbfb3SApril Chin
2087c2fbfb3SApril Chin		return 0
2097c2fbfb3SApril Chin	}
2107c2fbfb3SApril Chin
2117c2fbfb3SApril Chin	function serverevent_ping
2127c2fbfb3SApril Chin	{
2137c2fbfb3SApril Chin		nameref args=$1
2147c2fbfb3SApril Chin
2157c2fbfb3SApril Chin		printf "PONG %s\n" "${args.line/~(Elr)([^ ]+) ([^ ]+).*/\2}" >&${_.fd}
2167c2fbfb3SApril Chin
2177c2fbfb3SApril Chin		return 0
2187c2fbfb3SApril Chin	}
2197c2fbfb3SApril Chin)
2207c2fbfb3SApril Chin
2217c2fbfb3SApril Chin# line buffer class
2227c2fbfb3SApril Chin# The buffer class tries to read characters from the given <fd> until
2237c2fbfb3SApril Chin# it has read a whole line.
2247c2fbfb3SApril Chintypeset -T linebuf_t=(
2257c2fbfb3SApril Chin	typeset buf
2267c2fbfb3SApril Chin
2277c2fbfb3SApril Chin	function reset
2287c2fbfb3SApril Chin	{
2297c2fbfb3SApril Chin		_.buf=""
2307c2fbfb3SApril Chin		return 0
2317c2fbfb3SApril Chin	}
2327c2fbfb3SApril Chin
2337c2fbfb3SApril Chin	function readbuf
2347c2fbfb3SApril Chin	{
2357c2fbfb3SApril Chin		nameref var=$1
2367c2fbfb3SApril Chin		typeset ch
2377c2fbfb3SApril Chin
2387c2fbfb3SApril Chin		while IFS='' read -t 0.2 -N 1 ch ; do
2397c2fbfb3SApril Chin			[[ "$ch" == $'\r' ]] && continue
2407c2fbfb3SApril Chin
2417c2fbfb3SApril Chin			if [[ "$ch" == $'\n' ]] ; then
2427c2fbfb3SApril Chin				var="${_.buf}"
2437c2fbfb3SApril Chin				_.reset
2447c2fbfb3SApril Chin				return 0
2457c2fbfb3SApril Chin			fi
2467c2fbfb3SApril Chin
2477c2fbfb3SApril Chin			_.buf+="$ch"
2487c2fbfb3SApril Chin		done
2497c2fbfb3SApril Chin
2507c2fbfb3SApril Chin		return 1
2517c2fbfb3SApril Chin	}
2527c2fbfb3SApril Chin)
2537c2fbfb3SApril Chin
2547c2fbfb3SApril Chinfunction usage
2557c2fbfb3SApril Chin{
2567c2fbfb3SApril Chin	OPTIND=0
2577c2fbfb3SApril Chin	getopts -a "${progname}" "${shircbot_usage}" OPT '-?'
2587c2fbfb3SApril Chin	exit 2
2597c2fbfb3SApril Chin}
2607c2fbfb3SApril Chin
2617c2fbfb3SApril Chin# program start
2627c2fbfb3SApril Chin# (be carefull with builtins here - they are unconditionally available
2637c2fbfb3SApril Chin# in the shell's "restricted" mode)
2647c2fbfb3SApril Chinbuiltin basename
2657c2fbfb3SApril Chinbuiltin sum
2667c2fbfb3SApril Chin
2677c2fbfb3SApril Chintypeset progname="${ basename "${0}" ; }"
2687c2fbfb3SApril Chin
2697c2fbfb3SApril Chintypeset -r shircbot_usage=$'+
27034f9b3eeSRoland Mainz[-?\n@(#)\$Id: shircbot (Roland Mainz) 2009-09-09 \$\n]
2717c2fbfb3SApril Chin[-author?Roland Mainz <roland.mainz@sun.com>]
2727c2fbfb3SApril Chin[-author?Roland Mainz <roland.mainz@nrubsig.org>]
2737c2fbfb3SApril Chin[+NAME?shircbot - simple IRC bot demo]
2747c2fbfb3SApril Chin[+DESCRIPTION?\bshircbot\b is a small demo IRC bot which provides
2757c2fbfb3SApril Chin	a simple IRC bot with several subcommands.]
2767c2fbfb3SApril Chin[n:nickname?IRC nickname for this bot.]:[nick]
2777c2fbfb3SApril Chin[s:ircserver?IRC servername.]:[servername]
2787c2fbfb3SApril Chin[j:joinchannel?IRC servername.]:[channelname]
2797c2fbfb3SApril Chin[+SEE ALSO?\bksh93\b(1)]
2807c2fbfb3SApril Chin'
2817c2fbfb3SApril Chin
28234f9b3eeSRoland Mainzcompound config=(
2837c2fbfb3SApril Chin	typeset nickname="${LOGNAME}bot"
2847c2fbfb3SApril Chin	typeset servername="irc.freenode.net"
2857c2fbfb3SApril Chin	integer port=6667
2867c2fbfb3SApril Chin	typeset -a join_channels
2877c2fbfb3SApril Chin)
2887c2fbfb3SApril Chin
2897c2fbfb3SApril Chinwhile getopts -a "${progname}" "${shircbot_usage}" OPT ; do
2907c2fbfb3SApril Chin#	printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
2917c2fbfb3SApril Chin	case ${OPT} in
2927c2fbfb3SApril Chin		n)	config.nickname="${OPTARG}" ;;
2937c2fbfb3SApril Chin		s)	config.servername="${OPTARG}" ;;
2947c2fbfb3SApril Chin		j)	config.join_channels+=( "${OPTARG}" ) ;;
2957c2fbfb3SApril Chin		*)	usage ;;
2967c2fbfb3SApril Chin	esac
2977c2fbfb3SApril Chindone
2987c2fbfb3SApril Chinshift $((OPTIND-1))
2997c2fbfb3SApril Chin
3007c2fbfb3SApril Chin# if no channel was provided we join a predefined set of channels
3017c2fbfb3SApril Chinif (( ${#config.join_channels[@]} == 0 )) ; then
3027c2fbfb3SApril Chin	if [[ "${config.servername}" == "irc.freenode.net" ]] ; then
3037c2fbfb3SApril Chin		config.join_channels+=( "#opensolaris" )
3047c2fbfb3SApril Chin		config.join_channels+=( "#opensolaris-dev" )
3057c2fbfb3SApril Chin		config.join_channels+=( "#opensolaris-arc" )
30634f9b3eeSRoland Mainz		config.join_channels+=( "#opensolaris-meeting" )
30734f9b3eeSRoland Mainz		config.join_channels+=( "#ospkg" )
3087c2fbfb3SApril Chin		config.join_channels+=( "#ksh" )
3097c2fbfb3SApril Chin	elif [[ "${config.servername}" == ~(E)irc.(sfbay|sweden) ]] ; then
3107c2fbfb3SApril Chin		config.join_channels+=( "#onnv" )
3117c2fbfb3SApril Chin	fi
3127c2fbfb3SApril Chinfi
3137c2fbfb3SApril Chin
3147c2fbfb3SApril Chinprint "## Start."
3157c2fbfb3SApril Chin
3167c2fbfb3SApril Chinircsession_t mybot
3177c2fbfb3SApril Chin
3187c2fbfb3SApril Chin# override ircsession_t::serverevent_privmsg with a new method for our bot
3197c2fbfb3SApril Chinfunction mybot.serverevent_privmsg
3207c2fbfb3SApril Chin{
3217c2fbfb3SApril Chin	nameref args=$1
3227c2fbfb3SApril Chin	typeset msguser="${args.msguser}"
3237c2fbfb3SApril Chin	typeset msgchannel="${args.msgchannel}"
3247c2fbfb3SApril Chin	typeset msg="${args.msg}"
3257c2fbfb3SApril Chin
3267c2fbfb3SApril Chin	printf "#message: user=%q, channel=%q, msg=%q\n" "$msguser" "$msgchannel" "$msg"
3277c2fbfb3SApril Chin
3287c2fbfb3SApril Chin	# Check if we get a private message
3297c2fbfb3SApril Chin	if [[ "${msgchannel}" == "${_.nick}" ]] ; then
3307c2fbfb3SApril Chin		# ${msgchannel} point to our own nick if we got a private message,
3317c2fbfb3SApril Chin		# we need to extract the sender's nickname from ${msguser} and put
3327c2fbfb3SApril Chin		# it into msgchannel
3337c2fbfb3SApril Chin		msgchannel="${msguser/~(El):(.*)!.*/\1}"
3347c2fbfb3SApril Chin	else
3357c2fbfb3SApril Chin		# check if this is a command for this bot
3367c2fbfb3SApril Chin		[[ "$msg" != ~(Eli):${_.nick}:[[:space:]]  ]] && return 0
3377c2fbfb3SApril Chin	fi
3387c2fbfb3SApril Chin
3397c2fbfb3SApril Chin	# strip beginning (e.g. ":<nick>:" or ":") plus extra spaces
3407c2fbfb3SApril Chin	msg="${msg/~(Eli)(:${_.nick})*:[[:space:]]*/}"
3417c2fbfb3SApril Chin
3427c2fbfb3SApril Chin	printf "botmsg=%q\n" "$msg"
3437c2fbfb3SApril Chin
3447c2fbfb3SApril Chin	case "$msg" in
3457c2fbfb3SApril Chin		~(Eli)date)
34634f9b3eeSRoland Mainz			_.send_privmsg "$msgchannel" "${
34734f9b3eeSRoland Mainz			        printf "%(%Y-%m-%d, %Th/%Z)T\n"
34834f9b3eeSRoland Mainz			}"
3497c2fbfb3SApril Chin			;;
3507c2fbfb3SApril Chin		~(Eli)echo)
3517c2fbfb3SApril Chin			_.send_privmsg "$msgchannel" "${msg#*echo}"
3527c2fbfb3SApril Chin			;;
3537c2fbfb3SApril Chin		~(Eli)exitbot)
3547c2fbfb3SApril Chin			typeset exitkey="$(print "$msguser" | sum -x sha1)" # this is unwise&&insecure
3557c2fbfb3SApril Chin			if [[ "$msg" == *${exitkey}* ]] ; then
3567c2fbfb3SApril Chin				_.running=false
3577c2fbfb3SApril Chin			fi
3587c2fbfb3SApril Chin			;;
3597c2fbfb3SApril Chin		~(Eli)help)
36034f9b3eeSRoland Mainz			_.send_privmsg "$msgchannel" "${
3617c2fbfb3SApril Chin				printf "Hello, this is shircbot, written in ksh93 (%s). " "${.sh.version}"
3627c2fbfb3SApril Chin				printf "Subcommands are 'say hello', 'math <math-expr>', 'stocks', 'uuid', 'date' and 'echo'."
36334f9b3eeSRoland Mainz				}"
3647c2fbfb3SApril Chin			;;
3657c2fbfb3SApril Chin		~(Eli)math)
3667c2fbfb3SApril Chin			if [[ "${msg}" == ~(E)[\`\$] ]] ; then
3677c2fbfb3SApril Chin				# "restricted" shell mode would prevent any damage but we try to be carefull...
3687c2fbfb3SApril Chin				_.send_privmsg "$msgchannel" "Syntax error."
3697c2fbfb3SApril Chin			else
3707c2fbfb3SApril Chin				typeset mathexpr="${msg#*math}"
3717c2fbfb3SApril Chin
3727c2fbfb3SApril Chin				printf "Calculating '%s'\n" "${mathexpr}"
37334f9b3eeSRoland Mainz				_.send_privmsg "$msgchannel" "${
37434f9b3eeSRoland Mainz				        ( printf 'export PATH=/usr/${RANDOM}/$$/${RANDOM}/foo ; set -o restricted ; printf "%%s = %%.40g\n" "%s" $(( %s ))\n' "${mathexpr}" "${mathexpr}" | source /dev/stdin 2>&1 )
37534f9b3eeSRoland Mainz				}"
3767c2fbfb3SApril Chin			fi
3777c2fbfb3SApril Chin			;;
3787c2fbfb3SApril Chin		~(Eli)say\ hello)
3797c2fbfb3SApril Chin			_.send_privmsg "$msgchannel" "Hello, this is a bot."
3807c2fbfb3SApril Chin			;;
3817c2fbfb3SApril Chin		~(Eli)stocks)
3827c2fbfb3SApril Chin			typeset stockmsg tickersymbol
38334f9b3eeSRoland Mainz			for tickersymbol in "JAVA" "ORCL" "IBM" "AAPL" "HPQ" ; do
3847c2fbfb3SApril Chin				stockmsg="$( /usr/sfw/bin/wget -q -O /dev/stdout "http://quote.yahoo.com/d/quotes.csv?f=sl1d1t1c1ohgv&e=.csv&s=${tickersymbol}" 2>&1 )"
3857c2fbfb3SApril Chin				_.send_privmsg "$msgchannel" "${tickersymbol}: ${stockmsg//,/ }"
3867c2fbfb3SApril Chin			done
3877c2fbfb3SApril Chin			;;
3887c2fbfb3SApril Chin		~(Eli)uuid)
38934f9b3eeSRoland Mainz			_.send_privmsg "$msgchannel" "${
39034f9b3eeSRoland Mainz			        print "%(%Y%M%D%S%N)T$((RANDOM))%s\n" "${msguser}" | sum -x sha256
39134f9b3eeSRoland Mainz			}"
3927c2fbfb3SApril Chin			;;
3937c2fbfb3SApril Chin	esac
3947c2fbfb3SApril Chin
3957c2fbfb3SApril Chin	return 0
3967c2fbfb3SApril Chin}
3977c2fbfb3SApril Chin
3987c2fbfb3SApril Chin# Automatically join the list of channels listed in |config.join_channels|
3997c2fbfb3SApril Chin# after the client is connected to the server for some time
4007c2fbfb3SApril Chinfunction mybot.mainloop_tick
4017c2fbfb3SApril Chin{
4027c2fbfb3SApril Chin	integer -S autojoin_done=2
4037c2fbfb3SApril Chin	integer i
4047c2fbfb3SApril Chin
4057c2fbfb3SApril Chin	if (( autojoin_done-- == 0 && ${#config.join_channels[@]} > 0 )) ; then
4067c2fbfb3SApril Chin		print "# Autojoin channels..."
4077c2fbfb3SApril Chin
4087c2fbfb3SApril Chin		for ((i=0 ; i < ${#config.join_channels[@]} ; i++ )) ; do
4097c2fbfb3SApril Chin			mybot.join_channel "${config.join_channels[i]}"
4107c2fbfb3SApril Chin		done
4117c2fbfb3SApril Chin	fi
4127c2fbfb3SApril Chin
4137c2fbfb3SApril Chin	return 0
4147c2fbfb3SApril Chin}
4157c2fbfb3SApril Chin
4167c2fbfb3SApril Chinmybot.createsession "${config.servername}" ${config.port} "${config.nickname}"
4177c2fbfb3SApril Chin
4187c2fbfb3SApril Chin# This is a network-facing application - once we've set eveything up
4197c2fbfb3SApril Chin# we set PATH to a random value and switch to the shell's restricted
4207c2fbfb3SApril Chin# mode to make sure noone can escape the jail.
4217c2fbfb3SApril Chin#export PATH=/usr/$RANDOM/foo
4227c2fbfb3SApril Chin#set -o restricted
4237c2fbfb3SApril Chin
4247c2fbfb3SApril Chinmybot.mainloop
4257c2fbfb3SApril Chin
4267c2fbfb3SApril Chinprint "## End."
4277c2fbfb3SApril Chin
4287c2fbfb3SApril Chinexit 0
429