Skip to content

Commit

Permalink
implement untapdd
Browse files Browse the repository at this point in the history
  • Loading branch information
benjaminbartels committed Dec 29, 2024
1 parent 3709d5d commit 2a56f89
Show file tree
Hide file tree
Showing 5 changed files with 431 additions and 8 deletions.
7 changes: 7 additions & 0 deletions cmd/brewbot/handlers/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,15 @@ func NewAPI(bot *discord.Bot, brewRepo dynamo.BrewRepo, leaderboardRepo dynamo.L
return errors.Wrap(err, "could not add 'style' command")
}

if err := bot.AddCommand(UntapddCommand()); err != nil {
return errors.Wrap(err, "could not add 'untapdd' command")
}

untapddHandler := NewUntapddHandler()

bot.AddHandler("brew", brewsHandler.BrewHandler)
bot.AddHandler("styles", stylesHandler.StyleHandler)
bot.AddHandler("untapdd", untapddHandler.UntapddHandler)

return nil
}
179 changes: 179 additions & 0 deletions cmd/brewbot/handlers/untapdd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package handlers

import (
"context"
"fmt"
"strings"

"github.com/benjaminbartels/brewbot/internal/untappd"
"github.com/bwmarrin/discordgo"
"github.com/pkg/errors"
)

const (
untapddCommand = "untapdd"
menusSubCommand = "menu"
untapddLeaderboardSubCommand = "leaderboard"
listVenuesSubCommand = "venues"
)

type UntapddHandler struct {
venues map[string]string
}

func NewUntapddHandler() UntapddHandler {
h := UntapddHandler{
venues: make(map[string]string),
}

h.venues["hbs"] = "homebrewstuff-craft-bottle-shop-and-taproom/960464"
h.venues["brownbeard"] = "brown-beard-brewing-company/11844307"

return h
}

func UntapddCommand() *discordgo.ApplicationCommand {
return &discordgo.ApplicationCommand{
Name: untapddCommand,
Description: "Issues untapdd realted commands to BrewBot",
Options: []*discordgo.ApplicationCommandOption{
{
Name: menusSubCommand,
Description: "Get a venue's current Untappd Menu",
Type: discordgo.ApplicationCommandOptionSubCommand,
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionString,
Name: "venueName",
Description: "Name of the venue",
Required: true,
},
},
},
{
Name: untapddLeaderboardSubCommand,
Description: "Get a venue's current Untappd Leaderboard",
Type: discordgo.ApplicationCommandOptionSubCommand,
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionString,
Name: "venueName",
Description: "Name of the venue",
Required: true,
},
},
},
{
Name: listVenuesSubCommand,
Description: "List available Untappd venues",
Type: discordgo.ApplicationCommandOptionSubCommand,
},
},
}
}

func (h *UntapddHandler) UntapddHandler(s *discordgo.Session, i *discordgo.InteractionCreate) error {
ctx := context.Background()
subcommand := i.ApplicationCommandData().Options[0].Name
opts := i.ApplicationCommandData().Options[0].Options

var err error

switch subcommand {
case menusSubCommand:
err = h.handleMenu(ctx, s, i, opts)
case untapddLeaderboardSubCommand:
err = h.handleLeaderboard(ctx, s, i, opts)
case listVenuesSubCommand:
err = h.handleListVenues(ctx, s, i)
}

if err != nil {
if err := respondToChannel(s, i, "There was a problem processing your request", true); err != nil {
return errors.Wrap(err, "could not respond with processing error")
}

return errors.Wrap(err, "unhandled error occurred")
}

return nil
}

func (h *UntapddHandler) handleMenu(_ context.Context, s *discordgo.Session,
i *discordgo.InteractionCreate, opts []*discordgo.ApplicationCommandInteractionDataOption,
) error {
venueName := opts[0].StringValue()

path, ok := h.venues[venueName]
if !ok {
if err := respondToChannel(s, i, fmt.Sprintf("Invalid venue: %s", opts[1].Value), true); err != nil {
return errors.Wrap(err, "could not respond with invalid amount error")
}
}

menus, _, err := untappd.Scrape(path)
if err != nil {
return errors.Wrap(err, "could not get scrape Untapdd")
}

var builder strings.Builder

for _, menu := range menus {
builder.WriteString(fmt.Sprintf("__**%s**__\n", menu))
for _, i := range menu.Items {
builder.WriteString(fmt.Sprintf("**%s:** *%s* (%s) %s%% ABV - %s IBU\n",
i.Name, i.Brewery, i.Style, i.ABV, i.IBU))
}
}

if err := respondToChannel(s, i, builder.String(), false); err != nil {
return errors.Wrap(err, "could not respond with menu")
}

return nil
}

func (h *UntapddHandler) handleLeaderboard(_ context.Context, s *discordgo.Session,
i *discordgo.InteractionCreate, opts []*discordgo.ApplicationCommandInteractionDataOption,
) error {
venueName := opts[0].StringValue()

path, ok := h.venues[venueName]
if !ok {
if err := respondToChannel(s, i, fmt.Sprintf("Invalid venue: %s", opts[1].Value), true); err != nil {
return errors.Wrap(err, "could not respond with invalid amount error")
}
}

_, patrons, err := untappd.Scrape(path)
if err != nil {
return errors.Wrap(err, "could not get scrape Untapdd")
}

var builder strings.Builder

for _, p := range patrons {
builder.WriteString(fmt.Sprintf("%2d : %4d %s\n", p.Rank, p.CheckIns, p.Name))
}

if err := respondToChannel(s, i, builder.String(), false); err != nil {
return errors.Wrap(err, "could not respond with leaderboard")
}

return nil
}

func (h *UntapddHandler) handleListVenues(_ context.Context, s *discordgo.Session,
i *discordgo.InteractionCreate,
) error {
var builder strings.Builder
for k := range h.venues {
builder.WriteString(fmt.Sprintf("%s\n", k))
}

if err := respondToChannel(s, i, builder.String(), false); err != nil {
return errors.Wrap(err, "could not respond with leaderboard")
}

return nil
}
25 changes: 22 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module github.com/benjaminbartels/brewbot

go 1.17
go 1.23

toolchain go1.23.1

require (
github.com/aws/aws-sdk-go v1.42.52
Expand All @@ -10,13 +12,20 @@ require (
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.6.0
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.13.0
github.com/bwmarrin/discordgo v0.23.3-0.20220202194601-aba5dc811da8
github.com/go-errors/errors v1.5.1
github.com/gocolly/colly v1.2.0
github.com/kelseyhightower/envconfig v1.4.0
github.com/matoous/go-nanoid/v2 v2.0.0
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.8.1
)

require (
github.com/PuerkitoBio/goquery v1.10.1 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/antchfx/htmlquery v1.3.4 // indirect
github.com/antchfx/xmlquery v1.4.3 // indirect
github.com/antchfx/xpath v1.3.3 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.2.0 // indirect
Expand All @@ -28,8 +37,18 @@ require (
github.com/aws/aws-sdk-go-v2/service/sso v1.9.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.14.0 // indirect
github.com/aws/smithy-go v1.10.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
golang.org/x/sys v0.0.0-20210423082822-04245dca01da // indirect
github.com/kennygrant/sanitize v1.2.4 // indirect
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
github.com/temoto/robotstxt v1.1.2 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/protobuf v1.26.0 // indirect
)
Loading

0 comments on commit 2a56f89

Please sign in to comment.