Skip to content

Commit

Permalink
add flows
Browse files Browse the repository at this point in the history
  • Loading branch information
piusalfred committed Jan 20, 2024
1 parent 0c2cd8d commit 9707541
Show file tree
Hide file tree
Showing 5 changed files with 349 additions and 7 deletions.
19 changes: 16 additions & 3 deletions base.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,27 @@ type (
}
)

// InteractiveCTAButtonURL sends a CTA button url request to the recipient.
func (c *Client) InteractiveCTAButtonURL(ctx context.Context, params *RequestParams,
request *factories.CTAButtonURLParameters,
) (*whttp.ResponseMessage, error) {
i := factories.NewInteractiveCTAURLButton(request)
message, err := factories.InteractiveMessage(params.Recipient, i)
if err != nil {
return nil, fmt.Errorf("interactive message: %w", err)
}

return c.Send(ctx, fmtParamsToContext(params, nil), message)
}

// RequestLocation sends a location request to the recipient.
// LINK: https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-messages/location-request-messages
func (c *Client) RequestLocation(ctx context.Context, params *LocationRequestParams) (
func (c *Client) RequestLocation(ctx context.Context, params *RequestParams, message string) (
*whttp.ResponseMessage, error,
) {
message := factories.LocationRequestMessage(params.Recipient, params.ReplyID, params.Message)
m := factories.LocationRequestMessage(params.Recipient, params.ReplyID, message)

return c.Send(ctx, nil, message)
return c.Send(ctx, fmtParamsToContext(params, nil), m)
}

func (c *Client) Image(ctx context.Context, params *RequestParams, image *models.Image,
Expand Down
28 changes: 28 additions & 0 deletions examples/base/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package main
import (
"context"
"fmt"
"github.com/piusalfred/whatsapp/pkg/models/factories"
"net/http"
"time"

Expand Down Expand Up @@ -131,6 +132,33 @@ func sendTextMessage(ctx context.Context, request *textRequest) error {

fmt.Printf("\n%+v\n", resp)

params := &whatsapp.RequestParams{
ID: "1234567890",
Metadata: map[string]string{"foo": "bar"},
Recipient: "+255767001828",
ReplyID: "",
}

resp, err = b.RequestLocation(ctx, params, "Where are you mate?")
if err != nil {
return err
}

fmt.Printf("\n%+v\n", resp)

buttonURL, err := b.InteractiveCTAButtonURL(ctx, params, &factories.CTAButtonURLParameters{
DisplayText: "link to github repo",
URL: "https://github.com/piusalfred/whatsapp",
Body: "The Golang client for the WhatsApp Business API offers a rich set of features for building interactive WhatsApp experiences.",
Footer: "You can fork,stargaze and contribute to this repo",
Header: "Hey look",
})
if err != nil {
return err
}

fmt.Printf("\n%+v\n", buttonURL)

return nil
}

Expand Down
243 changes: 243 additions & 0 deletions flows/flows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
/*
* Copyright 2023 Pius Alfred <me.pius1102@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the “Software”), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

// Package flows provides a set of functions for creating and sending FlowJSON messages.
package flows

//{
// "version": "3.1",
// "data_api_version": "3.0",
// "routing_model": {"MY_FIRST_SCREEN": ["MY_SECOND_SCREEN"] },
// "screens": [...]
//}

const (
LowestSupportedVersion = "3.1"
LowestSupportedDataApiVersion = "3.0"
)

type (
Flow struct {
Version string `json:"version,omitempty"`
DataAPIVersion string `json:"data_api_version,omitempty"`
RoutingModel map[string][]string `json:"routing_model,omitempty"`
Screens []*Screen `json:"screens,omitempty"`
}

Screen struct {
ID string `json:"id,omitempty"`
Terminal bool `json:"terminal,omitempty"`
Success bool `json:"success,omitempty"`
Title string `json:"title,omitempty"`
RefreshOnBack bool `json:"refresh_on_back,omitempty"`
Data Data `json:"data,omitempty"`
Layout *Layout `json:"layout,omitempty"`
}

DataValue struct {
Type string `json:"type,omitempty"`
Example string `json:"__example__,omitempty"`
}

Data map[string]DataValue

Layout struct {
Type string `json:"type,omitempty"`
// Children a list of components that are rendered in the layout.
}

// Components
//Text (Heading, Subheading, Caption, Body)
//
//TextEntry
//
//CheckboxGroup
//
//RadioButtonsGroup
//
//Footer
//
//OptIn
//
//Dropdown
//
//EmbeddedLink
//
//DatePicker
//
//Image

TextComponent struct {
Type string `json:"type,omitempty"`
Text string `json:"text,omitempty"`
Visible bool `json:"visible,omitempty"`
FontWeight FontWeight `json:"font-weight,omitempty"`
Strikethrough bool `json:"strikethrough,omitempty"`
}

TextInputComponent struct {
Type string `json:"type,omitempty"`
Label string `json:"label,omitempty"`
InputType string `json:"input-type,omitempty"` //enum {'text','number','email', 'password', 'passcode', 'phone'}
Required bool `json:"required,omitempty"`
MinChars int `json:"min-chars,omitempty"`
MaxChars int `json:"max-chars,omitempty"`
HelperText string `json:"helper-text,omitempty"`
Name string `json:"name,omitempty"`
Visible bool `json:"visible,omitempty"`
OnClickAction *Action `json:"on-click-action,omitempty"`
}

TextAreaComponent struct {
Type string `json:"type,omitempty"`
Label string `json:"label,omitempty"`
Required bool `json:"required,omitempty"`
MaxLength int `json:"max-length,omitempty"`
Name string `json:"name,omitempty"`
HelperText string `json:"helper-text,omitempty"`
Enabled bool `json:"enabled,omitempty"`
Visible bool `json:"visible,omitempty"`
OnClickAction *Action `json:"on-click-action,omitempty"`
}

CheckboxGroupComponent struct {
Type string `json:"type,omitempty"`
DataSource []*DropDownData `json:"data-source,omitempty"`
Name string `json:"name,omitempty"`
MinSelectedItems int `json:"min-selected-items,omitempty"`
MaxSelectedItems int `json:"max-selected-items,omitempty"`
Enabled bool `json:"enabled,omitempty"`
Label string `json:"label,omitempty"`
Required bool `json:"required,omitempty"`
Visible bool `json:"visible,omitempty"`
OnSelectAction *Action `json:"on-select-action,omitempty"`
}

RadioButtonsGroupComponent struct {
Type string `json:"type,omitempty"`
DataSource []*DropDownData `json:"data-source,omitempty"`
Name string `json:"name,omitempty"`
Enabled bool `json:"enabled,omitempty"`
Label string `json:"label,omitempty"`
Required bool `json:"required,omitempty"`
Visible bool `json:"visible,omitempty"`
OnSelectAction *Action `json:"on-select-action,omitempty"`
}

FooterComponent struct {
Type string `json:"type,omitempty"`
Label string `json:"label,omitempty"`
LeftCaption string `json:"left-caption,omitempty"`
CenterCaption string `json:"center-caption,omitempty"`
RightCaption string `json:"right-caption,omitempty"`
Enabled bool `json:"enabled,omitempty"`
OnClickAction *Action `json:"on-click-action,omitempty"`
}

OptInComponent struct {
Type string `json:"type,omitempty"`
Label string `json:"label,omitempty"`
Required bool `json:"required,omitempty"`
Name string `json:"name,omitempty"`
OnClickAction *Action `json:"on-click-action,omitempty"`
Visible bool `json:"visible,omitempty"`
}

DropdownComponent struct {
Type string `json:"type,omitempty"`
Label string `json:"label,omitempty"`
DataSource []*DropDownData `json:"data-source,omitempty"`
Required bool `json:"required,omitempty"`
Enabled bool `json:"enabled,omitempty"`
Visible bool `json:"visible,omitempty"`
OnSelectAction *Action `json:"on-select-action,omitempty"`
}

DropDownData struct {
ID string `json:"id,omitempty"`
Title string `json:"title,omitempty"`
Enabled bool `json:"enabled,omitempty"`
Description string `json:"description,omitempty"`
Metadata string `json:"metadata,omitempty"`
}

EmbeddedLinkComponent struct {
Type string `json:"type,omitempty"`
Text string `json:"text,omitempty"`
OnClickAction *Action `json:"on-click-action,omitempty"`
Visible bool `json:"visible,omitempty"`
}

DatePickerComponent struct {
Type string `json:"type,omitempty"`
Label string `json:"label,omitempty"`
MinDate string `json:"min-date,omitempty"`
MaxDate string `json:"max-date,omitempty"`
Name string `json:"name,omitempty"`
Unavailable []string `json:"unavailable-dates,omitempty"`
Visible bool `json:"visible,omitempty"`
HelperText string `json:"helper-text,omitempty"`
Enabled bool `json:"enabled,omitempty"`
OnSelect *Action `json:"on-select-action,omitempty"`
}

ImageComponent struct {
Type string `json:"type,omitempty"`
Src string `json:"src,omitempty"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
ScaleType string `json:"scale-type,omitempty"`
AspectRatio float64 `json:"aspect-ratio,omitempty"`
AltText string `json:"alt-text,omitempty"`
}

Action struct {
Name string `json:"name,omitempty"`
}
)

type ImageScaleType string

const (
ScaleTypeCover ImageScaleType = "cover"
ScaleTypeContain ImageScaleType = "contain"
)

const (
ComponentTypeFooter = "Footer"
ComponentTypeOptIn = "OptIn"
)

type TextComponentType string

const (
TextComponentTypeHeading TextComponentType = "TextHeading"
TextComponentTypeSubheading TextComponentType = "TextSubheading"
TextComponentTypeBody TextComponentType = "TextBody"
TextComponentTypeCaption TextComponentType = "TextCaption"
)

type FontWeight string

const (
FontWeightBold FontWeight = "bold"
FontWeightItalic FontWeight = "italic"
FontWeightBoldItalic FontWeight = "bold_italic"
FontWeightNormal FontWeight = "normal"
)
55 changes: 55 additions & 0 deletions flows/flows_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2023 Pius Alfred <me.pius1102@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the “Software”), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package flows

type (
// ListFlowsResponse represents the response from the list flows endpoint.
ListFlowsResponse struct {
Data []*Details `json:"data,omitempty"`
Paging *Paging `json:"paging,omitempty"`
}

Details struct {
Name string `json:"name,omitempty"`
Status string `json:"status,omitempty"`
Categories []string `json:"categories,omitempty"`
ValidationErrors []*ValidationError `json:"validation_errors,omitempty"`
ID string `json:"id,omitempty"`
}

ValidationError struct {
Error string `json:"error,omitempty"`
ErrorType string `json:"error_type,omitempty"`
Message string `json:"message,omitempty"`
LineStart int `json:"line_start,omitempty"`
LineEnd int `json:"line_end,omitempty"`
ColumnStart int `json:"column_start,omitempty"`
ColumnEnd int `json:"column_end,omitempty"`
}

Paging struct {
Cursors *Cursors `json:"cursors,omitempty"`
}

Cursors struct {
Before string `json:"before,omitempty"`
After string `json:"after,omitempty"`
}
)
11 changes: 7 additions & 4 deletions pkg/models/factories/interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,19 @@ func LocationRequestMessage(recipient, reply, message string) *models.Message {
Header: nil,
}

return &models.Message{
Context: &models.Context{
MessageID: reply,
},
m := &models.Message{
Product: MessagingProductWhatsApp,
RecipientType: RecipientTypeIndividual,
Type: "interactive",
To: recipient,
Interactive: i,
}

if reply != "" {
m.Context = &models.Context{MessageID: reply}
}

return m
}

func NewInteractiveCTAURLButton(parameters *CTAButtonURLParameters) *models.Interactive {
Expand Down

0 comments on commit 9707541

Please sign in to comment.