docker-planesnitch: Toarnă Fiecare Avion Care Îndrăznește Să Zboare Pe Lângă Tine

Locuiesc sub un culoar de zbor. Avioane militare de transport, jeturi guvernamentale, elicoptere de poliție, câte un squawk de urgență din când în când, toate îmi trec pe deasupra capului și habar nu aveam ce sunt. Sigur, puteam să deschid Flightradar24 și să mă holbez la o hartă toată ziua, dar nu sunt pensionar cu binoclu. Voiam ceva care să se uite la cer în locul meu și să zbiere la mine când apare ceva interesant.
Așa că am construit planesnitch. Un container Docker care interoghează API-uri ADS-B publice, verifică avioanele față de listele tale de urmărire și trimite alerte spre Telegram sau webhookuri. Jeturi militare, spioni guvernamentali, squawkuri de urgență, zburători jos și dubioși, ce îi zici tu să urmărească. Mai multe locații în același timp. Fără hardware SDR, fără antenă, fără căcaturi. Doar un fișier de configurare și o conexiune la internet.

Cum Funcționează

La fiecare ciclu de interogare (configurabil, implicit 1 minut), planesnitch aduce date despre avioane de la până la 4 API-uri ADS-B publice, în paralel:

Deduplică după codul hex ICAO, păstrând intrarea cu cele mai multe câmpuri de date. Unele surse întorc date îmbogățite, tipul avionului, proprietarul sau operatorul, înmatricularea, anul fabricației, în timp ce altele îți dau doar câmpuri ADS-B brute. Planesnitch păstrează automat intrarea cea mai bogată pentru fiecare avion.
Pe urmă trece fiecare avion prin listele tale de urmărire și prin regulile de alertă. A găsit o potrivire? Formatează un mesaj și îl aruncă spre botul tău de Telegram sau spre endpointul de webhook. Cooldownurile per alertă și per avion îl împiedică să te spameze despre același C-17 care dă ture de 3 ore.
Dacă îți rulezi propriul receptor ADS-B cu ultrafeeder, poți îndrepta planesnitch și spre el, aceeași configurare, doar adaugi un URL de sursă locală.

Instalare

# Grab the config and edit it
curl -sL 
  https://raw.githubusercontent.com/psyb0t/docker-planesnitch/main/config.yaml.example 
  -o config.yaml
# Optional: download military/gov/police watchlists
mkdir -p csv
BASE=https://raw.githubusercontent.com/sdr-enthusiasts/plane-alert-db/main
curl -sLo csv/plane-alert-mil.csv $BASE/plane-alert-mil.csv
curl -sLo csv/plane-alert-gov.csv $BASE/plane-alert-gov.csv
curl -sLo csv/plane-alert-pol.csv $BASE/plane-alert-pol.csv
# Run it
docker run 
  -v ./config.yaml:/app/config.yaml:ro 
  -v ./csv:/csv:ro 
  psyb0t/planesnitch

Asta e tot. Editezi configurarea cu coordonatele tale și cu tokenul botului de Telegram, și gata, torni.

Configurarea

Un singur fișier YAML controlează tot. Definești unde ești, ce să urmărească, când să alerteze și unde să trimită.

Locații

Monitorizezi câte locuri vrei. Casa ta, biroul, casa bunicii, Zona 51, fiecare cu propria rază de căutare.

locations:
  home:
    name: "Home"
    lat: 38.8719
    lon: -77.0563
    radius: 150km
  area51:
    name: "Area 51"
    lat: 37.2350
    lon: -115.8111
    radius: 50nm

