busetabot

package module
v4.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 4, 2019 License: MIT Imports: 26 Imported by: 0

README

Bus Eta Bot

Weekly Users Build Status CircleCI codecov Go Report Card

A practical Telegram bot for getting bus etas in Singapore supporting refreshing, bus stop search and showing nearby bus stops.

Getting started

Telegram: @BusEtaBot

Features

Get etas by bus stop code

Etas by bus stop code

Get etas by bus stop code and service

Etas by bus stop code and service

Send a location to get nearby bus stops

Looking up nearby bus stops

Search bus stops by bus stop code, description or road name

Searching bus stops

Get nearby bus stops

Location-based inline query

Send etas as inline messages

Inline message

Documentation

Index

Constants

View Source
const (
	ApplicationName    = "Bus Eta Bot"
	ApplicationID      = "github.com/yi-jiayu/bus-eta-bot"
	ApplicationVersion = Version
)

Application details

View Source
const (
	MeasurementProtocolVersion            = "1"
	MeasurementProtocolEndpoint           = "https://www.google-analytics.com/collect"
	MeasurementProtocolValidationEndpoint = "https://www.google-analytics.com/debug/collect"
)

Measurement Protocol constants

View Source
const (
	CategoryCommand            = "command"
	CategoryMessage            = "message"
	CategoryInlineQuery        = "inline_query"
	CategoryChosenInlineResult = "chosen_inline_result"
	CategoryCallback           = "callback_query"
	CategoryError              = "error"
)

Event categories

View Source
const (
	ActionEtaCommandWithArgs    = "eta_command_with_args"
	ActionEtaCommandWithoutArgs = "eta_command_without_args"
	ActionStartCommand          = "start_command"
	ActionAboutCommand          = "about_command"
	ActionVersionCommand        = "version_command"
	ActionHelpCommand           = "help_command"
	ActionPrivacyCommand        = "privacy_command"
	ActionFeedbackCommand       = "feedback_command"

	ActionEtaTextMessage       = "eta_text_message"
	ActionContinuedTextMessage = "continued_text_message"
	ActionIgnoredTextMessage   = "ignored_text_message"
	ActionLocationMessage      = "location_message"

	ActionNewInlineQuery       = "new_inline_query"
	ActionNewNearbyInlineQuery = "new_nearby_inline_query"
	ActionOffsetInlineQuery    = "offset_inline_query"

	ActionChosenInlineResult       = "chosen_inline_result"
	ActionChosenNearbyInlineResult = "chosen_nearby_inline_result"

	ActionRefreshCallback         = "refresh_callback"
	ActionResendCallback          = "resend_callback"
	ActionEtaCallback             = "eta_callback"
	ActionEtaDemoCallback         = "eta_demo_callback"
	ActionEtaFromLocationCallback = "eta_from_location_callback"
	ActionAddFavouriteCalback     = "add_favourite_callback"
	ActionRemoveFavouriteCalback  = "remove_favourite_callback"

	ActionCommandError     = "command_error"
	ActionMessageError     = "message_error"
	ActionInlineQueryError = "inline_query_error"
	ActionCallbackError    = "callback_error"
)

Event actions

View Source
const (
	RepoURL          = "https://github.com/yi-jiayu/bus-eta-bot"
	PrivacyPolicyURL = "https://t.me/iv?url=https%3A%2F%2Fgithub.com%2Fyi-jiayu%2Fbus-eta-bot%2Fblob%2Fmaster%2FPRIVACY.md&rhash=a44cb5372834ee"
	HelpURL          = "http://telegra.ph/Bus-Eta-Bot-Help-02-23"
)

Links to relevant documents

View Source
const (
	EnvironmentDev        = devEnvironment
	EnvironmentStaging    = stagingEnvironment
	EnvironmentProduction = productionEnvironment
)
View Source
const (
	FormatterSummary  = "s"
	FormatterFeatures = "f"
)
View Source
const (
	EquatorialLatitude  = 110574.0
	EquatorialLongitude = 111320.0
)
View Source
const (
	KindFavourites = "Favourites"
	KindUser       = "User"
)
View Source
const InlineQueryResultsLimit = 50
View Source
const (
	LabelInlineMessage = "inline_message"
)

