feat(init): Start a bot for tesla
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Matthieu 'JP' DERASSE
2023-06-05 13:41:48 +00:00
commit 444d9b1563
20 changed files with 2105 additions and 0 deletions

174
bot/bot.go Normal file
View File

@ -0,0 +1,174 @@
package bot
import (
"context"
"fmt"
"strings"
"time"
"git.dev.m-and-m.ovh/mderasse/tesla/alert"
"git.dev.m-and-m.ovh/mderasse/tesla/api"
"gopkg.in/telebot.v3/middleware"
log "github.com/sirupsen/logrus"
tele "gopkg.in/telebot.v3"
)
var apiClient *api.Client
// Init will initialize telegram bot.
func Init(alertChan chan api.Availability) {
log.Info("Starting bot initialization")
log.Debug("Loading bot configuration")
config, err := initBotConfig()
if err != nil {
log.Fatalf("Fail to initialize bot configuration. Error: %s", err.Error())
}
apiClient, err = api.NewClient()
if err != nil {
log.Fatalf("Fail to instantiate the HTTP Client. Error: %s", err.Error())
}
pref := tele.Settings{
Token: config.Token,
Poller: &tele.LongPoller{Timeout: 10 * time.Second},
ParseMode: tele.ModeMarkdownV2,
}
bot, err := tele.NewBot(pref)
if err != nil {
log.Fatal(err)
return
}
log.Debug("Initializing bot middlewares")
initMiddlewares(bot, config)
log.Debug("Initializing bot commands")
initCommands(bot, config)
log.Debug("Launching alert handler")
go handleAlert(bot, config, alertChan)
bot.Start()
}
func handleAlert(bot *tele.Bot, config *botConfig, alertChan chan api.Availability) {
for availability := range alertChan {
log.Warn("ALLERLRTEFDF")
for _, chatId := range config.AlertChatIds {
log.Infof("Sending alert to chat %d", chatId)
_, err := bot.Send(
tele.ChatID(chatId),
fmt.Sprintf(
"ALERT\\!\\! Found a Tesla Long Range in *%s* color is: *%d* €",
strings.Join(availability.Paint, " and "),
availability.Price,
),
)
if err != nil {
log.Warnf("Fail to send alert message. Error: %s", err.Error())
}
}
}
}
func initMiddlewares(bot *tele.Bot, config *botConfig) {
log.Infof("Trusting the following chats: %v", config.WhiteListChatIds)
bot.Use(
middleware.Whitelist(config.WhiteListChatIds...),
)
}
func initCommands(bot *tele.Bot, config *botConfig) {
bot.Handle(tele.OnText, func(c tele.Context) error {
msg := c.Text()
if c.Chat().ID < 0 {
if !strings.HasPrefix(msg, fmt.Sprintf("@%s", config.BotName)) {
return nil
}
msg = strings.TrimPrefix(msg, fmt.Sprintf("@%s ", config.BotName))
}
log.Debugf("receive telegram message: %s", msg)
switch strings.ToLower(msg) {
case "help":
return help(c)
case "price":
return price(c)
case "list":
return list(c)
default:
return home(c)
}
})
bot.Handle(&tele.Btn{Unique: "list"}, list)
bot.Handle(&tele.Btn{Unique: "price"}, price)
bot.Handle(&tele.Btn{Unique: "help"}, help)
bot.Handle(&tele.Btn{Unique: "home"}, home)
}
func home(c tele.Context) error {
return c.EditOrReply(
"Welcome to Tesla Alerter\\. What can i do for you ?",
&homeMenu,
)
}
func help(c tele.Context) error {
return c.EditOrReply(
fmt.Sprintf(`I'm a bot that have for main objective to inform you about tesla price\.
I will also send you alert when tesla car are at a price under %d€`, alert.PriceAlert),
&helpMenu,
)
}
func price(c tele.Context) error {
availabilities, err := apiClient.GetAvailabilities(context.Background(), &api.AvailabilityParams{
Query: carFilter,
Count: 1,
})
if err != nil {
log.Warnf("Fail to retrieve availability from tesla website. Error: %s", err.Error())
return c.Send("Fail to retrieve availability from tesla website :(")
}
return c.Reply(
fmt.Sprintf("The lowest price currently found for a *Tesla Long Range* in *%s* color is: *%d* €",
strings.Join(availabilities.Results[0].Paint, " and "),
availabilities.Results[0].Price,
),
)
}
func list(c tele.Context) error {
availabilities, err := apiClient.GetAvailabilities(context.Background(), &api.AvailabilityParams{
Query: carFilter,
Count: 30,
})
if err != nil {
log.Warnf("Fail to retrieve availability from tesla website. Error: %s", err.Error())
return c.Send("Fail to retrieve availability from tesla website :(")
}
availabilitiesStr := fmt.Sprintf("Found *%s* cars\n", availabilities.TotalMatchesFound)
for _, availability := range availabilities.Results {
availabilitiesStr = fmt.Sprintf(
"%sTesla Long Range, color *%s* available in *%s* for *%d*€\n",
availabilitiesStr,
strings.Join(availability.Paint, " and "),
strings.ReplaceAll(availability.City, "-", "\\-"),
availability.Price,
)
}
return c.Reply(availabilitiesStr)
}