Sufixele de unități merg peste tot, km, mi, nm, ft, m. Numerele simple cad implicit pe km la distanțe și pe ft la altitudini.
Grupare automată pentru locațiile apropiate. Dacă două locații sunt destul de aproape cât un singur apel de API să le acopere pe amândouă, planesnitch le grupează automat și face o singură cerere în amonte, în loc de două. Îți configurezi casa și biroul, amândouă cu rază de 50km, și sunt la 30km una de alta? O singură lovitură de API, rezultatele împărțite la ambele locații. Definești o duzină de puncte care se suprapun, planesnitch tot unește până când un grup lovește cercul maxim de încadrare, apoi începe un grup nou. Te oprește să îți arzi cota de rate limit pe interogări redundante spre aceeași bucată de cer.
Cooldownuri per sursă. Fiecare sursă din amonte are propriul cooldown de rate limit, dacă adsb.lol începe să dea 429, sursa aia se retrage după programul ei, în timp ce adsb.fi și ultrafeederul tău local interoghează mai departe normal. Nicio strangulare de la un singur API nu îți pune tot fluxul la pământ.

Liste de Urmărire

Șase tipuri de liste de urmărire îi zic lui planesnitch pe cine să toarne:

watchlists:
  # Emergency squawk codes
  emergencies:
    type: squawk
    values: ["7500", "7600", "7700", "7400", "7777"]
  # 8,709 military aircraft from community database
  military:
    type: icao_csv
    source: plane-alert-mil.csv
  # Government aircraft
  government:
    type: icao_csv
    source: plane-alert-gov.csv
  # Stalk specific aircraft by ICAO hex
  my_planes:
    type: icao
    values: ["4ca123", "a12345"]
  # Stalk by aircraft type — any A400M, Rafale, or Alpha Jet
  cool_jets:
    type: icao_type
    values: ["A400", "RFAL", "AJET"]
  # Everything within radius
  everything:
    type: all
  # WTF just buzzed my house
  low_flyers:
    type: proximity
    min_altitude: 0ft
    max_altitude: 3000ft

Codurile squawk sunt butoanele de panică ale aviației, 7700 e urgență generală, 7600 e pană de radio, 7500 e deturnare, 7400 e urgență de aeronavă fără pilot, 7777 e interceptare militară. Genul de căcat despre care vrei să știi când se întâmplă la 6 mile de casa ta.
Tipul icao_csv se integrează cu plane-alert-db, o bază de date curatoriată de comunitate, cu peste 15.000 de avioane interesante, întreținută de degenerații simpatici din comunitatea de plane spotting:

  • Militare, 8.709 aeronave
  • Guvernamentale, 1.743 aeronave
  • Poliție, 932 aeronave
  • Civile, 4.530 aeronave notabile
  • Confidențialitate (PIA), 94 de operatori preocupați de intimitate
  • Tot, 15.914 la un loc

Lista de urmărire icao_type (v1.6) se potrivește pe designatorul de tip ICAO doc 8643, codul de 3-4 caractere pe care aviația îl folosește ca să identifice modelul de avion în sine, nu înmatricularea. C17 înseamnă fiecare C-17 Globemaster de pe planetă. B738 înseamnă fiecare 737-800. RFAL înseamnă fiecare Rafale. AJET înseamnă fiecare Alpha Jet. Bagi designatorii care te interesează în values: și capeți alerte pentru fiecare aparat de tipul ăla care îți intră în rază, indiferent cine îl deține sau ce coadă poartă.
Lista de urmărire de proximitate e pentru prins zburătorii jos. Setezi un interval de altitudine și planesnitch te alertează când ceva zboară în raza ta sub plafonul ăla. Bună ca să răspunzi la “ce mama dracului a fost aia” când ceva îți zdrăngăne geamurile la 2 noaptea.

Alerte

Legi listele de urmărire la ținte de notificare. Opțional filtrezi după locație, dacă o omiți, se verifică toate locațiile. Cooldownurile previn spamul:

alerts:
  - name: "Emergency Alert"
    watchlists: [emergencies]
    cooldown: 1m
    notify: [tg_emergencies]
  - name: "Military Spotter"
    watchlists: [military, government]
    cooldown: 5m
    notify: [tg_spotting]
  - name: "Everything at Home"
    locations: [home]
    watchlists: [everything]
    cooldown: 1m
    notify: [tg_main]

