add telegram notification

This commit is contained in:
2025-09-16 16:22:49 +02:00
parent 053569b628
commit e15ee93f08
8 changed files with 133 additions and 11 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
git:
repo_url: "git@git.kolspace.cc:victor.kolomin/homelab.git"
repo_url: "git@git.kolspace.cc:victor.kolomin/test-homelab.git"
branch: "main"
ssh_key_path: "./id_rsa"
+2
View File
@@ -12,6 +12,7 @@ require (
require (
dario.cat/mergo v1.0.0 // indirect
github.com/Masterminds/semver v1.5.0
github.com/Masterminds/semver/v3 v3.4.0
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
@@ -20,6 +21,7 @@ require (
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
+4
View File
@@ -2,6 +2,8 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
@@ -36,6 +38,8 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
+10 -6
View File
@@ -10,9 +10,11 @@ import (
// AppConfig holds all application-wide configuration from environment variables.
type AppConfig struct {
DockerUser string
DockerPass string
GithubToken string
DockerUser string
DockerPass string
GithubToken string
TelegramToken string
TelegramChatID string
}
type Config struct {
@@ -54,9 +56,11 @@ func Load(path string) (*Config, error) {
// LoadFromEnv reads configuration values from environment variables.
func LoadFromEnv() AppConfig {
cfg := AppConfig{
DockerUser: os.Getenv("DOCKERHUB_USERNAME"),
DockerPass: os.Getenv("DOCKERHUB_PASSWORD"),
GithubToken: os.Getenv("GITHUB_TOKEN"),
DockerUser: os.Getenv("CONTROLLA__DOCKERHUB__USERNAME"),
DockerPass: os.Getenv("CONTROLLA__DOCKERHUB__PASSWORD"),
GithubToken: os.Getenv("CONTROLLA__GITHUB__TOKEN"),
TelegramToken: os.Getenv("CONTROLLA__TELEGRAM__TOKEN"),
TelegramChatID: os.Getenv("CONTROLLA__TELEGRAM__CHAT__ID"),
}
// Simple validation to ensure we have at least one token
+26
View File
@@ -0,0 +1,26 @@
package notifier
import (
"log"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
// SendTelegramMessage отправляет сообщение в указанный чат
func SendTelegramMessage(token, chatID string, message string) {
bot, err := tgbotapi.NewBotAPI(token)
if err != nil {
log.Printf("Error creating Telegram bot: %v", err)
return
}
msg := tgbotapi.NewMessageToChannel(chatID, message)
// Используем NewMessageToChannel вместо NewMessage для отправки в канал/чат по ID
_, err = bot.Send(msg)
if err != nil {
log.Printf("Error sending message to Telegram: %v", err)
} else {
log.Println("Message sent to Telegram successfully.")
}
}
+22 -3
View File
@@ -3,6 +3,7 @@ package versionutils
import (
"regexp"
"sort"
"strconv"
"strings"
"github.com/Masterminds/semver"
@@ -129,14 +130,32 @@ func GetLatestTags(tags []string, count int) []string {
// isIgnoredTag checks if a tag contains keywords that should be ignored.
func isIgnoredTag(tag string) bool {
// Список игнорируемых слов и фраз
ignoredKeywords := []string{"alpha", "beta", "rc", "test", "testing", "preview", "amd64", "arm64", "linux", "windows", "debug"}
lowerTag := strings.ToLower(tag)
// Игнорируем теги с архитектурой, статусом и ОС
ignoredKeywords := []string{
"alpha", "beta", "rc", "test", "testing", "preview", "next", "latest",
"amd64", "arm64", "linux", "windows", "i386", "armhf", "aarch64", "ppc64le",
"bookworm", "trixie", "ubuntu", "buster", "bullseye", "debug",
}
for _, keyword := range ignoredKeywords {
if strings.Contains(lowerTag, keyword) {
return true
}
}
// Дополнительная проверка на нестандартные теги
parts := strings.Split(tag, ".")
if len(parts) > 3 {
// Игнорируем теги с более чем 3 точками (например, 3.5.5.0, RELEASE.2025...)
return true
}
// Игнорируем теги, которые начинаются с очень больших чисел (как в Grafana)
firstPart, err := strconv.Atoi(parts[0])
if err == nil && firstPart > 100000000 {
return true
}
return false
}
+46
View File
@@ -0,0 +1,46 @@
package versionutils
import (
"log"
"strings"
"github.com/Masterminds/semver/v3"
)
// CheckForUpdate compares the current tag with the latest tag from the registry.
// It returns the latest available tag and a boolean indicating if an update is available.
func CheckForUpdate(currentTag string, latestTags []string) (string, bool) {
if len(latestTags) == 0 {
return "", false
}
latest := latestTags[0]
// Если текущий тэг совпадает с последним, обновления нет
if latest == currentTag {
return latest, false
}
// Парсим текущий тэг
currentVersion, err := semver.NewVersion(strings.TrimPrefix(currentTag, "v"))
if err != nil {
log.Printf("Could not parse current tag '%s' as SemVer. Skipping comparison.", currentTag)
return latest, false
}
// Парсим последний тэг
latestVersion, err := semver.NewVersion(strings.TrimPrefix(latest, "v"))
if err != nil {
log.Printf("Could not parse latest tag '%s' as SemVer. Skipping comparison.", latest)
return latest, false
}
// Сравниваем версии
isUpdateAvailable := latestVersion.GreaterThan(currentVersion)
return latest, isUpdateAvailable
}
// GetLatestTags ... (остальной код функции GetLatestTags) ...
// isIgnoredTag ... (остальной код функции isIgnoredTag) ...
+22 -1
View File
@@ -4,11 +4,15 @@ import (
"controlla/internal/checker"
"controlla/internal/config"
"controlla/internal/gitrepo"
"controlla/internal/notifier"
"controlla/internal/registry"
"controlla/internal/versionutils"
"fmt"
"log"
"path/filepath"
"strings"
"github.com/Masterminds/semver"
"github.com/sirupsen/logrus"
)
@@ -105,8 +109,25 @@ func main() {
}
latestTags := versionutils.GetLatestTags(tags, cfg.Checker.ProposedTagsCount)
log.Printf("Docker Hub tags for %s: %v", parsedImage.Image, latestTags)
// Parse tag for checks
_, err = semver.NewVersion(strings.TrimPrefix(parsedImage.Tag, "v"))
if err != nil {
log.Printf("⚠️ Can't check updates for %s. Current tag: %s, it is not use SemVer template.", parsedImage.Image, parsedImage.Tag)
continue
}
latestAvailable, isUpdateAvailable := versionutils.CheckForUpdate(parsedImage.Tag, latestTags)
if isUpdateAvailable {
log.Printf("🚨 New update for %s. Current tag: %s, Avaliable: %s", parsedImage.Image, parsedImage.Tag, latestAvailable)
log.Printf("Send notification to telegram")
message := fmt.Sprintf("🚨 New update for %s\nCurrent version: `%s`\nAvaliable version: `%s`", parsedImage.Image, parsedImage.Tag, latestAvailable)
notifier.SendTelegramMessage(appConfig.TelegramToken, appConfig.TelegramChatID, message)
} else {
log.Printf("✅ %s is actual. Current tag: %s", parsedImage.Image, parsedImage.Tag)
}
} else {
log.Printf("Skipping image %s: registry %s is not yet supported.", imageString, parsedImage.Registry)
// log.Printf("Skipping image %s: registry %s is not yet supported.", imageString, parsedImage.Registry)
}
}
// logrus.Infof("Controlla started. Checking every %d minutes.", cfg.Checker.IntervalMinutes)