Event labels

View Source
const MaxMessageLength = 35
View Source
const NearbyBusStopsRadius = 1000.0

NearbyBusStopsRadius is the search range in metres for inline queries for nearby bus stops.

View Source
const ResponseBufferSize = 10

ResponseBufferSize is the size of the channel used to queue responses to be sent via the Telegram Bot API.

View Source
const StreetViewEndpoint = "https://maps.googleapis.com/maps/api/streetview"

StreetViewEndpoint is the endpoint for the Street View Image API.

View Source
const Version = "VERSION"

Version is the current Bus Eta Bot version

Variables

View Source
var (
	Formatters = map[string]Formatter{
		FormatterSummary:  summaryFormatter,
		FormatterFeatures: featuresFormatter,
	}
)
View Source
var HTTPFormat = propagation.HTTPFormat{}

Functions

func AboutHandler

func AboutHandler(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message, responses chan<- Response)

AboutHandler handles the /about command

func ChosenInlineResultHandler

func ChosenInlineResultHandler(ctx context.Context, bot *BusEtaBot, cir *tgbotapi.ChosenInlineResult) error

ChosenInlineResultHandler handles a chosen inline result

func ETAMessageText

func ETAMessageText(busStops BusStopRepository, etaService ETAService, formatter ETAFormatter, t time.Time, code string, services []string) (string, error)

func EtaCallbackHandler

func EtaCallbackHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response)

EtaCallbackHandler handles callback queries from eta messages from old versions of the bot for backwards-compatibility.

func EtaDemoCallbackHandler

func EtaDemoCallbackHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response)

EtaDemoCallbackHandler handles an eta_demo callback from a start command.

func EtaHandler

func EtaHandler(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message, responses chan<- Response)

EtaHandler handles the /eta command.

func EtaTable

func EtaTable(etas [][4]string) string

EtaTable generates the table of bus etas used in a eta message

func FallbackCommandHandler

func FallbackCommandHandler(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message) error

FallbackCommandHandler catches commands which don't match any other handler.

func FeedbackCmdHandler

func FeedbackCmdHandler(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message, responses chan<- Response)

FeedbackCmdHandler handles the /feedback command.

func GetBotEnvironment

func GetBotEnvironment() string

func GetNearbyInlineQueryResults

func GetNearbyInlineQueryResults(ctx context.Context, streetView StreetViewProvider, busStops BusStopRepository, lat, lon float64) ([]telegram.InlineQueryResult, error)

func HelpHandler

func HelpHandler(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message, responses chan<- Response)

HelpHandler handles the /help command

func HideFavouritesCmdHandler

func HideFavouritesCmdHandler(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message, responses chan<- Response)

HideFavouritesCmdHandler hides the favourites keyboard.

func InferEtaQuery

func InferEtaQuery(text string) (string, []string, error)

InferEtaQuery extracts a bus stop ID and service numbers from a text message.

func InlineQueryHandler

func InlineQueryHandler(ctx context.Context, bot *BusEtaBot, ilq *tgbotapi.InlineQuery) error

InlineQueryHandler handles inline queries

func LocationHandler

func LocationHandler(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message) error

LocationHandler handles messages contain a location

func NewContext

func NewContext(r *http.Request) (ctx context.Context)

func NewETAMessageReplyMarkup

func NewETAMessageReplyMarkup(busStopCode string, serviceNos []string, formatter string, inline bool) telegram.InlineKeyboardMarkup

func NewEtaHandler

func NewEtaHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response)

NewEtaHandler sends etas for a bus stop when a user taps "Get etas" on a bus stop location returned from a location query

func NewIncomingBusDetailsButton added in v4.2.0

func NewIncomingBusDetailsButton(busStopCode string, serviceNos []string) telegram.InlineKeyboardButton

func NewIncomingBusSummaryButton added in v4.2.0

func NewIncomingBusSummaryButton(busStopCode string, serviceNos []string) telegram.InlineKeyboardButton

func NewRefreshButton

func NewRefreshButton(busStopCode string, serviceNos []string, formatter string) telegram.InlineKeyboardButton

func NewResendButton

func NewResendButton(busStopCode string, serviceNos []string, formatter string) telegram.InlineKeyboardButton

