inwx_domains_owner: optionalen Zeitstempel im citeq-TXT auswerten

TXT-Werte koennen jetzt 'citeq:<ziffer>;<YYYYMMDD>;' lauten. get_group()
liefert (ziffer, datum); die Domains werden je Gruppe (und im Block
'Ohne Zuordnung') nach Datum absteigend sortiert (juengste oben,
undatierte ans Ende) und das Datum als eigene Spalte ausgegeben.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 19:48:13 +02:00
parent 3a84aea64d
commit a9b6d00fca
+46 -12
View File
@@ -85,11 +85,15 @@ def load_owners():
def get_group(domain): def get_group(domain):
"""Ermittelt die Gruppen-Ziffer aus dem citeq:-TXT-Eintrag einer Domain. """Ermittelt Gruppen-Ziffer und optionalen Zeitstempel aus dem citeq:-TXT.
Nutzt eine echte DNS-Abfrage (dig +short TXT) und funktioniert daher Nutzt eine echte DNS-Abfrage (dig +short TXT) und funktioniert daher
unabhängig davon, wo die DNS-Zone gehostet wird. Liefert die Ziffer als unabhängig davon, wo die DNS-Zone gehostet wird. Der TXT-Wert hat die Form
String oder None, wenn kein passender TXT-Eintrag gefunden wird. 'citeq:<ziffer>' oder mit Zeitstempel 'citeq:<ziffer>;<YYYYMMDD>;'
(der abschließende Strichpunkt ist optional).
Liefert das Tupel (ziffer, datum), jeweils als String oder None, wenn kein
passender TXT-Eintrag bzw. kein Datum gefunden wird.
""" """
try: try:
out = subprocess.run( out = subprocess.run(
@@ -98,20 +102,25 @@ def get_group(domain):
) )
except FileNotFoundError: except FileNotFoundError:
print("⚠️ 'dig' nicht gefunden Gruppen können nicht ermittelt werden.") print("⚠️ 'dig' nicht gefunden Gruppen können nicht ermittelt werden.")
return None return None, None
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
return None return None, None
if out.returncode != 0: if out.returncode != 0:
return None return None, None
for line in out.stdout.splitlines(): for line in out.stdout.splitlines():
# dig liefert TXT-Werte in Anführungszeichen, lange Werte ggf. in # dig liefert TXT-Werte in Anführungszeichen, lange Werte ggf. in
# mehreren Teilen ("teil1" "teil2") -> zusammenfügen und entkleiden. # mehreren Teilen ("teil1" "teil2") -> zusammenfügen und entkleiden.
content = line.replace('" "', '').strip().strip('"') content = line.replace('" "', '').strip().strip('"')
if content.startswith(TXT_PREFIX): if content.startswith(TXT_PREFIX):
return content[len(TXT_PREFIX):].strip() rest = content[len(TXT_PREFIX):].strip()
return None # Ziffer und optionalen Zeitstempel am Semikolon trennen.
parts = [p.strip() for p in rest.split(";")]
ziffer = parts[0] if parts and parts[0] else None
datum = parts[1] if len(parts) > 1 and parts[1] else None
return ziffer, datum
return None, None
def tld_of(domain): def tld_of(domain):
@@ -153,6 +162,18 @@ def fmt_price(value):
return f"{value:.2f}".replace(".", ",") + "" return f"{value:.2f}".replace(".", ",") + ""
def fmt_date(datum):
"""Formatiert einen Zeitstempel 'YYYYMMDD' als 'DD.MM.YYYY'.
Liefert '-' bei fehlendem und den Rohwert bei unerwartetem Format.
"""
if not datum:
return "-"
if len(datum) == 8 and datum.isdigit():
return f"{datum[6:8]}.{datum[4:6]}.{datum[0:4]}"
return datum
def print_group_sums(recs, ziffer, prices): def print_group_sums(recs, ziffer, prices):
"""Gibt für eine Gruppe die Anzahl Domains pro Top-Level-Domain aus. """Gibt für eine Gruppe die Anzahl Domains pro Top-Level-Domain aus.
@@ -177,7 +198,18 @@ def format_domain_line(rec):
ns = "(keine NS-Einträge gefunden)" ns = "(keine NS-Einträge gefunden)"
else: else:
ns = ", ".join(rec['ns']) ns = ", ".join(rec['ns'])
return f" {rec['name']:<35} {rec['status']:<8} {ns}" return f" {rec['name']:<35} {rec['status']:<8} {fmt_date(rec['datum']):<12} {ns}"
def sort_by_date_desc(recs):
"""Sortiert Domains nach Zeitstempel absteigend (jüngste zuerst).
Domains ohne Zeitstempel wandern ans Ende. Bei gleichem Datum bleibt die
vorherige (alphabetische) Reihenfolge erhalten sorted() ist stabil, auch
mit reverse=True.
"""
recs.sort(key=lambda r: (r['datum'] is not None, r['datum'] or ""), reverse=True)
return recs
def main(): def main():
@@ -198,10 +230,12 @@ def main():
records = [] records = []
for d in domains: for d in domains:
name = d.get('domain', '') name = d.get('domain', '')
ziffer, datum = get_group(name)
records.append({ records.append({
'name': name, 'name': name,
'status': d.get('status', ''), 'status': d.get('status', ''),
'ziffer': get_group(name), 'ziffer': ziffer,
'datum': datum,
'ns': get_nameservers(api, name), 'ns': get_nameservers(api, name),
}) })
@@ -212,7 +246,7 @@ def main():
print() print()
for ziffer, klarname in owners.items(): for ziffer, klarname in owners.items():
print(f"{ziffer} ({klarname})") print(f"{ziffer} ({klarname})")
matching = [r for r in records if r['ziffer'] == ziffer] matching = sort_by_date_desc([r for r in records if r['ziffer'] == ziffer])
if matching: if matching:
for rec in matching: for rec in matching:
print(format_domain_line(rec)) print(format_domain_line(rec))
@@ -222,7 +256,7 @@ def main():
print() # Leerzeile zwischen den Gruppen print() # Leerzeile zwischen den Gruppen
# Domains, die keiner Ziffer aus domain_owner zugeordnet sind # Domains, die keiner Ziffer aus domain_owner zugeordnet sind
rest = [r for r in records if r['ziffer'] not in owners] rest = sort_by_date_desc([r for r in records if r['ziffer'] not in owners])
if rest: if rest:
print("Ohne Zuordnung") print("Ohne Zuordnung")
for rec in rest: for rec in rest: