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] ` - 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 } }