Stringurile de durată acceptă s, m, h, deci merg și 5m, și 1h30m, și 90s, și secunde simple. Alerte diferite pot pleca spre canale de Telegram diferite, urgențele spre unul, spotting militar spre altul, zburătorii jos spre al treilea.

Notificări

Telegram și webhookuri. Rutezi alerte diferite spre destinații diferite:

notifications:
  tg_emergencies:
    type: telegram
    bot_token: "123456:ABC-DEF"
    chat_id: "-100123456789"
  tg_spotting:
    type: telegram
    bot_token: "123456:ABC-DEF"
    chat_id: "-100987654321"
  my_webhook:
    type: webhook
    url: "https://example.com/hook"
    headers:
      Authorization: "Bearer xxx"

Poze cu Avioane (v1.6)

Când un avion are un designator de tip ICAO (de exemplu C17, B738, RFAL), planesnitch aduce poza potrivită de avion de pe doc8643.com și:

  • o atașează la mesajul de Telegram ca poză (textul alertei devine legenda pozei, iar dacă legenda trece de limita de 1024 de caractere a Telegramului, se rupe într-un mesaj text urmat de poză, ca să nu pierzi niciodată conținutul)
  • înglobează octeții JPEG (base64) în payloadurile de webhook, în noul câmp image_base64, null dacă nu e nicio imagine în cache

Imaginile sunt puse în cache pe disc, sub /images în interiorul containerului, montezi -v ./images:/images ca să le păstrezi peste reporniri. Cache-ul e indexat după designatorul de tip, deci toate cele peste 8.000 de C-17 împart același fișier de poză. Ratările sunt înregistrate ca markere .notfound, ca tipurile fără poze pe doc8643 să nu fie cerute din nou la fiecare ciclu. Ștergi directorul de cache ca să forțezi o împrospătare.

Directorul de cache folosește scrieri atomice (.tmp plus os.replace) și un asyncio.Lock per tip, ca mai multe alerte care se declanșează deodată pe același tip de avion să nu se calce în picioare la aducere. Designatorii de tip veniți din fluxurile ADS-B din amonte sunt validați față de ^[A-Z0-9]{1,8}$ înainte de orice atingere de sistem de fișiere, fără traversare de căi strecurată printr-un câmp t otrăvit, iar orice răspuns cu un content type care nu e image/* e respins, ca paginile de challenge HTML de la Cloudflare să nu poată otrăvi cache-ul.

Renunțare la Imagini, per Țintă (v1.7)

Pozele sunt grozave pentru un canal personal de Telegram. Sunt un coșmar nenorocit pentru un receptor de webhook Home Assistant care trebuie să mestece 80 KB de base64 la fiecare ciclu, sau pentru un chat aglomerat de spotteri, unde toată lumea a văzut deja cum arată un C-17. Așa că fiecare țintă de notificare acceptă attach_image: false, ca să coboare la doar text:

notifications:
  tg_main:
    type: telegram
    bot_token: "..."
    chat_id: "..."
    attach_image: false   # plain sendMessage, no photo
  my_webhook:
    type: webhook
    url: "https://example.com/hook"
    attach_image: false   # image_base64 will be null

Implicit e true, deci alegi tu explicit varianta doar text. Partea deșteaptă: dacă toate țintele de notificare legate de o anumită regulă de alertă renunță, planesnitch sare complet peste aducerea de pe doc8643 pentru alerta aia. Nicio lovitură de rețea irosită, nicio citire de disc irosită, nicio blocare inutilă. Amestecul de ținte cu attach_image=true și attach_image=false pe aceeași alertă tot aduce o singură dată și le servește pe amândouă, cache-ul de poze e comun.

Cum Arată Alertele

Alertele de Telegram sunt formatate cu emoji și cu toate datele pe care le-ai vrea dintr-o privire:

🔔 Emergency Alert
🚨 squawk 7700 (EMERGENCY)
✈️ RYR1234
🛩️ BOEING 737-800 | EI-ABC | 2015
💼 RYANAIR
📍 45.5000, 28.1000 | 3,200 ft
📏 6 nm from home
💨 280 kts
🗺️ https://globe.adsb.fi/?icao=4ca123
🔔 Military Spotter
✈️ TEDDY64
🛩️ BOEING C-17A Globemaster III | 94-0067 | 1994
💼 USAF
🏷️ USAF — USAF
📍 37.9306, -78.7019 | 12,350 ft
📏 99 nm from home
💨 413 kts
📡 squawk 1613
🗺️ https://globe.adsb.fi/?icao=ae07e1

Apeși pe link și capeți o hartă în timp real a avionului pe globe.adsb.fi. Linia de squawk include și înțelesul, planesnitch are o bază de date încorporată cu înțelesurile și domeniile codurilor squawk, deci îți zice de ce contează codul ăla.
Payloadurile de webhook sunt array-uri JSON cu metadate complete, detalii despre avion, motivul potrivirii, informații despre lista de urmărire, metadate din CSV dacă vine din plane-alert-db, distanța față de locație și unitățile de afișare. Tot ce îți trebuie ca să îți construiești propriile integrări peste el.

Unități de Afișare

Trei presetări controlează cum apar altitudinea, distanța și viteza în alerte:

display_units: aviation   # ft / nm / kts (default)
display_units: metric     # m / km / km/h
display_units: imperial   # ft / mi / mph

Conversiile se fac la trimitere. Matematica internă e mereu metrică. Folosește ce are sens pentru situația ta, dacă ești pilot, unități de aviație. Dacă ești om normal, metric. Dacă ești american, imperial.

Reîncărcare la Cald și Sănătate

Schimbările de configurare sunt preluate la următorul ciclu de interogare, fără să repornești containerul. Listele de urmărire din CSV se împrospătează automat la fiecare 24 de ore. Un endpoint de sănătate pe portul 8080 expune uptimeul, ora ultimei interogări și numărul de avioane, pentru monitorizare și orchestrare.

Pe Scurt

Un container Docker care se uită la cer și toarnă fiecare avion interesant în Telegramul tău. Jeturi militare, avioane guvernamentale, squawkuri de urgență, zburători jos și dubioși, numere de coadă anume, tipuri întregi de avioane, ce vrei tu. Mai multe locații, mai multe surse, mai multe ținte de notificare, poze de avioane de pe doc8643 atașate direct la alerte, toate dintr-un singur fișier YAML.
Fără hardware SDR. Fără antenă. Fără receptor dedicat. Doar API-uri ADS-B publice și un fișier de configurare paranoic.

Acum Cu Skill de Agent

Repo-ul livrează un skill de agent la .agents/skills/planesnitch/, publicat pe ClawHub de CI la push-uri de tag. Documentează toată suprafața de urmărire, CSV-urile militare, guvernamentale și de poliție din plane-alert-db, squawkurile de urgență 7500/7600/7700, listele personalizate de hex și de tip ICAO, pragurile de altitudine pentru zburătorii jos, sau pur și simplu “tot”, dacă te urăști, peste toate API-urile ADS-B gratuite din care poate trage.
Deci, în loc să îi explici de fiecare dată unui asistent propriul tău turnător de avioane, el instalează skillul și știe deja ce butoane există.
Ia-l de aici: github.com/psyb0t/docker-planesnitch
Licențiat sub WTFPL, pentru că turnatul avioanelor nu ar trebui să ceară un acord de licențiere.

Cum Îl Instalezi în Agentul Tău

Ca asistentul tău să poată monta turnătorul de avioane fără să îi narezi tu configurarea. Tot ce e sub .agents/ e catalogat într-un singur marketplace, deci sunt două comenzi:

claude plugin marketplace add psyb0t/agents
claude plugin install planesnitch@psyb0t

Codex folosește același marketplace cu alt verb, codex plugin add planesnitch@psyb0t, pentru că nu există codex plugin install. Găsește singur și skillul într-un checkout al repo-ului, pentru că scanează .agents/skills/ nativ, fără să fie instalat absolut nimic.