From a9b6d00fca4d3c9aeb22be771f484cad671fdc45 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 2 Jul 2026 19:48:13 +0200 Subject: [PATCH] inwx_domains_owner: optionalen Zeitstempel im citeq-TXT auswerten TXT-Werte koennen jetzt 'citeq:;;' 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 --- inwx_domains_owner.py | 58 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/inwx_domains_owner.py b/inwx_domains_owner.py index b296158..d004e25 100644 --- a/inwx_domains_owner.py +++ b/inwx_domains_owner.py @@ -85,11 +85,15 @@ def load_owners(): 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 - unabhängig davon, wo die DNS-Zone gehostet wird. Liefert die Ziffer als - String oder None, wenn kein passender TXT-Eintrag gefunden wird. + unabhängig davon, wo die DNS-Zone gehostet wird. Der TXT-Wert hat die Form + 'citeq:' oder – mit Zeitstempel – 'citeq:;;' + (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: out = subprocess.run( @@ -98,20 +102,25 @@ def get_group(domain): ) except FileNotFoundError: print("⚠️ 'dig' nicht gefunden – Gruppen können nicht ermittelt werden.") - return None + return None, None except subprocess.TimeoutExpired: - return None + return None, None if out.returncode != 0: - return None + return None, None for line in out.stdout.splitlines(): # dig liefert TXT-Werte in Anführungszeichen, lange Werte ggf. in # mehreren Teilen ("teil1" "teil2") -> zusammenfügen und entkleiden. content = line.replace('" "', '').strip().strip('"') if content.startswith(TXT_PREFIX): - return content[len(TXT_PREFIX):].strip() - return None + rest = content[len(TXT_PREFIX):].strip() + # 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): @@ -153,6 +162,18 @@ def fmt_price(value): 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): """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)" else: 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(): @@ -198,10 +230,12 @@ def main(): records = [] for d in domains: name = d.get('domain', '') + ziffer, datum = get_group(name) records.append({ 'name': name, 'status': d.get('status', ''), - 'ziffer': get_group(name), + 'ziffer': ziffer, + 'datum': datum, 'ns': get_nameservers(api, name), }) @@ -212,7 +246,7 @@ def main(): print() for ziffer, klarname in owners.items(): 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: for rec in matching: print(format_domain_line(rec)) @@ -222,7 +256,7 @@ def main(): print() # Leerzeile zwischen den Gruppen # 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: print("Ohne Zuordnung") for rec in rest: