From 1bf6240808b9f5dce8ea66587121065c13de0f2b Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Sat, 12 Oct 2024 07:30:17 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20change=20user=20workflow,=20add?= =?UTF-8?q?=20RecipientNickname=20RemindMessage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/handler/line.go | 31 ++++++++++++++++++++++++++++--- pkg/model/user.go | 10 ++++++---- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/pkg/handler/line.go b/pkg/handler/line.go index 51e8248..3252e92 100644 --- a/pkg/handler/line.go +++ b/pkg/handler/line.go @@ -95,7 +95,7 @@ func (l *LINEWebhookHandler) HandleFollowEvent(event webhook.FollowEvent) { l.lineBotSvc.ReplyTextMessage(event.ReplyToken, "Failed to add you as a friend. Please try again later.") return } - l.lineBotSvc.ReplyTextMessage(event.ReplyToken, "友達登録ありがとうございます!\n\nまずは電話対象者のニックネームを入力してください") + l.lineBotSvc.ReplyTextMessage(event.ReplyToken, "友達登録ありがとうございます!\n\nまずはあなたのニックネームを入力してください") } func (l *LINEWebhookHandler) HandleUnfollowEvent(event webhook.UnfollowEvent) { @@ -125,13 +125,22 @@ func (l *LINEWebhookHandler) HandleMessageEvent(event webhook.MessageEvent) { doc.DataTo(&userdata) if userdata.Target.Nickname == "" { - event.Message.GetType() userdata.Target.Nickname = txtMsg.Text _, err := ref.Set(ctx, userdata) if err != nil { slog.Error("failed to set user", err) return } + l.lineBotSvc.ReplyTextMessage(event.ReplyToken, "次に相手のニックネームを入力してください") + return + } + if userdata.Target.RecipientNickname == "" { + userdata.Target.RecipientNickname = txtMsg.Text + _, err := ref.Set(ctx, userdata) + if err != nil { + slog.Error("failed to set user", err) + return + } l.lineBotSvc.ReplyTextMessage(event.ReplyToken, "次に電話番号を入力してください (例: 09012345678)") return } @@ -164,8 +173,24 @@ func (l *LINEWebhookHandler) HandleMessageEvent(event webhook.MessageEvent) { slog.Error("failed to set user", err) return } - l.lineBotSvc.ReplyTextMessage(event.ReplyToken, "登録が完了しました!") + l.lineBotSvc.ReplyTextMessage(event.ReplyToken, "最後にリマインドメッセージがある場合は入力してください。ない場合は「なし」と入力してください") + return + } + if userdata.Target.RemindMessage == "" && !userdata.Target.Confirm { + if txtMsg.Text == "なし" { + userdata.Target.Confirm = true + } else { + userdata.Target.RemindMessage = txtMsg.Text + } + _, err := ref.Set(ctx, userdata) + if err != nil { + slog.Error("failed to set user", err) + return + } + l.lineBotSvc.ReplyTextMessage(event.ReplyToken, "登録完了しました!") + return } + l.lineBotSvc.ReplyTextMessage(event.ReplyToken, "登録が完了しています!") } func (l *LINEWebhookHandler) HandleLeaveEvent(event webhook.UnfollowEvent) { diff --git a/pkg/model/user.go b/pkg/model/user.go index 3d7e65d..c7efd36 100644 --- a/pkg/model/user.go +++ b/pkg/model/user.go @@ -11,8 +11,10 @@ type User struct { type PhoneNumber string type Target struct { - Nickname string `firestore:"nickname"` - Phone PhoneNumber `firestore:"phone_number"` - CallTime string `firestore:"call_time"` // 12:00 - Confirm bool `firestore:"confirm"` + Nickname string `firestore:"nickname"` + RecipientNickname string `firestore:"r_nickname"` + Phone PhoneNumber `firestore:"phone_number"` + CallTime string `firestore:"call_time"` // 12:00 + RemindMessage string `firestore:"remind_message"` + Confirm bool `firestore:"confirm"` } From d30c3900d6baf56b1eaa81d414e64f6cacc782ef Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Sat, 12 Oct 2024 07:46:50 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20vonage=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/service/vonage/vonage.go | 102 +++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 pkg/service/vonage/vonage.go diff --git a/pkg/service/vonage/vonage.go b/pkg/service/vonage/vonage.go new file mode 100644 index 0000000..2636747 --- /dev/null +++ b/pkg/service/vonage/vonage.go @@ -0,0 +1,102 @@ +package vonage + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/a-company/yoriai-backend/pkg/config" + "io" + "log/slog" + "net/http" +) + +const ( + API_URL = "https://studio-api-us.ai.vonage.com/telephony/make-call" +) + +type VonageService struct { + conf config.VonageConfig +} + +func NewVonage() *VonageService { + return &VonageService{ + conf: config.Config.VonageConfig, + } +} + +type PhoneAPIInput struct { + PhoneNumber string + ReceiverName string + CallerName string + RemindMessage string +} + +func (s VonageService) CallPhoneAPI(input PhoneAPIInput) error { + slog.Info("CallPhoneAPI", slog.Any("input", input)) + requestBody, err := createRequestBody(s.conf.AgentID, input) + if err != nil { + return err + } + + responseBody, err := sendApiRequest(requestBody, s.conf.VgaiKey) + if err != nil { + return err + } + + slog.Info("Response Body:", slog.String("response", responseBody)) + return nil +} + +func createRequestBody(agentId string, input PhoneAPIInput) ([]byte, error) { + requestBody, err := json.Marshal(map[string]interface{}{ + "agent_id": agentId, + "to": input.PhoneNumber, + "session_parameters": []map[string]string{ + { + "name": "RECEIVER_NAME", + "value": input.ReceiverName, + }, + { + "name": "CALLER_NAME", + "value": input.CallerName, + }, + { + "name": "REMIND_MESSAGE", + "value": input.RemindMessage, + }, + }, + }) + + if err != nil { + fmt.Println("Error marshalling JSON:", err) + return nil, err + } + return requestBody, nil +} + +func sendApiRequest(requestBody []byte, XKey string) (string, error) { + req, err := http.NewRequest("POST", API_URL, bytes.NewBuffer(requestBody)) + if err != nil { + slog.Error("Error creating request:", err) + return "", err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Vgai-Key", XKey) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + slog.Error("Error sending request:", err) + return "", err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + slog.Error("Error reading response body", err) + return "", err + } + + return string(body), nil +} From d076482229421ff50758f22c72c23f9eef2c1995 Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Sat, 12 Oct 2024 07:47:17 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=90=9B=20change=20phone=20number=20on?= =?UTF-8?q?=20registration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/handler/line.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/handler/line.go b/pkg/handler/line.go index 3252e92..df79ba1 100644 --- a/pkg/handler/line.go +++ b/pkg/handler/line.go @@ -152,6 +152,8 @@ func (l *LINEWebhookHandler) HandleMessageEvent(event webhook.MessageEvent) { l.lineBotSvc.ReplyTextMessage(event.ReplyToken, "電話番号の形式が正しくありません。もう一度入力してください。(例: 09012345678)") return } + // 先頭の0を81に変換 + userdata.Target.Phone = model.PhoneNumber("81" + string(userdata.Target.Phone)[1:]) _, err := ref.Set(ctx, userdata) if err != nil { slog.Error("failed to set user", err) From 6e0b4cf122ce0415727264d3bd1eb2a4cbcd1dff Mon Sep 17 00:00:00 2001 From: Shion Ichikawa Date: Sat, 12 Oct 2024 07:48:10 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=E2=9C=A8=20trigger=20phone=20call?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/handler/invoke.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/pkg/handler/invoke.go b/pkg/handler/invoke.go index 3fc6ecd..a836576 100644 --- a/pkg/handler/invoke.go +++ b/pkg/handler/invoke.go @@ -3,6 +3,8 @@ package handler import ( "cloud.google.com/go/firestore" "fmt" + "github.com/a-company/yoriai-backend/pkg/model" + "github.com/a-company/yoriai-backend/pkg/service/vonage" "github.com/gin-gonic/gin" "log/slog" "time" @@ -22,21 +24,32 @@ func NewInvokeHandler( func (h *CallInvoke) Handle(c *gin.Context) { // get time - timeVal := fmt.Sprintf("%2d:%2d", time.Now().Hour(), 0) + timeVal := fmt.Sprintf("%02d:%02d", time.Now().Hour(), 0) + slog.Info("invoke call", slog.String("time", timeVal)) res := h.fs.Collection("users").Where("call_time", "==", timeVal).Documents(c) if res == nil { c.JSON(400, gin.H{"error": "no data"}) return } + + v := vonage.NewVonage() for { doc, err := res.Next() if err != nil { break } - userdata := doc.Data() + userdata := model.User{} + doc.DataTo(&userdata) slog.Info("invoke call on user", slog.Any("data", userdata)) // vonage callを発火 + v.CallPhoneAPI( + vonage.PhoneAPIInput{ + PhoneNumber: string(userdata.Phone), + ReceiverName: userdata.Nickname, + CallerName: "Yoriai", + RemindMessage: "今日の通話の時間です", + }) } c.JSON(200, gin.H{ "message": "success",