mirror of
https://github.com/aykhans/azal-bot.git
synced 2025-04-20 22:07:16 +00:00
🎉first commit
This commit is contained in:
commit
1574f3014c
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
binaries/
|
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
FROM golang:1.22.6-alpine AS builder
|
||||
|
||||
WORKDIR /azalbot
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY main.go ./main.go
|
||||
|
||||
RUN go build -ldflags "-s -w" -o azal-bot
|
||||
|
||||
FROM gcr.io/distroless/static-debian12:latest
|
||||
|
||||
WORKDIR /azalbot
|
||||
|
||||
COPY --from=builder /azalbot/azal-bot /azalbot/azal-bot
|
||||
|
||||
ENTRYPOINT ["./azal-bot"]
|
81
README.md
Normal file
81
README.md
Normal file
@ -0,0 +1,81 @@
|
||||
<h1 align="center">Azal-Bot: Receive Notifications for Flights on https://azal.az</h1>
|
||||
|
||||
## Installation
|
||||
|
||||
### With Docker (Recommended)
|
||||
Pull the Docker image from Docker Hub:
|
||||
```sh
|
||||
docker pull aykhans/dodo:latest
|
||||
```
|
||||
|
||||
### With Binary File
|
||||
You can download the binaries from the [releases](https://github.com/aykhans/azal-bot/releases) section.
|
||||
|
||||
### Build from Source
|
||||
To build Azal-Bot from source, you need to have [Go 1.22+](https://golang.org/dl/) installed. Follow these steps:
|
||||
|
||||
1. **Clone the repository:**
|
||||
|
||||
```sh
|
||||
git clone https://github.com/aykhans/azal-bot.git
|
||||
```
|
||||
|
||||
2. **Navigate to the project directory:**
|
||||
|
||||
```sh
|
||||
cd azal-bot
|
||||
```
|
||||
|
||||
3. **Build the project:**
|
||||
|
||||
```sh
|
||||
go build -ldflags "-s -w" -o azal-bot
|
||||
```
|
||||
|
||||
This will generate an executable named `azal-bot` in the project directory.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
Search for flights from 2024-09-24T00:00:00 to 2024-09-27T23:59:59 every 60 seconds, then print the results to the CLI:
|
||||
```sh
|
||||
azal-bot \
|
||||
--first-date 2024-09-24 \
|
||||
--last-date 2024-09-27 \
|
||||
--from NAJ \
|
||||
--to BAK
|
||||
```
|
||||
With Docker:
|
||||
```sh
|
||||
docker run --rm -d \
|
||||
aykhans/azal-bot \
|
||||
--first-date 2024-09-24 \
|
||||
--last-date 2024-09-27 \
|
||||
--from NAJ \
|
||||
--to BAK
|
||||
```
|
||||
|
||||
### With All Flags
|
||||
Search for flights from 2024-09-24T15:00:00 to 2024-09-27T21:32:10 every 120 seconds, print the results to the CLI, and send a notification via Telegram if any flights are found:
|
||||
```sh
|
||||
azal-bot \
|
||||
--first-date 2024-09-24T15:00:00 \
|
||||
--last-date 2024-09-27T21:32:10 \
|
||||
--repeat-interval 120 \ # seconds
|
||||
--from NAJ \
|
||||
--to BAK \
|
||||
--telegram-bot-key "key" \
|
||||
--telegram-chat-id "id"
|
||||
```
|
||||
With Docker:
|
||||
```sh
|
||||
docker run --rm -d \
|
||||
aykhans/azal-bot \
|
||||
--first-date 2024-09-24T15:00:00 \
|
||||
--last-date 2024-09-27T21:32:10 \
|
||||
--repeat-interval 120 \ # seconds
|
||||
--from NAJ \
|
||||
--to BAK \
|
||||
--telegram-bot-key "key" \
|
||||
--telegram-chat-id "id"
|
||||
```
|
32
build.sh
Executable file
32
build.sh
Executable file
@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
platforms=(
|
||||
"darwin,amd64"
|
||||
"darwin,arm64"
|
||||
"freebsd,386"
|
||||
"freebsd,amd64"
|
||||
"freebsd,arm"
|
||||
"linux,386"
|
||||
"linux,amd64"
|
||||
"linux,arm"
|
||||
"linux,arm64"
|
||||
"netbsd,386"
|
||||
"netbsd,amd64"
|
||||
"netbsd,arm"
|
||||
"openbsd,386"
|
||||
"openbsd,amd64"
|
||||
"openbsd,arm"
|
||||
"openbsd,arm64"
|
||||
"windows,386"
|
||||
"windows,amd64"
|
||||
"windows,arm64"
|
||||
)
|
||||
|
||||
for platform in "${platforms[@]}"; do
|
||||
IFS=',' read -r build_os build_arch <<< "$platform"
|
||||
ext=""
|
||||
if [ "$build_os" == "windows" ]; then
|
||||
ext=".exe"
|
||||
fi
|
||||
GOOS="$build_os" GOARCH="$build_arch" go build -ldflags "-s -w" -o "./binaries/azal-bot-$build_os-$build_arch$ext"
|
||||
done
|
10
go.mod
Normal file
10
go.mod
Normal file
@ -0,0 +1,10 @@
|
||||
module github.com/aykhans/azal-bot
|
||||
|
||||
go 1.22.6
|
||||
|
||||
require (
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
)
|
||||
|
||||
require github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
10
go.sum
Normal file
10
go.sum
Normal file
@ -0,0 +1,10 @@
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
514
main.go
Normal file
514
main.go
Normal file
@ -0,0 +1,514 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
RequestURL = "https://azal.az/book/api/flights/search/by-deeplink"
|
||||
Version = "0.1.0"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorNoFlightsAvailable = fmt.Errorf("no flights available")
|
||||
)
|
||||
|
||||
var Colors = struct {
|
||||
reset string
|
||||
Red string
|
||||
Green string
|
||||
Yellow string
|
||||
Orange string
|
||||
Blue string
|
||||
Magenta string
|
||||
Cyan string
|
||||
Gray string
|
||||
White string
|
||||
}{
|
||||
reset: "\033[0m",
|
||||
Red: "\033[31m",
|
||||
Green: "\033[32m",
|
||||
Yellow: "\033[33m",
|
||||
Orange: "\033[38;5;208m",
|
||||
Blue: "\033[34m",
|
||||
Magenta: "\033[35m",
|
||||
Cyan: "\033[36m",
|
||||
Gray: "\033[37m",
|
||||
White: "\033[97m",
|
||||
}
|
||||
|
||||
func Colored(color string, a ...any) string {
|
||||
return color + fmt.Sprint(a...) + Colors.reset
|
||||
}
|
||||
|
||||
type AvialableFlights map[string][]time.Time
|
||||
|
||||
type UserInput struct {
|
||||
FirstDate time.Time
|
||||
LastDate time.Time
|
||||
From string
|
||||
To string
|
||||
TelegramBotKey string
|
||||
TelegramChatID string
|
||||
RepetInterval time.Duration
|
||||
}
|
||||
|
||||
type BotConfig struct {
|
||||
FirstDate time.Time
|
||||
LastDate time.Time
|
||||
From string
|
||||
To string
|
||||
days []string
|
||||
RepetInterval time.Duration
|
||||
}
|
||||
|
||||
type ResponseTime struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
func (responseTime *ResponseTime) UnmarshalJSON(b []byte) error {
|
||||
s := string(b)
|
||||
s = s[1 : len(s)-1]
|
||||
|
||||
t, err := time.Parse("2006-01-02T15:04:05", s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
responseTime.Time = t
|
||||
return nil
|
||||
}
|
||||
|
||||
type SuccessResponse struct {
|
||||
Search struct {
|
||||
OptionSets []struct {
|
||||
Options []struct {
|
||||
ID string `json:"id"`
|
||||
Available bool `json:"available"`
|
||||
Route struct {
|
||||
ID string `json:"id"`
|
||||
DepartureDate ResponseTime `json:"departureDate"`
|
||||
} `json:"route"`
|
||||
} `json:"options"`
|
||||
} `json:"optionSets"`
|
||||
} `json:"search"`
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
Error struct {
|
||||
Code string `json:"code"`
|
||||
Text string `json:"text"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
type HeaderConfig struct {
|
||||
Host string `req_header:"Host"`
|
||||
UserAgent string `req_header:"User-Agent"`
|
||||
Accept string `req_header:"Accept"`
|
||||
AcceptLanguage string `req_header:"Accept-Language"`
|
||||
AcceptEncoding string `req_header:"Accept-Encoding"`
|
||||
XApplication string `req_header:"x-application"`
|
||||
XLocale string `req_header:"x-locale"`
|
||||
Connection string `req_header:"Connection"`
|
||||
Referer string `req_header:"Referer"`
|
||||
SecFetchDest string `req_header:"Sec-Fetch-Dest"`
|
||||
SecFetchMode string `req_header:"Sec-Fetch-Mode"`
|
||||
SecFetchSite string `req_header:"Sec-Fetch-Site"`
|
||||
TE string `req_header:"TE"`
|
||||
}
|
||||
|
||||
func (headerConf *HeaderConfig) setDefaults() {
|
||||
if headerConf.Host == "" {
|
||||
headerConf.Host = "book.azal.az"
|
||||
}
|
||||
if headerConf.UserAgent == "" {
|
||||
headerConf.UserAgent = "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0"
|
||||
}
|
||||
if headerConf.Accept == "" {
|
||||
headerConf.Accept = "application/json, text/plain, */*"
|
||||
}
|
||||
if headerConf.AcceptLanguage == "" {
|
||||
headerConf.AcceptLanguage = "en-US,en;q=0.5"
|
||||
}
|
||||
if headerConf.AcceptEncoding == "" {
|
||||
headerConf.AcceptEncoding = "gzip, deflate, br"
|
||||
}
|
||||
if headerConf.XApplication == "" {
|
||||
headerConf.XApplication = "ibe"
|
||||
}
|
||||
if headerConf.XLocale == "" {
|
||||
headerConf.XLocale = "az"
|
||||
}
|
||||
if headerConf.Connection == "" {
|
||||
headerConf.Connection = "keep-alive"
|
||||
}
|
||||
if headerConf.SecFetchDest == "" {
|
||||
headerConf.SecFetchDest = "empty"
|
||||
}
|
||||
if headerConf.SecFetchMode == "" {
|
||||
headerConf.SecFetchMode = "cors"
|
||||
}
|
||||
if headerConf.SecFetchSite == "" {
|
||||
headerConf.SecFetchSite = "same-origin"
|
||||
}
|
||||
if headerConf.TE == "" {
|
||||
headerConf.TE = "trailers"
|
||||
}
|
||||
}
|
||||
|
||||
func (headerConf *HeaderConfig) setToRequest(req *http.Request) {
|
||||
t := reflect.TypeOf(*headerConf)
|
||||
v := reflect.ValueOf(headerConf).Elem()
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
tag := field.Tag.Get("req_header")
|
||||
value := v.Field(i).String()
|
||||
req.Header.Set(tag, value)
|
||||
}
|
||||
}
|
||||
|
||||
type QueryConfig struct {
|
||||
Lang string `req_query:"lang"`
|
||||
From string `req_query:"from"`
|
||||
To string `req_query:"to"`
|
||||
DepartureDate string `req_query:"departure_date"`
|
||||
TripType string `req_query:"tripType"`
|
||||
AdultCount string `req_query:"adult_count"`
|
||||
ChildCount string `req_query:"child_count"`
|
||||
InfantCount string `req_query:"infant_count"`
|
||||
IsStudent string `req_query:"is_student"`
|
||||
Timestamp string `req_query:"timestamp"`
|
||||
IsCitizen string `req_query:"is_citizen"`
|
||||
Currency string `req_query:"currency"`
|
||||
Theme string `req_query:"theme"`
|
||||
}
|
||||
|
||||
func (queryConf *QueryConfig) setDefaults() {
|
||||
if queryConf.Lang == "" {
|
||||
queryConf.Lang = "az"
|
||||
}
|
||||
if queryConf.TripType == "" {
|
||||
queryConf.TripType = "OW"
|
||||
}
|
||||
if queryConf.AdultCount == "" {
|
||||
queryConf.AdultCount = "1"
|
||||
}
|
||||
if queryConf.ChildCount == "" {
|
||||
queryConf.ChildCount = "0"
|
||||
}
|
||||
if queryConf.InfantCount == "" {
|
||||
queryConf.InfantCount = "0"
|
||||
}
|
||||
if queryConf.IsStudent == "" {
|
||||
queryConf.IsStudent = "0"
|
||||
}
|
||||
if queryConf.Timestamp == "" {
|
||||
queryConf.Timestamp = fmt.Sprintf("%d", time.Now().UnixNano()/int64(time.Millisecond))
|
||||
}
|
||||
if queryConf.IsCitizen == "" {
|
||||
queryConf.IsCitizen = "1"
|
||||
}
|
||||
if queryConf.Currency == "" {
|
||||
queryConf.Currency = "AZN"
|
||||
}
|
||||
if queryConf.Theme == "" {
|
||||
queryConf.Theme = "dark"
|
||||
}
|
||||
}
|
||||
|
||||
func (queryConf *QueryConfig) setToRequest(req *http.Request) {
|
||||
q := req.URL.Query()
|
||||
t := reflect.TypeOf(*queryConf)
|
||||
v := reflect.ValueOf(queryConf).Elem()
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
tag := field.Tag.Get("req_query")
|
||||
value := v.Field(i).String()
|
||||
q.Add(tag, value)
|
||||
}
|
||||
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
func handleErrorResponse(errorResponse *ErrorResponse) error {
|
||||
switch errorResponse.Error.Code {
|
||||
case "no.flights.available":
|
||||
return ErrorNoFlightsAvailable
|
||||
default:
|
||||
return fmt.Errorf("unknown error: %s", errorResponse.Error.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func sendRequest(queryConf *QueryConfig, headerConf *HeaderConfig) (*SuccessResponse, error) {
|
||||
req, err := http.NewRequest("GET", RequestURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
headerConf.setToRequest(req)
|
||||
queryConf.setToRequest(req)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data map[string]interface{}
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("status code: %d", resp.StatusCode)
|
||||
}
|
||||
if err := json.Unmarshal(respBody, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
errorResponseData := &ErrorResponse{}
|
||||
if err := json.Unmarshal(respBody, &errorResponseData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if errorResponseData.Error.Code != "" {
|
||||
return nil, handleErrorResponse(errorResponseData)
|
||||
}
|
||||
successResponseData := &SuccessResponse{}
|
||||
if err := json.Unmarshal(respBody, &successResponseData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return successResponseData, nil
|
||||
}
|
||||
|
||||
func getUserInput() *UserInput {
|
||||
var (
|
||||
firstDate,
|
||||
lastDate,
|
||||
from,
|
||||
to,
|
||||
telegramBotKey,
|
||||
telegramChatID string
|
||||
repetInterval uint32
|
||||
userInput = &UserInput{}
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "Azal Bot",
|
||||
Short: "A CLI tool to find the flights",
|
||||
Version: Version,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
first, err := time.Parse("2006-01-02T15:04:05", firstDate)
|
||||
if err != nil {
|
||||
first, err = time.Parse("2006-01-02", firstDate)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: parsing FirstDate: %v\n", err)
|
||||
cmd.Help()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
last, err := time.Parse("2006-01-02T15:04:05", lastDate)
|
||||
if err != nil {
|
||||
last, err = time.Parse("2006-01-02", lastDate)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: parsing LastDate: %v\n", err)
|
||||
cmd.Help()
|
||||
os.Exit(1)
|
||||
}
|
||||
last = last.AddDate(0, 0, 1)
|
||||
last = last.Add(-time.Second)
|
||||
}
|
||||
if first.After(last) || first.Equal(last) {
|
||||
fmt.Println("Error: first date should be before last date and they should not be equal")
|
||||
cmd.Help()
|
||||
os.Exit(1)
|
||||
}
|
||||
if repetInterval < 1 {
|
||||
fmt.Println("Error: repetInterval should be greater than 0")
|
||||
cmd.Help()
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(from) > 5 || len(from) < 2 {
|
||||
fmt.Println("Error: from should be between 2 and 5 characters")
|
||||
cmd.Help()
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(to) > 5 || len(to) < 2 {
|
||||
fmt.Println("Error: to should be between 2 and 5 characters")
|
||||
cmd.Help()
|
||||
os.Exit(1)
|
||||
}
|
||||
if telegramBotKey != "" {
|
||||
if telegramChatID == "" {
|
||||
fmt.Println("Error: telegramChatID is required if telegramBotKey is provided")
|
||||
cmd.Help()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
if telegramChatID != "" {
|
||||
if telegramBotKey == "" {
|
||||
fmt.Println("Error: telegramBotKey is required if telegramChatID is provided")
|
||||
cmd.Help()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
userInput.FirstDate = first
|
||||
userInput.LastDate = last
|
||||
userInput.From = from
|
||||
userInput.To = to
|
||||
userInput.TelegramBotKey = telegramBotKey
|
||||
userInput.TelegramChatID = telegramChatID
|
||||
userInput.RepetInterval = time.Duration(repetInterval) * time.Second
|
||||
},
|
||||
}
|
||||
|
||||
rootCmd.Flags().StringVarP(&firstDate, "first-date", "i", "", "First date in format '2006-01-02T15:04:05'")
|
||||
rootCmd.Flags().StringVarP(&lastDate, "last-date", "l", "", "Last date in format '2006-01-02T15:04:05'")
|
||||
rootCmd.Flags().StringVarP(&from, "from", "f", "", "From where you want to fly (e.g. NAJ)")
|
||||
rootCmd.Flags().StringVarP(&to, "to", "t", "", "To where you want to fly (e.g. BAK)")
|
||||
rootCmd.Flags().StringVar(&telegramBotKey, "telegram-bot-key", "", "Telegram bot key")
|
||||
rootCmd.Flags().StringVar(&telegramChatID, "telegram-chat-id", "", "Telegram chat id")
|
||||
rootCmd.Flags().Uint32VarP(&repetInterval, "repet-interval", "r", 60, "Repetition interval in seconds")
|
||||
|
||||
rootCmd.MarkFlagRequired("first-date")
|
||||
rootCmd.MarkFlagRequired("last-date")
|
||||
rootCmd.MarkFlagRequired("from")
|
||||
rootCmd.MarkFlagRequired("to")
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
rootCmd.Flags().Visit(func(flag *pflag.Flag) {
|
||||
switch flag.Name {
|
||||
case "version":
|
||||
os.Exit(0)
|
||||
case "help":
|
||||
os.Exit(0)
|
||||
}
|
||||
})
|
||||
|
||||
return userInput
|
||||
}
|
||||
|
||||
func sendTelegramMessage(avialableFlights AvialableFlights, botKey string, chatID string) error {
|
||||
url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", botKey)
|
||||
|
||||
req, err := http.NewRequest("POST", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
message := "Azal Bot\n\n"
|
||||
for day, flights := range avialableFlights {
|
||||
message += fmt.Sprintf("%s\n-----------\n", day)
|
||||
for _, flight := range flights {
|
||||
message += fmt.Sprintf("%s\n", flight.Format("15:04:05"))
|
||||
}
|
||||
message += "\n"
|
||||
}
|
||||
message = message[:len(message)-1]
|
||||
q := req.URL.Query()
|
||||
q.Add("chat_id", chatID)
|
||||
q.Add("text", message)
|
||||
q.Add("parse_mode", "HTML")
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("telegram send message status code: %d", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func startBot(botConfig *BotConfig, ifAvailable func(avialableFlights AvialableFlights) error) {
|
||||
queryConf := QueryConfig{
|
||||
From: botConfig.From,
|
||||
To: botConfig.To,
|
||||
}
|
||||
queryConf.setDefaults()
|
||||
headerConf := HeaderConfig{}
|
||||
headerConf.setDefaults()
|
||||
|
||||
for {
|
||||
avialableFlights := make(AvialableFlights)
|
||||
for _, day := range botConfig.days {
|
||||
queryConf.DepartureDate = day
|
||||
data, err := sendRequest(&queryConf, &headerConf)
|
||||
if err != nil {
|
||||
if err == ErrorNoFlightsAvailable {
|
||||
log.Println(Colored(Colors.Yellow, "No flights available for ", day))
|
||||
continue
|
||||
}
|
||||
log.Println(Colored(Colors.Red, err.Error()))
|
||||
continue
|
||||
}
|
||||
|
||||
for _, option := range data.Search.OptionSets[0].Options {
|
||||
departureDate := option.Route.DepartureDate
|
||||
if (departureDate.After(botConfig.FirstDate) || departureDate.Equal(botConfig.FirstDate)) &&
|
||||
(departureDate.Before(botConfig.LastDate) || departureDate.Equal(botConfig.LastDate)) {
|
||||
|
||||
avialableFlights[day] = append(avialableFlights[day], departureDate.Time)
|
||||
log.Println(Colored(Colors.Green, "Flight available for ", departureDate))
|
||||
} else {
|
||||
log.Println(Colored(Colors.Yellow, "No flights available for ", departureDate))
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := ifAvailable(avialableFlights); err != nil {
|
||||
log.Println(Colored(Colors.Red, "Error: ", err.Error()))
|
||||
}
|
||||
time.Sleep(botConfig.RepetInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
userInput := getUserInput()
|
||||
botConfig := &BotConfig{
|
||||
FirstDate: userInput.FirstDate,
|
||||
LastDate: userInput.LastDate,
|
||||
From: userInput.From,
|
||||
To: userInput.To,
|
||||
RepetInterval: userInput.RepetInterval,
|
||||
}
|
||||
for current := userInput.FirstDate; !current.After(userInput.LastDate); current = current.AddDate(0, 0, 1) {
|
||||
botConfig.days = append(botConfig.days, current.Format("2006-01-02"))
|
||||
}
|
||||
|
||||
ifAvailableFunc := func(avialableFlights AvialableFlights) error { return nil }
|
||||
if userInput.TelegramBotKey != "" {
|
||||
ifAvailableFunc = func(avialableFlights AvialableFlights) error {
|
||||
if len(avialableFlights) > 0 {
|
||||
return sendTelegramMessage(
|
||||
avialableFlights,
|
||||
userInput.TelegramBotKey,
|
||||
userInput.TelegramChatID,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
startBot(
|
||||
botConfig,
|
||||
ifAvailableFunc,
|
||||
)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user