16
bot/botMenu.go Normal file
View File

@ -0,0 +1,16 @@
package bot
import tele "gopkg.in/telebot.v3"
var homeMenu tele.ReplyMarkup = tele.ReplyMarkup{
InlineKeyboard: [][]tele.InlineButton{
{tele.InlineButton{Text: "Lowest Price", Unique: "price"}, tele.InlineButton{Text: "List", Unique: "list"}},
{tele.InlineButton{Text: "Help", Unique: "help"}},
},
}
var helpMenu tele.ReplyMarkup = tele.ReplyMarkup{
InlineKeyboard: [][]tele.InlineButton{
{tele.InlineButton{Text: "Back", Unique: "home"}},
},
}

92
bot/config.go Normal file
View File

@ -0,0 +1,92 @@
package bot
import (
"fmt"
"os"
"strconv"
"strings"
"git.dev.m-and-m.ovh/mderasse/tesla/api"
)
var carFilter api.AvailabilityQueryParams = api.AvailabilityQueryParams{
Model: "my",
Condition: "new",
Options: api.OptionsParams{
Trim: []string{"LRAWD"},
},
Arrangeby: "Price",
Order: "asc",
Market: "FR",
Language: "fr",
SuperRegion: "north america",
Lng: 3.0511,
Lat: 50.624,
Zip: "59100",
Range: 0,
Region: "FR",
}
// botConfig contain configuration for the telegram bot.
type botConfig struct {
AlertChatIds []int64
BotName string
WhiteListChatIds []int64
Token string
}
func initBotConfig() (*botConfig, error) {
alertChatIdsArrStr := strings.Split(os.Getenv("TELEGRAM_ALERT_CHAT_IDS"), ",")
whitelistChatIdsArrStr := strings.Split(os.Getenv("TELEGRAM_WHITELIST_CHAT_IDS"), ",")
botName := os.Getenv("TELEGRAM_BOT_NAME")
token := os.Getenv("TELEGRAM_TOKEN")
// FOR DEV
// botName = "M_AND_M_Test_bot"
// token = "6273217607:AAGvMIU6bpc7xFWKDhd4llPH6f9zhgUzirs"
// alertChatIdsArrStr = []string{"648385994", "-894368809", "-893895409"}
// whitelistChatIdsArrStr = []string{"648385994", "-894368809", "-893895409"}
if len(alertChatIdsArrStr) == 0 {
return nil, fmt.Errorf("missing TELEGRAM_ALERT_CHAT_IDS environment variable")
}
if len(whitelistChatIdsArrStr) == 0 {
return nil, fmt.Errorf("missing TELEGRAM_WHITELIST_CHAT_IDS environment variable")
}
if botName == "" {
return nil, fmt.Errorf("missing TELEGRAM_BOT_NAME environment variable")
}
if token == "" {
return nil, fmt.Errorf("missing TELEGRAM_TOKEN environment variable")
}
config := botConfig{
AlertChatIds: []int64{},
BotName: botName,
Token: token,
WhiteListChatIds: []int64{},
}
for _, chatIdStr := range alertChatIdsArrStr {
chatId, err := strconv.ParseInt(chatIdStr, 10, 64)
if err != nil {
return nil, fmt.Errorf("impossible to use TELEGRAM_ALERT_CHAT_IDS. format expected: int64,int64")
}
config.AlertChatIds = append(config.AlertChatIds, chatId)
}
for _, chatIdStr := range whitelistChatIdsArrStr {
chatId, err := strconv.ParseInt(chatIdStr, 10, 64)
if err != nil {
return nil, fmt.Errorf("impossible to use TELEGRAM_WHITELIST_CHAT_IDS. format expected: int64,int64")
}
config.WhiteListChatIds = append(config.AlertChatIds, chatId)
}
return &config, nil
}