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