mattnさんのgo-xmpp使ったらあっさり作れた。 スピーチエンジンはOSX内蔵のものかGoogleのものを選べるようにしてみた。 ユーザー名やパスワードを指定して起動するとチャットメッセージを受け取るとそれをしゃべります。
動作に必要なもの
- go get github.com/mattn/go-xmpp
- soxのインストールまたはOS-Xのスピーチボイスにkyokoを追加。
ソースコード
package main
import (
"bufio"
"flag"
"fmt"
"github.com/mattn/go-xmpp"
"io"
"log"
"net/http"
"net/url"
"os"
"os/exec"
"strings"
)
type Speaker interface {
Speech(string)
}
type OSXSpeech struct {
}
func NewOSXSpeech() Speaker {
return &OSXSpeech{}
}
func (o *OSXSpeech) Speech(msg string) {
fmt.Println("Say:", msg)
exec.Command("say", msg).Run()
}
const (
TTSPEECHURL = "http://translate.google.com/translate_tts?"
USERAGENT = "Mozilla/5.0 AppleWebKit Safari"
)
type GoogleSpeech struct {
}
func NewGoogleSpeech() Speaker {
return &GoogleSpeech{}
}
func (o *GoogleSpeech) Speech(msg string) {
values := &url.Values{}
values.Set("tl", "ja")
values.Set("q", msg)
req, err := http.NewRequest("GET", TTSPEECHURL+values.Encode(), nil)
if err != nil {
log.Fatalln(err)
}
req.Header.Set("User-Agent", USERAGENT)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatalln(err)
}
defer resp.Body.Close()
fmt.Println("Say:", msg)
// soxのplayコマンドでmp3型式の再生をします。
cmd := exec.Command("play", "-t", "mp3", "-")
wr, err := cmd.StdinPipe()
if err != nil {
log.Fatalln(err)
}
er := cmd.Start()
if er != nil {
log.Fatalln(er)
}
// GoogleTTSからのレスポンスを標準入力に流し込む。
go func() {
io.Copy(wr, resp.Body)
wr.Close()
}()
cmd.Wait()
}
var (
server = flag.String("server", "talk.google.com:443", "server")
username = flag.String("username", "", "username")
password = flag.String("password", "", "password")
notls = flag.Bool("notls", false, "No TLS")
debug = flag.Bool("debug", false, "debug output")
google = flag.Bool("google", false, "use google TTS engine")
)
func main() {
var speech Speaker
if *google {
speech = NewGoogleSpeech()
} else {
speech = NewOSXSpeech()
}
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "usage: example [options]\n")
flag.PrintDefaults()
os.Exit(2)
}
flag.Parse()
if *username == "" || *password == "" {
flag.Usage()
}
var talk *xmpp.Client
var err error
if *notls {
talk, err = xmpp.NewClientNoTLS(*server, *username, *password, *debug)
} else {
talk, err = xmpp.NewClient(*server, *username, *password, *debug)
}
if err != nil {
log.Fatal(err)
}
go func() {
for {
chat, err := talk.Recv()
if err != nil {
log.Fatal(err)
}
switch v := chat.(type) {
case xmpp.Chat:
if len(v.Text) > 0 {
fmt.Println(v.Remote, v.Text)
speech.Speech(v.Text)
}
case xmpp.Presence:
fmt.Println(v.From, v.Show)
}
}
}()
for {
in := bufio.NewReader(os.Stdin)
line, err := in.ReadString('\n')
if err != nil {
continue
}
line = strings.TrimRight(line, "\n")
tokens := strings.SplitN(line, " ", 2)
if len(tokens) == 2 {
talk.Send(xmpp.Chat{Remote: tokens[0], Type: "chat", Text: tokens[1]})
}
}
}
まとめ
以下のような要素で実現されていますー。
- 基本はmattnさんのexampleにしゃべる機能をつけました。
- HTTPリクエストでユーザーエージェントの書き換え。
- 子プロセスにパイプでデータを流し込む。
パイプの受け渡しをgoroutineでこんなに簡単に書けるんですねー。