func NewToggleFavouriteButton

func NewToggleFavouriteButton(busStopCode string, serviceNos []string) telegram.InlineKeyboardButton

func PrivacyHandler

func PrivacyHandler(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message, responses chan<- Response)

PrivacyHandler handles the /privacy command.

func RefreshCallbackHandler

func RefreshCallbackHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response)

RefreshCallbackHandler handles the callback for the Refresh button on an eta message.

func ShowFavouritesCmdHandler

func ShowFavouritesCmdHandler(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message, responses chan<- Response)

ShowFavouritesCmdHandler will display a reply keyboard for quick access to the user's favourites.

func SquaredEuclideanDistanceAtEquator

func SquaredEuclideanDistanceAtEquator(lat0, lon0, lat1, lon1 float64) float64

EuclideanDistanceAtEquator returns the approximate squared distance between two points near the equator.

func StartHandler

func StartHandler(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message, responses chan<- Response)

StartHandler handles a /start command.

func TextHandler

func TextHandler(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message) error

TextHandler handles incoming text messages

func ToggleFavouritesHandler

func ToggleFavouritesHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response)

ToggleFavouritesHandler handles the toggle favourite callback button on etas

func VersionHandler

func VersionHandler(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message, responses chan<- Response)

VersionHandler handles the /version command

Types

type ArrivingBus added in v4.2.0

type ArrivingBus struct {
	ServiceNo string
	datamall.ArrivingBus
}

type BusEtaBot

type BusEtaBot struct {
	Handlers            Handlers
	Telegram            *tgbotapi.BotAPI
	Datamall            ETAService
	StreetView          StreetViewProvider
	MeasurementProtocol *MeasurementProtocolClient
	NowFunc             func() time.Time
	BusStops            BusStopRepository
	Users               UserRepository
	TelegramService     TelegramService
}

BusEtaBot contains all the bot's dependencies

func NewBot

func NewBot(handlers Handlers, tg *tgbotapi.BotAPI, dm ETAService, sv *StreetViewAPI, mp *MeasurementProtocolClient) BusEtaBot

NewBot creates a new Bus Eta Bot with the provided tgbotapi.BotAPI and datamall.APIClient.

func (*BusEtaBot) Dispatch

func (bot *BusEtaBot) Dispatch(ctx context.Context, responses <-chan Response)

Dispatch makes requests to the Telegram Bot API for each response in responses.

func (*BusEtaBot) HandleUpdate

func (bot *BusEtaBot) HandleUpdate(ctx context.Context, update *tgbotapi.Update)

HandleUpdate dispatches an incoming update to the corresponding handler depending on the update type

func (*BusEtaBot) LogEvent

func (bot *BusEtaBot) LogEvent(ctx context.Context, user *tgbotapi.User, category, action, label string)

LogEvent logs an event to the Measurement Protocol if a MeasurementProtocolClient is set on the bot.

type BusEtas

type BusEtas struct {
	BusStopID   string
	UpdatedTime time.Time
	Services    []IncomingBuses
}

BusEtas represents the calculated time before buses arrive at a bus stop

func CalculateEtas

func CalculateEtas(t time.Time, busArrival datamall.BusArrival) BusEtas

CalculateEtas calculates the time before buses arrive from the LTA DataMall bus arrival response

type BusStop

type BusStop struct {
	BusStopCode string   `json:"code"`
	RoadName    string   `json:"road_name"`
	Description string   `json:"description"`
	Latitude    float64  `json:"latitude"`
	Longitude   float64  `json:"longitude"`
	Services    []string `json:"services"`
}

BusStop represents a bus stop.

type BusStopGetter added in v4.2.0

type BusStopGetter interface {
	Get(ID string) *BusStop
}

type BusStopRepository

type BusStopRepository interface {
	BusStopGetter
	Nearby(ctx context.Context, lat, lon, radius float64, limit int) (nearby []NearbyBusStop)
	Search(ctx context.Context, query string, limit int) []BusStop
}

BusStopRepository provides bus stop information.

type CallbackData

