mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 00:39:13 +00:00

* refactor: update http handlers * feat: add trace log level * refactir: irc handler * refactor(definitions): add irc settings and invite cmd: * feat: add dft values to inputs * refactor: indexer irc forms * refactor(definitions): fix nickserv.password var: * feat: pre fill indexer name field * refactor: handle stopping and updates
342 lines
8.1 KiB
Go
342 lines
8.1 KiB
Go
package filter
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"github.com/autobrr/autobrr/internal/domain"
|
|
"github.com/autobrr/autobrr/internal/indexer"
|
|
"github.com/autobrr/autobrr/pkg/wildcard"
|
|
)
|
|
|
|
type Service interface {
|
|
//FindFilter(announce domain.Announce) (*domain.Filter, error)
|
|
|
|
FindByID(filterID int) (*domain.Filter, error)
|
|
FindByIndexerIdentifier(announce domain.Announce) (*domain.Filter, error)
|
|
ListFilters() ([]domain.Filter, error)
|
|
Store(filter domain.Filter) (*domain.Filter, error)
|
|
Update(filter domain.Filter) (*domain.Filter, error)
|
|
Delete(filterID int) error
|
|
}
|
|
|
|
type service struct {
|
|
repo domain.FilterRepo
|
|
actionRepo domain.ActionRepo
|
|
indexerSvc indexer.Service
|
|
}
|
|
|
|
func NewService(repo domain.FilterRepo, actionRepo domain.ActionRepo, indexerSvc indexer.Service) Service {
|
|
return &service{
|
|
repo: repo,
|
|
actionRepo: actionRepo,
|
|
indexerSvc: indexerSvc,
|
|
}
|
|
}
|
|
|
|
func (s *service) ListFilters() ([]domain.Filter, error) {
|
|
// get filters
|
|
filters, err := s.repo.ListFilters()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var ret []domain.Filter
|
|
|
|
for _, filter := range filters {
|
|
indexers, err := s.indexerSvc.FindByFilterID(filter.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
filter.Indexers = indexers
|
|
|
|
ret = append(ret, filter)
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func (s *service) FindByID(filterID int) (*domain.Filter, error) {
|
|
// find filter
|
|
filter, err := s.repo.FindByID(filterID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// find actions and attach
|
|
//actions, err := s.actionRepo.FindFilterActions(filter.ID)
|
|
actions, err := s.actionRepo.FindByFilterID(filter.ID)
|
|
if err != nil {
|
|
log.Error().Msgf("could not find filter actions: %+v", &filter.ID)
|
|
}
|
|
filter.Actions = actions
|
|
|
|
// find indexers and attach
|
|
indexers, err := s.indexerSvc.FindByFilterID(filter.ID)
|
|
if err != nil {
|
|
log.Error().Err(err).Msgf("could not find indexers for filter: %+v", &filter.Name)
|
|
return nil, err
|
|
}
|
|
filter.Indexers = indexers
|
|
|
|
//log.Debug().Msgf("found filter: %+v", filter)
|
|
|
|
return filter, nil
|
|
}
|
|
|
|
func (s *service) FindByIndexerIdentifier(announce domain.Announce) (*domain.Filter, error) {
|
|
// get filter for tracker
|
|
filters, err := s.repo.FindByIndexerIdentifier(announce.Site)
|
|
if err != nil {
|
|
log.Error().Err(err).Msgf("could not find filters for indexer: %v", announce.Site)
|
|
return nil, err
|
|
}
|
|
|
|
// match against announce/releaseInfo
|
|
for _, filter := range filters {
|
|
// if match, return the filter
|
|
matchedFilter := s.checkFilter(filter, announce)
|
|
if matchedFilter {
|
|
log.Trace().Msgf("found filter: %+v", &filter)
|
|
log.Debug().Msgf("found filter: %+v", &filter.Name)
|
|
|
|
// find actions and attach
|
|
actions, err := s.actionRepo.FindByFilterID(filter.ID)
|
|
if err != nil {
|
|
log.Error().Err(err).Msgf("could not find filter actions: %+v", &filter.ID)
|
|
return nil, err
|
|
}
|
|
|
|
// if no actions found, check next filter
|
|
if actions == nil {
|
|
continue
|
|
}
|
|
|
|
filter.Actions = actions
|
|
|
|
return &filter, nil
|
|
}
|
|
}
|
|
|
|
// if no match, return nil
|
|
return nil, nil
|
|
}
|
|
|
|
//func (s *service) FindFilter(announce domain.Announce) (*domain.Filter, error) {
|
|
// // get filter for tracker
|
|
// filters, err := s.repo.FindFiltersForSite(announce.Site)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
//
|
|
// // match against announce/releaseInfo
|
|
// for _, filter := range filters {
|
|
// // if match, return the filter
|
|
// matchedFilter := s.checkFilter(filter, announce)
|
|
// if matchedFilter {
|
|
//
|
|
// log.Debug().Msgf("found filter: %+v", &filter)
|
|
//
|
|
// // find actions and attach
|
|
// actions, err := s.actionRepo.FindByFilterID(filter.ID)
|
|
// if err != nil {
|
|
// log.Error().Msgf("could not find filter actions: %+v", &filter.ID)
|
|
// }
|
|
// filter.Actions = actions
|
|
//
|
|
// return &filter, nil
|
|
// }
|
|
// }
|
|
//
|
|
// // if no match, return nil
|
|
// return nil, nil
|
|
//}
|
|
|
|
func (s *service) Store(filter domain.Filter) (*domain.Filter, error) {
|
|
// validate data
|
|
|
|
// store
|
|
f, err := s.repo.Store(filter)
|
|
if err != nil {
|
|
log.Error().Err(err).Msgf("could not store filter: %v", filter)
|
|
return nil, err
|
|
}
|
|
|
|
return f, nil
|
|
}
|
|
|
|
func (s *service) Update(filter domain.Filter) (*domain.Filter, error) {
|
|
// validate data
|
|
|
|
// store
|
|
f, err := s.repo.Update(filter)
|
|
if err != nil {
|
|
log.Error().Err(err).Msgf("could not update filter: %v", filter.Name)
|
|
return nil, err
|
|
}
|
|
|
|
// take care of connected indexers
|
|
if err = s.repo.DeleteIndexerConnections(f.ID); err != nil {
|
|
log.Error().Err(err).Msgf("could not delete filter indexer connections: %v", filter.Name)
|
|
return nil, err
|
|
}
|
|
|
|
for _, i := range filter.Indexers {
|
|
if err = s.repo.StoreIndexerConnection(f.ID, int(i.ID)); err != nil {
|
|
log.Error().Err(err).Msgf("could not store filter indexer connections: %v", filter.Name)
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return f, nil
|
|
}
|
|
|
|
func (s *service) Delete(filterID int) error {
|
|
if filterID == 0 {
|
|
return nil
|
|
}
|
|
|
|
// delete
|
|
if err := s.repo.Delete(filterID); err != nil {
|
|
log.Error().Err(err).Msgf("could not delete filter: %v", filterID)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// checkFilter tries to match filter against announce
|
|
func (s *service) checkFilter(filter domain.Filter, announce domain.Announce) bool {
|
|
|
|
if !filter.Enabled {
|
|
return false
|
|
}
|
|
|
|
if filter.Scene && announce.Scene != filter.Scene {
|
|
return false
|
|
}
|
|
|
|
if filter.Freeleech && announce.Freeleech != filter.Freeleech {
|
|
return false
|
|
}
|
|
|
|
if filter.Shows != "" && !checkFilterStrings(announce.TorrentName, filter.Shows) {
|
|
return false
|
|
}
|
|
|
|
//if filter.Seasons != "" && !checkFilterStrings(announce.TorrentName, filter.Seasons) {
|
|
// return false
|
|
//}
|
|
//
|
|
//if filter.Episodes != "" && !checkFilterStrings(announce.TorrentName, filter.Episodes) {
|
|
// return false
|
|
//}
|
|
|
|
// matchRelease
|
|
if filter.MatchReleases != "" && !checkFilterStrings(announce.TorrentName, filter.MatchReleases) {
|
|
return false
|
|
}
|
|
|
|
if filter.MatchReleaseGroups != "" && !checkFilterStrings(announce.TorrentName, filter.MatchReleaseGroups) {
|
|
return false
|
|
}
|
|
|
|
if filter.ExceptReleaseGroups != "" && checkFilterStrings(announce.TorrentName, filter.ExceptReleaseGroups) {
|
|
return false
|
|
}
|
|
|
|
if filter.MatchUploaders != "" && !checkFilterStrings(announce.Uploader, filter.MatchUploaders) {
|
|
return false
|
|
}
|
|
|
|
if filter.ExceptUploaders != "" && checkFilterStrings(announce.Uploader, filter.ExceptUploaders) {
|
|
return false
|
|
}
|
|
|
|
if len(filter.Resolutions) > 0 && !checkFilterSlice(announce.TorrentName, filter.Resolutions) {
|
|
return false
|
|
}
|
|
|
|
if len(filter.Codecs) > 0 && !checkFilterSlice(announce.TorrentName, filter.Codecs) {
|
|
return false
|
|
}
|
|
|
|
if len(filter.Sources) > 0 && !checkFilterSlice(announce.TorrentName, filter.Sources) {
|
|
return false
|
|
}
|
|
|
|
if len(filter.Containers) > 0 && !checkFilterSlice(announce.TorrentName, filter.Containers) {
|
|
return false
|
|
}
|
|
|
|
if filter.Years != "" && !checkFilterStrings(announce.TorrentName, filter.Years) {
|
|
return false
|
|
}
|
|
|
|
if filter.MatchCategories != "" && !checkFilterStrings(announce.Category, filter.MatchCategories) {
|
|
return false
|
|
}
|
|
|
|
if filter.ExceptCategories != "" && checkFilterStrings(announce.Category, filter.ExceptCategories) {
|
|
return false
|
|
}
|
|
|
|
if filter.Tags != "" && !checkFilterStrings(announce.Tags, filter.Tags) {
|
|
return false
|
|
}
|
|
|
|
if filter.ExceptTags != "" && checkFilterStrings(announce.Tags, filter.ExceptTags) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func checkFilterSlice(name string, filterList []string) bool {
|
|
name = strings.ToLower(name)
|
|
|
|
for _, filter := range filterList {
|
|
filter = strings.ToLower(filter)
|
|
// check if line contains * or ?, if so try wildcard match, otherwise try substring match
|
|
a := strings.ContainsAny(filter, "?|*")
|
|
if a {
|
|
match := wildcard.Match(filter, name)
|
|
if match {
|
|
return true
|
|
}
|
|
} else {
|
|
b := strings.Contains(name, filter)
|
|
if b {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func checkFilterStrings(name string, filterList string) bool {
|
|
filterSplit := strings.Split(filterList, ",")
|
|
name = strings.ToLower(name)
|
|
|
|
for _, s := range filterSplit {
|
|
s = strings.ToLower(s)
|
|
// check if line contains * or ?, if so try wildcard match, otherwise try substring match
|
|
a := strings.ContainsAny(s, "?|*")
|
|
if a {
|
|
match := wildcard.Match(s, name)
|
|
if match {
|
|
return true
|
|
}
|
|
} else {
|
|
b := strings.Contains(name, s)
|
|
if b {
|
|
return true
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
}
|