mods: Add api.ai module (replaces wit.ai WIP module)

This commit is contained in:
Hamcha 2017-05-09 13:34:52 +02:00
parent 1ae8af29ce
commit 318a3f8d16
2 changed files with 143 additions and 46 deletions

View file

@ -43,11 +43,10 @@ var mods = map[string]Mod{
OnInit: initproverbio,
OnMessage: proverbio,
},
/*
"talk": {
OnInit: inittalk,
OnMessage: talk,
},*/
"talk": {
OnInit: inittalk,
OnMessage: talk,
},
"stt": {
OnInit: initstt,
OnMessage: stt,
@ -96,7 +95,7 @@ var impact *string
var gillmt *string
var sourcesans *string
var proverbi *string
var wittoken *string
var talktoken *string
var gapifile *string
func main() {
@ -108,7 +107,7 @@ func main() {
macropath = flag.String("macropath", "macros.json", "Path to macros db (JSON)")
remindpath = flag.String("remindpath", "reminders.json", "Path to reminder db (JSON)")
proverbi = flag.String("proverbi", "proverbi.txt", "Path to proverbi pairs (separated by /)")
wittoken = flag.String("wit", "", "Wit.ai token")
talktoken = flag.String("apiai", "", "api.ai token")
gapifile = flag.String("gapi", "gapi.json", "Google API Service Credentials file")
disable := flag.String("disable", "", "Blacklist mods (separated by comma)")
enable := flag.String("enable", "", "Whitelist mods (separated by comma)")

View file

@ -1,58 +1,156 @@
package main
import (
"errors"
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"strings"
"time"
"github.com/hamcha/clessy/tg"
"github.com/kurrik/witgo/v1/witgo"
)
type ClessyHandler struct {
type QRequest struct {
SessionID string `json:"sessionId"`
Query string `json:"query"`
Language string `json:"lang"`
}
var wit *witgo.Witgo
var witHandler ClessyHandler
type QResponse struct {
ID string `json:"id"`
Timestamp time.Time `json:"timestamp"`
Result struct {
Source string `json:"source"`
ResolvedQuery string `json:"resolvedQuery"`
Action string `json:"action"`
ActionIncomplete bool `json:"actionIncomplete"`
Parameters struct {
Name string `json:"name"`
} `json:"parameters"`
Contexts []struct {
Name string `json:"name"`
Parameters struct {
Name string `json:"name"`
} `json:"parameters"`
Lifespan int `json:"lifespan"`
} `json:"contexts"`
Metadata struct {
IntentID string `json:"intentId"`
IntentName string `json:"intentName"`
} `json:"metadata"`
Fulfillment struct {
Speech string `json:"speech"`
} `json:"fulfillment"`
} `json:"result"`
Status struct {
Code int `json:"code"`
ErrorType string `json:"errorType"`
} `json:"status"`
}
const talkBaseURL = "https://api.api.ai/v1"
func inittalk() {
// Disable if token is not provided
if *wittoken == "" {
panic(errors.New("No or empty wit token provided (-wit)"))
if *talktoken == "" {
panic(fmt.Errorf("API token for api.ai must be provided! (provide it or --disable talk)"))
}
witgo.NewWitgo(witgo.NewClient(*wittoken), &witHandler)
}
func talk(broker *tg.Broker, update tg.APIMessage) {
}
func (h *ClessyHandler) Action(session *witgo.Session, action string) (response *witgo.Session, err error) {
response = session
response.Context.Set("forecast", "sunny")
return
}
func (h *ClessyHandler) Say(session *witgo.Session, msg string) (response *witgo.Session, err error) {
response = session
fmt.Printf("< %v\n", msg)
return
}
func (h *ClessyHandler) Merge(session *witgo.Session, entities witgo.EntityMap) (response *witgo.Session, err error) {
var (
value string
)
response = session
if value, err = entities.FirstEntityValue("location"); err != nil {
response.Context = witgo.Context{}
err = nil
} else {
response.Context.Merge(witgo.Context{
"loc": value,
})
// Must be a text message
if update.Text == nil {
return
}
return
}
text := *(update.Text)
func (h *ClessyHandler) Error(session *witgo.Session, msg string) {
// Make sure it's aimed at Clessy
if strings.Index(text, "@"+*botname) >= 0 {
// @maudbot <something>
text = strings.Replace(text, "@"+*botname, "", 1)
} else if idx := strings.Index(strings.ToLower(text), "clessy"); idx == 0 && len(text) > 7 {
// Clessy, <something>
text = strings.TrimLeft(text[len("clessy"):], ":,")
} else if text[0] != '/' && update.ReplyTo != nil && update.ReplyTo.User.Username == *botname {
// Reply to Clessy (previous prompt?) which is not a command (such as unsplash), pass
} else if update.Chat.Username != nil {
// Private chat, pass
} else {
return
}
text = strings.TrimSpace(text)
// Generate unique id for user
id := strconv.FormatInt(update.User.UserID, 36)
if len(id) > 36 {
id = id[:36]
}
// Create POST body
data, err := json.Marshal(QRequest{
SessionID: id,
Query: text,
Language: "it",
})
if err != nil {
log.Printf("[talk] Could not create JSON body: %s\n", err.Error())
broker.SendTextMessage(update.Chat, "<b>ERRORE!</b> @hamcha controlla la console!", &update.MessageID)
return
}
// Build the request
req, err := http.NewRequest("POST", talkBaseURL+"/query?v=20150910", bytes.NewReader(data))
if err != nil {
log.Printf("[talk] Could not create POST request: %s\n", err.Error())
broker.SendTextMessage(update.Chat, "<b>ERRORE!</b> @hamcha controlla la console!", &update.MessageID)
return
}
req.Header.Add("Authorization", "Bearer "+*talktoken)
req.Header.Set("Content-Type", "application/json; charset=utf-8")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("[talk] Request error: %s\n", err.Error())
broker.SendTextMessage(update.Chat, "<b>ERRORE!</b> @hamcha controlla la console!", &update.MessageID)
return
}
defer resp.Body.Close()
var record QResponse
if err := json.NewDecoder(resp.Body).Decode(&record); err != nil {
log.Printf("[talk] Could not decode JSON response: %s\n", err.Error())
broker.SendTextMessage(update.Chat, "<b>ERRORE!</b> @hamcha controlla la console!", &update.MessageID)
return
}
if record.Status.ErrorType != "success" {
body, _ := json.MarshalIndent(record, "", " ")
log.Printf("[talk] Non-success status, full response body follows:\n%s\n", body)
broker.SendTextMessage(update.Chat, "<b>ERRORE!</b> @hamcha controlla la console!", &update.MessageID)
return
}
reply := record.Result.Fulfillment.Speech
// Replace tokens if found
if strings.Index(reply, "$") >= 0 {
reply = strings.Replace(reply, "$name", update.User.FirstName, -1)
}
// Call command if needed
if reply[0] == '#' {
//TODO Find a better way?
if strings.HasPrefix(reply, "#metafora") {
message := "/metafora"
update.Text = &message
metafora(broker, update)
}
return
}
broker.SendTextMessage(update.Chat, reply, &update.MessageID)
}