type CallbackData struct {
	Type       string   `json:"t"`
	BusStopID  string   `json:"b,omitempty"`
	ServiceNos []string `json:"s,omitempty"`
	Argstr     string   `json:"a,omitempty"`
	Formatter  string   `json:"f,omitempty"`
}

CallbackData represents the data to be included with the Refresh inline keyboard button in eta messages.

type CallbackQueryHandler

type CallbackQueryHandler func(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response)

CallbackQueryHandler is a handler for callback queries

type CommandHandler

type CommandHandler func(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message, responses chan<- Response)

CommandHandler is a handler for incoming commands.

type DatastoreUserRepository

type DatastoreUserRepository struct {
}

func (*DatastoreUserRepository) GetFormatter added in v4.2.0

func (r *DatastoreUserRepository) GetFormatter(ctx context.Context, userID int) Formatter

GetFormatter returns the user's preferred ETA formatter, or the default ETA formatter.

func (*DatastoreUserRepository) GetUserFavourites

func (r *DatastoreUserRepository) GetUserFavourites(ctx context.Context, userID int) (favourites []string, err error)

func (*DatastoreUserRepository) SetUserFavourites

func (r *DatastoreUserRepository) SetUserFavourites(ctx context.Context, userID int, favourites []string) error

func (*DatastoreUserRepository) UpdateUserLastSeenTime

func (r *DatastoreUserRepository) UpdateUserLastSeenTime(ctx context.Context, userID int, t time.Time) error

type ETA added in v4.2.0

type ETA struct {
	BusStop  BusStop
	Now      time.Time
	Services []datamall.Service
	Error    string
}

func NewETA added in v4.2.0

func NewETA(ctx context.Context, busStopGetter BusStopGetter, etaService ETAService, request ETARequest) (eta ETA)

type ETAFormatter

type ETAFormatter interface {
	Format(etas BusEtas, services []string) string
}

type ETAMessageFactory added in v4.2.0

type ETAMessageFactory struct {
	// contains filtered or unexported fields
}

func (ETAMessageFactory) Text added in v4.2.0

func (f ETAMessageFactory) Text(ctx context.Context, request ETARequest) (string, error)

type ETARequest added in v4.2.0

type ETARequest struct {
	UserID   int
	Time     time.Time
	Code     string
	Services []string
}

type ETAService

type ETAService interface {
	GetBusArrival(busStopCode string, serviceNo string) (datamall.BusArrival, error)
}

type Favourites

type Favourites struct {
	Favourites []string
}

Favourites contains a user's saved favourites.

type Formatter added in v4.2.0

type Formatter interface {
	Format(eta ETA) (string, error)
}

type FormatterFactory added in v4.2.0

type FormatterFactory interface {
	GetFormatter(ctx context.Context, userID int) Formatter
}

type Handlers

type Handlers struct {
	CommandHandlers           map[string]CommandHandler
	FallbackCommandHandler    MessageHandler
	TextHandler               MessageHandler
	LocationHandler           MessageHandler
	CallbackQueryHandlers     map[string]CallbackQueryHandler
	InlineQueryHandler        func(ctx context.Context, bot *BusEtaBot, ilq *tgbotapi.InlineQuery) error
	ChosenInlineResultHandler func(ctx context.Context, bot *BusEtaBot, cir *tgbotapi.ChosenInlineResult) error
	MessageErrorHandler       func(ctx context.Context, bot *BusEtaBot, message *tgbotapi.Message, err error)
	CallbackErrorHandler      func(ctx context.Context, bot *BusEtaBot, query *tgbotapi.CallbackQuery, err error)
}

Handlers contains all the handlers used by the bot.

func DefaultHandlers

func DefaultHandlers() Handlers

DefaultHandlers returns a default set of handlers.

type HitParsingResult

type HitParsingResult struct {
	Valid          bool            `json:"valid"`
	Hit            string          `json:"hit"`
	ParserMessages []ParserMessage `json:"parserMessage"`
}

HitParsingResult contains whether the a hit is valid and additional information fom the validation server

type InMemoryBusStopRepository

type InMemoryBusStopRepository struct {
	// contains filtered or unexported fields
}

func NewInMemoryBusStopRepository

func NewInMemoryBusStopRepository(busStops []BusStop, synonyms map[string]string) *InMemoryBusStopRepository

