Skip to content

Rules Architecture

Stephen Cox edited this page Dec 10, 2025 · 2 revisions

Execution Flow

┌─────────────────────────────────────────┐
│ Trigger Execution                        │
│ (manual/scheduled/on_added/on_completed) │
└────────────────┬────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────┐
│ Load Torrent(s)                          │
│ • All torrents (manual/scheduled)        │
│ • Single torrent (webhooks)              │
└────────────────┬────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────┐
│ Load Rules (config/rules.yml)            │
│ Rules execute in file order              │
└────────────────┬────────────────────────┘
                 │
    ┌────────────┴────────────┐
    │ For Each Rule:          │
    │ 1. Check if enabled     │
    │ 2. Check trigger filter │
    └────────────┬────────────┘
                 │
    ┌────────────▼────────────┐
    │ For Each Torrent:       │
    └────────────┬────────────┘
                 │
                 ▼
┌─────────────────────────────────────────┐
│ Evaluate Conditions                      │
│ 1. Check "all" group (AND)               │
│ 2. Check "any" group (OR)                │
│ 3. Check "none" group (NOT)              │
│ All groups must pass                     │
└────────────────┬────────────────────────┘
                 │
        ┌────────┴────────┐
        │   Matched?      │
        └────────┬────────┘
          Yes ◄──┴──► No
           │          │
           ▼          ▼
    ┌──────────┐   Continue
    │ Execute  │   to next
    │ Actions  │   torrent
    └─────┬────┘
          │
          ▼
    ┌──────────────┐
    │ stop_on_match│
    │   = true?    │
    └──────┬───────┘
      Yes  │  No
      Skip │  Continue
      this │  rules for
      torrent  this torrent

Condition Evaluation Logic

Rules support three logical groups that can be combined:

  • all: Every condition in the list must be true (AND logic)
  • any: At least one condition must be true (OR logic)
  • none: No conditions can be true (NOT logic)

Evaluation Order:

  1. All three groups are evaluated independently
  2. The rule matches only if all groups pass
  3. Empty/missing groups are skipped (treated as pass)

Examples:

# Only "all" group
conditions:
  all:
    - field: info.ratio
      operator: ">="
      value: 2.0
    - field: info.state
      operator: ==
      value: uploading
# Result: ratio >= 2.0 AND state = uploading
# Only "any" group
conditions:
  any:
    - field: info.state
      operator: ==
      value: error
    - field: info.state
      operator: ==
      value: missingFiles
# Result: state = error OR state = missingFiles
# Combining groups
conditions:
  all:
    - field: info.ratio
      operator: ">="
      value: 2.0
  none:
    - field: info.category
      operator: ==
      value: keep
# Result: (ratio >= 2.0) AND NOT (category = keep)
# Complex combination
conditions:
  all:
    - field: info.size
      operator: ">"
      value: 10737418240  # 10 GB
  any:
    - field: info.name
      operator: contains
      value: "1080p"
    - field: info.name
      operator: contains
      value: "2160p"
  none:
    - field: info.category
      operator: in
      value: [keep, seedbox]
# Result: (size > 10GB) AND (name has 1080p OR 2160p) AND NOT (category in [keep, seedbox])

Field Resolution & Caching

The engine uses dot notation to access different qBittorrent API endpoints efficiently.

Dot Notation Format: endpoint.property

Examples:

  • info.name → Torrent name from torrents/info endpoint
  • trackers.url → Tracker URLs from torrents/trackers endpoint
  • files.name → File names from torrents/files endpoint

API Call Patterns:

Field Category API Endpoint When Called Cache Scope Cost
info.* /torrents/info Pre-loaded Per execution Free
trackers.* /torrents/trackers First use per torrent Per execution 1 call/torrent
files.* /torrents/files First use per torrent Per execution 1 call/torrent
peers.* /sync/torrentPeers First use per torrent Per execution 1 call/torrent
properties.* /torrents/properties First use per torrent Per execution 1 call/torrent
webseeds.* /torrents/webseeds First use per torrent Per execution 1 call/torrent
transfer.* /transfer/info First use (global) Per execution 1 call total
app.* /app/preferences First use (global) Per execution 1 call total

Caching Behavior:

  • info.*: Already loaded, zero cost
  • Per-torrent fields: Called once per torrent, then cached for that execution
  • Global fields: Called once total, shared across all torrents

Performance Tips:

  • Use info.* fields whenever possible (free)
  • Avoid expensive fields (files.*, trackers.*) in high-frequency webhooks
  • Global fields (transfer.*, app.*) are cheap to use

Collection Field Behavior:

Some fields return lists (collections):

  • trackers.* - List of trackers
  • files.* - List of files
  • peers.* - List of peers
  • webseeds.* - List of web seeds

Operators check if ANY item matches:

- field: trackers.url
  operator: contains
  value: ".private"
# Returns TRUE if ANY tracker URL contains ".private"
- field: files.name
  operator: matches
  value: '(?i)\.rar$'
# Returns TRUE if ANY file name ends with .rar

🎯 qbt-rules

v0.3.0 | GitHub


📚 Getting Started

📖 Core Concepts

🔧 Configuration

🆘 Help & Support

🤝 Contributing


🆕 What's New (v0.3.0)

  • ✅ Unified CLI (qbt-rules)
  • ✅ Custom triggers
  • ✅ Trigger-agnostic mode
  • ✅ Size operators (50GB)

🔗 Quick Links

Clone this wiki locally