## Text-To-Speech script for ircII EPIC5 ## ## This script can either run a local program external ## to EPIC5 that takes input on STDIN to produce speech ## output, or it can connect to a defined IP and port via ## TCP and send the same data to that daemon instead, making ## it possible to run a daemon on a Windows system where a ## greater variety of voices may be available. ## ## The external program or TCP daemon are handed the arguments ## of $* from /speak if SPEECH is set to be ON. /speak is called ## by /cspeak, /mspeak and /nspeak which are wrappers to simplify ## things for us. ## # This script takes advantage of "newaway" and "netsplit" if loaded #load newaway #load netsplit package irctts ## Set some defaults and bind some keys. addset speech bool addset speech_speak_all_channels bool addset speech_speak_modes int addset speech_output str addset speech_rc_file str addset speech_hear_myself bool addset speech_default_voice str addset speech_default_msg_voice str addset speech_default_notice_voice str addset speech_default_self_voice str addset speech_queue bool set speech on set speech_default_voice ms_mike:9 set speech_default_notice_voice rs_jill:5 set speech_default_msg_voice rs_daniel:5 set speech_default_self_voice loq_dave:2 set speech_output sock:persephone 8255 set speech_rc_file ~/.ircttsrc set mode_stripper on bind ^S parse_command set speech toggle bind ^D parse_command tts.addsrc $T bind ^F parse_command {set speech_queue toggle;^queue -do tts} ## Set up events to speak on ^exec "irctts *" on ^dcc_raw '$_tts.sockfd *' on #-msg 5 "*" {mspeak $0 $1-} on #-dcc_chat 5 "*" {mspeak $0 $1-} on #-action 5 '% $N *' {mspeak $0 $ts($0) $2-} on #-notice 5 "*" {mspeak $0 $ts($0) NOTICE: $1-} on #-public 5 "*" {cspeak $1 $0 $2-} on #-public_other 5 "*" {cspeak $1 $0 $2-} on #-action 5 '*' {cspeak $1 $0 $ts($0) $2-} on #-join -5 "*" { @:so1 = encode($tolower($1)) @:so2 = encode($0) if (signoffs[$so1][$so2]) { if (isbroke[$signoffs[$so1][$so2]]) { nspeak force Netsplit finished. } } else { nspeak $1 $ts($0) has joined channel $ts($1) } } on #-channel_signoff 5 "*" { @:cs1 = encode($tolower($0)) @:cs2 = encode($1) @:cs3 = encode($2).$encode($3) if (isbroke[$cs3] ) {[ nspeak $0 $ts($1) has quit IRC: $2- } else { nspeak force Netsplit detected } } on #-part 5 "*" {nspeak $1 $ts($0) has left channel $ts($1)} on #-channel_nick 5 "*" {nspeak $0 $ts($1) is now known as $ts($2)} on #-topic 5 "*" {nspeak $1 $ts($0) set new topic on $ts($1): $2-} on #-kick 5 "*" {nspeak $2 $ts($1) kicked $ts($0) from $ts($2): $3-} on #-kick 5 '$N *' {nspeak force $ts($1) kicked you from $ts($2): $3-} on #-mode_stripped 5 "*" { if (ischannel($1)) {modespeak $*} } on #-send_public 5 "*" {mespeak $0 $1-} on #-send_msg 5 "*" {mespeak $0 $1-} on #-send_action 5 "*" {mespeak $0 $ts($N) $1-} on #-send_dcc_chat 5 "*" {mespeak $0 $1-} on #-notify_signon 5 "*" {nspeak force $ts($0) has just signed on to I R C.} on #-notify_signoff 5 "*" {nspeak force $ts($0) has signed off from I R C.} on #-ctcp_reply 5 "*" {nspeak force CTCP $2 reply from $ts($0): $3-} on #-301 5 "*" { # The or part is to force a true if newaway isn't loaded. if (newaway.show($*)||newaway.show($*)==[]) { nspeak force $0 is away: $1- } } on #-305 5 "*" {nspeak force $1-} on #-306 5 "*" {nspeak force $1-} on #-311 5 "*" {nspeak force $1 is $2 at $3. $5-} on #-312 5 "*" {nspeak force $1 is on server $2} on #-313 5 "*" {nspeak force $1-} on #-317 5 "*" {nspeak force $1 has been idle $tdiff($2)} on #-319 5 "*" {nspeak force $1 is on channels $2-} on #-401 5 "*" {nspeak force Error. $1: $2-} on #-402 5 "*" {nspeak force Error. $1: $2-} on #-403 5 "*" {nspeak force Error. $1: $2-} on #-404 5 "*" {nspeak force Error. $1: $2-} on #-405 5 "*" {nspeak force Error. $1: $2-} on #-406 5 "*" {nspeak force Error. $1: $2-} on #-invite 5 "*" { nspeak force $ts($0) invited you to $ts($1). nspeak force Type /join minus i to join this channel. } on #-dcc_request 5 "*" {dccspeak $*} on #-dcc_offer 5 "*" {dccspeak $0 OFFR $1-} on #-dcc_connect 5 "*" {dccspeak $0 INIT $1-} on #-dcc_lost 5 "*" {dccspeak $0 LOST $1-} on #-server_lost 5 "*" {nspeak force Disconnected from $1: $2-} on #-server_state 5 "% % CONNECTING" { nspeak force Connecting to server $serverctl(GET $0 name) } on #-server_state 5 "% % ACTIVE" { nspeak force Connected to server $serverctl(GET $0 name) } ## Setup our commands alias speak { if (getset(speech)==[ON] && _tts.last != [$*]) { defer @_tts.last = [$*] ^queue tts {speakit $0 $tts.patsub($1-)} if (getset(speech_queue)==[OFF]) {^queue -do tts} #speakit $0 $tts.patsub($1-) } } alias speakit { @:_tts.sot = before(: $getset(speech_output)) @:_tts.soa = after(: $getset(speech_output)) switch ( $_tts.sot ) { ( sock ) { if (dccctl(TYPEMATCH RAW) == []) { @_tts.sockfd = connect($_tts.soa) pause 1 } ^msg =$_tts.sockfd $* } ( pipe ) { # This will silently fail if %irctts already exists ^exec -name irctts $_tts.soa ^msg %irctts $* } ( file ) { if (isfilevalid($_tts.sofd)||(_tts.sofd=open("$_tts.soa" w)) > -1) { @write($_tts.sofd $*) } else { xecho -b Error: Couldn't open file $soa } } ( * ) { xecho -b Error: Unknown output type. } } } alias wtf {speakit $_tts.last} alias dccspeak { switch ( $1 ) { ( SEND ) {nspeak force $ts($0) wants to send you $remw($~ $5-), $~ bytes.} ( CHAT ) {nspeak force $ts($0) wants to dcc chat with you} ( INIT ) {nspeak force dcc $2 connection established with $ts($0)} ( OFFR ) { if ([$2]==[SEND]) { nspeak force Sent dcc $2 request for $3, $4 bytes, to $ts($0) } else { nspeak force Sent dcc $2 request to $ts($0) } } ( LOST ) { switch ( $2 ) { ( GET ) {nspeak force dcc $2 of $3 from $ts($0) lost: $5-} ( SEND ) {nspeak force dcc $2 to $ts($0) lost: $3-} ( CHAT ) {nspeak force dcc $2 to $ts($0) lost: $3-} ( RAW ) { # If we're speaking via tcp sock, trying to # speak now might be a bad idea. if (before(: $getset(speech_output))!=[sock]) { nspeak force dcc $2 to $ts($0) lost: $3- } } ( * ) {nspeak force Unknown dcc $2 with $ts($0) lost: $3-} } } ( * ) {nspeak force Unkonwn dcc request $1 from $ts($0). $2-} } } alias modespeak { switch ( $getset(speech_speak_modes) ) { ( 1 ) { modespeaker obmsp $* } ( 2 ) { modespeaker obmspv $* } ( 3 ) { modespeaker obmspvl $* } ( 4 ) { modespeaker * $* } ( * ) { modespeaker - $* } } } alias modespeaker { @:_tts.mn = (left(1 $3)==[+]) ? [plus $right(1 $3)] : [minus $right(1 $3)] switch ( $3 ) { ( +b ) ( -b ) { @:_tts.mw = ([$3]==[+b]) ? [banned] : [unbanned] if (match($4 $N!$X)) { @:_tts.critmode = [$ts($1) $_tts.mw you on $ts($2) with mask $4] } else { @:_tts.mode = [$ts($1) $_tts.mw mask $4 on $ts($2)] } } ( +o ) ( -o ) { @:_tts.mw = ([$3]==[+o]) ? [opped] : [de-opped] if ([$4]==N) { @:_tts.critmode = [$ts($1) $_tts.mw you on $ts($2)] } else { @:_tts.mode = [$ts($1) $_tts.mw $ts($4) on $ts($2)] } } ( +i ) {@:_tts.crtimode = [$ts($1) made $ts($2) invite only]} ( -i ) {@:_tts.critmode = [$ts($1) made $ts($2) no longer invite only]} ( +k ) {@:_tts.critmode = [$ts($1) set $ts($2) channel key to: $4-]} ( -k ) {@:_tts.critmode = [$ts($1) removed key from $ts($2)]} ( * ) { if ([$0]==[*]||(strip($3 $0)!=[$0]&&!_tts.critmode)) { if (!_tts.mode) { @:_tts.mode = [$ts($1) set mode $_tts.mn $4- on $ts($2)] } } } } if (_tts.critmode) { nspeak $2 $_tts.critmode } else { if (_tts.mode) {nspeak $2 $_tts.mode} } } alias mespeak { if (getset(speech_hear_myself)==[ON] && (!ischannel($0) || tts.chattykathy($0))) { if (_tts.focus!=[$0]) {nspeak force To $ts($0)} @_tts.focus = [$0] speak $tts.pickvoice($N) $1- } } alias mspeak { if (_tts.focus != [$0]) {nspeak force From $ts($0)} @_tts.focus = [$0] @:_tts.voice = tts.pickvoice($0) if (_tts.last != [$_tts.voice $1-]) { speak $_tts.voice $1- } } alias cspeak { if (tts.chattykathy($0 $1) && !tts.gross($1-)) { if (_tts.focus != [$0]) { nspeak $0 On $ts($0) } if (!(_tts.src==[$1] && _tts.focus==[$0])) { nspeak $0 By $ts($1) } @_tts.focus = [$0] @_tts.src = [$1] speak $tts.pickvoice($1 $0) $2- } } alias nspeak { if (tts.chattykathy($*)) { speak $getset(speech_default_notice_voice) $1- } } alias tts.addpattern { tts.load push tts.patterns $encode($*) tts.save } alias tts.addsub { tts.load @tts[subs][$encode($tolower($0))] = [$1-] xecho -b Added TTS sub for $0 to $1- tts.save } alias tts.addsrc { if (match($0 $_tts.ttssrc)) { @_tts.ttssrc = remw($0 $_tts.ttssrc) } else { push _tts.ttssrc $0 } xecho -b Public text will be spoken from: $_tts.ttssrc } alias gross { tts.load xecho -b Added pattern(s): $* push tts.grosspats $* tts.save } alias vmap.remove { @:vmap = tts.vmapmake($*) # Remove redundant vmaps fe ($tts.vmaps) vm { fe ($vmap) vm2 { if (match($before(= $vm2) $before(= $vm))) { xecho -b Removing existing/redudant $vm vmap @tts.vmaps = remw($vm $tts.vmaps) } } } } alias vmap { if ([$1]) { tts.load @:vmap = tts.vmapmake($*) vmap.remove $0 if (vmap) { push tts.vmaps $vmap xecho -b Mapped $0 to voice: $1 } tts.save } else { vmap.list $0 } } alias vmap.list { if ([$0]) { @:_tts.vmuh = (match(*!*@* $0)) ? [$0] : [$0!$tts.uhost($0)] } @:_tts.vmlen = 0 fe ( $tts.vmaps ) vm { if (@vm > _tts.vmlen) {@_tts.vmlen = @vm}} fe ( $tts.vmaps ) vm { if (match($before(= $vm) $_tts.vmuh) || ![$0]) { @:_tts.vmask = before(= $vm) @:_tts.vmvoice = after(= $vm) xecho -b $[${_tts.vmlen + 3}]before(= $vm) $after(= $vm) } } } alias tts.load { if (fexist($getset(speech_rc_file))) { load $getset(speech_rc_file) } } alias tts.save { if ((:_tts.savefd = open("$getset(speech_rc_file)" w)) > -1) { xecho -b Saving TTS settings to $getset(speech_rc_file) fe ( $symbolctl(pmatch assign tts.*) ) _tts.varname { @write($_tts.savefd ASSIGN $_tts.varname $uniq(${$_tts.varname})) } fe ( $symbolctl(pmatch builtin_variable speech_*) ) _tts.setname { @write($_tts.savefd SET $_tts.setname $getset($_tts.setname)) } } else { xecho -b Could not open() $getset(speech_rc_file) for writing } @close($_tts.savefd) } ## Functions alias tts.uhost { @:_tts.uhost = userhost($0) if (_tts.uhost==[@]) { wait for { ^userhost $0 -cmd {bless;@_tts.uhost = [$3@$4]} } } if (_tts.uhost!=[@]) { return $_tts.uhost } } alias tts.chattykathy { if (getset(speech_speak_all_channels)==[ON] || common($strip(/ $*)/force $_tts.ttssrc)) { return 1 } else { return 0 } } # Returns true if $* is judged to be gross alias tts.gross { if (rmatch($1 hi hiya hey* *ello) && [$2]!=N && ![$4]) { return 1 } fe ($tts.grosspats) gross { if ([$1-] =~ gross) { return 1 } } return 0 } alias tts.pickvoice { if ([$0]==N && getset(speech_default_self_voice)) { return $getset(speech_default_self_voice) } @:_tts.nuh = match(*!*@* $0) ? [$0] : [$0!$userhost()] fe ($tts.vmaps) vm { if (match($before(= $vm) $_tts.nuh)) { return $after(= $vm) } } if ([$1]) { return $getset(speech_default_voice) } else { return $getset(speech_default_msg_voice) } } alias tts.vmapmake { if (match(*!*@* $0)) { return $0=$1 } else { if (:guh = tts.uhost($0)) { return $0!*@*=$1 $mask(3 $0!$guh)=$1 } else { return $0!*@*=$1 } } } alias ts { ^local _tts.tsret fe ( $* ) _tts.ts { @:_tts.enc = encode($tolower($strip(= $_tts.ts))) if (:_tts.rc = tts[subs][$_tts.enc]) { push _tts.tsret $_tts.rc } else { push _tts.tsret $tts.patsub($_tts.ts) } } return $_tts.tsret } alias tts.patsub { @:_tts.line = [$*] fe ( $tts.patterns ) _tts.pat { @:_tts.line = sar(g$decode($_tts.pat)$_tts.line) } return $_tts.line } # load defaults tts.load # Written by wuf