mirror of
https://github.com/napnap75/multiarch-docker-images.git
synced 2025-12-15 03:04:19 +01:00
First version of log-alert
This commit is contained in:
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
repository: [log-alert, chrooted-sshd, immich-souvenirs, dnsupdater, rsync-server, sshd, webhook, gandi, http-tunnel, restic-auto, restic-rest, shairport-sync, telegraf]
|
||||
repository: [log-alert, immich-souvenirs, dnsupdater, webhook, restic-auto, shairport-sync]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:latest
|
||||
FROM alpine:3.21
|
||||
|
||||
RUN apk add --no-cache openssh
|
||||
|
||||
|
||||
0
chrooted-sshd/entrypoint.sh
Normal file → Executable file
0
chrooted-sshd/entrypoint.sh
Normal file → Executable file
@@ -1,8 +1,20 @@
|
||||
FROM alpine:latest AS builder
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
RUN DOWNLOAD_ARCH=$(echo ${TARGETPLATFORM} | cut -d"/" -f 2) \
|
||||
&& apk add --no-cache curl \
|
||||
&& DOWNLOAD_URL=$(curl -s https://api.github.com/repos/bdd/runitor/releases/latest | grep "browser_download_url" | grep "linux-"${DOWNLOAD_ARCH} | cut -d\" -f4) \
|
||||
&& curl --retry 3 -L -s -o runitor ${DOWNLOAD_URL} \
|
||||
&& chmod +x runitor
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
ADD dnsupdater.sh /usr/bin/dnsupdater.sh
|
||||
COPY --from=builder runitor /usr/bin/
|
||||
|
||||
ADD dnsupdater.sh docker-command.sh /usr/bin/
|
||||
|
||||
RUN apk add --no-cache bash curl jq bind-tools \
|
||||
&& chmod +x /usr/bin/dnsupdater.sh
|
||||
|
||||
CMD /usr/bin/dnsupdater.sh
|
||||
CMD ["/usr/bin/docker-command.sh"]
|
||||
|
||||
@@ -1,59 +1,58 @@
|
||||
#!/bin/bash
|
||||
|
||||
while true ; do
|
||||
# Get my current IP
|
||||
my_ip=$(curl -s https://api.ipify.org)
|
||||
# Get my IP
|
||||
my_ip=$(curl -s https://api.ipify.org)
|
||||
if [[ ! ("$my_ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$) ]] ; then
|
||||
echo "Got incorrect IP: $my_ip"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get my currently registered IP
|
||||
current_ip=
|
||||
if [[ "$GANDI_API_KEY" != "" ]] ; then
|
||||
current_ip=$(curl -s -H"X-Api-Key: $GANDI_API_KEY" https://dns.api.gandi.net/api/v5/domains/$DNS_DOMAIN/records | jq -r '.[] | select(.rrset_name == "'$DNS_HOST'") | select(.rrset_type == "A") | .rrset_values[0]')
|
||||
elif [[ "$OVH_USERNAME" != "" ]] ; then
|
||||
master_server=$(dig +short -t NS $DNS_DOMAIN | head -n 1)
|
||||
current_ip=$(dig +short @$master_server $DNS_HOST.$DNS_DOMAIN)
|
||||
# Get my registered IP
|
||||
current_ip=
|
||||
if [[ "$GANDI_API_KEY" != "" ]] ; then
|
||||
current_ip=$(curl -s -H"X-Api-Key: $GANDI_API_KEY" https://dns.api.gandi.net/api/v5/domains/$DNS_DOMAIN/records | jq -r '.[] | select(.rrset_name == "'$DNS_HOST'") | select(.rrset_type == "A") | .rrset_values[0]')
|
||||
elif [[ "$OVH_USERNAME" != "" ]] ; then
|
||||
master_server=$(dig +short -t NS $DNS_DOMAIN | head -n 1)
|
||||
current_ip=$(dig +short @$master_server $DNS_HOST.$DNS_DOMAIN)
|
||||
else
|
||||
echo "No DNS provider configured"
|
||||
exit 2
|
||||
fi
|
||||
if [[ ! ("$current_ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$) ]] ; then
|
||||
echo "Incorrect registered IP: $current_ip"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# If they match, do nothing
|
||||
if [[ "$my_ip" == "$current_ip" ]]; then
|
||||
echo "$DNS_HOST.$DNS_DOMAIN record already up-to-date with IP $my_ip"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Update the DNS record
|
||||
echo "Updating $DNS_HOST.$DNS_DOMAIN record with IP $my_ip"
|
||||
if [[ "$GANDI_API_KEY" != "" ]] ; then
|
||||
current_record=$(curl -s -H"X-Api-Key: $GANDI_API_KEY" https://dns.api.gandi.net/api/v5/domains/$DNS_DOMAIN/records | jq -c '.[] | select(.rrset_name == "'$DNS_HOST'") | select(.rrset_type == "A")')
|
||||
current_ttl=$(echo $current_record | jq -r '.rrset_ttl')
|
||||
curl -s -X PUT -H "Content-Type: application/json" -H "X-Api-Key: $GANDI_API_KEY" -d '{"rrset_ttl": '$current_ttl', "rrset_values":["'$my_ip'"]}' https://dns.api.gandi.net/api/v5/domains/$DNS_DOMAIN/records/$DNS_HOST/A
|
||||
if [[ $? != 0 ]] ; then
|
||||
echo "Unable to update GANDI record"
|
||||
exit 4
|
||||
fi
|
||||
|
||||
# Check if both IP addresses are correct
|
||||
if [[ "$my_ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ && "$current_ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] ; then
|
||||
# If they do not match, change it (and keep the TTL and TYPE)
|
||||
if [[ "$my_ip" != "$current_ip" ]]; then
|
||||
result=1
|
||||
echo "Updating $DNS_HOST.$DNS_DOMAIN record with IP $my_ip"
|
||||
if [[ "$GANDI_API_KEY" != "" ]] ; then
|
||||
current_record=$(curl -s -H"X-Api-Key: $GANDI_API_KEY" https://dns.api.gandi.net/api/v5/domains/$DNS_DOMAIN/records | jq -c '.[] | select(.rrset_name == "'$DNS_HOST'") | select(.rrset_type == "A")')
|
||||
current_ttl=$(echo $current_record | jq -r '.rrset_ttl')
|
||||
curl -s -X PUT -H "Content-Type: application/json" -H "X-Api-Key: $GANDI_API_KEY" -d '{"rrset_ttl": '$current_ttl', "rrset_values":["'$my_ip'"]}' https://dns.api.gandi.net/api/v5/domains/$DNS_DOMAIN/records/$DNS_HOST/A
|
||||
result=$?
|
||||
elif [[ "$OVH_USERNAME" != "" ]] ; then
|
||||
curl -s --user "$OVH_USERNAME:$OVH_PASSWORD" "http://www.ovh.com/nic/update?system=dyndns&hostname=$DNS_HOST.$DNS_DOMAIN&myip=$my_ip"
|
||||
result=$?
|
||||
fi
|
||||
|
||||
# If the update was OK
|
||||
if [[ $result == 0 ]] ; then
|
||||
# Send a notification to Slack
|
||||
if [[ "$SLACK_URL" != "" ]] ; then
|
||||
curl -o /dev/null -s -m 10 --retry 5 -X POST -d "payload={\"username\": \"gandi\", \"icon_emoji\": \":dart:\", \"text\": \"New IP $my_ip for host $DNS_HOST.$DNS_DOMAIN\"}" $SLACK_URL
|
||||
fi
|
||||
|
||||
# Send a notification to Gotify
|
||||
if [[ "$GOTIFY_URL" != "" ]] ; then
|
||||
curl -o /dev/null -s -m 10 --retry 5 -X POST -H "accept: application/json" -H "Content-Type: application/json" -d "{\"priority\": 5, \"title\": \"New IP for host $DNS_HOST.$DNS_DOMAIN\", \"message\": \"New IP $my_ip for host $DNS_HOST.$DNS_DOMAIN, the old IP was $current_ip\"}" $GOTIFY_URL
|
||||
fi
|
||||
|
||||
# Send a notification to Healthchecks
|
||||
if [[ "$HEALTHCHECKS_URL" != "" ]] ; then
|
||||
curl -o /dev/null -s -m 10 --retry 5 $HEALTHCHECKS_URL
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# Send a notification to Healthchecks
|
||||
if [[ "$HEALTHCHECKS_URL" != "" ]] ; then
|
||||
curl -o /dev/null -s -m 10 --retry 5 $HEALTHCHECKS_URL
|
||||
fi
|
||||
fi
|
||||
elif [[ "$OVH_USERNAME" != "" ]] ; then
|
||||
curl -s --user "$OVH_USERNAME:$OVH_PASSWORD" "http://www.ovh.com/nic/update?system=dyndns&hostname=$DNS_HOST.$DNS_DOMAIN&myip=$my_ip"
|
||||
if [[ $? != 0 ]] ; then
|
||||
echo "Unable to update OVH record"
|
||||
exit 5
|
||||
fi
|
||||
fi
|
||||
|
||||
# Wait 5 minutes
|
||||
sleep 300
|
||||
done
|
||||
# Send a notification to Slack
|
||||
if [[ "$SLACK_URL" != "" ]] ; then
|
||||
curl -o /dev/null -s -m 10 --retry 5 -X POST -d "payload={\"username\": \"gandi\", \"icon_emoji\": \":dart:\", \"text\": \"New IP $my_ip for host $DNS_HOST.$DNS_DOMAIN\"}" $SLACK_URL
|
||||
fi
|
||||
|
||||
# Send a notification to Gotify
|
||||
if [[ "$GOTIFY_URL" != "" ]] ; then
|
||||
curl -o /dev/null -s -m 10 --retry 5 -X POST -H "accept: application/json" -H "Content-Type: application/json" -d "{\"priority\": 5, \"title\": \"New IP for host $DNS_HOST.$DNS_DOMAIN\", \"message\": \"New IP $my_ip for host $DNS_HOST.$DNS_DOMAIN, the old IP was $current_ip\"}" $GOTIFY_URL
|
||||
fi
|
||||
|
||||
3
dnsupdater/docker-command.sh
Executable file
3
dnsupdater/docker-command.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
/usr/bin/runitor -every 5m -slug ${HOSTNAME}-dnsupdater -- /usr/bin/dnsupdater.sh
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.21-alpine AS builder
|
||||
FROM golang:1.23-alpine AS builder
|
||||
|
||||
WORKDIR $GOPATH/src/napnap75/immich-souvenirs/
|
||||
COPY immich-souvenirs.go .
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
## Development env
|
||||
docker run --rm -it -v $PWD:/go/src/napnap75/immich-souvenirs -v immich-souvenirs_config:/config golang:1.21-alpine /bin/sh
|
||||
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
|
||||
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
|
||||
@@ -1,11 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -13,472 +9,229 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"github.com/mdp/qrterminal/v3"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"go.mau.fi/whatsmeow"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
"go.mau.fi/whatsmeow/store/sqlstore"
|
||||
waProto "go.mau.fi/whatsmeow/binary/proto"
|
||||
waLog "go.mau.fi/whatsmeow/util/log"
|
||||
|
||||
"github.com/napnap75/multiarch-docker-files/immich-souvenirs/internals/immich"
|
||||
"github.com/napnap75/multiarch-docker-files/immich-souvenirs/internals/whatsapp"
|
||||
)
|
||||
|
||||
type Parameters struct {
|
||||
ImmichURL string
|
||||
ImmichKey string
|
||||
WhatsappSessionFile string
|
||||
WhatsappGroup string
|
||||
TimeToRun string
|
||||
RunOnce bool
|
||||
HealthchecksURL string
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deviceStore, err := container.GetFirstDevice()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientLog := waLog.Stdout("Client", "ERROR", true)
|
||||
client := whatsmeow.NewClient(deviceStore, clientLog)
|
||||
|
||||
if client.Store.ID == nil {
|
||||
// No ID stored, new login
|
||||
qrChan, _ := client.GetQRChannel(context.Background())
|
||||
err = client.Connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for evt := range qrChan {
|
||||
if evt.Event == "code" {
|
||||
qrterminal.GenerateHalfBlock(evt.Code, qrterminal.L, os.Stdout)
|
||||
fmt.Println("QR code:", evt.Code)
|
||||
} else {
|
||||
fmt.Println("Login event:", evt.Event)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Already logged in, just connect
|
||||
err = client.Connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func sendMessage(client *whatsmeow.Client, group string, message string, url string, title string, thumbnail []byte) error {
|
||||
jid, err := types.ParseJID(group)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Incorrect group identifier '%s': %v", group, err)
|
||||
}
|
||||
|
||||
msg := &waProto.Message{ExtendedTextMessage: &waProto.ExtendedTextMessage{
|
||||
Text: proto.String(message),
|
||||
Title: proto.String(title),
|
||||
Description: proto.String(title),
|
||||
CanonicalURL: proto.String(url),
|
||||
MatchedText: proto.String(url),
|
||||
JPEGThumbnail: thumbnail,
|
||||
}}
|
||||
ts, err := client.SendMessage(context.Background(), jid, msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error sending message with title '%s': %v", title, err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stdout, "Message with title '%s' sent (timestamp: %s)\n", title, ts)
|
||||
return nil
|
||||
}
|
||||
|
||||
func testConnexions(param Parameters) error {
|
||||
// Create new WhatsApp connection and connect
|
||||
client, err := connect(param)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error connecting to WhatsApp: %v", err)
|
||||
}
|
||||
<-time.After(3 * time.Second)
|
||||
defer client.Disconnect()
|
||||
|
||||
// Prints the available groups if none provided
|
||||
if param.WhatsappGroup == "" {
|
||||
fmt.Fprintf(os.Stdout, "No WhatsApp group provided, showing all available groups\n")
|
||||
groups, err := client.GetJoinedGroups()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting groups list: %v", err)
|
||||
}
|
||||
for _, groupInfo := range groups {
|
||||
fmt.Fprintf(os.Stdout, "%s | %s\n", groupInfo.JID, groupInfo.GroupName)
|
||||
}
|
||||
|
||||
return fmt.Errorf("No WhatsApp group provided")
|
||||
} else {
|
||||
jid, err := types.ParseJID(param.WhatsappGroup)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Connects to Immich and load albums
|
||||
spaceClient := http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.ImmichURL, err)
|
||||
}
|
||||
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 nil
|
||||
}
|
||||
|
||||
func getSharingKey(album Album, param Parameters) (string, error) {
|
||||
spaceClient := http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
if (album.HasSharedLink) {
|
||||
// Retrieve the existing key
|
||||
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", album.Name, err)
|
||||
}
|
||||
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", 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", album.Name, res.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error retrieving sharing key for album '%s': %v", album.Name, err)
|
||||
}
|
||||
var keys []Key
|
||||
err = json.Unmarshal([]byte(body), &keys)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error retrieving sharing key for album '%s': %v", album.Name, err)
|
||||
}
|
||||
for _, key := range keys {
|
||||
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", album.Name)
|
||||
} else {
|
||||
// Create the missing key
|
||||
var jsonData = []byte(`{
|
||||
"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))
|
||||
if err != nil {
|
||||
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("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", 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", album.Name, res.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error creating missing key for album '%s': %v", album.Name, err)
|
||||
}
|
||||
var key Key
|
||||
err = json.Unmarshal([]byte(body), &key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error creating missing key for album '%s': %v", album.Name, err)
|
||||
}
|
||||
return key.Key, nil
|
||||
}
|
||||
}
|
||||
|
||||
func getThumbnail(album Album, param Parameters) ([]byte, error) {
|
||||
spaceClient := http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
|
||||
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", album.Name, err)
|
||||
}
|
||||
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", 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", album.Name, res.StatusCode)
|
||||
}
|
||||
thumbnail, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error retrieving thumbnail for album '%s': %v", album.Name, err)
|
||||
}
|
||||
|
||||
return thumbnail, nil
|
||||
}
|
||||
|
||||
func runLoop(param Parameters) error {
|
||||
// Create new WhatsApp connection and connect
|
||||
client, err := connect(param)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating connection to WhatsApp: %v", err)
|
||||
}
|
||||
<-time.After(3 * time.Second)
|
||||
defer client.Disconnect()
|
||||
|
||||
|
||||
// Connects to Immich and load albums
|
||||
spaceClient := http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error connecting to Immich with URL '%s': %v", param.ImmichURL, err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
for _, album := range albums {
|
||||
// Get albums from x years ago
|
||||
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", album.Name, err)
|
||||
}
|
||||
|
||||
// Retrieve the thumbnail
|
||||
thumbnail, err := getThumbnail(album, param)
|
||||
if err != nil {
|
||||
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()-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", album.Name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Get albums created yesterday
|
||||
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", album.Name, err)
|
||||
}
|
||||
|
||||
// Retrieve the thumbnail
|
||||
thumbnail, err := getThumbnail(album, param)
|
||||
if err != nil {
|
||||
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, album.Name, thumbnail)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error sending message to WhatsApp for album '%s': %v\n", album.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
ImmichURL string
|
||||
ImmichKey string
|
||||
WhatsappSessionFile string
|
||||
WhatsappGroup string
|
||||
TimeToRun string
|
||||
DevelopmentMode string
|
||||
HealthchecksURL string
|
||||
}
|
||||
|
||||
func parseTime(timeStr string) (int, int, error) {
|
||||
// Split the string into hours and minutes
|
||||
parts := strings.Split(timeStr, ":")
|
||||
if len(parts) != 2 {
|
||||
return 0, 0, fmt.Errorf("invalid format: %s", timeStr)
|
||||
}
|
||||
|
||||
// Convert the parts to integers
|
||||
hours, err := strconv.Atoi(parts[0])
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("error converting hours: %v", err)
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
minutes, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("error converting minutes: %v", err)
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
return hours, minutes, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Handle interrupts to clean properly
|
||||
c := make(chan os.Signal)
|
||||
// Capture interrupt signal for graceful shutdown
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
select {
|
||||
case sig := <-c:
|
||||
fmt.Printf("Got %s signal. Aborting...\n", sig)
|
||||
os.Exit(1)
|
||||
}
|
||||
<-c
|
||||
fmt.Println("Got interrupt signal. Exiting...")
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
// Load the parameters
|
||||
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.TimeToRun = os.Getenv("TIME-TO-RUN")
|
||||
param.HealthchecksURL = os.Getenv("HEALTHCHECKS-URL")
|
||||
_, param.RunOnce = os.LookupEnv("RUN-ONCE")
|
||||
// Load parameters from environment variables
|
||||
param := &Parameters{
|
||||
ImmichURL: os.Getenv("IMMICH-URL"),
|
||||
ImmichKey: os.Getenv("IMMICH-KEY"),
|
||||
WhatsappSessionFile: os.Getenv("WHATSAPP-SESSION-FILE"),
|
||||
WhatsappGroup: os.Getenv("WHATSAPP-GROUP"),
|
||||
TimeToRun: os.Getenv("TIME-TO-RUN"),
|
||||
DevelopmentMode: os.Getenv("DEVELOPMENT-MODE"),
|
||||
HealthchecksURL: os.Getenv("HEALTHCHECKS-URL"),
|
||||
}
|
||||
|
||||
if param.RunOnce {
|
||||
err := runLoop(*param)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
// Initialize WhatsApp connection
|
||||
wac, err := whatsapp.New(param.WhatsappSessionFile)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error initializing WhatsApp: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Initialize Immich client
|
||||
immichClient := immich.New(param.ImmichURL, param.ImmichKey)
|
||||
|
||||
switch param.DevelopmentMode {
|
||||
case "run-once", "run-last":
|
||||
if err := runLoop(wac, immichClient, param); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error in runLoop: %v\n", err)
|
||||
}
|
||||
} else {
|
||||
// Test the connexion on startup
|
||||
err := testConnexions(*param)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unable to connect: %v\n", err)
|
||||
case "listen":
|
||||
if err := listen(wac); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error in listen: %v\n", err)
|
||||
}
|
||||
default:
|
||||
// Test connection on startup
|
||||
if err := testConnections(wac, immichClient, param); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Connection test failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Run the loop everyday
|
||||
// Main scheduled loop
|
||||
for {
|
||||
// First, wait for the appropriate time
|
||||
hours, minutes, err := parseTime(param.TimeToRun)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unable to read the time provided: %v\n", err)
|
||||
fmt.Fprintf(os.Stderr, "Invalid time format: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
n := time.Date(t.Year(), t.Month(), t.Day(), hours, minutes, 0, 0, t.Location())
|
||||
d := n.Sub(t)
|
||||
if d < 0 {
|
||||
n = n.Add(24 * time.Hour)
|
||||
d = n.Sub(t)
|
||||
now := time.Now()
|
||||
target := time.Date(now.Year(), now.Month(), now.Day(), hours, minutes, 0, 0, now.Location())
|
||||
if target.Before(now) {
|
||||
target = target.Add(24 * time.Hour)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Sleeping for: %s\n", d)
|
||||
time.Sleep(d)
|
||||
sleepDuration := target.Sub(now)
|
||||
fmt.Printf("Sleeping for: %v\n", sleepDuration)
|
||||
time.Sleep(sleepDuration)
|
||||
|
||||
// Then try to run it 3 times in case of error
|
||||
// Retry logic
|
||||
for i := 0; i < 3; i++ {
|
||||
err = runLoop(*param)
|
||||
err := runLoop(wac, immichClient, param)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Print the error and attempt number
|
||||
fmt.Fprintf(os.Stderr, "Attempt %d failed: %v\n", i+1, err)
|
||||
// Wait before the next attempt
|
||||
time.Sleep(time.Duration(math.Pow(2, float64(i))) * 30 * time.Second)
|
||||
}
|
||||
|
||||
// Manage the error
|
||||
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)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Error after retries: %v\n", err)
|
||||
} else if param.HealthchecksURL != "" {
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
_, err := client.Head(param.HealthchecksURL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Healthcheck error: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fonction pour tester la connexion à WhatsApp et Immich
|
||||
func testConnections(wac *whatsapp.WhatsAppClient, immichClient *immich.ImmichClient, param *Parameters) error {
|
||||
fmt.Println("Testing connections...")
|
||||
// Test WhatsApp
|
||||
if err := wac.Client.Connect(); err != nil {
|
||||
return fmt.Errorf("WhatsApp connection failed: %v", err)
|
||||
}
|
||||
defer wac.Client.Disconnect()
|
||||
fmt.Println("WhatsApp connected.")
|
||||
|
||||
// Test Immich
|
||||
albums, err := immichClient.FetchAlbums()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Immich connection failed: %v", err)
|
||||
}
|
||||
fmt.Printf("Fetched %d albums from Immich.\n", len(albums))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fonction principale pour exécuter la logique
|
||||
func runLoop(wac *whatsapp.WhatsAppClient, immichClient *immich.ImmichClient, param *Parameters) error {
|
||||
// Connecter WhatsApp si nécessaire
|
||||
if !wac.Client.IsConnected() {
|
||||
if err := wac.Client.Connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer wac.Client.Disconnect()
|
||||
|
||||
// Charger albums depuis Immich
|
||||
albums, err := immichClient.FetchAlbums()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, album := range albums {
|
||||
if album.Shared {
|
||||
// Récupérer les albums anniversaire
|
||||
if param.DevelopmentMode == "run-last" || (album.StartDate.Month() == time.Now().Month() && album.StartDate.Day() == time.Now().Day()) {
|
||||
// Obtenir la clé de partage
|
||||
sharingKey, err := immichClient.GetSharingKey(album)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Erreur récupération clé partage: %v\n", err)
|
||||
continue
|
||||
}
|
||||
link := param.ImmichURL + "/share/" + sharingKey
|
||||
|
||||
// Récupérer la miniature
|
||||
thumbnail, err := immichClient.GetThumbnail(album.AlbumThumbnailAssetId)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Erreur miniature: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Envoyer le message
|
||||
err = wac.SendMessage(param.WhatsappGroup, album.Name, album.Description, fmt.Sprintf("Il y a %d an(s) : %s", time.Now().Year()-album.StartDate.Year(), link), link, thumbnail)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Erreur envoi WhatsApp: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
if param.DevelopmentMode == "run-last" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Récupérer les albums de la veille
|
||||
if 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()) {
|
||||
// Obtenir la clé de partage
|
||||
sharingKey, err := immichClient.GetSharingKey(album)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Erreur récupération clé partage: %v\n", err)
|
||||
continue
|
||||
}
|
||||
link := param.ImmichURL + "/share/" + sharingKey
|
||||
|
||||
// Récupérer la miniature
|
||||
thumbnail, err := immichClient.GetThumbnail(album.AlbumThumbnailAssetId)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Erreur miniature: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Envoyer le message
|
||||
err = wac.SendMessage(param.WhatsappGroup, album.Name, album.Description, fmt.Sprintf("Nouvel album : %s", link), link, thumbnail)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Erreur envoi WhatsApp: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fonction pour écouter les événements WhatsApp
|
||||
func listen(wac *whatsapp.WhatsAppClient) error {
|
||||
if err := wac.Client.Connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer wac.Client.Disconnect()
|
||||
|
||||
wac.Client.AddEventHandler(func(evt interface{}) {
|
||||
fmt.Println("Réception d'un message:", evt)
|
||||
})
|
||||
|
||||
// Attendre une interruption
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
<-c
|
||||
fmt.Println("Arrêt demandé.")
|
||||
return nil
|
||||
}
|
||||
|
||||
169
immich-souvenirs/internals/immich/immich.go
Normal file
169
immich-souvenirs/internals/immich/immich.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package immich
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Album struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"albumName"`
|
||||
Description string `json:"description"`
|
||||
Shared bool `json:"shared"`
|
||||
HasSharedLink bool `json:"hasSharedLink"`
|
||||
StartDate time.Time `json:"startDate"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
AlbumThumbnailAssetId string `json:"albumThumbnailAssetId"`
|
||||
}
|
||||
|
||||
type Key struct {
|
||||
ID string `json:"id"`
|
||||
Key string `json:"key"`
|
||||
Album *Album `json:"album"`
|
||||
}
|
||||
|
||||
type ImmichClient struct {
|
||||
BaseURL string
|
||||
APIKey string
|
||||
}
|
||||
|
||||
// Fonction pour créer une instance d'ImmichClient
|
||||
func New(baseURL string, apiKey string) *ImmichClient {
|
||||
return &ImmichClient{
|
||||
BaseURL: baseURL,
|
||||
APIKey: apiKey,
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer la liste des albums
|
||||
func (ic *ImmichClient) FetchAlbums() ([]Album, error) {
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
req, err := http.NewRequest(http.MethodGet, ic.BaseURL+"/api/albums", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("x-api-key", ic.APIKey)
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("Status code %d", res.StatusCode)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var albums []Album
|
||||
if err := json.Unmarshal(body, &albums); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return albums, nil
|
||||
}
|
||||
|
||||
// Obtenir la clé de partage pour un album
|
||||
func (ic *ImmichClient) GetSharingKey(album Album) (string, error) {
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
if album.HasSharedLink {
|
||||
// Récupérer la clé existante
|
||||
req, err := http.NewRequest(http.MethodGet, ic.BaseURL+"/api/shared-links", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("x-api-key", ic.APIKey)
|
||||
req.Header.Set("Accept", "application/json")
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
return "", fmt.Errorf("Status code %d", res.StatusCode)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var keys []Key
|
||||
if err := json.Unmarshal(body, &keys); err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, key := range keys {
|
||||
if key.Album != nil && key.Album.ID == album.ID {
|
||||
return key.Key, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("No sharing key found for album '%s'", album.Name)
|
||||
} else {
|
||||
// Créer un nouveau lien partagé
|
||||
jsonData := []byte(`{
|
||||
"albumId": "` + album.ID + `",
|
||||
"allowDownload": true,
|
||||
"allowUpload": false,
|
||||
"showMetadata": true,
|
||||
"type": "ALBUM"
|
||||
}`)
|
||||
req, err := http.NewRequest(http.MethodPost, ic.BaseURL+"/api/shared-links", bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("x-api-key", ic.APIKey)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Accept", "application/octet-stream")
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != 201 {
|
||||
return "", fmt.Errorf("Status code %d", res.StatusCode)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var key Key
|
||||
if err := json.Unmarshal(body, &key); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return key.Key, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer la miniature d'un album
|
||||
func (ic *ImmichClient) GetThumbnail(assetID string) ([]byte, error) {
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
req, err := http.NewRequest(http.MethodGet, ic.BaseURL+"/api/assets/"+assetID+"/thumbnail?size=preview", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("x-api-key", ic.APIKey)
|
||||
req.Header.Set("Accept", "application/octet-stream")
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("Status code %d", res.StatusCode)
|
||||
}
|
||||
|
||||
return ioutil.ReadAll(res.Body)
|
||||
}
|
||||
84
immich-souvenirs/internals/whatsapp/whatsapp.go
Normal file
84
immich-souvenirs/internals/whatsapp/whatsapp.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/mdp/qrterminal/v3"
|
||||
"go.mau.fi/whatsmeow"
|
||||
"go.mau.fi/whatsmeow/store/sqlstore"
|
||||
waLog "go.mau.fi/whatsmeow/util/log"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
waProto "go.mau.fi/whatsmeow/binary/proto"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type WhatsAppClient struct {
|
||||
Client *whatsmeow.Client
|
||||
}
|
||||
|
||||
// Fonction pour initialiser la connexion et retourner une instance de WhatsAppClient
|
||||
func New(sessionFile string) (*WhatsAppClient, error) {
|
||||
dbLog := waLog.Stdout("Database", "ERROR", true)
|
||||
container, err := sqlstore.New(context.Background(), "sqlite3", "file:"+sessionFile+"?_foreign_keys=on", dbLog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deviceStore, err := container.GetFirstDevice(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientLog := waLog.Stdout("Client", "ERROR", true)
|
||||
client := whatsmeow.NewClient(deviceStore, clientLog)
|
||||
|
||||
if client.Store.ID == nil {
|
||||
// No ID stored, nouvelle connexion
|
||||
qrChan, _ := client.GetQRChannel(context.Background())
|
||||
err = client.Connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for evt := range qrChan {
|
||||
if evt.Event == "code" {
|
||||
qrterminal.GenerateHalfBlock(evt.Code, qrterminal.L, os.Stdout)
|
||||
fmt.Println("QR code:", evt.Code)
|
||||
} else {
|
||||
fmt.Println("Event:", evt.Event)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Connexion existante
|
||||
err = client.Connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &WhatsAppClient{Client: client}, nil
|
||||
}
|
||||
|
||||
// Méthode pour envoyer un message
|
||||
func (wac *WhatsAppClient) SendMessage(group string, title string, description string, message string, url string, thumbnail []byte) error {
|
||||
jid, err := types.ParseJID(group)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Incorrect group identifier '%s': %v", group, err)
|
||||
}
|
||||
|
||||
msg := &waProto.Message{
|
||||
ExtendedTextMessage: &waProto.ExtendedTextMessage{
|
||||
Title: proto.String(title),
|
||||
Description: proto.String(description),
|
||||
Text: proto.String(message),
|
||||
MatchedText: proto.String(url),
|
||||
JPEGThumbnail: thumbnail,
|
||||
},
|
||||
}
|
||||
ts, err := wac.Client.SendMessage(context.Background(), jid, msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error sending message with title '%s': %v", title, err)
|
||||
}
|
||||
fmt.Printf("Message with title '%s' sent (timestamp: %s)\n", title, ts)
|
||||
return nil
|
||||
}
|
||||
10
immich-souvenirs/test.sh
Executable file
10
immich-souvenirs/test.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "-----------------------------------------------------------------------"
|
||||
echo "apk add --no-cache git gcc musl-dev"
|
||||
echo "go mod init github.com/napnap75/multiarch-docker-files/immich-souvenirs"
|
||||
echo "go mod tidy"
|
||||
echo "env CGO_ENABLED=1 env DEVELOPMENT-MODE=run-once go run immich-souvenirs.go"
|
||||
echo "-----------------------------------------------------------------------"
|
||||
|
||||
docker run -it -v $(pwd)/immich-souvenirs.go:/app/immich-souvenirs.go -v $(pwd)/internals:/app/internals -w /app -v immich-souvenirs_config:/config --env-file test.env --rm golang:1.23-alpine /bin/sh
|
||||
@@ -7,11 +7,14 @@ RUN DOWNLOAD_ARCH=$(echo ${TARGETPLATFORM} | cut -d"/" -f 2) \
|
||||
&& DOWNLOAD_URL=$(curl -s https://api.github.com/repos/restic/restic/releases/latest | grep "browser_download_url" | grep "linux_"${DOWNLOAD_ARCH}"\." | cut -d\" -f4) \
|
||||
&& curl --retry 3 -L -s -o restic.bz2 ${DOWNLOAD_URL} \
|
||||
&& bunzip2 restic.bz2 \
|
||||
&& chmod +x restic
|
||||
&& chmod +x restic \
|
||||
&& DOWNLOAD_URL=$(curl -s https://api.github.com/repos/bdd/runitor/releases/latest | grep "browser_download_url" | grep "linux-"${DOWNLOAD_ARCH} | cut -d\" -f4) \
|
||||
&& curl --retry 3 -L -s -o runitor ${DOWNLOAD_URL} \
|
||||
&& chmod +x runitor
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
COPY --from=builder restic /usr/bin/
|
||||
COPY --from=builder restic runitor /usr/bin/
|
||||
|
||||
RUN apk add --no-cache bash curl jq openssh-client dcron tzdata docker
|
||||
|
||||
|
||||
@@ -37,7 +37,10 @@ else
|
||||
else
|
||||
echo -n "0 4 * * *" >> /tmp/crontab
|
||||
fi
|
||||
echo -n " restic-auto >> /var/log/cron.log" >> /tmp/crontab
|
||||
if [[ "$HC_PING_KEY" ]] ; then
|
||||
echo -n " runitor -slug ${HOSTNAME}-restic-backup --" >> /tmp/crontab
|
||||
fi
|
||||
echo -n " restic-auto >> /var/log/cron.log" >> /tmp/crontab
|
||||
if [[ "$POST_BACKUP_COMMAND" ]] ; then
|
||||
echo -n " && $POST_BACKUP_COMMAND" >> /tmp/crontab
|
||||
fi
|
||||
@@ -47,7 +50,14 @@ else
|
||||
else
|
||||
echo -n "0 1 * * 0" >> /tmp/crontab
|
||||
fi
|
||||
echo -n " restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --keep-yearly 2 --prune >> /var/log/cron.log && restic check >> /var/log/cron.log" >> /tmp/crontab
|
||||
if [[ "$HC_PING_KEY" ]] ; then
|
||||
echo -n " runitor -slug ${HOSTNAME}-restic-forget --" >> /tmp/crontab
|
||||
fi
|
||||
echo -n " restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --keep-yearly 2 --prune >> /var/log/cron.log &&" >> /tmp/crontab
|
||||
if [[ "$HC_PING_KEY" ]] ; then
|
||||
echo -n " runitor -slug ${HOSTNAME}-restic-check --" >> /tmp/crontab
|
||||
fi
|
||||
echo -n " restic check >> /var/log/cron.log" >> /tmp/crontab
|
||||
if [[ "$POST_MAINTENANCE_COMMAND" ]] ; then
|
||||
echo -n " && $POST_MAINTENANCE_COMMAND" >> /tmp/crontab
|
||||
fi
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
# Backup one directory
|
||||
function backup_dir {
|
||||
# Check if the dir to backup is mounted as a subdirectory of /root inside this container
|
||||
if [ -d "/root_fs$1" ]; then
|
||||
if [ "$1" = "" ]; then
|
||||
echo "[ERROR] Cannot backup the root directory. Have you correctly configured the volumes to backup for this container ?"
|
||||
return -1
|
||||
elif [ -d "/root_fs$1" ]; then
|
||||
restic backup $RESTIC_BACKUP_FLAGS /root_fs$1
|
||||
return $?
|
||||
else
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:latest AS builder
|
||||
FROM alpine:3.21 AS builder
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
@@ -12,7 +12,7 @@ RUN apk add --no-cache curl jq \
|
||||
&& curl --retry 3 -L -s -o /tmp/s6-overlay.tar.xz $S6_DOWNLOAD_URL \
|
||||
&& tar -xf /tmp/s6-overlay.tar.xz -C /tmp/s6-overlay
|
||||
|
||||
FROM alpine:latest
|
||||
FROM alpine:3:21
|
||||
|
||||
RUN apk add --no-cache bash curl openssh-server rsync rrsync borgbackup
|
||||
COPY --from=builder /tmp/s6-overlay /
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:alpine3.11 AS build
|
||||
FROM golang:1-alpine AS build
|
||||
WORKDIR /go/src/github.com/adnanh/webhook
|
||||
RUN apk add --update -t build-deps curl libc-dev gcc libgcc jq \
|
||||
&& DOWNLOAD_URL=$(curl -s https://api.github.com/repos/adnanh/webhook/releases/latest | jq -r '.tarball_url') \
|
||||
@@ -7,7 +7,7 @@ RUN apk add --update -t build-deps curl libc-dev gcc libgcc jq \
|
||||
&& go get -d \
|
||||
&& go build -o /usr/local/bin/webhook
|
||||
|
||||
FROM alpine:3.11
|
||||
FROM alpine:latest
|
||||
RUN apk add --no-cache openssl expect
|
||||
COPY --from=build /usr/local/bin/webhook /usr/local/bin/webhook
|
||||
WORKDIR /etc/webhook
|
||||
|
||||
Reference in New Issue
Block a user