initial commit

This commit is contained in:
2026-07-11 23:10:14 -04:00
commit db7ad3eec2
8 changed files with 247 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
package main
import (
"log/slog"
"os"
"regexp"
"time"
"git.sbstp.ca/sbstp/qbittorrent-sentinel/api"
"github.com/samber/lo"
)
var rxExecutable = regexp.MustCompile(`\.(exe|scr|lnk|bat|vbs|js|cmd)$`)
func isExecutable(t *api.TorrentFile) bool {
// - File has extension commonly used for viruses/executables
return rxExecutable.MatchString(t.Name)
}
func isCandidate(t *api.TorrentInfo) bool {
// - Less downloaded than total size, indicates it's stalled
// - Size is less than total size, indicates some files have been filtered out
return t.Downloaded < t.TotalSize && t.Size < t.TotalSize && time.Since(t.AddedOn.Time) > time.Minute*15
}
func checkTorrents(baseURL, username, password string) error {
client, err := api.NewClient(baseURL, username, password)
if err != nil {
slog.Error("Could not connect to qBittorrent", slog.String("error", err.Error()))
return err
}
list, err := client.LisTorrents()
if err != nil {
slog.Error("Could not list torrents", slog.String("error", err.Error()))
return err
}
slog.Info("Successfully listed torrents", slog.Int("num", len(list)))
for _, t := range list {
if isCandidate(&t) {
slog.Info("Torrent is a candidate", slog.String("hash", t.Hash), slog.String("name", t.Name))
files, err := client.TorrentFiles(t.Hash)
if err != nil {
slog.Error("Could not get torrent files", slog.String("error", err.Error()))
continue
}
executables := lo.Filter(files, func(f api.TorrentFile, _ int) bool {
return isExecutable(&f)
})
if len(executables) > 0 {
slog.Info("Torrent contains executables")
for _, exe := range executables {
slog.Info("Torrent file is executable", slog.String("name", exe.Name))
}
slog.Info("Deleting torrent...")
if err := client.DeleteTorrent(t.Hash, true); err != nil {
slog.Error("Failed to delete torrent", slog.String("error", err.Error()))
} else {
slog.Info("Torrent deleted successfully")
}
}
}
}
slog.Info("Done")
return nil
}
func main() {
baseURL := os.Getenv("QBITTORRENT_BASE_URL")
username := os.Getenv("QBITTORRENT_USERNAME")
password := os.Getenv("QBITTORRENT_PASSWORD")
if len(baseURL) == 0 {
slog.Error("QBITTORRENT_BASE_URL environment variable is required")
os.Exit(2)
}
if err := checkTorrents(baseURL, username, password); err != nil {
os.Exit(1)
}
}