func NewInMemoryBusStopRepositoryFromFile

func NewInMemoryBusStopRepositoryFromFile(path, synonymsPath string) (*InMemoryBusStopRepository, error)

func (*InMemoryBusStopRepository) Get

func (*InMemoryBusStopRepository) Nearby

func (r *InMemoryBusStopRepository) Nearby(ctx context.Context, lat, lon, radius float64, limit int) (nearby []NearbyBusStop)

Nearby returns up to limit bus stops which are within a given radius from a point as well as their distance from that point.

func (*InMemoryBusStopRepository) Search

func (r *InMemoryBusStopRepository) Search(ctx context.Context, query string, limit int) []BusStop

type IncomingBuses

type IncomingBuses struct {
	ServiceNo string
	Etas      [3]string
	Loads     [3]string
	Features  [3]string
	Types     [3]string
}

IncomingBuses contains information about incoming buses for a single service at a bus stop.

type MeasurementProtocolClient

type MeasurementProtocolClient struct {
	Endpoint   string
	TrackingID string
	Client     *http.Client
}

MeasurementProtocolClient contains the endpoint, tracking id and http client to use to send hits to the Measurement Protocol

func NewMeasurementProtocolClientWithClient

func NewMeasurementProtocolClientWithClient(tid string, client *http.Client) MeasurementProtocolClient

NewMeasurementProtocolClientWithClient constructs a MeasurementProtocolClient with the provided tid and http.Client

func (MeasurementProtocolClient) LogEvent

func (c MeasurementProtocolClient) LogEvent(userID int, languageCode, category, action, label string) (*http.Response, error)

LogEvent logs an event to the Measurement Protocol.

type MessageHandler

type MessageHandler func(context.Context, *BusEtaBot, *tgbotapi.Message) error

MessageHandler is a handler for incoming messages

type NearbyBusStop

type NearbyBusStop struct {
	BusStop
	Distance float64
}

type ParserMessage

type ParserMessage struct {
	Type        string `json:"messageType"`
	Description string `json:"description"`
	Parameter   string `json:"parameter"`
}

ParserMessage is a message from the validation server parser.

type Response

type Response struct {
	Request telegram.Request
	Error   error
}

type StreetViewAPI

type StreetViewAPI struct {
	Endpoint string
	APIKey   string
}

StreetViewAPI contains information for making a request to the Street View Image API.

func NewStreetViewAPI

func NewStreetViewAPI(key string) StreetViewAPI

NewStreetViewAPI returns a StreetViewAPI with an API key.

func (StreetViewAPI) GetPhotoURLByLocation

func (s StreetViewAPI) GetPhotoURLByLocation(lat, lon float64, width, height int) (string, error)

GetPhotoURLByLocation returns a street view image url based on a latitude and longitude.

type StreetViewProvider

type StreetViewProvider interface {
	GetPhotoURLByLocation(lat, lon float64, width, height int) (string, error)
}

type SummaryETAFormatter

type SummaryETAFormatter struct{}

SummaryETAFormatter displays ETAs for up to the next 3 buses at a bus stop.

func (SummaryETAFormatter) Format

func (SummaryETAFormatter) Format(etas BusEtas, serviceNos []string) string

type TelegramService

type TelegramService interface {
	Do(request telegram.Request) error
}

type TemplateFormatter added in v4.2.0

type TemplateFormatter struct {
	// contains filtered or unexported fields
}

func (TemplateFormatter) Format added in v4.2.0

func (f TemplateFormatter) Format(eta ETA) (string, error)

type User

type User struct {
	LastSeenTime time.Time
	Favourites   []string
}

type UserRepository

type UserRepository interface {
	UpdateUserLastSeenTime(ctx context.Context, userID int, t time.Time) error
	GetUserFavourites(ctx context.Context, userID int) (favourites []string, err error)
	SetUserFavourites(ctx context.Context, userID int, favourites []string) error
}

type ValidationServerResponse

type ValidationServerResponse struct {
	Results []HitParsingResult `json:"hitParsingResult"`
}

ValidationServerResponse is a response from the Measurement Protocol Validation Server

Directories

Path Synopsis
Package mocks is a generated GoMock package.
Package mocks is a generated GoMock package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL