hampbot/internal/commands/studentlife/pvta.go

280 lines
6.4 KiB
Go

package studentlife
import (
"bytes"
"fmt"
"image/color"
"image/png"
"os"
"strconv"
"strings"
"github.com/bwmarrin/discordgo"
sm "github.com/flopp/go-staticmaps"
"github.com/golang/geo/s2"
"github.com/jackmerrill/hampbot/internal/utils/config"
"github.com/jackmerrill/hampbot/internal/utils/embed"
pvtago "github.com/jackmerrill/pvta-go"
"github.com/zekroTJA/shireikan"
)
type PVTA struct {
}
func (c *PVTA) GetInvokes() []string {
return []string{"pvta", "bus"}
}
func (c *PVTA) GetDescription() string {
return "Get the details and location of a PVTA bus"
}
func (c *PVTA) GetHelp() string {
return "`pvta [route] <vehicle>` - Get the details and location of a PVTA bus"
}
func (c *PVTA) GetGroup() string {
return config.GroupStudentLife
}
func (c *PVTA) GetDomainName() string {
return "hamp.studentlife.pvta"
}
func (c *PVTA) GetSubPermissionRules() []shireikan.SubPermission {
return nil
}
func (c *PVTA) IsExecutableInDMChannels() bool {
return true
}
func getRouteByShortName(shortName string) (*pvtago.RouteDetail, error) {
client := pvtago.NewClient()
routes, err := client.GetRoutes()
if err != nil {
return nil, err
}
for _, route := range routes {
if route.ShortName == shortName {
return &route, nil
}
if route.RouteAbbreviation == shortName {
return &route, nil
}
}
return nil, nil
}
func (c *PVTA) Exec(ctx shireikan.Context) error {
client := pvtago.NewClient()
route := ctx.GetArgs().Get(0).AsString()
if route == "" {
routes, err := client.GetVisibleRoutes()
if err != nil {
return err
}
var routeIds []string
for _, route := range routes {
if route.ShortName != "" {
routeIds = append(routeIds, route.ShortName)
}
}
ctx.ReplyEmbed(embed.NewSuccessEmbed(ctx).SetTitle("Available PVTA routes").SetDescription(strings.Join(routeIds, ", ")).MessageEmbed)
return nil
}
vehicle := ctx.GetArgs().Get(1).AsString()
if vehicle == "" {
route, err := getRouteByShortName(route)
if err != nil {
ctx.ReplyEmbed(embed.NewErrorEmbed(ctx).SetTitle("Error").SetDescription("An error occured while fetching the route.").AddField("Error", err.Error(), false).MessageEmbed)
return err
}
if route == nil {
ctx.ReplyEmbed(embed.NewErrorEmbed(ctx).SetTitle("Error").SetDescription("The route you specified does not exist.").MessageEmbed)
return nil
}
vehicles, err := client.GetVehiclesForRoute(route.RouteID)
if err != nil {
return err
}
if len(vehicles) == 0 {
ctx.ReplyEmbed(embed.NewErrorEmbed(ctx).SetTitle("Error").SetDescription("There are no vehicles on this route.").MessageEmbed)
return nil
}
e := embed.NewSuccessEmbed(ctx).SetTitle(":bus: PVTA Route Details").SetDescription(fmt.Sprintf("**Route:** %s\n**Name:** %s", route.ShortName, route.LongName))
tileProvider := &sm.TileProvider{
Name: "thunderforest-transit",
Attribution: "Maps (c) Thundeforest; Data (c) OSM and contributors, ODbL",
TileSize: 256,
URLPattern: "https://%[1]s.tile.thunderforest.com/transport/%[2]d/%[3]d/%[4]d.png?apikey=" + os.Getenv("TF_API_KEY"),
Shards: []string{"a", "b", "c"},
}
mapCtx := sm.NewContext()
mapCtx.SetSize(600, 400)
mapCtx.SetTileProvider(tileProvider)
for _, vehicle := range vehicles {
e.AddField(fmt.Sprintf("Vehicle #`%d`", vehicle.VehicleID), fmt.Sprintf(":round_pushpin: **Destination:** %s (%s)\n:globe_with_meridians: **Lat/Long:** `%f` `%f`", vehicle.Destination, vehicle.Direction, vehicle.Latitude, vehicle.Longitude), false)
rgb, err := config.Hex2RGB(config.Hex(route.Color))
if err != nil {
return err
}
marker := sm.NewMarker(
s2.LatLngFromDegrees(vehicle.Latitude, vehicle.Longitude),
color.RGBA{R: rgb.Red, G: rgb.Green, B: rgb.Blue, A: 255},
16.0,
)
marker.Label = fmt.Sprintf("%s (%s)", vehicle.Destination, vehicle.Direction)
marker.LabelColor = color.RGBA{R: 0, G: 0, B: 0, A: 255}
marker.LabelYOffset = 3
mapCtx.AddObject(marker)
}
img, err := mapCtx.Render()
if err != nil {
return err
}
// convert to base64
buf := new(bytes.Buffer)
err = png.Encode(buf, img)
if err != nil {
return err
}
e.SetImage("attachment://map.png")
_, err = ctx.GetSession().ChannelMessageSendComplex(ctx.GetChannel().ID, &discordgo.MessageSend{
Embed: e.MessageEmbed,
Reference: ctx.GetMessage().Reference(),
Files: []*discordgo.File{
{
Name: "map.png",
ContentType: "image/png",
Reader: buf,
},
},
})
if err != nil {
return err
}
return nil
} else {
id, err := strconv.ParseInt(vehicle, 10, 64)
if err != nil {
ctx.ReplyEmbed(embed.NewErrorEmbed(ctx).SetTitle("Error").SetDescription("An error occured while parsing the vehicle ID.").AddField("Error", err.Error(), false).MessageEmbed)
return err
}
v, err := client.GetVehicle(id)
if err != nil {
return err
}
route, err := client.GetRoute(v.RouteID)
if err != nil {
return err
}
e := embed.NewSuccessEmbed(ctx).SetTitle(":bus: Vehicle Details").SetDescription(fmt.Sprintf(":round_pushpin: **Destination:** %s (%s)\n:globe_with_meridians: **Lat/Long:** `%f` `%f`", v.Destination, v.Direction, v.Latitude, v.Longitude))
tileProvider := &sm.TileProvider{
Name: "thunderforest-transit",
Attribution: "Maps (c) Thundeforest; Data (c) OSM and contributors, ODbL",
TileSize: 256,
URLPattern: "https://%[1]s.tile.thunderforest.com/transport/%[2]d/%[3]d/%[4]d.png?apikey=" + os.Getenv("TF_API_KEY"),
Shards: []string{"a", "b", "c"},
}
mapCtx := sm.NewContext()
mapCtx.SetSize(600, 400)
mapCtx.SetTileProvider(tileProvider)
rgb, err := config.Hex2RGB(config.Hex(route.Color))
if err != nil {
return err
}
mapCtx.AddObject(
sm.NewMarker(
s2.LatLngFromDegrees(v.Latitude, v.Longitude),
color.RGBA{R: rgb.Red, G: rgb.Green, B: rgb.Blue, A: 255},
16.0,
),
)
img, err := mapCtx.Render()
if err != nil {
return err
}
// convert to base64
buf := new(bytes.Buffer)
err = png.Encode(buf, img)
if err != nil {
return err
}
e.SetImage("attachment://map.png")
_, err = ctx.GetSession().ChannelMessageSendComplex(ctx.GetChannel().ID, &discordgo.MessageSend{
Embed: e.MessageEmbed,
Reference: ctx.GetMessage().Reference(),
Files: []*discordgo.File{
{
Name: "map.png",
ContentType: "image/png",
Reader: buf,
},
},
})
if err != nil {
return err
}
return nil
}
}