diff --git a/README.md b/README.md index 8d0adb7..f5d8407 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,7 @@ docker run -itd --restart=always --name tgbot -p 8443:8443 \ -v $PWD/conf:/bot/conf \ coolrc/tgbot ``` + +# TODO + +[] 增加语音支持 diff --git a/conf/config.json b/conf/config.json index 0ffd08c..75dded1 100644 --- a/conf/config.json +++ b/conf/config.json @@ -1,6 +1,9 @@ { "hook": "https://bot.coolrc.me:8443/", "token": "775353elKLd9XXhT-BN7GnPjc", - "tuling": "1cac81900f8e16b079" + "tuling": "1cac81900f8e16b079", + "projectid": "newagent-8c7ad", + "jsonfile": "conf/newagent-8c7ad-b39848d5c5ce.json", + "lang": "zh-CN" } diff --git a/conf/df.json b/conf/df.json deleted file mode 100644 index 5e5aaac..0000000 --- a/conf/df.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "projectid": "newagent-8c7ad", - "jsonfile": "conf/newagent-8c7ad-b39848d5c5ce.json", - "lang": "zh-CN", - "timezone":"Asia/Hong_Kong" -} diff --git a/config/config.go b/config/config.go index 57a7939..6c9dd90 100644 --- a/config/config.go +++ b/config/config.go @@ -9,14 +9,15 @@ import ( ) type Conf struct { - Hook string `json:"hook"` - Token string `json:"token"` - Tuling string `json:"tuling"` + Hook string `json:"hook"` + Token string `json:"token"` + Tuling string `json:"tuling"` + Projectid string `json:"projectid"` + Jsonfile string `json:"jsonfile"` + Lang string `json:"lang"` } -var Token string -var Webhook string -var Tuling_token string +var Token, Webhook, Tuling_token, Projectid, Jsonfile, Lang string func ReadConf() { var Config Conf @@ -32,4 +33,7 @@ func ReadConf() { Token = Config.Token Webhook = Config.Hook Tuling_token = Config.Tuling + Projectid = Config.Projectid + Jsonfile = Config.Jsonfile + Lang = Config.Lang } diff --git a/dialogflow/df.go b/dialogflow/df.go index d04f76a..46f240f 100644 --- a/dialogflow/df.go +++ b/dialogflow/df.go @@ -1,112 +1,147 @@ -package dialog_flow +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package dialogflow + +// [START import_libraries] import ( "context" "fmt" - "log" "strconv" - "encoding/json" - "os" dialogflow "cloud.google.com/go/dialogflow/apiv2" - "github.com/golang/protobuf/ptypes/struct" "google.golang.org/api/option" + "github.com/golang/protobuf/ptypes/struct" dialogflowpb "google.golang.org/genproto/googleapis/cloud/dialogflow/v2" ) -type DialogflowConfig struct { - ProjectID string `json:"projectid"` - AuthJSONFilePath string `json:"jsonfile"` - Lang string `json:"lang` - TimeZone string `json:"timezone"` -} - - -// DialogflowProcessor has all the information for connecting with Dialogflow -type DialogflowProcessor struct { - projectID string - authJSONFilePath string - lang string - timeZone string - sessionClient *dialogflow.SessionsClient - ctx context.Context +//DfApi : dialogflow instance +type DfApi struct { + operation string + projectID string + languageCode string + sessionClient *dialogflow.SessionsClient + ctx context.Context + sessionPath string } -// NLPResponse is the struct for the response type NLPResponse struct { Intent string `json:"intent"` Confidence float32 `json:"confidence"` - Result string `json:"result"` + Result string `json:"result"` Entities map[string]string `json:"entities"` } -var dp DialogflowProcessor +// [END import_libraries] +//NewDfApi return new DfApi +func NewDfApi(projectid string, languagecode string, authJSONFilePath string) *DfApi { + dfapi := new(DfApi) + dfapi.operation = "text" //@TODO: add more type + dfapi.projectID = projectid + dfapi.languageCode = languagecode + dfapi.ctx = context.Background() -func init() { - configFile, err := os.Open("conf/df.json") -if err != nil { - fmt.Printf("opening config file", err.Error()) -} - var DfConfig DialogflowConfig - jsonParser := json.NewDecoder(configFile) -if err = jsonParser.Decode(&DfConfig); err != nil { - fmt.Printf("parsing config file", err.Error()) + sessionClient, _ := dialogflow.NewSessionsClient(dfapi.ctx, option.WithCredentialsFile(authJSONFilePath)) + dfapi.sessionClient = sessionClient + return dfapi } - dp.init(DfConfig.ProjectID, DfConfig.AuthJSONFilePath, DfConfig.Lang, DfConfig.TimeZone) +//GetMsg :get response from dialogflow +func (dfapi *DfApi) GetMsg(input string, userid string) (string, string) { + // flag.Usage = func() { + // fmt.Fprintf(os.Stderr, "Usage: %s -project-id -session-id -language-code \n", filepath.Base(os.Args[0])) + // fmt.Fprintf(os.Stderr, " must be your Google Cloud Platform project id\n") + // fmt.Fprintf(os.Stderr, " must be a Dialogflow session ID\n") + // fmt.Fprintf(os.Stderr, " must be a language code from https://dialogflow.com/docs/reference/language; defaults to en\n") + // fmt.Fprintf(os.Stderr, " must be one of text, audio, stream\n") + // fmt.Fprintf(os.Stderr, " can be a series of text inputs if is text, or a path to an audio file if is audio or stream\n") + // } -} + // var projectID, sessionID, languageCode string + // flag.StringVar(&projectID, "project-id", "", "Google Cloud Platform project ID") + // flag.StringVar(&sessionID, "session-id", "", "Dialogflow session ID") + // flag.StringVar(&languageCode, "language-code", "en", "Dialogflow language code from https://dialogflow.com/docs/reference/language; defaults to en") + // flag.Parse() + // args := flag.Args() -func Df(msg string, id string) (string,string){ - // Use NLP - response := dp.processNLP(msg, id) + // if len(args) == 0 { + // flag.Usage() + // os.Exit(1) + // } - return response.Intent,response.Result + // operation := args[0] + // inputs := args[1:] -} + switch dfapi.operation { + case "text": + response := dfapi.DetectIntentText(userid, input) + fmt.Printf("Output: %s\n", response) + return response.Intent,response.Result -func (dp *DialogflowProcessor) init(a ...string) (err error) { - dp.projectID = a[0] - dp.authJSONFilePath = a[1] - dp.lang = a[2] - dp.timeZone = a[3] + //@TODO:增加音频处理 + // case "audio": + // response, err := DetectIntentAudio(dfapi.projectID, dfapi.sessionID, dfapi.audioFile, dfapi.languageCode) + // if err != nil { + // log.Fatal(err) + // } + // fmt.Printf("Response: %s\n", response) + // return response - // Auth process: https://dialogflow.com/docs/reference/v2-auth-setup + // case "stream": + // response, err := DetectIntentStream(dfapi.projectID, dfapi.sessionID, dfapi.audioFile, dfapi.languageCode) + // if err != nil { + // log.Fatal(err) + // } + // fmt.Printf("Response: %s\n", response) + // return response - dp.ctx = context.Background() - sessionClient, err := dialogflow.NewSessionsClient(dp.ctx, option.WithCredentialsFile(dp.authJSONFilePath)) - if err != nil { - log.Fatal("Error in auth with Dialogflow") + default: + response := dfapi.DetectIntentText(userid, input) + fmt.Printf("Output: %s\n", response) + return response.Intent,response.Result } - dp.sessionClient = sessionClient - - return } -func (dp *DialogflowProcessor) processNLP(rawMessage string, username string) (r NLPResponse) { - sessionID := username - request := dialogflowpb.DetectIntentRequest{ - Session: fmt.Sprintf("projects/%s/agent/sessions/%s", dp.projectID, sessionID), - QueryInput: &dialogflowpb.QueryInput{ - Input: &dialogflowpb.QueryInput_Text{ - Text: &dialogflowpb.TextInput{ - Text: rawMessage, - LanguageCode: dp.lang, - }, - }, - }, - QueryParams: &dialogflowpb.QueryParameters{ - TimeZone: dp.timeZone, - }, - } - response, err := dp.sessionClient.DetectIntent(dp.ctx, &request) +// [START dialogflow_detect_intent_text] +func (dfapi *DfApi) DetectIntentText(userid, input string) (r NLPResponse) { + // ctx := context.Background() + + // sessionClient, err := dialogflow.NewSessionsClient(ctx) + // if err != nil { + // return "", err + // } + // defer sessionClient.Close() + + // if projectID == "" || sessionID == "" { + // return "", errors.New(fmt.Sprintf("Received empty project (%s) or session (%s)", projectID, sessionID)) + // } + + // sessionPath := fmt.Sprintf("projects/%s/agent/sessions/%s", projectID, sessionID) + dfapi.sessionPath = fmt.Sprintf("projects/%s/agent/sessions/%s", dfapi.projectID, userid) + textInput := dialogflowpb.TextInput{Text: input, LanguageCode: dfapi.languageCode} + queryTextInput := dialogflowpb.QueryInput_Text{Text: &textInput} + queryInput := dialogflowpb.QueryInput{Input: &queryTextInput} + request := dialogflowpb.DetectIntentRequest{Session: dfapi.sessionPath, QueryInput: &queryInput} + + response, err := dfapi.sessionClient.DetectIntent(dfapi.ctx, &request) if err != nil { - log.Fatalf("Error in communication with Dialogflow %s", err.Error()) return } + queryResult := response.GetQueryResult() if queryResult.Intent != nil { r.Intent = queryResult.Intent.DisplayName @@ -125,6 +160,136 @@ func (dp *DialogflowProcessor) processNLP(rawMessage string, username string) (r return } +// [END dialogflow_detect_intent_text] + +// [START dialogflow_detect_intent_audio] + +// func DetectIntentAudio(projectID, sessionID, audioFile, languageCode string) (string, error) { +// ctx := context.Background() + +// sessionClient, err := dialogflow.NewSessionsClient(ctx) +// if err != nil { +// return "", err +// } +// defer sessionClient.Close() + +// if projectID == "" || sessionID == "" { +// return "", errors.New(fmt.Sprintf("Received empty project (%s) or session (%s)", projectID, sessionID)) +// } + +// sessionPath := fmt.Sprintf("projects/%s/agent/sessions/%s", projectID, sessionID) + +// // In this example, we hard code the encoding and sample rate for simplicity. +// audioConfig := dialogflowpb.InputAudioConfig{AudioEncoding: dialogflowpb.AudioEncoding_AUDIO_ENCODING_LINEAR_16, SampleRateHertz: 16000, LanguageCode: languageCode} + +// queryAudioInput := dialogflowpb.QueryInput_AudioConfig{AudioConfig: &audioConfig} + +// audioBytes, err := ioutil.ReadFile(audioFile) +// if err != nil { +// return "", err +// } + +// queryInput := dialogflowpb.QueryInput{Input: &queryAudioInput} +// request := dialogflowpb.DetectIntentRequest{Session: sessionPath, QueryInput: &queryInput, InputAudio: audioBytes} + +// response, err := sessionClient.DetectIntent(ctx, &request) +// if err != nil { +// return "", err +// } + +// queryResult := response.GetQueryResult() +// fulfillmentText := queryResult.GetFulfillmentText() +// return fulfillmentText, nil +// } + +// // [END dialogflow_detect_intent_audio] + +// // [START dialogflow_detect_intent_streaming] +// func DetectIntentStream(projectID, sessionID, audioFile, languageCode string) (string, error) { +// ctx := context.Background() + +// sessionClient, err := dialogflow.NewSessionsClient(ctx) +// if err != nil { +// return "", err +// } +// defer sessionClient.Close() + +// if projectID == "" || sessionID == "" { +// return "", errors.New(fmt.Sprintf("Received empty project (%s) or session (%s)", projectID, sessionID)) +// } + +// sessionPath := fmt.Sprintf("projects/%s/agent/sessions/%s", projectID, sessionID) + +// // In this example, we hard code the encoding and sample rate for simplicity. +// audioConfig := dialogflowpb.InputAudioConfig{AudioEncoding: dialogflowpb.AudioEncoding_AUDIO_ENCODING_LINEAR_16, SampleRateHertz: 16000, LanguageCode: languageCode} + +// queryAudioInput := dialogflowpb.QueryInput_AudioConfig{AudioConfig: &audioConfig} + +// queryInput := dialogflowpb.QueryInput{Input: &queryAudioInput} + +// streamer, err := sessionClient.StreamingDetectIntent(ctx) +// if err != nil { +// return "", err +// } + +// f, err := os.Open(audioFile) +// if err != nil { +// return "", err +// } + +// defer f.Close() + +// go func() { +// audioBytes := make([]byte, 1024) + +// request := dialogflowpb.StreamingDetectIntentRequest{Session: sessionPath, QueryInput: &queryInput} +// err = streamer.Send(&request) +// if err != nil { +// log.Fatal(err) +// } + +// for { +// _, err := f.Read(audioBytes) +// if err == io.EOF { +// streamer.CloseSend() +// break +// } +// if err != nil { +// log.Fatal(err) +// } + +// request = dialogflowpb.StreamingDetectIntentRequest{InputAudio: audioBytes} +// err = streamer.Send(&request) +// if err != nil { +// log.Fatal(err) +// } +// } +// }() + +// var queryResult *dialogflowpb.QueryResult + +// for { +// response, err := streamer.Recv() +// if err == io.EOF { +// break +// } +// if err != nil { +// log.Fatal(err) +// } + +// recognitionResult := response.GetRecognitionResult() +// transcript := recognitionResult.GetTranscript() +// log.Printf("Recognition transcript: %s\n", transcript) + +// queryResult = response.GetQueryResult() +// } + +// fulfillmentText := queryResult.GetFulfillmentText() +// return fulfillmentText, nil +// } + +// [END dialogflow_detect_intent_streaming] + func extractDialogflowEntities(p *structpb.Value) (extractedEntity string) { kind := p.GetKind() switch kind.(type) { @@ -161,4 +326,4 @@ func extractDialogflowEntities(p *structpb.Value) (extractedEntity string) { default: return "" } -} +} \ No newline at end of file diff --git a/handle/handle.go b/handle/handle.go index 1d80c75..3b986a6 100644 --- a/handle/handle.go +++ b/handle/handle.go @@ -15,9 +15,10 @@ func Handle(updates *tgbotapi.UpdatesChannel,bot *tgbotapi.BotAPI) { //init Tuling := tuling.NewApi(config.Tuling_token) + DfApi := df.NewDfApi(config.Projectid,config.Lang,config.Jsonfile) for update := range *updates { //消息处理 - log.Printf("%+v\n", update) + //log.Printf("%+v\n", update) msg := tgbotapi.NewMessage(update.Message.Chat.ID, "") if update.Message.IsCommand() { //解析命令 @@ -33,11 +34,14 @@ func Handle(updates *tgbotapi.UpdatesChannel,bot *tgbotapi.BotAPI) { } } else { - intent,dfmsg :=df.Df(update.Message.Text, fmt.Sprintf("%d", update.Message.Chat.ID)) + intent,dfmsg :=DfApi.GetMsg(update.Message.Text, fmt.Sprintf("%d", update.Message.Chat.ID)) + fmt.Print("df returns :\n",intent) if intent != "Default Fallback Intent" { - msg.Text = dfmsg + msg.Text = dfmsg + fmt.Print("df returns :\n",msg.Text) } else { - msg.Text = Tuling.GetMsg(update.Message.Text, fmt.Sprintf("%d", update.Message.Chat.ID)) + msg.Text = Tuling.GetMsg(update.Message.Text, fmt.Sprintf("%d", update.Message.Chat.ID)) + fmt.Print("tuling returns :\n",msg.Text) } }