From a627a09fefbdfb8e5359a281c0e8e83d5f1a617f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Pe=C5=A1ek=20=28Kof=29?= Date: Wed, 4 Nov 2026 16:34:26 +0100 Subject: [PATCH] feat: add jitter-ping and update imp2ics --- imp2ics | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++++ jitter-ping | 35 ++++++ 2 files changed, 337 insertions(+) create mode 100755 imp2ics create mode 100755 jitter-ping diff --git a/imp2ics b/imp2ics new file mode 100755 index 0000000..54b0561 --- /dev/null +++ b/imp2ics @@ -0,0 +1,302 @@ +#!/usr/bin/env python3 +""" +imp2ics: Convert OKsoftware "Svátky a výročí" (.imp) files to RFC 5545 iCalendar (.ics) and Google Calendar CSV. +Developed for Kof / Dell backup restoration. + +Supports strict RFC 5545 compliance: +- CRLF line endings +- 75-octet line folding +- DTSTART and DTEND (DATE format, exclusive DTEND = next day) +- TRANSP:TRANSPARENT (all-day events don't block user availability) +- RRULE:FREQ=YEARLY for recurring dates +- Proper text escaping (commas, semicolons, backslashes, newlines) +- CSV export for Google Calendar and Microsoft Outlook +""" +import sys +import os +import io +import csv +import glob +import re +import struct +import uuid +from datetime import datetime, timedelta, timezone + +DELPHI_EPOCH = datetime(1899, 12, 30) + +TYPE_NAMES = { + 0: "Narozeniny", + 1: "Výročí", + 2: "Událost", + 3: "Připomenutí", + 4: "Svátek" +} + +def normalize_name(name): + s = re.sub(r'\s+', ' ', name.strip()) + s = re.sub(r'^\*\s*', '* ', s) + s = re.sub(r'^\†\s*', '† ', s) + return s + +def escape_ics_text(text): + if not text: + return "" + s = str(text) + s = s.replace('\\', '\\\\') + s = s.replace(';', r'\;') + s = s.replace(',', r'\,') + s = s.replace('\r\n', r'\n').replace('\r', r'\n').replace('\n', r'\n') + return s + +def fold_line(line, limit=75): + encoded = line.encode('utf-8') + if len(encoded) <= limit: + return line + parts = [] + while len(encoded) > limit: + cut = limit + # Walk backward if we land in the middle of a multi-byte UTF-8 character + while cut > 0 and (encoded[cut] & 0xC0) == 0x80: + cut -= 1 + parts.append(encoded[:cut].decode('utf-8')) + encoded = b' ' + encoded[cut:] + if encoded: + parts.append(encoded.decode('utf-8')) + return "\r\n".join(parts) + +def parse_imp(filepath): + with open(filepath, 'rb') as f: + data = f.read() + + pos = 0 + tokens = [] + while pos < len(data): + b = data[pos] + pos += 1 + if b == 0x00: + tokens.append(('END', None)) + elif b == 0x01: + tokens.append(('LIST', None)) + elif b == 0x02: + tokens.append(('INT8', data[pos])) + pos += 1 + elif b == 0x03: + tokens.append(('INT16', struct.unpack(' 1, generates repeating occurrences for future years + (since CSV does not natively support RRULE recurrence). + """ + deduped = deduplicate_events(events) + output = io.StringIO() + writer = csv.writer(output, quoting=csv.QUOTE_MINIMAL) + writer.writerow([ + "Subject", + "Start Date", + "Start Time", + "End Date", + "End Time", + "All Day Event", + "Description", + "Location", + "Private" + ]) + + current_year = datetime.now().year + years_to_project = [current_year + i for i in range(project_upcoming_years)] if project_upcoming_years > 1 else [None] + + for ev in deduped: + dt = ev["date"] + etype = ev["type"] + ename = ev["name"] + tname = TYPE_NAMES.get(etype, "Událost") + + if etype == 0: + base_desc = f"Narozeniny: {ename} (rok narození {dt.year})" + elif etype == 1: + base_desc = f"Výročí: {ename} (od roku {dt.year})" + elif etype == 4: + base_desc = f"Svátek: {ename}" + else: + base_desc = f"{tname}: {ename} (záznam z roku {dt.year})" + + if project_upcoming_years > 1 and etype != 3: + for yr in years_to_project: + try: + occ_dt = dt.replace(year=yr) + except ValueError: + occ_dt = dt.replace(year=yr, day=28) + + age = yr - dt.year + if etype == 0 and age >= 0: + desc = f"{base_desc} - {age}. narozeniny" + elif etype == 1 and age >= 0: + desc = f"{base_desc} - {age}. výročí" + else: + desc = base_desc + + d_str = occ_dt.strftime("%m/%d/%Y") + writer.writerow([ename, d_str, "", d_str, "", "True", desc, "", "False"]) + else: + d_str = dt.strftime("%m/%d/%Y") + writer.writerow([ename, d_str, "", d_str, "", "True", base_desc, "", "False"]) + + return output.getvalue() + +def main(): + import argparse + parser = argparse.ArgumentParser(description="Convert OKsoftware .imp files to iCalendar (.ics) and Google Calendar CSV") + parser.add_argument("inputs", nargs="+", help="Files (.imp) or directories") + parser.add_argument("-o", "--output", help="Output file path (.ics or .csv)") + parser.add_argument("--format", choices=["ics", "csv"], default="ics", help="Output format (default: ics)") + parser.add_argument("--name", default="Svátky a výročí", help="Calendar name (X-WR-CALNAME)") + parser.add_argument("--project-years", type=int, default=5, help="Number of upcoming years to project in CSV (default: 5)") + + args = parser.parse_args() + + files = [] + for p in args.inputs: + if os.path.isdir(p): + files.extend(glob.glob(os.path.join(p, "**", "*.imp"), recursive=True)) + else: + files.append(p) + + all_events = [] + for f in sorted(files): + evs = parse_imp(f) + all_events.extend(evs) + + fmt = args.format + if args.output: + if args.output.lower().endswith(".csv"): + fmt = "csv" + elif args.output.lower().endswith(".ics"): + fmt = "ics" + + if fmt == "csv": + content = events_to_csv(all_events, project_upcoming_years=args.project_years) + else: + content = events_to_ics(all_events, cal_name=args.name) + + if args.output: + with open(args.output, 'w', encoding='utf-8') as f: + f.write(content) + print(f"Exported {len(all_events)} events -> {args.output} ({fmt.upper()})", file=sys.stderr) + else: + sys.stdout.write(content) + +if __name__ == '__main__': + main() diff --git a/jitter-ping b/jitter-ping new file mode 100755 index 0000000..211388e --- /dev/null +++ b/jitter-ping @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# jitter-ping: Quick ICMP RTT Jitter Benchmark in Bash +# Usage: jitter-ping [count] [interval_sec] + +TARGET="${1:-192.168.1.104}" +COUNT="${2:-100}" +INTERVAL="${3:-0.02}" # 20ms by default (50 pkts/sec) + +echo "[*] Spouštím ICMP jitter benchmark na cíl $TARGET ($COUNT paketů, interval ${INTERVAL}s)..." + +# Run ping and extract rtt times +ping -c "$COUNT" -i "$INTERVAL" -q "$TARGET" | awk -F'/' ' +/min\/avg\/max\/mdev/ { + split($5, val, " ") + min = $4 + avg = $5 + max = $6 + mdev = $7 + sub(/ .*/, "", min) + sub(/ .*/, "", avg) + sub(/ .*/, "", max) + sub(/ .*/, "", mdev) + + printf "\n==================== ICMP RTT VÝSLEDKY ====================\n" + printf " Cíl: %s\n", "'"$TARGET"'" + printf " Minimální RTT: %8.3f ms\n", min + printf " Průměrný RTT: %8.3f ms\n", avg + printf " Maximální RTT: %8.3f ms\n", max + printf " Jitter (mdev / dev): %8.3f ms (%8.1f µs)\n", mdev, mdev * 1000.0 + printf "===========================================================\n" +} +/packets transmitted/ { + printf " Přenos paketů: %s\n", $0 +} +'