diff --git a/implementations/go/data/data.go b/implementations/go/data/data.go new file mode 100644 index 0000000..120dd8c --- /dev/null +++ b/implementations/go/data/data.go @@ -0,0 +1,77 @@ +package data + +import ( + "github.com/FBDevCLagos/soccergist/implementations/go/data/football_data" + "github.com/FBDevCLagos/soccergist/implementations/go/data/reddit" +) + +type League interface { + Table() *football_data.LeagueTable + PresentMatchday() int + TotalMatchdays() int + GetMatchdayFixtures(int) *football_data.MatchDayFixtures +} + +type Highlight struct { + Title string + Name string + URLs []string +} + +type LeagueTableTeamInfo struct { + Name, Crest string + Position, Points, MatchPlayed int +} + +func PremierLeagueInfo() League { + competition := football_data.PremierLeague() + l := League(competition) + return l +} + +func FirstFour(table *football_data.LeagueTable) []LeagueTableTeamInfo { + info := []LeagueTableTeamInfo{} + for i, team := range table.Standing { + info = append(info, LeagueTableTeamInfo{ + Position: team.Position, + Name: team.TeamName, + Crest: substitueTeamLogo(team.TeamName), + Points: team.Points, + MatchPlayed: team.PlayedGames, + }) + + if i == 3 { + break + } + } + + return info +} + +func Highlights(after string) (highlights []Highlight) { + posts := reddit.GetHighlightPosts(after) + + for _, post := range posts { + if len(post.URLs) == 0 { + continue + } + + highlights = append(highlights, Highlight{ + Title: post.Title, + Name: post.Name, + URLs: post.URLs, + }) + } + return +} + +var teamsLogo = map[string]string{ + "Manchester City FC": "https://logoeps.com/wp-content/uploads/2011/08/manchester-city-logo-vector.png", + "Manchester United FC": "https://logoeps.com/wp-content/uploads/2011/08/manchester-united-logo-vector.png", + "Liverpool FC": "https://logoeps.com/wp-content/uploads/2011/08/liverpool-logo-vector.png", + "Chelsea FC": "https://logoeps.com/wp-content/uploads/2011/08/chelsea-logo-vector.png", +} + +func substitueTeamLogo(teamName string) string { + return teamsLogo[teamName] +} diff --git a/implementations/go/data/football_data/api.go b/implementations/go/data/football_data/api.go new file mode 100644 index 0000000..d12c522 --- /dev/null +++ b/implementations/go/data/football_data/api.go @@ -0,0 +1,59 @@ +package football_data + +import ( + "encoding/json" + "log" + "net/http" + "strings" + + "github.com/FBDevCLagos/soccergist/implementations/go/utils" +) + +const ( + URL = "https://api.football-data.org/v1/competitions?season=2017" + PremierLeagueSymbol = "PL" +) + +func PremierLeague() *Competition { + req, err := utils.APIRequest(URL, "GET", nil) + if err != nil || req.StatusCode != http.StatusOK { + log.Println("Error occurred in PremierLeague while making request to: ", URL, err) + } + + return filterPremierLeague(req) +} + +func filterPremierLeague(req *http.Response) (premierLeague *Competition) { + var competitions []Competition + err := json.NewDecoder(req.Body).Decode(&competitions) + if err != nil { + log.Println("Error occurred parsing json: ", err) + return + } + + for _, competition := range competitions { + if competition.League == PremierLeagueSymbol { + premierLeague = &competition + break + } + } + return +} + +func fetchLeagueTable(url string) *LeagueTable { + table := &LeagueTable{} + url = strings.Replace(url, "http://", "https://", 1) + + req, err := utils.APIRequest(url, "GET", nil) + if err != nil || req.StatusCode != http.StatusOK { + log.Println("Error occurred in fetchLeagueTable while making request to: ", url, err) + return nil + } + + err = json.NewDecoder(req.Body).Decode(table) + if err != nil { + log.Println("Error occurred parsing leagueTable json: ", err) + return nil + } + return table +} diff --git a/implementations/go/data/football_data/types.go b/implementations/go/data/football_data/types.go new file mode 100644 index 0000000..07cca11 --- /dev/null +++ b/implementations/go/data/football_data/types.go @@ -0,0 +1,157 @@ +package football_data + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "strings" + + "github.com/FBDevCLagos/soccergist/implementations/go/utils" +) + +type Competition struct { + Links struct { + Fixtures struct { + Href string `json:"href"` + } `json:"fixtures"` + LeagueTable struct { + Href string `json:"href"` + } `json:"leagueTable"` + Self struct { + Href string `json:"href"` + } `json:"self"` + Teams struct { + Href string `json:"href"` + } `json:"teams"` + } `json:"_links"` + Caption string `json:"caption"` + CurrentMatchday int `json:"currentMatchday"` + ID int `json:"id"` + LastUpdated string `json:"lastUpdated"` + League string `json:"league"` + NumberOfGames int `json:"numberOfGames"` + NumberOfMatchdays int `json:"numberOfMatchdays"` + NumberOfTeams int `json:"numberOfTeams"` + Year string `json:"year"` +} + +type LeagueTable struct { + Links struct { + Self struct { + Href string `json:"href"` + } `json:"self"` + Competition struct { + Href string `json:"href"` + } `json:"competition"` + } `json:"_links"` + LeagueCaption string `json:"leagueCaption"` + Matchday int `json:"matchday"` + Standing []struct { + Links struct { + Team struct { + Href string `json:"href"` + } `json:"team"` + } `json:"_links"` + Position int `json:"position"` + TeamName string `json:"teamName"` + CrestURI string `json:"crestURI"` + PlayedGames int `json:"playedGames"` + Points int `json:"points"` + Goals int `json:"goals"` + GoalsAgainst int `json:"goalsAgainst"` + GoalDifference int `json:"goalDifference"` + Wins int `json:"wins"` + Draws int `json:"draws"` + Losses int `json:"losses"` + Home struct { + Goals int `json:"goals"` + GoalsAgainst int `json:"goalsAgainst"` + Wins int `json:"wins"` + Draws int `json:"draws"` + Losses int `json:"losses"` + } `json:"home"` + Away struct { + Goals int `json:"goals"` + GoalsAgainst int `json:"goalsAgainst"` + Wins int `json:"wins"` + Draws int `json:"draws"` + Losses int `json:"losses"` + } `json:"away"` + } `json:"standing"` +} + +type MatchDayFixtures struct { + Links struct { + Competition struct { + Href string `json:"href"` + } `json:"competition"` + Self struct { + Href string `json:"href"` + } `json:"self"` + } `json:"_links"` + Count int `json:"count"` + Fixtures []struct { + Links struct { + AwayTeam struct { + Href string `json:"href"` + } `json:"awayTeam"` + Competition struct { + Href string `json:"href"` + } `json:"competition"` + HomeTeam struct { + Href string `json:"href"` + } `json:"homeTeam"` + Self struct { + Href string `json:"href"` + } `json:"self"` + } `json:"_links"` + AwayTeamName string `json:"awayTeamName"` + Date string `json:"date"` + HomeTeamName string `json:"homeTeamName"` + Matchday int `json:"matchday"` + Odds interface{} `json:"odds"` + Result struct { + GoalsAwayTeam int `json:"goalsAwayTeam"` + GoalsHomeTeam int `json:"goalsHomeTeam"` + HalfTime struct { + GoalsAwayTeam int `json:"goalsAwayTeam"` + GoalsHomeTeam int `json:"goalsHomeTeam"` + } `json:"halfTime"` + } `json:"result"` + Status string `json:"status"` + } `json:"fixtures"` +} + +func (c *Competition) Table() *LeagueTable { + url := c.Links.LeagueTable.Href + return fetchLeagueTable(url) +} + +func (c *Competition) PresentMatchday() int { + return c.CurrentMatchday +} + +func (c *Competition) TotalMatchdays() int { + return c.NumberOfMatchdays +} + +func (c *Competition) GetMatchdayFixtures(matchday int) *MatchDayFixtures { + url := fmt.Sprintf("%s?matchday=%d", c.Links.Fixtures.Href, matchday) + matchDayFixtures := &MatchDayFixtures{} + url = strings.Replace(url, "http://", "https://", 1) + + req, err := utils.APIRequest(url, "GET", nil) + if err != nil || req.StatusCode != http.StatusOK { + log.Println("Error occurred in GetMatchdayFixtures while making request to: ", url, err) + return nil + } + + err = json.NewDecoder(req.Body).Decode(matchDayFixtures) + if err != nil { + log.Println("Error occurred parsing json: ", err) + return nil + } + + return matchDayFixtures +} diff --git a/implementations/go/data/reddit/reddit.go b/implementations/go/data/reddit/reddit.go new file mode 100644 index 0000000..a468055 --- /dev/null +++ b/implementations/go/data/reddit/reddit.go @@ -0,0 +1,67 @@ +package reddit + +import ( + "fmt" + "log" + "regexp" + "strings" + + "github.com/turnage/graw/reddit" +) + +const ( + subreddit = "/r/footballhighlights" +) + +var ( + redditBot reddit.Bot + videoURLRegex = regexp.MustCompile(`href="([\w\/:?\.-=]+|[\w\/:\.-]+)"`) +) + +func init() { + var err error + redditBot, err = reddit.NewBotFromAgentFile("reddit.agent", 0) + if err != nil { + log.Fatal("Failed to create bot handle: ", err) + } +} + +type Highlight struct { + *reddit.Post + URLs []string +} + +// GetHighlightPosts returns posts in the highlights subreddit +// Filtering only posts that have 'Premier' league in their title +func GetHighlightPosts(after string) (highlights []*Highlight) { + params := map[string]string{"after": after} + harvest, err := redditBot.ListingWithParams(subreddit, params) + if err != nil { + log.Printf("Failed to fetch: %s %s\n", subreddit, err) + return + } + + for _, post := range harvest.Posts { + if !strings.Contains(post.Title, "Premier") { + continue + } + + highlight := &Highlight{Post: post, URLs: getHighlightVideoURLs(post)} + if len(highlight.URLs) == 0 { + fmt.Println(highlight.Title) + fmt.Println(post.SelfTextHTML) + } + highlights = append(highlights, highlight) + } + + return +} + +func getHighlightVideoURLs(post *reddit.Post) (urls []string) { + videoURLs := videoURLRegex.FindAllStringSubmatch(post.SelfTextHTML, -1) + for _, url := range videoURLs { + // TODO: handle unique videos + urls = append(urls, url[1]) + } + return +} diff --git a/implementations/go/main.go b/implementations/go/main.go new file mode 100644 index 0000000..59ca565 --- /dev/null +++ b/implementations/go/main.go @@ -0,0 +1,139 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "os" + "reflect" + "strings" + + "github.com/FBDevCLagos/soccergist/implementations/go/utils" + + "github.com/julienschmidt/httprouter" +) + +// VerificationToken is the random string entered in the verification prompt when setting up the app on Facbook +// It can be any string provide it matches what you will enter in the setup prompt +const VerificationToken = "bots are awesome" + +var ( + AccessToken = os.Getenv("ACCESS_TOKEN") + fbURL = fmt.Sprintf("https://graph.facebook.com/v2.6/me/messages?access_token=%s", AccessToken) +) + +func verifyWebhook(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + mode := r.URL.Query().Get("hub.mode") + token := r.URL.Query().Get("hub.verify_token") + challenge := r.URL.Query().Get("hub.challenge") + + if mode == "subscribe" && token == VerificationToken { + fmt.Fprint(w, challenge) + } else { + w.WriteHeader(http.StatusForbidden) + } +} + +func handleWebhookEvents(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + // Parse the request payload + payload := webhookPayload{} + if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { + log.Println("Unmarshalling webhook payload resulted in an error: ", err) + return + } + + // Make sure this is a page subscription + if payload.Object == "page" { + // Iterate over each entry + // There may be multiple if batched + for _, entry := range payload.Entry { + // Iterate over each messaging event + for _, messaging := range entry.Messaging { + switch { + case !reflect.DeepEqual(messaging.Message, messageEvent{}): + handleMessageEvent(messaging.Message, messaging.Sender.ID) + case !reflect.DeepEqual(messaging.Postback, postbackEvent{}): + handlePostbackEvent(messaging.Postback, messaging.Sender.ID) + default: + log.Printf("No handler found for: %+v\n", messaging.Message) + } + } + } + } +} + +var postbackHandlers = map[string]func(string, string){ + "league-table-postback": handleLeagueTablePostbackEvent, + "match-schedules-postback": handleMatchSchedulesPostbackEvent, + "league-highlights-postback": handleLeagueHighlightsPostbackEvent, + + "More Highlights": handleLeagueMoreHighlightsPostbackEvent, +} + +func handlePostbackEvent(msgEvnt postbackEvent, senderID string) { + + if postbackHandler, ok := postbackHandlers[msgEvnt.Payload]; ok { + postbackHandler(msgEvnt.Payload, senderID) + return + } else if postbackHandler, ok := postbackHandlers[msgEvnt.Title]; ok { + postbackHandler(msgEvnt.Payload, senderID) + return + } + + reply := templateResponse{} + reply.Recipient.ID = senderID + reply.Message.Text = fmt.Sprintf("%s - coming soon 🤠", msgEvnt.Title) + sendResponse(reply) +} + +func handleMessageEvent(msgEvnt messageEvent, senderID string) { + if msgEvnt.QuickReply.Payload != "" { + handleQuickReplyEvent(msgEvnt, senderID) + return + } + handleTextMessageEvent(msgEvnt, senderID) +} + +func handleTextMessageEvent(msgEvnt messageEvent, senderID string) { + reply := templateResponse{} + reply.Recipient.ID = senderID + reply.Message.Attachment.Type = "template" + reply.Message.Attachment.Payload.TemplateType = "button" + reply.Message.Attachment.Payload.Text = "What do you want to do?" + + matchSchedulesPostbackBtn := buildPostbackBtn("View match schedules", "match-schedules-postback") + leagueTablePostbackBtn := buildPostbackBtn("View league table", "league-table-postback") + leagueHighlightsBtn := buildPostbackBtn("View Highlights", "league-highlights-postback") + + reply.Message.Attachment.Payload.Buttons = []button{matchSchedulesPostbackBtn, leagueHighlightsBtn, leagueTablePostbackBtn} + sendResponse(reply) +} + +func handleQuickReplyEvent(msgEvnt messageEvent, senderID string) { + payload := msgEvnt.QuickReply.Payload + if strings.Contains(payload, "match-schedules-postback-") { + handleMatchSchedulesPostbackEvent(payload, senderID) + return + } + log.Println("Unrecognized payload") +} + +func sendResponse(payload interface{}) { + utils.APIRequest(fbURL, "POST", payload) +} + +func setupRouter() *httprouter.Router { + r := httprouter.New() + r.GET("/webhook", verifyWebhook) + r.POST("/webhook", handleWebhookEvents) + return r +} + +func main() { + err := http.ListenAndServe(":3000", setupRouter()) + if err != nil { + panic(err) + } +} diff --git a/implementations/go/postbacks.go b/implementations/go/postbacks.go new file mode 100644 index 0000000..e26cd0c --- /dev/null +++ b/implementations/go/postbacks.go @@ -0,0 +1,164 @@ +package main + +import ( + "fmt" + "regexp" + "strconv" + "time" + + "github.com/FBDevCLagos/soccergist/implementations/go/data" +) + +func handleLeagueTablePostbackEvent(msgEvnt, senderID string) { + league := data.PremierLeagueInfo() + leagueTable := league.Table() + firstFour := data.FirstFour(leagueTable) + elements := []element{} + moreDetailsBtn := buildPostbackBtn("more details", "") + viewMoreBtn := buildPostbackBtn("view more", "league-table-view-more-postback") + + for _, team := range firstFour { + moreDetailsBtn.Payload = fmt.Sprintf("league-table-position-%d-more-details-postback", team.Position) + element := buildBasicElement( + fmt.Sprintf("Position %d: %s", team.Position, team.Name), + fmt.Sprintf("Matches played: %d \n Points: %d", team.MatchPlayed, team.Points), + team.Crest, + ) + + element.Buttons = append(element.Buttons, moreDetailsBtn) + elements = append(elements, element) + } + + reply := templateResponse{} + reply.Recipient.ID = senderID + reply.Message.Attachment.Type = "template" + reply.Message.Attachment.Payload.TemplateType = "list" + reply.Message.Attachment.Payload.TopElementStyle = "large" + reply.Message.Attachment.Payload.Elements = elements + reply.Message.Attachment.Payload.Buttons = []button{viewMoreBtn} + + sendResponse(reply) +} + +func handleMatchSchedulesPostbackEvent(payload, senderID string) { + premierLeague := data.PremierLeagueInfo() + rr := regexp.MustCompile("(\\d+)") + matchday := premierLeague.PresentMatchday() + if mday := rr.FindStringSubmatch(payload); len(mday) > 0 { + matchday, _ = strconv.Atoi(mday[0]) + } + + fixtures := premierLeague.GetMatchdayFixtures(matchday) + msg := fmt.Sprintf("Fixtures for Matchday: %d", matchday) + + if fixtures == nil { + return + } + + for _, fixture := range fixtures.Fixtures { + if fixture.Status == "FINISHED" { + msg = fmt.Sprintf("%s\n-----\n%s VS %s => (%d : %d)", msg, fixture.HomeTeamName, fixture.AwayTeamName, fixture.Result.GoalsHomeTeam, fixture.Result.GoalsAwayTeam) + } else if t, err := time.Parse(time.RFC3339, fixture.Date); err == nil { + msg = fmt.Sprintf("%s\n-----\n%s VS %s - %s", msg, fixture.HomeTeamName, fixture.AwayTeamName, t.Format("Mon, Jan 2, 3:04PM")) + } else { + msg = fmt.Sprintf("%s\n-----\n%s VS %s - %s", msg, fixture.HomeTeamName, fixture.AwayTeamName, fixture.Status) + } + } + reply := buildTextMsg(senderID, msg) + + sendResponse(reply) + sendMatchFixuresPagination(senderID, matchday, premierLeague.PresentMatchday(), premierLeague.TotalMatchdays()) +} + +func sendMatchFixuresPagination(senderID string, matchday, currentMatchday, totalMatchdays int) { + contents := []quickReply{} + + if day := matchday - 2; day > 0 { + content := quickReply{ + ContentType: "text", + Payload: fmt.Sprintf("match-schedules-postback-%d", day), + Title: fmt.Sprintf("<< matchday %d", day), + } + contents = append(contents, content) + } + + if day := matchday - 1; day > 0 { + content := quickReply{ + ContentType: "text", + Payload: fmt.Sprintf("match-schedules-postback-%d", day), + Title: fmt.Sprintf("< matchday %d", day), + } + contents = append(contents, content) + } + + if matchday != currentMatchday { + content := quickReply{ + ContentType: "text", + Payload: fmt.Sprintf("match-schedules-postback-%d", currentMatchday), + Title: "current matchday", + } + contents = append(contents, content) + } + + if day := matchday + 1; day <= totalMatchdays { + content := quickReply{ + ContentType: "text", + Payload: fmt.Sprintf("match-schedules-postback-%d", day), + Title: fmt.Sprintf("matchday %d >", day), + } + contents = append(contents, content) + } + + if day := matchday + 2; day <= totalMatchdays { + content := quickReply{ + ContentType: "text", + Payload: fmt.Sprintf("match-schedules-postback-%d", day), + Title: fmt.Sprintf("matchday %d >>", day), + } + contents = append(contents, content) + } + + reply := buildQuickReply("navigation", senderID, contents) + sendResponse(reply) +} + +func handleLeagueMoreHighlightsPostbackEvent(payload, senderID string) { + handleLeagueHighlights(payload, senderID) +} + +func handleLeagueHighlightsPostbackEvent(payload, senderID string) { + handleLeagueHighlights("", senderID) +} + +func handleLeagueHighlights(payload, senderID string) { + elements := []element{} + posts := data.Highlights(payload) + + for _, post := range posts { + // TODO: handle more than one highlight url + btn := button{Type: "web_url", Title: "Watch highlight", URL: post.URLs[0]} + element := buildBasicElement(post.Title, "", "https://i.vimeocdn.com/portrait/6640852_640x640") + element.Buttons = []button{btn} + elements = append(elements, element) + + if len(elements) == 9 { + break + } + } + + if len(posts) > 9 { + viewMore := buildBasicElement("View More Highlights", "", "http://icons-for-free.com/free-icons/png/512/1814113.png") + btn := buildPostbackBtn("More Highlights", posts[9].Name) + viewMore.Buttons = []button{btn} + + elements = append(elements, viewMore) + } + + reply := templateResponse{} + reply.Recipient.ID = senderID + reply.Message.Attachment.Type = "template" + reply.Message.Attachment.Payload.TemplateType = "generic" + reply.Message.Attachment.Payload.Elements = elements + + sendResponse(reply) +} diff --git a/implementations/go/reddit.agent.sample b/implementations/go/reddit.agent.sample new file mode 100644 index 0000000..c2e6496 --- /dev/null +++ b/implementations/go/reddit.agent.sample @@ -0,0 +1,5 @@ +user_agent: ":: (by /u/)" +client_id: "client id (looks kind of like: sdkfbwi48rhijwsdn)" +client_secret: "client secret (looks kind of like: ldkvblwiu34y8hsldjivn)" +username: "reddit username" +password: "reddit password" \ No newline at end of file diff --git a/implementations/go/templates.go b/implementations/go/templates.go new file mode 100644 index 0000000..4824091 --- /dev/null +++ b/implementations/go/templates.go @@ -0,0 +1,30 @@ +package main + +func buildPostbackBtn(title, payload string) button { + return button{ + Type: "postback", + Title: title, + Payload: payload, + } +} + +func buildBasicElement(title, subtitle, imageURL string) element { + return element{ + Title: title, + ImageURL: imageURL, + Subtitle: subtitle, + } +} + +func buildTextMsg(senderID, text string) (msg basicResponseTemplate) { + msg.Message.Text = text + msg.Recipient.ID = senderID + return msg +} + +func buildQuickReply(text, senderID string, quickReplies []quickReply) (msg basicResponseTemplate) { + msg.Recipient.ID = senderID + msg.Message.Text = text + msg.Message.QuickReplies = quickReplies + return +} diff --git a/implementations/go/types.go b/implementations/go/types.go new file mode 100644 index 0000000..e0aa9de --- /dev/null +++ b/implementations/go/types.go @@ -0,0 +1,92 @@ +package main + +type webhookPayload struct { + Object string `json:"object,omitempty"` + Entry []struct { + ID string `json:"id,omitempty"` + Messaging []struct { + Message messageEvent `json:"message,omitempty"` + Recipient struct { + ID string `json:"id,omitempty"` + } `json:"recipient,omitempty"` + Sender struct { + ID string `json:"id,omitempty"` + } `json:"sender,omitempty"` + Timestamp int `json:"timestamp,omitempty"` + Postback postbackEvent `json:"postback,omitempty"` + } `json:"messaging,omitempty"` + Time int `json:"time,omitempty"` + } `json:"entry,omitempty"` +} + +type messageEvent struct { + Mid string + Seq int + Text string + QuickReply struct { + Payload string `json:"payload,omitempty"` + } `json:"quick_reply,omitempty"` +} + +type postbackEvent struct { + Title string `json:"title,omitempty"` + Payload string `json:"payload,omitempty"` +} + +type quickReply struct { + ContentType string `json:"content_type,omitempty"` + ImageURL string `json:"image_url,omitempty"` + Payload string `json:"payload,omitempty"` + Title string `json:"title,omitempty"` +} + +type templateResponse struct { + Recipient struct { + ID string `json:"id,omitempty"` + } `json:"recipient,omitempty"` + Message struct { + Text string `json:"text,omitempty"` + Attachment struct { + Type string `json:"type,omitempty"` + Payload struct { + TemplateType string `json:"template_type,omitempty"` + TopElementStyle string `json:"top_element_style,omitempty"` + Text string `json:"text,omitempty"` + Buttons []button `json:"buttons,omitempty"` + Elements []element `json:"elements,omitempty"` + } `json:"payload,omitempty"` + } `json:"attachment,omitempty"` + } `json:"message,omitempty"` +} + +type basicResponseTemplate struct { + Recipient struct { + ID string `json:"id,omitempty"` + } `json:"recipient,omitempty"` + Message struct { + Text string `json:"text,omitempty"` + QuickReplies []quickReply `json:"quick_replies,omitempty"` + } `json:"message,omitempty"` +} + +type button struct { + Type string `json:"type,omitempty"` + Title string `json:"title,omitempty"` + Payload string `json:"payload,omitempty"` + URL string `json:"url,omitempty"` +} + +type element struct { + Buttons []button `json:"buttons,omitempty"` + Title string `json:"title,omitempty"` + Subtitle string `json:"subtitle,omitempty"` + ImageURL string `json:"image_url,omitempty"` + MediaType string `json:"media_type,omitempty"` + AttachmentID string `json:"attachment_id,omitempty"` + DefaultAction *struct { + Type string `json:"type,omitempty"` + URL string `json:"url,omitempty"` + MessengerExtensions bool `json:"messenger_extensions,omitempty"` + WebviewHeightRatio string `json:"webview_height_ratio,omitempty"` + } `json:"default_action,omitempty"` +} diff --git a/implementations/go/utils/utils.go b/implementations/go/utils/utils.go new file mode 100644 index 0000000..2039405 --- /dev/null +++ b/implementations/go/utils/utils.go @@ -0,0 +1,36 @@ +package utils + +import ( + "bytes" + "encoding/json" + "log" + "net/http" +) + +func APIRequest(url string, requestMethod string, payload interface{}) (*http.Response, error) { + var req *http.Request + var err error + + if payload != nil { + // Parse the response payload + pkg, err := json.Marshal(payload) + if err != nil { + log.Println("Sending response parsing in an error: ", err) + return nil, err + } + body := bytes.NewBuffer(pkg) + req, err = http.NewRequest(requestMethod, url, body) + } else { + req, err = http.NewRequest(requestMethod, url, nil) + } + + if err != nil { + log.Println("Error creating request: ", err) + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + client := &http.Client{} + + return client.Do(req) +}