diff --git a/.github/workflows/oci-publish.yml b/.github/workflows/oci-publish.yml new file mode 100644 index 0000000..275702e --- /dev/null +++ b/.github/workflows/oci-publish.yml @@ -0,0 +1,55 @@ +# This workflow will perform a test whenever there +# is some change in code done to ensure that the changes +# are not buggy and we are getting the desired output. +name: Push to GHCR +on: + push: + workflow_dispatch: +env: + IMAGE_NAME: communautofinder_telegrambot + IMAGE_TAGS: latest + IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }} + REGISTRY_USER: ${{ github.actor }} + REGISTRY_PASSWORD: ${{ github.token }} + +jobs: + push-ghcr: + name: Build and push image + runs-on: ubuntu-22.04 + + steps: + # Checkout push-to-registry action github repository + - name: Checkout Push to Registry action + uses: actions/checkout@v4 + + - name: Install latest podman + if: matrix.install_latest + run: | + bash .github/install_latest_podman.sh + + # Build image using Buildah action + - name: Build Image + id: build_image + uses: redhat-actions/buildah-build@v2 + with: + image: ${{ env.IMAGE_NAME }} + tags: ${{ env.IMAGE_TAGS }} + containerfiles: ./Containerfile + oci: false + + # Push the image to GHCR (Image Registry) + - name: Push To GHCR + uses: redhat-actions/push-to-registry@v2 + id: push + with: + image: ${{ steps.build_image.outputs.image }} + tags: ${{ steps.build_image.outputs.tags }} + registry: ${{ env.IMAGE_REGISTRY }} + username: ${{ env.REGISTRY_USER }} + password: ${{ env.REGISTRY_PASSWORD }} + extra-args: | + --disable-content-trust + + - name: Echo outputs + run: | + echo "${{ toJSON(steps.push.outputs) }}" \ No newline at end of file diff --git a/Containerfile b/Containerfile new file mode 100644 index 0000000..5ed6fdf --- /dev/null +++ b/Containerfile @@ -0,0 +1,22 @@ +FROM golang:alpine as build + +WORKDIR /app + +COPY go.mod ./ +COPY go.sum ./ +RUN go mod download + +COPY *.go ./ + +RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o ./communautoFinderBot +RUN go clean -cache -testcache -fuzzcache -modcache + +FROM alpine +RUN apk update && apk add --no-cache curl +COPY --from=build /app /app +WORKDIR /app +EXPOSE 8443 8444 + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD curl http://localhost:8444/health || exit 1 + +CMD ./communautoFinderBot \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 166e7b7..0000000 --- a/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# syntax=docker/dockerfile:1 - -FROM golang:latest - -WORKDIR /app - -COPY go.mod ./ -COPY go.sum ./ -RUN go mod download - -COPY *.go ./ - -RUN go build -o ./communautoFinderBot - -EXPOSE 8443 - -CMD [ "./communautoFinderBot" ] \ No newline at end of file diff --git a/README.md b/README.md index 4b53900..857921d 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,25 @@ ## Goal -The goal of this Telegram bot is to provide an interface for launching a Communauto car search on Telegram. When it finds at least one car, the bot will return the number of cars found. +The goal of this Telegram bot is to provide an interface for launching a Communauto car search on Telegram. When it finds at least one car, the bot will return the number of cars found. Interaction with the bot is in french. ## Commands -- **/help**: View available commands. -- **/start**: Begin a discussion with the bot to set search parameters. -- **/restart**: Start a new search with the same parameters as the previous one. +- **/aide**: View available commands. +- **/chercher**: Begin a discussion with the bot to set search parameters. +- **/recommencer**: Start a new search with the same parameters as the previous one. ## Usage -To use this bot, you need to define an environment variable named _TOKEN_COMMUNAUTOSEARCH_BOT_ in a .env file and set its value to your Telegram token. +To use this bot, you need to define an system environment variable named _TOKEN_COMMUNAUTOSEARCH_BOT_ and set its value to your Telegram token. After that, run `go run main.go` to launch the bot. Then, search for the bot on Telegram to start a discussion with it. ## Dependencies -- [communautofinder](https://github.com/craftlion/communautofinder) +- [communautofinder](https://github.com/mguaylam/communautofinder) - [telegram-bot-api](https://github.com/go-telegram-bot-api/telegram-bot-api) + +## Thank you + +Thanks to craftlion for writing all of this. I forked his project to adapt it to my needs. \ No newline at end of file diff --git a/go.mod b/go.mod index fab74f8..f393727 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ -module github.com/craftlion/communautofinder_telegrambot +module github.com/mguaylam/communautofinder_telegrambot go 1.20 require github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible -require github.com/joho/godotenv v1.5.1 +require github.com/joho/godotenv v1.5.1 // indirect require ( - github.com/craftlion/communautofinder v1.2.0 + github.com/mguaylam/communautofinder v1.2.1 github.com/technoweenie/multipartstreamer v1.0.1 // indirect ) diff --git a/go.sum b/go.sum index b35eab5..a61dec0 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ -github.com/craftlion/communautofinder v1.2.0 h1:eblDFzB36erPjZLRQC4lR5va2AP9Bh8OYB8MqfjRSyA= -github.com/craftlion/communautofinder v1.2.0/go.mod h1:aUPA9bhCTDcsHax45j1149Hql6d1hNMLwI9TAAYEfso= github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU= github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/mguaylam/communautofinder v1.2.1 h1:vvSp4tA/jVxHsq1g0LRV1eSozNPtmEU5tJHRewNaf0A= +github.com/mguaylam/communautofinder v1.2.1/go.mod h1:dnvRAoTOGp8BHCY084W1QqUWGQ03hS/ZiX8yrdvz418= github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= diff --git a/main.go b/main.go index eea59c2..e3d628f 100644 --- a/main.go +++ b/main.go @@ -4,16 +4,16 @@ import ( "context" "fmt" "log" + "net/http" "os" "strconv" "strings" "sync" "time" - "github.com/joho/godotenv" - - "github.com/craftlion/communautofinder" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" + "github.com/joho/godotenv" + "github.com/mguaylam/communautofinder" ) // Possible states in conversation with the bot @@ -60,18 +60,22 @@ var mutex = sync.Mutex{} func main() { - // Find .env file - err := godotenv.Load(".env") - if err != nil { - log.Fatalf("Error loading .env file: %s", err) - } + http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + go http.ListenAndServe(":8444", nil) + + // Find TOKEN in .env file if exist + godotenv.Load() + var err error bot, err = tgbotapi.NewBotAPI(os.Getenv("TOKEN_COMMUNAUTOSEARCH_BOT")) if err != nil { log.Fatal(err) } - log.Printf("Authorized on account %s", bot.Self.UserName) + log.Printf("Authorized on Telegram account %s", bot.Self.UserName) u := tgbotapi.NewUpdate(0) u.Timeout = 60 @@ -114,26 +118,30 @@ func generateResponse(userCtx *UserContext, message *tgbotapi.Message) string { messageText := message.Text - if strings.ToLower(messageText) == "/help" { - return "Type:\n/start to initiate a new search\n/restart to restart a search with the same parameters as the previous search" - } else if strings.ToLower(messageText) == "/start" { + if strings.ToLower(messageText) == "/aide" { + return "Écrire:\n/chercher pour initier une nouvelle recherche.\n/recommencer pour redémarrer une recherche avec les mêmes paramètres que la recherche précédente." + } else if strings.ToLower(messageText) == "/chercher" { if userCtx.state == Searching { + log.Printf("Cancelling searching for user " + strconv.FormatInt(userCtx.chatId, 10)) cancelSearchingMethod[userCtx.chatId]() } userCtx.state = AskingType - return "Hello! Type:\n- station to search for a communauto station\n- flex to search for a communauto flex vehicle ?" + log.Printf("Asking user " + strconv.FormatInt(userCtx.chatId, 10) + " vehicule type") + return "Bonjour ! Tapez :\n- station pour rechercher une Communauto en station.\n- flex pour rechercher un véhicule Communauto Flex." } else if userCtx.state == AskingType { if strings.ToLower(messageText) == "station" { userCtx.searchType = Station userCtx.state = AskingMargin - return "What is your search radius in Km ?" + log.Printf("Asking user " + strconv.FormatInt(userCtx.chatId, 10) + " station radius search") + return "Quelle est votre distance de recherche en kilomètres ?" } else if strings.ToLower(messageText) == "flex" { userCtx.searchType = Flex userCtx.state = AskingMargin - return "What is your search radius in Km ?" + log.Printf("Asking user " + strconv.FormatInt(userCtx.chatId, 10) + " flex radius search") + return "Quelle est votre distance de recherche en kilomètres ?" } } else if userCtx.state == AskingMargin { @@ -144,11 +152,12 @@ func generateResponse(userCtx *UserContext, message *tgbotapi.Message) string { if margin > 0 { userCtx.kmMargin = margin userCtx.state = AskingPosition - return "Please share the GPS location for your search" + log.Printf("Asking user " + strconv.FormatInt(userCtx.chatId, 10) + " location") + return "Veuillez partager votre position pour votre recherche." } } - return "Please enter a correct search radius" + return "Veuillez entrer un rayon de recherche correct." } else if userCtx.state == AskingPosition { if message.Location != nil { @@ -162,8 +171,8 @@ func generateResponse(userCtx *UserContext, message *tgbotapi.Message) string { } else if userCtx.searchType == Station { userCtx.state = AskingDateStart - - return fmt.Sprintf("What is the start date and time for the rental in the format %s ?", dateExample) + log.Printf("Asking user " + strconv.FormatInt(userCtx.chatId, 10) + " start date and time for station") + return fmt.Sprintf("Quelle est la date et l'heure de début de la location au format %s ?", dateExample) } } } else if userCtx.state == AskingDateStart { @@ -173,7 +182,8 @@ func generateResponse(userCtx *UserContext, message *tgbotapi.Message) string { if err == nil { userCtx.dateStart = t userCtx.state = AskingDateEnd - return fmt.Sprintf("What is the end date and time for the rental in the format %s ?", dateExample) + log.Printf("Asking user " + strconv.FormatInt(userCtx.chatId, 10) + " end date and time for station") + return fmt.Sprintf("Quelle est la date et l'heure de fin de la location au format %s ?", dateExample) } } else if userCtx.state == AskingDateEnd { @@ -187,7 +197,7 @@ func generateResponse(userCtx *UserContext, message *tgbotapi.Message) string { return generateMessageResearch(*userCtx) } - } else if strings.ToLower(messageText) == "/restart" { + } else if strings.ToLower(messageText) == "/recommencer" { if userCtx.state == EndSearch { userCtx.state = Searching @@ -195,12 +205,12 @@ func generateResponse(userCtx *UserContext, message *tgbotapi.Message) string { go launchSearch(*userCtx) return generateMessageResearch(*userCtx) } else { - return "Please initiate a new search before restarting it." + return "Veuillez initier une nouvelle recherche avant de la redémarrer." } } - - return "I didn't quite understand 😕" + log.Printf("Invalid input from user " + strconv.FormatInt(userCtx.chatId, 10)) + return "Je n'ai pas bien compris. 😕" } func generateMessageResearch(userCtx UserContext) string { @@ -215,10 +225,10 @@ func generateMessageResearch(userCtx UserContext) string { roundedKmMargin := int(userCtx.kmMargin) - message := fmt.Sprintf("🔍 Searching for a %s vehicle within %dkm of the position you entered... you will receive a message when one is found", typeSearch, roundedKmMargin) + message := fmt.Sprintf("🔍 Recherche d'un véhicule %s dans un rayon de %dkm autour de la position que vous avez entrée. Vous recevrez un message lorsque l'un sera trouvé.", typeSearch, roundedKmMargin) if userCtx.searchType == Station { - message += fmt.Sprintf(" from %s to %s", userCtx.dateStart.Format(layoutDate), userCtx.dateEnd.Format(layoutDate)) + message += fmt.Sprintf(" de %s a %s", userCtx.dateStart.Format(layoutDate), userCtx.dateEnd.Format(layoutDate)) } return message @@ -234,8 +244,10 @@ func launchSearch(userCtx UserContext) { if userCtx.searchType == Flex { go communautofinder.SearchFlexCarForGoRoutine(cityId, currentCoordinate, userCtx.kmMargin, resultChannel[userCtx.chatId], ctx, cancel) + log.Printf("Searching a flex vehicule for user " + strconv.FormatInt(userCtx.chatId, 10)) } else if userCtx.searchType == Station { go communautofinder.SearchStationCarForGoRoutine(cityId, currentCoordinate, userCtx.kmMargin, userCtx.dateStart, userCtx.dateEnd, resultChannel[userCtx.chatId], ctx, cancel) + log.Printf("Searching a station vehicule for user " + strconv.FormatInt(userCtx.chatId, 10)) } nbCarFound := <-resultChannel[userCtx.chatId] @@ -243,9 +255,11 @@ func launchSearch(userCtx UserContext) { var msg tgbotapi.MessageConfig if nbCarFound != -1 { - msg = tgbotapi.NewMessage(userCtx.chatId, fmt.Sprintf("💡 Found ! %d vehicle(s) available according to your search criteria", nbCarFound)) + msg = tgbotapi.NewMessage(userCtx.chatId, fmt.Sprintf("💡 Trouvé ! %d véhicule(s) disponible(s) selon vos critères de recherche.", nbCarFound)) + log.Printf("Found vehicule(s) for user " + strconv.FormatInt(userCtx.chatId, 10)) } else { - msg = tgbotapi.NewMessage(userCtx.chatId, "😞 An error occurred in your search criteria. Please launch a new search") + msg = tgbotapi.NewMessage(userCtx.chatId, "😞 Une erreur est survenue dans vos critères de recherche. Veuillez lancer une nouvelle recherche.") + log.Printf("Search failure for user " + strconv.FormatInt(userCtx.chatId, 10)) } bot.Send(msg)