add telegram notification
This commit is contained in:
+1
-1
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) ...
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user