1#!/usr/bin/vm shell
2# Robert J�rdens <rjo@gmx.de>
3#
4# This is the dtmf script. It is called by vgetty when a dtmf code was send by
5# the user.
6#
7# $1 - received DTMF code
8#
9# $2 - name of the recorded voice file or if "-cons_mode", switches on console
10# mode so you can enter the dtmf-codes on your console and will hear all sound
11# going to your soundcard! (you also have to toogle the magic line at the
12# beginning!)
13#
14
15if [ "1$2" = "1-cons_mode" ]; then
16	CONS_MODE="yes"
17else
18	CONS_MODE="no"
19fi
20
21#
22BASENAME=`basename $0`
23DTMF="$1"
24ALREADY_REC="$2"
25# Some dir's
26VOICE_DIR=/var/spool/voice
27MSG_DIR=$VOICE_DIR/messages
28INC_DIR=$VOICE_DIR/incoming
29# Some files (scripts may have to be written)
30CODE=`cat $VOICE_DIR/.code`
31FLAG=$VOICE_DIR/.flag
32TIMESTAMP=$VOICE_DIR/.timestamp
33CONNECT_SCRIPT=$VOICE_DIR/ppp.sh
34FAX_ACTIVATE_SCRIPT=$VOICE_DIR/fax_activate.sh
35# Sound-Files
36GET_CODE=$MSG_DIR/get-code.rmd
37INCORRECT=$MSG_DIR/incorrect.rmd
38GOODBYE=$MSG_DIR/goodbye.rmd
39NO_NEW_MESSAGES=$MSG_DIR/no_new_messages.rmd
40GIMME_COMM=$MSG_DIR/gimme_comm.rmd
41OKAY_DRIN=$MSG_DIR/okay_drin.rmd
42BYE=$MSG_DIR/bye.rmd
43# The TTY's where to toogle the Scroll-LED
44LEDTTY=/dev/tty[1-8]
45# Tries Code
46MAXTRIES=3
47# Timeout beetween two equal numbers to be recognized as two single. Under
48# TIMEOUT: increment (see dtmf_alph)
49TIMEOUT=2
50# Type of your Modem
51MODEM_TYPE=Rockwell
52# Compression
53COMPRESSION=4
54# Sample Rate needed
55SPEED=7200
56
57
58### BEGIN FUNCTIONS
59
60# Define the function to check for an answer and if it is not the desired it
61# will quit!
62answer () {
63    ANSWER=`receive`
64    if [ "$ANSWER" != "$1" ]; then
65	logger -t "${BASENAME}[$$]" "Got '$ANSWER', expected '$1'. Harakiri"
66       	kill -KILL $$
67    fi
68}
69
70# Define the function to receive an answer from the voice library
71receive () {
72  if [ $CONS_MODE = "no" ]; then
73     read -r INPUT <&$VOICE_INPUT
74  else
75     read -r -p "receive: " INPUT
76  fi
77  echo "$INPUT"
78}
79
80# Define the function to send a command to the voice library
81send () {
82  if [ $CONS_MODE = "no" ]; then
83     echo $1 >&$VOICE_OUTPUT
84     kill -PIPE $VOICE_PID
85  else
86     echo "send: $1"
87  fi
88}
89
90# Define the function send a beep
91beep () {
92  if [ $CONS_MODE = "no" ]; then
93     send "BEEP $1 $2"
94     answer "BEEPING"
95     answer "READY"
96  else
97     echo "beep: $1 $2"
98  fi
99}
100
101# Define the function to play a file
102play () {
103   if [ $CONS_MODE = "no" ]; then
104     send "PLAY $1"
105     answer "PLAYING"
106     answer "READY"
107   else
108     rmdtopvf $1 | pvfspeed -s 8000 | pvftobasic > /dev/audio
109   fi
110     logger -t "${BASENAME}[$$]" "Played $1"
111}
112
113# Define the function to play the new messages
114messages () {
115     if [ ! -f $TIMESTAMP ]; then
116          MSGS=`find $INC_DIR/ -type f -name 'v*.rmd' -print`
117     else
118          MSGS=`find $INC_DIR/ -type f -name 'v*.rmd' -newer $TIMESTAMP -print`
119          if [ -z "$MSGS" ]; then
120               BASENAME=`basename $TIMESTAMP`
121               NEWSTAMP=`find $VOICE_DIR -name $BASENAME -cmin -10 -print`
122               if [ "$NEWSTAMP" = "$TIMESTAMP" ]; then
123                    MSGS=`find $INC_DIR -type f -name 'v*.rmd' -print`
124               fi
125          fi
126     fi
127     touch $TIMESTAMP-n
128     if [ -x $VOICE_DIR/speakdate.sh ]; then
129          TIME=yes
130     else
131          TIME=no
132     fi
133     TMPDIR=${TMPDIR-/tmp}/dtmf.$$
134     mkdir -m 0700 $TMPDIR || exit 1
135     trap "rm -rf $TMPDIR" 0 1 2 3 7 13 15
136     TMP=${TMPDIR}/time.rmd.$$
137     LOCK=${TMPDIR}/time-lock.$$
138     for i in $MSGS
139     do
140          if [ $TIME = yes ]; then
141               (touch $LOCK ;\
142               $VOICE_DIR/speakdate.sh $i $SPEED $MODEM_TYPE $COMPRESSION >$TMP ;\
143               rm -f $LOCK) &
144          fi
145          beep 1320 100
146          play $i
147          beep 1320 100
148          if [ $TIME = yes ]; then
149               while [ -f $LOCK ]
150               do
151                    sleep 1
152               done
153               play $TMP
154               rm -f $TMP
155          fi
156     done
157     if [ -z "$MSGS" ]; then
158          play "$NO_NEW_MESSAGES"
159     fi
160     beep 1320 1000
161     rm -f $FLAG $TIMESTAMP
162     mv $TIMESTAMP-n $TIMESTAMP
163#     for tty in $LEDTTY; do
164#         setleds -scroll < $tty
165#     done
166}
167
168# Define the function to read one dtmf code string terminated by #
169getcode () {
170  if [ $CONS_MODE = "no" ]; then
171     RECEIVED=""
172     send "ENABLE EVENTS"
173     answer "READY"
174     send "WAIT 30"
175     answer "WAITING"
176     ANSWER=""
177     while [ "$ANSWER" != "READY" ]
178     do
179          ANSWER=`receive`
180          if [ "$ANSWER" = "RECEIVED_DTMF" ]; then
181               ANSWER=`receive`
182               if [ "$ANSWER" = "*" ]; then
183                    RECEIVED=""
184               else
185                    case $ANSWER in
186                    "#")
187                         send "STOP"
188                    ;;
189                    0|1|2|3|4|5|6|7|8|9)
190                         RECEIVED=$RECEIVED$ANSWER
191                    ;;
192                    *)
193                         logger -t "${BASENAME}[$$]" "Ignoring DTMF $ANSWER"
194                    ;;
195                    esac
196               fi
197          else
198               if [ "$ANSWER" = "SILENCE_DETECTED" ]; then
199                    send "STOP"
200               else
201                    if [ "$ANSWER" != "READY" ]; then
202                         logger -t "${BASENAME}[$$]" "Ignoring $ANSWER"
203                    fi
204               fi
205          fi
206     done
207     send "DISABLE EVENTS"
208     answer "READY"
209  else
210     read -r -p "Code: " RECEIVED
211  fi
212     echo "$RECEIVED"
213     }
214
215# Get a SMS-like String from DTMF's. The Code-Table is not standard!!
216dtmf_alph () {
217    TAB_0=(0 + - '#' '*' , '.' : ';' '?' '!' '|' @ / '\\' '$' % � '~' � � � � �)
218    TAB_1=(" " '"' "'" "(" ")" "[" "]" "{" "}" "<" ">")
219    TAB_2=(a b c 2 �)
220    TAB_3=(d e f 3)
221    TAB_4=(g h i 4)
222    TAB_5=(j k l 5)
223    TAB_6=(m n o 6 �)
224    TAB_7=(p q r s 7 �)
225    TAB_8=(t u v 8 �)
226    TAB_9=(w x y z 9)
227    TAB_r=()
228    TAB_s=()
229
230#    stty cbreak </dev/tty >/dev/tty 2>&1
231
232    LAST_TIME=10000000000
233    THIS_TIME="0"
234    LAST_CHAR=""
235    THIS_CHAR=""
236    LAST_NUM=""
237    THIS_NUM=""
238    LIST_CHAR=()
239    LIST_NUM=()
240    REPT=0
241    THIS_TAB=""
242    THIS_POS=""
243    LAST_POS=""
244    ALPH=""
245    CHAR=""
246
247    while true; do
248	THIS_NUM=`getone`
249#        THIS_NUM=`perl -e 'print getc(STDIN);'`
250#       echo -n "  "
251        THIS_TIME=`date +'%s'`
252        LIST_NUM=(${LIST_NUM[@]} $THIS_NUM)
253        case $THIS_NUM in
254        "*")
255            if [ "1$LAST_CHAR" != "1" ]; then
256                eval $LAST_CHAR=\${TAB_${LAST_NUM}[$REPT]}
257            fi
258            if [ "$CAPS" = "yes" ]; then
259                caps="no"
260            else
261                caps="yes"
262                if [ "1$LAST_CHAR" != "1" ]; then
263                    LAST_CHAR=`echo "$LAST_CHAR" | tr "[:lower:]" "[:upper:]"`
264                fi
265            fi
266#           echo -n "made '$LAST_CHAR' caps: '$CAPS'"
267            continue
268        ;;
269        "#")
270            LIST_CHAR[$LAST_POS]=$LAST_CHAR
271            break
272        ;;
273        0|1|2|3|4|5|6|7|8|9)
274            if [ "$THIS_NUM" != "$LAST_NUM" -o $(($THIS_TIME - $TIMEOUT)) \
275                                                -ge $LAST_TIME ]; then
276                LIST_CHAR[$LAST_POS]=$LAST_CHAR
277                LAST_NUM=$THIS_NUM
278                LAST_POS=$THIS_POS
279                THIS_POS=$(($THIS_POS + 1))
280                REPT=0
281#               echo -n "added '$LAST_CHAR'"
282            else
283                eval THIS_TAB=(\${TAB_${THIS_NUM}[@]})
284                if [ "$REPT" -ge "${#THIS_TAB[@]}" ]; then
285                    REPT=0
286                else
287                    REPT=$(($REPT + 1))
288                fi
289#               echo -n "incremented to '$REPT'"
290            fi
291        ;;
292        *)
293#           echo -n "Invalid: '$THIS_CHAR'"
294            THIS_NUM=$LAST_NUM
295            THIS_CHAR=$LAST_CHAR
296            continue
297        ;;
298        esac
299        eval THIS_CHAR=\${TAB_${THIS_NUM}[$REPT]}
300        if [ "$CAPS" = "yes" ]; then
301            THIS_CHAR=`echo "$THIS_CHAR" | tr "[:lower:]" "[:upper:]"`
302        fi
303        LAST_CHAR=$THIS_CHAR
304        LAST_NUM=$THIS_NUM
305        LAST_TIME=$THIS_TIME
306#       echo "--now: list '${LIST_CHAR[@]}' and LAST_CHAR '$LAST_CHAR'"
307    done
308
309#    stty -cbreak </dev/tty >/dev/tty 2>&1
310#    export LIST_CHAR
311    for CHAR in "${LIST_CHAR[@]}"; do
312#       echo -n "$CHAR"
313        ALPH="$ALPH$CHAR"
314    done
315    echo "$ALPH"
316}
317
318# exitvm
319exitvm () {
320  play $GOODBYE
321  if [ $CONS_MODE = "no" ]; then
322    send "GOODBYE"
323    answer "GOODBYE SHELL"
324  else
325    echo "Bye"
326  fi
327  exit 0
328}
329
330# getone
331getone () {
332  if [ $CONS_MODE = "no" ]; then
333     RECEIVED=""
334     send "ENABLE EVENTS"
335     answer "READY"
336     send "WAIT 30"
337     answer "WAITING"
338     ANSWER=""
339     while [ "$ANSWER" != "READY" ]
340     do
341          ANSWER=`receive`
342          if [ "$ANSWER" = "RECEIVED_DTMF" ]; then
343               ANSWER=`receive`
344               send "STOP"
345               RECEIVED=$ANSWER
346          else
347               if [ "$ANSWER" = "SILENCE_DETECTED" ]; then
348                    send "STOP"
349               else
350                    if [ "$ANSWER" != "READY" ]; then
351                         logger -t "${BASENAME}[$$]" "Ignoring $ANSWER"
352                    fi
353               fi
354          fi
355     done
356     send "DISABLE EVENTS"
357     answer "READY"
358  else
359     read -r -p "Tone: " RECEIVED
360  fi
361     echo "$RECEIVED"
362     }
363
364# Start the festival Server if necessary
365check_fest () {
366	if ps ax 2>&1 | grep festival_server | grep -v grep >/dev/null; then
367		logger -t "${BASENAME}[$$]" "fesitval_server running"
368	else
369		nohup festival_server 2>&1 >/dev/null &
370		logger -t "${BASENAME}[$$]" "fesitval_server started"
371		sleep 5
372	fi
373}
374
375# Say something
376say () {
377	check_fest
378#	I really want vm�shell to be able to play from a pipe! --async
379	if [ "1$1" != "1" ]; then
380	   echo "$1" | festival_client --tts_mode fundamental --ttw --otype ulaw | basictopvf | pvfspeed -s $SPEED | pvfamp -A 6 | pvftormd $MODEM_TYPE $COMPRESSION > $MSG_DIR/voice_say.rmd
381	else
382	   cat - | festival_client --tts_mode fundamental --ttw --otype ulaw | basictopvf | pvfspeed -s $SPEED | pvfamp -A 6 | pvftormd $MODEM_TYPE $COMPRESSION > $MSG_DIR/voice_say.rmd
383	fi
384	play $MSG_DIR/voice_say.rmd
385	rm -f $MSG_DIR/voice_say.rmd
386}
387
388# Synthesise some Speech for later playing (to save time)
389prep_say () {
390	if [ "1$2" == "1" -o ! -s $MSG_DIR/message_${2-prep_say}.rmd ]; then
391		check_fest
392#		I really want vm�shell to be able to play from a pipe! --async
393		echo "$1" | festival_client --tts_mode fundamental --ttw --otype ulaw |	basictopvf | pvfspeed -s $SPEED | pvfamp -A 6 | pvftormd $MODEM_TYPE $COMPRESSION > $MSG_DIR/message_${2-prep_say}.rmd &
394		sleep 5
395	fi
396}
397
398# Play prepared voice
399play_say () {
400	play $MSG_DIR/message_${1-prep_say}.rmd
401#	Don't remove special generated files -- maybe we need 'em later
402#	rm -f $MSG_DIR/message_prep_say.rmd
403}
404
405
406### END FUNCTIONS
407###  --MAIN--
408
409if [ $CONS_MODE = "no" ]; then
410  # Let's see if the voice library is talking to us
411  answer "HELLO SHELL"
412  # Let's answer the message
413  send "HELLO VOICE PROGRAM"
414  answer "READY"
415  # Let's check the code
416fi
417
418TRIES=1
419GOT_CORRECT_CODE=no
420
421while [ $TRIES -le $MAXTRIES ]
422do
423     if [ "$DTMF" = "$CODE" ]; then
424          if [ -f $ALREADY_REC ]; then
425               rm -f $ALREADY_REC
426          fi
427          GOT_CORRECT_CODE=yes
428          break
429     else
430          logger -t "${BASENAME}[$$]" "Incorrect DTMF code on try $TRIES"
431          beep 1320 100
432          play "$INCORRECT"
433     fi
434     if [ $TRIES -lt $MAXTRIES ]; then
435          play "$GET_CODE"
436          beep 1320 100
437          DTMF=`getcode`
438     else
439          play "$BYE"
440          play "$GOODBYE"
441     fi
442     TRIES=`expr $TRIES + 1`
443done
444#
445###
446### We are in!! Lets give the Menu!
447###
448
449if [ "$GOT_CORRECT_CODE" = "yes" ]; then
450    prep_say "hash is menu" "help"
451
452    prep_say "zeero is command mode. Enter your command with the provided table
453    and press hash. Then confirm it with hash. -- One plays your list of
454    messages. If there are no new and you ask again it will give you all. --
455    Two will connect you to your internet provider in a bout one minute. --
456    Three will activate faxgetty for the next 30 minutes. -- Four lets you
457    record something from the soundcard and play it. -- Star leaves the menu
458    and will hang up the phone." "menu"
459
460    play "$OKAY_DRIN"
461    while true ; do
462	play_say "help"
463	ANSWER=`getone`
464	case "$ANSWER" in
465	    "#")
466		play_say "menu"
467	    ;;
468	    0)
469		logger -t "${BASENAME}[$$]" "Command mode"
470		say "Command mode"
471		COMMAND=`dtmf_alph`
472		beep 1320 100
473		say "Really __ ${COMMAND} __ ?"
474		ANSWER=`getone`
475		beep 1320 100
476		case "$ANSWER" in
477		"#")
478		    logger -t "${BASENAME}[$$]" "Command '$COMMAND'"
479		    su - rj -c "${COMMAND}" 2>&1 | say
480		;;
481		*)
482		    say "Aborted"
483		;;
484		esac
485	    ;;
486	    1)
487		logger -t "${BASENAME}[$$]" "Playing messages"
488		say "Your list of messages"
489		messages
490	    ;;
491	    2)
492		logger -t "${BASENAME}[$$]" "Connecting via PPP"
493		say "In a bout 1 minute you will be connected"
494		nohup $CONNECT_SCRIPT >/dev/null 2>&1 &
495	    ;;
496	    3)
497		logger -t "${BASENAME}[$$]" "Run faxgetty for the next 30 min"
498		say "In a bout 1 minute I will be able to receive faxes"
499		nohup $ACTIVATE_FAX >/dev/null 2>&1 &
500	    ;;
501	    4)
502		say "Seconds to receive and amplification?"
503		SECS=`getcode`
504		beep 1320 100
505		AMP=`getcode`
506		beep 1320 100
507		dd if=/dev/audio bs=8000 count=$SECS | basictopvf | pvfspeed -s $SPEED | pvfamp -A $AMP | pvftormd $MODEM_TYPE $COMPRESSION > $MSG_DIR/abhorch.rmd
508		beep 1320 100
509		play "$MSG_DIR/abhorch.rmd"
510		beep 1320 100
511#		rm -f $MSG_DIR/abhorch.rmd
512	    ;;
513	    "*")
514		play $BYE
515		beep 1320 100
516		exitvm
517	    ;;
518	esac
519    done
520fi
521
522###
523### All right, that's it, leave!
524###
525
526exitvm
527