From 91d572fb1ae9c2da8425663f8ceca4e58dcd02f3 Mon Sep 17 00:00:00 2001 From: napnap75 Date: Sun, 16 Jun 2024 18:36:16 +0200 Subject: [PATCH] Moved the parameters to env variable + Added healthchecks --- immich-souvenirs/README.md | 2 +- immich-souvenirs/immich-souvenirs.go | 211 ++++++++++++++------------- 2 files changed, 110 insertions(+), 103 deletions(-) diff --git a/immich-souvenirs/README.md b/immich-souvenirs/README.md index 2c699c9..b8df464 100644 --- a/immich-souvenirs/README.md +++ b/immich-souvenirs/README.md @@ -4,4 +4,4 @@ apk add --no-cache git gcc musl-dev nano cd src/napnap75/immich-souvenirs/ go mod init github.com/napnap75/multiarch-docker-files/immich-souvenirs go get -d -v -go run immich-souvenirs.go --immich-url="https://immich.nappez.com" --immich-key="wRJWYUGlEfWGQ3a5lckHivLCU40s7ldEZSmikpmsE30" --whatsapp-session-file=/config/ws.gob --whatsapp-group="120363288639885954@g.us" --run-once +env IMMICH-URL="https://immich.nappez.com" env IMMICH-KEY="wRJWYUGlEfWGQ3a5lckHivLCU40s7ldEZSmikpmsE30" env WHATSAPP-SESSION-FILE="/config/ws.gob" env WHATSAPP-GROUP="120363288639885954@g.us" env RUN-ONCE=true go run immich-souvenirs.go diff --git a/immich-souvenirs/immich-souvenirs.go b/immich-souvenirs/immich-souvenirs.go index 4f95c72..50a7f5f 100644 --- a/immich-souvenirs/immich-souvenirs.go +++ b/immich-souvenirs/immich-souvenirs.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "encoding/json" - "flag" "fmt" "io/ioutil" "os" @@ -21,28 +20,34 @@ import ( waLog "go.mau.fi/whatsmeow/util/log" ) -type parameters struct { - immichURL string - immichKey string - whatsappSessionFile string - whatsappGroup string - runOnce bool +type Parameters struct { + ImmichURL string + ImmichKey string + WhatsappSessionFile string + WhatsappGroup string + RunOnce bool + HealthchecksURL string } -func loadParameters() (parameters) { - param := new(parameters) - flag.StringVar(¶m.immichURL, "immich-url", "", "The url of the Immich server to connect to") - flag.StringVar(¶m.immichKey, "immich-key", "", "The API KEY to use with Immich") - flag.StringVar(¶m.whatsappSessionFile, "whatsapp-session-file", "", "The file to save the WhatsApp session to") - flag.StringVar(¶m.whatsappGroup, "whatsapp-group", "", "The ID of the WhatsApp group to send the message to") - flag.BoolVar(¶m.runOnce, "run-once", false, "Run once and exits (default to false)") - flag.Parse() - return *param +type Album struct { + ID string `json:"id"` + Name string `json:"albumName"` + Shared bool `json:"shared"` + HasSharedLink bool `json:"hasSharedLink"` + StartDate time.Time `json:"startDate"` + CreatedAt time.Time `json:"createdAt"` + AlbumThumbnailAssetId string `json:"albumThumbnailAssetId"` } -func connect(param parameters) (*whatsmeow.Client, error) { +type Key struct { + ID string `json:"id"` + Key string `json:"key"` + Album *Album `json:"album"` +} + +func connect(param Parameters) (*whatsmeow.Client, error) { dbLog := waLog.Stdout("Database", "ERROR", true) - container, err := sqlstore.New("sqlite3", "file:" + param.whatsappSessionFile + "?_foreign_keys=on", dbLog) + container, err := sqlstore.New("sqlite3", "file:" + param.WhatsappSessionFile + "?_foreign_keys=on", dbLog) if err != nil { return nil, err } @@ -102,7 +107,7 @@ func sendMessage(client *whatsmeow.Client, group string, message string, url str return nil } -func testConnexions(param parameters) error { +func testConnexions(param Parameters) error { // Create new WhatsApp connection and connect client, err := connect(param) if err != nil { @@ -112,7 +117,7 @@ func testConnexions(param parameters) error { defer client.Disconnect() // Prints the available groups if none provided - if param.whatsappGroup == "" { + if param.WhatsappGroup == "" { fmt.Fprintf(os.Stdout, "No WhatsApp group provided, showing all available groups\n") groups, err := client.GetJoinedGroups() if err != nil { @@ -124,13 +129,13 @@ func testConnexions(param parameters) error { return fmt.Errorf("No WhatsApp group provided") } else { - jid, err := types.ParseJID(param.whatsappGroup) + jid, err := types.ParseJID(param.WhatsappGroup) if err != nil { - return fmt.Errorf("Incorrect group identifier '%s': %v", param.whatsappGroup, err) + return fmt.Errorf("Incorrect group identifier '%s': %v", param.WhatsappGroup, err) } _, err = client.GetGroupInfo(jid) if err != nil { - return fmt.Errorf("Unknown WhatsApp group %s", param.whatsappGroup) + return fmt.Errorf("Unknown WhatsApp group %s", param.WhatsappGroup) } } @@ -138,147 +143,143 @@ func testConnexions(param parameters) error { spaceClient := http.Client{ Timeout: time.Second * 10, } - req, err := http.NewRequest(http.MethodGet, param.immichURL + "/api/albums", nil) + req, err := http.NewRequest(http.MethodGet, param.ImmichURL + "/api/albums", nil) if err != nil { - return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.immichURL, err) + return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.ImmichURL, err) } - req.Header.Set("x-api-key", param.immichKey) + req.Header.Set("x-api-key", param.ImmichKey) req.Header.Set("Accept", "application/json") res, err := spaceClient.Do(req) if err != nil { - return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.immichURL, err) + return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.ImmichURL, err) } if res.Body != nil { defer res.Body.Close() } if res.StatusCode != 200 { - return fmt.Errorf("Error connecting to Immich with URL '%s': Status code %d", param.immichURL, res.StatusCode) + return fmt.Errorf("Error connecting to Immich with URL '%s': Status code %d", param.ImmichURL, res.StatusCode) } body, err := ioutil.ReadAll(res.Body) if err != nil { - return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.immichURL, err) + return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.ImmichURL, err) } - var albums []map[string]interface{} + var albums []Album err = json.Unmarshal([]byte(body), &albums) if err != nil { - return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.immichURL, err) + return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.ImmichURL, err) } return nil } -func getSharingKey(album map[string]interface{}, param parameters) (string, error) { +func getSharingKey(album Album, param Parameters) (string, error) { spaceClient := http.Client{ Timeout: time.Second * 10, } - albumId := album["id"].(string) - albumName := album["albumName"].(string) - if (album["hasSharedLink"].(bool)) { + if (album.HasSharedLink) { // Retrieve the existing key - req, err := http.NewRequest(http.MethodGet, param.immichURL + "/api/shared-links", nil) + req, err := http.NewRequest(http.MethodGet, param.ImmichURL + "/api/shared-links", nil) if err != nil { - return "", fmt.Errorf("Error retrieving sharing key for album '%s': %v", albumName, err) + return "", fmt.Errorf("Error retrieving sharing key for album '%s': %v", album.Name, err) } - req.Header.Set("x-api-key", param.immichKey) + req.Header.Set("x-api-key", param.ImmichKey) req.Header.Set("Accept", "application/json") res, err := spaceClient.Do(req) if err != nil { - return "", fmt.Errorf("Error retrieving sharing key for album '%s': %v", albumName, err) + return "", fmt.Errorf("Error retrieving sharing key for album '%s': %v", album.Name, err) } if res.Body != nil { defer res.Body.Close() } if res.StatusCode != 200 { - return "", fmt.Errorf("Error retrieving sharing key for album '%s': Status code %d", albumName, res.StatusCode) + return "", fmt.Errorf("Error retrieving sharing key for album '%s': Status code %d", album.Name, res.StatusCode) } body, err := ioutil.ReadAll(res.Body) if err != nil { - return "", fmt.Errorf("Error retrieving sharing key for album '%s': %v", albumName, err) + return "", fmt.Errorf("Error retrieving sharing key for album '%s': %v", album.Name, err) } - var keys []map[string]interface{} + var keys []Key err = json.Unmarshal([]byte(body), &keys) if err != nil { - return "", fmt.Errorf("Error retrieving sharing key for album '%s': %v", albumName, err) + return "", fmt.Errorf("Error retrieving sharing key for album '%s': %v", album.Name, err) } for _, key := range keys { - alb := key["album"].(map[string]interface{}) - if (albumId == alb["id"].(string)) { - return key["key"].(string), nil + if (album.ID == key.Album.ID) { + return key.Key, nil } } - return "", fmt.Errorf("Error retrieving sharing key for album '%s': no key found for this albume", albumName) + return "", fmt.Errorf("Error retrieving sharing key for album '%s': no key found for this albume", album.Name) } else { // Create the missing key var jsonData = []byte(`{ - "albumId": "` + albumId + `", + "albumId": "` + album.ID + `", "allowDownload": true, "allowUpload": false, "showMetadata": true, "type": "ALBUM" }`) - req, err := http.NewRequest(http.MethodPost, param.immichURL + "/api/shared-links", bytes.NewBuffer(jsonData)) + req, err := http.NewRequest(http.MethodPost, param.ImmichURL + "/api/shared-links", bytes.NewBuffer(jsonData)) if err != nil { - return "", fmt.Errorf("Error creating missing key for album '%s': %v", albumName, err) + return "", fmt.Errorf("Error creating missing key for album '%s': %v", album.Name, err) } - req.Header.Set("x-api-key", param.immichKey) + req.Header.Set("x-api-key", param.ImmichKey) req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/octet-stream") res, err := spaceClient.Do(req) if err != nil { - return "", fmt.Errorf("Error creating missing key for album '%s': %v", albumName, err) + return "", fmt.Errorf("Error creating missing key for album '%s': %v", album.Name, err) } if res.Body != nil { defer res.Body.Close() } if res.StatusCode != 201 { - return "", fmt.Errorf("Error creating missing key for album '%s': Status code %d", albumName, res.StatusCode) + return "", fmt.Errorf("Error creating missing key for album '%s': Status code %d", album.Name, res.StatusCode) } body, err := ioutil.ReadAll(res.Body) if err != nil { - return "", fmt.Errorf("Error creating missing key for album '%s': %v", albumName, err) + return "", fmt.Errorf("Error creating missing key for album '%s': %v", album.Name, err) } - var key map[string]interface{} + var key Key err = json.Unmarshal([]byte(body), &key) if err != nil { - return "", fmt.Errorf("Error creating missing key for album '%s': %v", albumName, err) + return "", fmt.Errorf("Error creating missing key for album '%s': %v", album.Name, err) } - return key["key"].(string), nil + return key.Key, nil } } -func getThumbnail(album map[string]interface{}, param parameters) ([]byte, error) { +func getThumbnail(album Album, param Parameters) ([]byte, error) { spaceClient := http.Client{ Timeout: time.Second * 10, } - albumName := album["albumName"].(string) - req, err := http.NewRequest(http.MethodGet, param.immichURL + "/api/assets/" + album["albumThumbnailAssetId"].(string) + "/thumbnail?size=preview", nil) + req, err := http.NewRequest(http.MethodGet, param.ImmichURL + "/api/assets/" + album.AlbumThumbnailAssetId + "/thumbnail?size=preview", nil) if err != nil { - return nil, fmt.Errorf("Error retrieving thumbnail for album '%s': %v", albumName, err) + return nil, fmt.Errorf("Error retrieving thumbnail for album '%s': %v", album.Name, err) } - req.Header.Set("x-api-key", param.immichKey) + req.Header.Set("x-api-key", param.ImmichKey) req.Header.Set("Accept", "application/octet-stream") res, err := spaceClient.Do(req) if err != nil { - return nil, fmt.Errorf("Error retrieving thumbnail for album '%s': %v", albumName, err) + return nil, fmt.Errorf("Error retrieving thumbnail for album '%s': %v", album.Name, err) } if res.Body != nil { defer res.Body.Close() } if res.StatusCode != 200 { - return nil, fmt.Errorf("Error retrieving thumbnail for album '%s': Status code %d", albumName, res.StatusCode) + return nil, fmt.Errorf("Error retrieving thumbnail for album '%s': Status code %d", album.Name, res.StatusCode) } thumbnail, err := ioutil.ReadAll(res.Body) if err != nil { - return nil, fmt.Errorf("Error retrieving thumbnail for album '%s': %v", albumName, err) + return nil, fmt.Errorf("Error retrieving thumbnail for album '%s': %v", album.Name, err) } return thumbnail, nil } -func runLoop(param parameters) error { +func runLoop(param Parameters) error { // Create new WhatsApp connection and connect client, err := connect(param) if err != nil { @@ -292,87 +293,76 @@ func runLoop(param parameters) error { spaceClient := http.Client{ Timeout: time.Second * 10, } - req, err := http.NewRequest(http.MethodGet, param.immichURL + "/api/albums", nil) + req, err := http.NewRequest(http.MethodGet, param.ImmichURL + "/api/albums", nil) if err != nil { - return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.immichURL, err) + return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.ImmichURL, err) } - req.Header.Set("x-api-key", param.immichKey) + req.Header.Set("x-api-key", param.ImmichKey) req.Header.Set("Accept", "application/json") res, err := spaceClient.Do(req) if err != nil { - return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.immichURL, err) + return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.ImmichURL, err) } if res.Body != nil { defer res.Body.Close() } if res.StatusCode != 200 { - return fmt.Errorf("Error connecting to Immich with URL '%s': Status code %d", param.immichURL, res.StatusCode) + return fmt.Errorf("Error connecting to Immich with URL '%s': Status code %d", param.ImmichURL, res.StatusCode) } body, err := ioutil.ReadAll(res.Body) if err != nil { - return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.immichURL, err) + return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.ImmichURL, err) } - var albums []map[string]interface{} + var albums []Album err = json.Unmarshal([]byte(body), &albums) if err != nil { - return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.immichURL, err) + return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.ImmichURL, err) } for _, album := range albums { - albumName := album["albumName"].(string) - albumDate, err := time.Parse(time.RFC3339, album["startDate"].(string)) - if err != nil { - return fmt.Errorf("Incorrect time format received from Immich: %v", err) - } - createDate, err := time.Parse(time.RFC3339, album["createdAt"].(string)) - if err != nil { - return fmt.Errorf("Incorrect time format received from Immich: %v", err) - } - isShared := album["shared"].(bool) - // Get albums from x years ago - if isShared && (albumDate.Month() == time.Now().Month()) && (albumDate.Day() == time.Now().Day()) { + if album.Shared && (album.StartDate.Month() == time.Now().Month()) && (album.StartDate.Day() == time.Now().Day()) { // Retrieve the sharing key sharingKey, err := getSharingKey(album, param) if err != nil { - return fmt.Errorf("Error retrieving the sharing key for album '%s': %v", albumName, err) + return fmt.Errorf("Error retrieving the sharing key for album '%s': %v", album.Name, err) } // Retrieve the thumbnail thumbnail, err := getThumbnail(album, param) if err != nil { - return fmt.Errorf("Error retrieving thumbnail for album '%s': %v", albumName, err) + return fmt.Errorf("Error retrieving thumbnail for album '%s': %v", album.Name, err) } // Send the message - link := param.immichURL + "/share/" + sharingKey - err = sendMessage(client, param.whatsappGroup, fmt.Sprintf("Il y a %d an(s) : %s", time.Now().Year()-albumDate.Year(), link), link, albumName, thumbnail) + link := param.ImmichURL + "/share/" + sharingKey + err = sendMessage(client, param.WhatsappGroup, fmt.Sprintf("Il y a %d an(s) : %s", time.Now().Year()-album.StartDate.Year(), link), link, album.Name, thumbnail) if err != nil { - fmt.Fprintf(os.Stderr, "Error sending message to WhatsApp for album '%s': %v\n", albumName, err) + fmt.Fprintf(os.Stderr, "Error sending message to WhatsApp for album '%s': %v\n", album.Name, err) continue } } // Get albums created yesterday - if isShared && (createDate.Year() == time.Now().AddDate(0, 0, -1).Year()) && (createDate.Month() == time.Now().AddDate(0, 0, -1).Month()) && (createDate.Day() == time.Now().AddDate(0, 0, -1).Day()) { + if album.Shared && (album.CreatedAt.Year() == time.Now().AddDate(0, 0, -1).Year()) && (album.CreatedAt.Month() == time.Now().AddDate(0, 0, -1).Month()) && (album.CreatedAt.Day() == time.Now().AddDate(0, 0, -1).Day()) { // Retrieve the sharing key sharingKey, err := getSharingKey(album, param) if err != nil { - return fmt.Errorf("Error retrieving the sharing key for album '%s': %v", albumName, err) + return fmt.Errorf("Error retrieving the sharing key for album '%s': %v", album.Name, err) } // Retrieve the thumbnail thumbnail, err := getThumbnail(album, param) if err != nil { - return fmt.Errorf("Error retrieving thumbnail for album '%s': %v", albumName, err) + return fmt.Errorf("Error retrieving thumbnail for album '%s': %v", album.Name, err) } // Send the message - link := param.immichURL + "/share/" + sharingKey - err = sendMessage(client, param.whatsappGroup, fmt.Sprintf("Nouvel album : %s", link), link, albumName, thumbnail) + link := param.ImmichURL + "/share/" + sharingKey + err = sendMessage(client, param.WhatsappGroup, fmt.Sprintf("Nouvel album : %s", link), link, album.Name, thumbnail) if err != nil { - fmt.Fprintf(os.Stderr, "Error sending message to WhatsApp for album '%s': %v\n", albumName, err) + fmt.Fprintf(os.Stderr, "Error sending message to WhatsApp for album '%s': %v\n", album.Name, err) continue } @@ -395,15 +385,22 @@ func main() { }() // Load the parameters - param := loadParameters() - if param.runOnce { - err := runLoop(param) + param := new(Parameters) + param.ImmichURL = os.Getenv("IMMICH-URL") + param.ImmichKey = os.Getenv("IMMICH-KEY") + param.WhatsappSessionFile = os.Getenv("WHATSAPP-SESSION-FILE") + param.WhatsappGroup = os.Getenv("WHATSAPP-GROUP") + param.HealthchecksURL = os.Getenv("HEALTHCHECKS-URL") + _, param.RunOnce = os.LookupEnv("RUN-ONCE") + + if param.RunOnce { + err := runLoop(*param) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) } } else { // Test the connexion on startup - err := testConnexions(param) + err := testConnexions(*param) if err != nil { fmt.Fprintf(os.Stderr, "Unable to connect: %v\n", err) return @@ -421,10 +418,20 @@ func main() { fmt.Fprintf(os.Stderr, "Sleeping for: %s\n", d) time.Sleep(d) - err := runLoop(param) + err := runLoop(*param) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) continue + } else { + if param.HealthchecksURL != "" { + client := http.Client{ + Timeout: time.Second * 10, + } + _, err := client.Head(param.HealthchecksURL) + if err != nil { + fmt.Fprintf(os.Stderr, "%s", err) + } + } } } }