Skip to content

Commit

Permalink
Translation to French and add CI/CD
Browse files Browse the repository at this point in the history
- Change dependencies.
- Change library.
- Adding logging.
- Add healthcheck.
- Provide environment thru os env.
- Create Buildah pipeline : OCI does not support HEALTHCHECK yet : opencontainers/image-spec#749 forced to docker format : containers/podman#18904
- Update README.
  • Loading branch information
craftlion authored and mguaylam committed Jul 14, 2024
1 parent bcb968f commit ce0cd4d
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 56 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/oci-publish.yml
Original file line number Diff line number Diff line change
@@ -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) }}"
22 changes: 22 additions & 0 deletions Containerfile
Original file line number Diff line number Diff line change
@@ -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
17 changes: 0 additions & 17 deletions Dockerfile

This file was deleted.

16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -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
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
70 changes: 42 additions & 28 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -187,20 +197,20 @@ 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

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 {
Expand All @@ -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
Expand All @@ -234,18 +244,22 @@ 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]

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)
Expand Down

0 comments on commit ce0cd4d

Please sign in to comment.