xref: /titanic_52/usr/src/cmd/ast/libshell/common/scripts/shircbot.sh (revision 906afcb89d0412cc073b95c2d701a804a8cdb62c)
1*906afcb8SAndy Fiddaman#!/usr/bin/ksh93
2*906afcb8SAndy Fiddaman
3*906afcb8SAndy Fiddaman#
4*906afcb8SAndy Fiddaman# CDDL HEADER START
5*906afcb8SAndy Fiddaman#
6*906afcb8SAndy Fiddaman# The contents of this file are subject to the terms of the
7*906afcb8SAndy Fiddaman# Common Development and Distribution License (the "License").
8*906afcb8SAndy Fiddaman# You may not use this file except in compliance with the License.
9*906afcb8SAndy Fiddaman#
10*906afcb8SAndy Fiddaman# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11*906afcb8SAndy Fiddaman# or http://www.opensolaris.org/os/licensing.
12*906afcb8SAndy Fiddaman# See the License for the specific language governing permissions
13*906afcb8SAndy Fiddaman# and limitations under the License.
14*906afcb8SAndy Fiddaman#
15*906afcb8SAndy Fiddaman# When distributing Covered Code, include this CDDL HEADER in each
16*906afcb8SAndy Fiddaman# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17*906afcb8SAndy Fiddaman# If applicable, add the following below this CDDL HEADER, with the
18*906afcb8SAndy Fiddaman# fields enclosed by brackets "[]" replaced with your own identifying
19*906afcb8SAndy Fiddaman# information: Portions Copyright [yyyy] [name of copyright owner]
20*906afcb8SAndy Fiddaman#
21*906afcb8SAndy Fiddaman# CDDL HEADER END
22*906afcb8SAndy Fiddaman#
23*906afcb8SAndy Fiddaman
24*906afcb8SAndy Fiddaman#
25*906afcb8SAndy Fiddaman# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
26*906afcb8SAndy Fiddaman#
27*906afcb8SAndy Fiddaman
28*906afcb8SAndy Fiddaman#
29*906afcb8SAndy Fiddaman# shircbot - a simple IRC client/bot demo
30*906afcb8SAndy Fiddaman#
31*906afcb8SAndy Fiddaman
32*906afcb8SAndy Fiddaman# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
33*906afcb8SAndy Fiddamanexport PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin
34*906afcb8SAndy Fiddaman
35*906afcb8SAndy Fiddaman# Make sure all math stuff runs in the "C" locale to avoid problems
36*906afcb8SAndy Fiddaman# with alternative # radix point representations (e.g. ',' instead of
37*906afcb8SAndy Fiddaman# '.' in de_DE.*-locales). This needs to be set _before_ any
38*906afcb8SAndy Fiddaman# floating-point constants are defined in this script).
39*906afcb8SAndy Fiddamanif [[ "${LC_ALL}" != "" ]] ; then
40*906afcb8SAndy Fiddaman    export \
41*906afcb8SAndy Fiddaman        LC_MONETARY="${LC_ALL}" \
42*906afcb8SAndy Fiddaman        LC_MESSAGES="${LC_ALL}" \
43*906afcb8SAndy Fiddaman        LC_COLLATE="${LC_ALL}" \
44*906afcb8SAndy Fiddaman        LC_CTYPE="${LC_ALL}"
45*906afcb8SAndy Fiddaman        unset LC_ALL
46*906afcb8SAndy Fiddamanfi
47*906afcb8SAndy Fiddamanexport LC_NUMERIC=C
48*906afcb8SAndy Fiddaman
49*906afcb8SAndy Fiddamanfunction fatal_error
50*906afcb8SAndy Fiddaman{
51*906afcb8SAndy Fiddaman	print -u2 "${progname}: $*"
52*906afcb8SAndy Fiddaman	exit 1
53*906afcb8SAndy Fiddaman}
54*906afcb8SAndy Fiddaman
55*906afcb8SAndy Fiddaman# Definition for a IRC session class
56*906afcb8SAndy Fiddamantypeset -T ircsession_t=(
57*906afcb8SAndy Fiddaman	compound server=(
58*906afcb8SAndy Fiddaman		typeset name
59*906afcb8SAndy Fiddaman		integer port
60*906afcb8SAndy Fiddaman	)
61*906afcb8SAndy Fiddaman
62*906afcb8SAndy Fiddaman	typeset nick="ksh93irc"
63*906afcb8SAndy Fiddaman
64*906afcb8SAndy Fiddaman	typeset running=true
65*906afcb8SAndy Fiddaman
66*906afcb8SAndy Fiddaman	integer fd=-1
67*906afcb8SAndy Fiddaman
68*906afcb8SAndy Fiddaman	function createsession
69*906afcb8SAndy Fiddaman	{
70*906afcb8SAndy Fiddaman		set -o xtrace
71*906afcb8SAndy Fiddaman
72*906afcb8SAndy Fiddaman		_.server.name=$1
73*906afcb8SAndy Fiddaman		_.server.port=$2
74*906afcb8SAndy Fiddaman		_.nick=$3
75*906afcb8SAndy Fiddaman
76*906afcb8SAndy Fiddaman		redirect {_.fd}<> "/dev/tcp/${_.server.name}/${_.server.port}"
77*906afcb8SAndy Fiddaman		(( $? == 0 )) || { print -n2 $"Could not open server connection." ; return 1 ; }
78*906afcb8SAndy Fiddaman
79*906afcb8SAndy Fiddaman		printf "fd=%d\n" _.fd
80*906afcb8SAndy Fiddaman
81*906afcb8SAndy Fiddaman		return 0
82*906afcb8SAndy Fiddaman	}
83*906afcb8SAndy Fiddaman
84*906afcb8SAndy Fiddaman	function login
85*906afcb8SAndy Fiddaman	{
86*906afcb8SAndy Fiddaman		{
87*906afcb8SAndy Fiddaman			printf "USER %s %s %s %s\n" "${_.nick}" "${_.nick}" "${_.nick}" "${_.nick}"
88*906afcb8SAndy Fiddaman			printf "NICK %s\n" "${_.nick}"
89*906afcb8SAndy Fiddaman		} >&${_.fd}
90*906afcb8SAndy Fiddaman
91*906afcb8SAndy Fiddaman		return 0
92*906afcb8SAndy Fiddaman	}
93*906afcb8SAndy Fiddaman
94*906afcb8SAndy Fiddaman	function join_channel
95*906afcb8SAndy Fiddaman	{
96*906afcb8SAndy Fiddaman		printf "JOIN %s\n" "$1" >&${_.fd}
97*906afcb8SAndy Fiddaman
98*906afcb8SAndy Fiddaman		return 0
99*906afcb8SAndy Fiddaman	}
100*906afcb8SAndy Fiddaman
101*906afcb8SAndy Fiddaman	function mainloop
102*906afcb8SAndy Fiddaman	{
103*906afcb8SAndy Fiddaman		typeset line
104*906afcb8SAndy Fiddaman		float -S last_tick=0
105*906afcb8SAndy Fiddaman		# We use the linebuf_t class here since network traffic
106*906afcb8SAndy Fiddaman		# isn't guranteed to fit a single $'\n'-terminated line
107*906afcb8SAndy Fiddaman		# into one TCP package. linebuf_t buffers characters
108*906afcb8SAndy Fiddaman		# until it has one complete line. This avoids the need for
109*906afcb8SAndy Fiddaman		# async I/O normally used by IRC clients
110*906afcb8SAndy Fiddaman		linebuf_t serverbuf
111*906afcb8SAndy Fiddaman		linebuf_t clientbuf
112*906afcb8SAndy Fiddaman		integer fd=${_.fd}
113*906afcb8SAndy Fiddaman
114*906afcb8SAndy Fiddaman		_.login
115*906afcb8SAndy Fiddaman
116*906afcb8SAndy Fiddaman		while ${_.running} ; do
117*906afcb8SAndy Fiddaman			while serverbuf.readbuf line <&${fd} ; do
118*906afcb8SAndy Fiddaman				_.dispatch_serverevent "$line"
119*906afcb8SAndy Fiddaman			done
120*906afcb8SAndy Fiddaman
121*906afcb8SAndy Fiddaman			while clientbuf.readbuf line </dev/stdin ; do
122*906afcb8SAndy Fiddaman				printf "client: %q\n" "${line}"
123*906afcb8SAndy Fiddaman				printf "%s\n" "${line}" >&${fd}
124*906afcb8SAndy Fiddaman			done
125*906afcb8SAndy Fiddaman
126*906afcb8SAndy Fiddaman			# call mainloop_tick function in intervals to handle
127*906afcb8SAndy Fiddaman			# async events (e.g. automatic /join etc.)
128*906afcb8SAndy Fiddaman			if (( (SECONDS-last_tick) > 5. )) ; then
129*906afcb8SAndy Fiddaman				(( last_tick=SECONDS ))
130*906afcb8SAndy Fiddaman				_.mainloop_tick
131*906afcb8SAndy Fiddaman			fi
132*906afcb8SAndy Fiddaman		done
133*906afcb8SAndy Fiddaman
134*906afcb8SAndy Fiddaman		return 0
135*906afcb8SAndy Fiddaman	}
136*906afcb8SAndy Fiddaman
137*906afcb8SAndy Fiddaman	function mainloop_tick
138*906afcb8SAndy Fiddaman	{
139*906afcb8SAndy Fiddaman		return 0
140*906afcb8SAndy Fiddaman	}
141*906afcb8SAndy Fiddaman
142*906afcb8SAndy Fiddaman	function dispatch_serverevent
143*906afcb8SAndy Fiddaman	{
144*906afcb8SAndy Fiddaman		typeset line="$1"
145*906afcb8SAndy Fiddaman
146*906afcb8SAndy Fiddaman		case "${line}" in
147*906afcb8SAndy Fiddaman			~(El)PING)
148*906afcb8SAndy Fiddaman				compound ping_args=(
149*906afcb8SAndy Fiddaman					line="$line"
150*906afcb8SAndy Fiddaman				)
151*906afcb8SAndy Fiddaman				_.serverevent_ping "ping_args"
152*906afcb8SAndy Fiddaman				;;
153*906afcb8SAndy Fiddaman			~(El):.*\ PRIVMSG)
154*906afcb8SAndy Fiddaman				compound privmsg_args=(
155*906afcb8SAndy Fiddaman					typeset line="$line"
156*906afcb8SAndy Fiddaman					typeset msguser="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\1}"
157*906afcb8SAndy Fiddaman					typeset msgchannel="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\3}"
158*906afcb8SAndy Fiddaman					typeset msg="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\4}"
159*906afcb8SAndy Fiddaman				)
160*906afcb8SAndy Fiddaman				_.serverevent_privmsg "privmsg_args"
161*906afcb8SAndy Fiddaman				;;
162*906afcb8SAndy Fiddaman			~(El):.*\ INVITE)
163*906afcb8SAndy Fiddaman				compound invite_args=(
164*906afcb8SAndy Fiddaman					typeset line="$line"
165*906afcb8SAndy Fiddaman					typeset inviteuser="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\1}"
166*906afcb8SAndy Fiddaman					typeset invitenick="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\3}"
167*906afcb8SAndy Fiddaman					typeset invitechannel="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\4}"
168*906afcb8SAndy Fiddaman				)
169*906afcb8SAndy Fiddaman				_.serverevent_invite "invite_args"
170*906afcb8SAndy Fiddaman				;;
171*906afcb8SAndy Fiddaman			*)
172*906afcb8SAndy Fiddaman				printf "server: %q\n" "${line}"
173*906afcb8SAndy Fiddaman				;;
174*906afcb8SAndy Fiddaman		esac
175*906afcb8SAndy Fiddaman
176*906afcb8SAndy Fiddaman		return 0
177*906afcb8SAndy Fiddaman	}
178*906afcb8SAndy Fiddaman
179*906afcb8SAndy Fiddaman	function serverevent_privmsg
180*906afcb8SAndy Fiddaman	{
181*906afcb8SAndy Fiddaman		nameref args=$1
182*906afcb8SAndy Fiddaman		typeset msguser="${args.msguser}"
183*906afcb8SAndy Fiddaman		typeset msgchannel="${args.msgchannel}"
184*906afcb8SAndy Fiddaman		typeset msg="${args.msg}"
185*906afcb8SAndy Fiddaman
186*906afcb8SAndy Fiddaman		printf "#privms: user=%q, channel=%q, msg=%q\n" "$msguser" "$msgchannel" "$msg"
187*906afcb8SAndy Fiddaman
188*906afcb8SAndy Fiddaman		return 0
189*906afcb8SAndy Fiddaman	}
190*906afcb8SAndy Fiddaman
191*906afcb8SAndy Fiddaman	function serverevent_invite
192*906afcb8SAndy Fiddaman	{
193*906afcb8SAndy Fiddaman		nameref args=$1
194*906afcb8SAndy Fiddaman
195*906afcb8SAndy Fiddaman		printf "JOIN %s\n" "${args.invitechannel/:/}" >&${_.fd}
196*906afcb8SAndy Fiddaman
197*906afcb8SAndy Fiddaman		return 0
198*906afcb8SAndy Fiddaman	}
199*906afcb8SAndy Fiddaman
200*906afcb8SAndy Fiddaman	function send_privmsg
201*906afcb8SAndy Fiddaman	{
202*906afcb8SAndy Fiddaman		typeset channel="$1"
203*906afcb8SAndy Fiddaman		typeset msg="$2"
204*906afcb8SAndy Fiddaman
205*906afcb8SAndy Fiddaman		# Do we have to escape any characters in "msg" ?
206*906afcb8SAndy Fiddaman		printf "PRIVMSG %s :%s\n" "${channel}" "${msg}" >&${_.fd}
207*906afcb8SAndy Fiddaman
208*906afcb8SAndy Fiddaman		return 0
209*906afcb8SAndy Fiddaman	}
210*906afcb8SAndy Fiddaman
211*906afcb8SAndy Fiddaman	function serverevent_ping
212*906afcb8SAndy Fiddaman	{
213*906afcb8SAndy Fiddaman		nameref args=$1
214*906afcb8SAndy Fiddaman
215*906afcb8SAndy Fiddaman		printf "PONG %s\n" "${args.line/~(Elr)([^ ]+) ([^ ]+).*/\2}" >&${_.fd}
216*906afcb8SAndy Fiddaman
217*906afcb8SAndy Fiddaman		return 0
218*906afcb8SAndy Fiddaman	}
219*906afcb8SAndy Fiddaman)
220*906afcb8SAndy Fiddaman
221*906afcb8SAndy Fiddaman# line buffer class
222*906afcb8SAndy Fiddaman# The buffer class tries to read characters from the given <fd> until
223*906afcb8SAndy Fiddaman# it has read a whole line.
224*906afcb8SAndy Fiddamantypeset -T linebuf_t=(
225*906afcb8SAndy Fiddaman	typeset buf
226*906afcb8SAndy Fiddaman
227*906afcb8SAndy Fiddaman	function reset
228*906afcb8SAndy Fiddaman	{
229*906afcb8SAndy Fiddaman		_.buf=""
230*906afcb8SAndy Fiddaman		return 0
231*906afcb8SAndy Fiddaman	}
232*906afcb8SAndy Fiddaman
233*906afcb8SAndy Fiddaman	function readbuf
234*906afcb8SAndy Fiddaman	{
235*906afcb8SAndy Fiddaman		nameref var=$1
236*906afcb8SAndy Fiddaman		typeset ch
237*906afcb8SAndy Fiddaman
238*906afcb8SAndy Fiddaman		while IFS='' read -t 0.2 -N 1 ch ; do
239*906afcb8SAndy Fiddaman			[[ "$ch" == $'\r' ]] && continue
240*906afcb8SAndy Fiddaman
241*906afcb8SAndy Fiddaman			if [[ "$ch" == $'\n' ]] ; then
242*906afcb8SAndy Fiddaman				var="${_.buf}"
243*906afcb8SAndy Fiddaman				_.reset
244*906afcb8SAndy Fiddaman				return 0
245*906afcb8SAndy Fiddaman			fi
246*906afcb8SAndy Fiddaman
247*906afcb8SAndy Fiddaman			_.buf+="$ch"
248*906afcb8SAndy Fiddaman		done
249*906afcb8SAndy Fiddaman
250*906afcb8SAndy Fiddaman		return 1
251*906afcb8SAndy Fiddaman	}
252*906afcb8SAndy Fiddaman)
253*906afcb8SAndy Fiddaman
254*906afcb8SAndy Fiddamanfunction usage
255*906afcb8SAndy Fiddaman{
256*906afcb8SAndy Fiddaman	OPTIND=0
257*906afcb8SAndy Fiddaman	getopts -a "${progname}" "${shircbot_usage}" OPT '-?'
258*906afcb8SAndy Fiddaman	exit 2
259*906afcb8SAndy Fiddaman}
260*906afcb8SAndy Fiddaman
261*906afcb8SAndy Fiddaman# program start
262*906afcb8SAndy Fiddaman# (be carefull with builtins here - they are unconditionally available
263*906afcb8SAndy Fiddaman# in the shell's "restricted" mode)
264*906afcb8SAndy Fiddamanbuiltin basename
265*906afcb8SAndy Fiddamanbuiltin sum
266*906afcb8SAndy Fiddaman
267*906afcb8SAndy Fiddamantypeset progname="${ basename "${0}" ; }"
268*906afcb8SAndy Fiddaman
269*906afcb8SAndy Fiddamantypeset -r shircbot_usage=$'+
270*906afcb8SAndy Fiddaman[-?\n@(#)\$Id: shircbot (Roland Mainz) 2009-09-09 \$\n]
271*906afcb8SAndy Fiddaman[-author?Roland Mainz <roland.mainz@sun.com>]
272*906afcb8SAndy Fiddaman[-author?Roland Mainz <roland.mainz@nrubsig.org>]
273*906afcb8SAndy Fiddaman[+NAME?shircbot - simple IRC bot demo]
274*906afcb8SAndy Fiddaman[+DESCRIPTION?\bshircbot\b is a small demo IRC bot which provides
275*906afcb8SAndy Fiddaman	a simple IRC bot with several subcommands.]
276*906afcb8SAndy Fiddaman[n:nickname?IRC nickname for this bot.]:[nick]
277*906afcb8SAndy Fiddaman[s:ircserver?IRC servername.]:[servername]
278*906afcb8SAndy Fiddaman[j:joinchannel?IRC servername.]:[channelname]
279*906afcb8SAndy Fiddaman[+SEE ALSO?\bksh93\b(1)]
280*906afcb8SAndy Fiddaman'
281*906afcb8SAndy Fiddaman
282*906afcb8SAndy Fiddamancompound config=(
283*906afcb8SAndy Fiddaman	typeset nickname="${LOGNAME}bot"
284*906afcb8SAndy Fiddaman	typeset servername="irc.freenode.net"
285*906afcb8SAndy Fiddaman	integer port=6667
286*906afcb8SAndy Fiddaman	typeset -a join_channels
287*906afcb8SAndy Fiddaman)
288*906afcb8SAndy Fiddaman
289*906afcb8SAndy Fiddamanwhile getopts -a "${progname}" "${shircbot_usage}" OPT ; do
290*906afcb8SAndy Fiddaman#	printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
291*906afcb8SAndy Fiddaman	case ${OPT} in
292*906afcb8SAndy Fiddaman		n)	config.nickname="${OPTARG}" ;;
293*906afcb8SAndy Fiddaman		s)	config.servername="${OPTARG}" ;;
294*906afcb8SAndy Fiddaman		j)	config.join_channels+=( "${OPTARG}" ) ;;
295*906afcb8SAndy Fiddaman		*)	usage ;;
296*906afcb8SAndy Fiddaman	esac
297*906afcb8SAndy Fiddamandone
298*906afcb8SAndy Fiddamanshift $((OPTIND-1))
299*906afcb8SAndy Fiddaman
300*906afcb8SAndy Fiddaman# if no channel was provided we join a predefined set of channels
301*906afcb8SAndy Fiddamanif (( ${#config.join_channels[@]} == 0 )) ; then
302*906afcb8SAndy Fiddaman	if [[ "${config.servername}" == "irc.freenode.net" ]] ; then
303*906afcb8SAndy Fiddaman		config.join_channels+=( "#opensolaris" )
304*906afcb8SAndy Fiddaman		config.join_channels+=( "#opensolaris-dev" )
305*906afcb8SAndy Fiddaman		config.join_channels+=( "#opensolaris-arc" )
306*906afcb8SAndy Fiddaman		config.join_channels+=( "#opensolaris-meeting" )
307*906afcb8SAndy Fiddaman		config.join_channels+=( "#ospkg" )
308*906afcb8SAndy Fiddaman		config.join_channels+=( "#ksh" )
309*906afcb8SAndy Fiddaman	elif [[ "${config.servername}" == ~(E)irc.(sfbay|sweden) ]] ; then
310*906afcb8SAndy Fiddaman		config.join_channels+=( "#onnv" )
311*906afcb8SAndy Fiddaman	fi
312*906afcb8SAndy Fiddamanfi
313*906afcb8SAndy Fiddaman
314*906afcb8SAndy Fiddamanprint "## Start."
315*906afcb8SAndy Fiddaman
316*906afcb8SAndy Fiddamanircsession_t mybot
317*906afcb8SAndy Fiddaman
318*906afcb8SAndy Fiddaman# override ircsession_t::serverevent_privmsg with a new method for our bot
319*906afcb8SAndy Fiddamanfunction mybot.serverevent_privmsg
320*906afcb8SAndy Fiddaman{
321*906afcb8SAndy Fiddaman	nameref args=$1
322*906afcb8SAndy Fiddaman	typeset msguser="${args.msguser}"
323*906afcb8SAndy Fiddaman	typeset msgchannel="${args.msgchannel}"
324*906afcb8SAndy Fiddaman	typeset msg="${args.msg}"
325*906afcb8SAndy Fiddaman
326*906afcb8SAndy Fiddaman	printf "#message: user=%q, channel=%q, msg=%q\n" "$msguser" "$msgchannel" "$msg"
327*906afcb8SAndy Fiddaman
328*906afcb8SAndy Fiddaman	# Check if we get a private message
329*906afcb8SAndy Fiddaman	if [[ "${msgchannel}" == "${_.nick}" ]] ; then
330*906afcb8SAndy Fiddaman		# ${msgchannel} point to our own nick if we got a private message,
331*906afcb8SAndy Fiddaman		# we need to extract the sender's nickname from ${msguser} and put
332*906afcb8SAndy Fiddaman		# it into msgchannel
333*906afcb8SAndy Fiddaman		msgchannel="${msguser/~(El):(.*)!.*/\1}"
334*906afcb8SAndy Fiddaman	else
335*906afcb8SAndy Fiddaman		# check if this is a command for this bot
336*906afcb8SAndy Fiddaman		[[ "$msg" != ~(Eli):${_.nick}:[[:space:]]  ]] && return 0
337*906afcb8SAndy Fiddaman	fi
338*906afcb8SAndy Fiddaman
339*906afcb8SAndy Fiddaman	# strip beginning (e.g. ":<nick>:" or ":") plus extra spaces
340*906afcb8SAndy Fiddaman	msg="${msg/~(Eli)(:${_.nick})*:[[:space:]]*/}"
341*906afcb8SAndy Fiddaman
342*906afcb8SAndy Fiddaman	printf "botmsg=%q\n" "$msg"
343*906afcb8SAndy Fiddaman
344*906afcb8SAndy Fiddaman	case "$msg" in
345*906afcb8SAndy Fiddaman		~(Eli)date)
346*906afcb8SAndy Fiddaman			_.send_privmsg "$msgchannel" "${
347*906afcb8SAndy Fiddaman			        printf "%(%Y-%m-%d, %Th/%Z)T\n"
348*906afcb8SAndy Fiddaman			}"
349*906afcb8SAndy Fiddaman			;;
350*906afcb8SAndy Fiddaman		~(Eli)echo)
351*906afcb8SAndy Fiddaman			_.send_privmsg "$msgchannel" "${msg#*echo}"
352*906afcb8SAndy Fiddaman			;;
353*906afcb8SAndy Fiddaman		~(Eli)exitbot)
354*906afcb8SAndy Fiddaman			typeset exitkey="$(print "$msguser" | sum -x sha1)" # this is unwise&&insecure
355*906afcb8SAndy Fiddaman			if [[ "$msg" == *${exitkey}* ]] ; then
356*906afcb8SAndy Fiddaman				_.running=false
357*906afcb8SAndy Fiddaman			fi
358*906afcb8SAndy Fiddaman			;;
359*906afcb8SAndy Fiddaman		~(Eli)help)
360*906afcb8SAndy Fiddaman			_.send_privmsg "$msgchannel" "${
361*906afcb8SAndy Fiddaman				printf "Hello, this is shircbot, written in ksh93 (%s). " "${.sh.version}"
362*906afcb8SAndy Fiddaman				printf "Subcommands are 'say hello', 'math <math-expr>', 'stocks', 'uuid', 'date' and 'echo'."
363*906afcb8SAndy Fiddaman				}"
364*906afcb8SAndy Fiddaman			;;
365*906afcb8SAndy Fiddaman		~(Eli)math)
366*906afcb8SAndy Fiddaman			if [[ "${msg}" == ~(E)[\`\$] ]] ; then
367*906afcb8SAndy Fiddaman				# "restricted" shell mode would prevent any damage but we try to be carefull...
368*906afcb8SAndy Fiddaman				_.send_privmsg "$msgchannel" "Syntax error."
369*906afcb8SAndy Fiddaman			else
370*906afcb8SAndy Fiddaman				typeset mathexpr="${msg#*math}"
371*906afcb8SAndy Fiddaman
372*906afcb8SAndy Fiddaman				printf "Calculating '%s'\n" "${mathexpr}"
373*906afcb8SAndy Fiddaman				_.send_privmsg "$msgchannel" "${
374*906afcb8SAndy Fiddaman				        ( printf 'export PATH=/usr/${RANDOM}/$$/${RANDOM}/foo ; set -o restricted ; printf "%%s = %%.40g\n" "%s" $(( %s ))\n' "${mathexpr}" "${mathexpr}" | source /dev/stdin 2>&1 )
375*906afcb8SAndy Fiddaman				}"
376*906afcb8SAndy Fiddaman			fi
377*906afcb8SAndy Fiddaman			;;
378*906afcb8SAndy Fiddaman		~(Eli)say\ hello)
379*906afcb8SAndy Fiddaman			_.send_privmsg "$msgchannel" "Hello, this is a bot."
380*906afcb8SAndy Fiddaman			;;
381*906afcb8SAndy Fiddaman		~(Eli)stocks)
382*906afcb8SAndy Fiddaman			typeset stockmsg tickersymbol
383*906afcb8SAndy Fiddaman			for tickersymbol in "JAVA" "ORCL" "IBM" "AAPL" "HPQ" ; do
384*906afcb8SAndy Fiddaman				stockmsg="$( /usr/sfw/bin/wget -q -O /dev/stdout "http://quote.yahoo.com/d/quotes.csv?f=sl1d1t1c1ohgv&e=.csv&s=${tickersymbol}" 2>&1 )"
385*906afcb8SAndy Fiddaman				_.send_privmsg "$msgchannel" "${tickersymbol}: ${stockmsg//,/ }"
386*906afcb8SAndy Fiddaman			done
387*906afcb8SAndy Fiddaman			;;
388*906afcb8SAndy Fiddaman		~(Eli)uuid)
389*906afcb8SAndy Fiddaman			_.send_privmsg "$msgchannel" "${
390*906afcb8SAndy Fiddaman			        print "%(%Y%M%D%S%N)T$((RANDOM))%s\n" "${msguser}" | sum -x sha256
391*906afcb8SAndy Fiddaman			}"
392*906afcb8SAndy Fiddaman			;;
393*906afcb8SAndy Fiddaman	esac
394*906afcb8SAndy Fiddaman
395*906afcb8SAndy Fiddaman	return 0
396*906afcb8SAndy Fiddaman}
397*906afcb8SAndy Fiddaman
398*906afcb8SAndy Fiddaman# Automatically join the list of channels listed in |config.join_channels|
399*906afcb8SAndy Fiddaman# after the client is connected to the server for some time
400*906afcb8SAndy Fiddamanfunction mybot.mainloop_tick
401*906afcb8SAndy Fiddaman{
402*906afcb8SAndy Fiddaman	integer -S autojoin_done=2
403*906afcb8SAndy Fiddaman	integer i
404*906afcb8SAndy Fiddaman
405*906afcb8SAndy Fiddaman	if (( autojoin_done-- == 0 && ${#config.join_channels[@]} > 0 )) ; then
406*906afcb8SAndy Fiddaman		print "# Autojoin channels..."
407*906afcb8SAndy Fiddaman
408*906afcb8SAndy Fiddaman		for ((i=0 ; i < ${#config.join_channels[@]} ; i++ )) ; do
409*906afcb8SAndy Fiddaman			mybot.join_channel "${config.join_channels[i]}"
410*906afcb8SAndy Fiddaman		done
411*906afcb8SAndy Fiddaman	fi
412*906afcb8SAndy Fiddaman
413*906afcb8SAndy Fiddaman	return 0
414*906afcb8SAndy Fiddaman}
415*906afcb8SAndy Fiddaman
416*906afcb8SAndy Fiddamanmybot.createsession "${config.servername}" ${config.port} "${config.nickname}"
417*906afcb8SAndy Fiddaman
418*906afcb8SAndy Fiddaman# This is a network-facing application - once we've set eveything up
419*906afcb8SAndy Fiddaman# we set PATH to a random value and switch to the shell's restricted
420*906afcb8SAndy Fiddaman# mode to make sure noone can escape the jail.
421*906afcb8SAndy Fiddaman#export PATH=/usr/$RANDOM/foo
422*906afcb8SAndy Fiddaman#set -o restricted
423*906afcb8SAndy Fiddaman
424*906afcb8SAndy Fiddamanmybot.mainloop
425*906afcb8SAndy Fiddaman
426*906afcb8SAndy Fiddamanprint "## End."
427*906afcb8SAndy Fiddaman
428*906afcb8SAndy Fiddamanexit 0
429