package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "log" "net/http" "strconv" "strings" "time" "github.com/hamcha/clessy/tg" ) type QRequest struct { SessionID string `json:"sessionId"` Query string `json:"query"` Language string `json:"lang"` } 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 map[string]string `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() { if strings.HasPrefix(*talktoken, "@") { data, err := ioutil.ReadFile((*talktoken)[1:]) if err != nil { panic(err) } *talktoken = strings.TrimSpace(string(data)) } if *talktoken == "" { panic(fmt.Errorf("API token for api.ai must be provided! (provide it or --disable talk)")) } } func talk(broker *tg.Broker, update tg.APIMessage) { // Must be a text message if update.Text == nil { return } text := *(update.Text) quoted := false // Make sure it's aimed at Clessy if strings.Index(text, "@"+*botname) >= 0 { // @maudbot text = strings.Replace(text, "@"+*botname, "", 1) } else if idx := strings.Index(strings.ToLower(text), "clessy"); idx == 0 && len(text) > 7 { // Clessy, 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 quoted = true } 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, "ERRORE! @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, "ERRORE! @hamcha controlla la console!", &update.MessageID) return } if !quoted { broker.SendChatAction(update.Chat, tg.ActionTyping) } 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, "ERRORE! @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, "ERRORE! @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, "ERRORE! @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") { reply = metaforaAPI() } return } if record.Result.Action != "" && !record.Result.ActionIncomplete { switch record.Result.Action { case "viaggi": // Check that both parameters are given if record.Result.Parameters["from-city"] != "" && record.Result.Parameters["to-city"] != "" { data, err := viaggiAPI(record.Result.Parameters["from-city"], record.Result.Parameters["to-city"]) if err != nil { log.Printf("[talk] viaggi failed:\n%s\n", record.Result.Action) broker.SendTextMessage(update.Chat, "ERRORE! @hamcha controlla la console!", &update.MessageID) return } reply += "\n" + data } case "search": result, err := searchAPI(record.Result.Parameters["search-item"]) if err != nil { log.Printf("[talk] search failed:\n%s\n", record.Result.Action) broker.SendTextMessage(update.Chat, "ERRORE! @hamcha controlla la console!", &update.MessageID) return } if result.None { broker.SendTextMessage(update.Chat, "Non ho trovato nulla, mi spiace :(", &update.MessageID) return } reply = fmt.Sprintf("%s\n%s", reply, result.Name) if result.Description != nil { reply += "\n" + *(result.Description) } if len(result.Alternatives) > 0 { reply += "\nAlternativamente:\n - " + strings.Join(result.Alternatives, "\n - ") } case "input.unknown": // Don't reply if quoted (probably just a comment) if quoted { return } default: log.Printf("[talk] Unknown action called:\n%s\n", record.Result.Action) } } broker.SendTextMessage(update.Chat, reply, &update.MessageID) }