104 lines
3.5 KiB
Python
104 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Bind Hermes Telegram participant names to the current update's visible handle."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import ast
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
PATCH_VERSION = "teleo-telegram-visible-handle-name-policy-v1"
|
|
PATCH_MARKER = " # TELEO_TELEGRAM_VISIBLE_HANDLE_NAME_POLICY_V1\n"
|
|
SOURCE_ANCHOR = " user_name=user.full_name if user else None,\n"
|
|
|
|
REPLACEMENT = ''' # TELEO_TELEGRAM_VISIBLE_HANDLE_NAME_POLICY_V1
|
|
# The current Telegram update is authoritative for the visible
|
|
# handle. The stable Telegram user_id continues to own session
|
|
# isolation; profile full_name is only a fallback when no public
|
|
# username exists on this update.
|
|
user_name=(
|
|
(user.username or "").strip().lstrip("@")
|
|
if user and (user.username or "").strip()
|
|
else user.full_name if user else None
|
|
),
|
|
'''
|
|
|
|
|
|
def sha256_text(value: str) -> str:
|
|
return hashlib.sha256(value.encode("utf-8")).hexdigest()
|
|
|
|
|
|
def patch_source(source: str) -> tuple[str, bool]:
|
|
if PATCH_MARKER in source:
|
|
required = (
|
|
'(user.username or "").strip().lstrip("@")',
|
|
"else user.full_name if user else None",
|
|
"user_id=str(user.id) if user else None",
|
|
)
|
|
missing = [item for item in required if item not in source]
|
|
if missing:
|
|
raise ValueError(f"installed Telegram name-policy marker is incomplete: {missing}")
|
|
if SOURCE_ANCHOR in source:
|
|
raise ValueError("installed Telegram name-policy marker still contains the stale full-name anchor")
|
|
return source, False
|
|
|
|
if source.count(SOURCE_ANCHOR) != 1:
|
|
raise ValueError("Hermes Telegram sender construction changed; refusing an unreviewed name-policy patch")
|
|
|
|
patched = source.replace(SOURCE_ANCHOR, REPLACEMENT, 1)
|
|
ast.parse(patched)
|
|
return patched, True
|
|
|
|
|
|
def install(path: Path, *, check: bool) -> tuple[dict[str, str | bool], int]:
|
|
source = path.read_text(encoding="utf-8")
|
|
patched, changed = patch_source(source)
|
|
before_sha = sha256_text(source)
|
|
after_sha = sha256_text(patched)
|
|
if check:
|
|
return {
|
|
"patch_version": PATCH_VERSION,
|
|
"status": "patch_required" if changed else "installed",
|
|
"target": str(path),
|
|
"sha256": before_sha,
|
|
}, 1 if changed else 0
|
|
|
|
if changed:
|
|
mode = path.stat().st_mode & 0o777
|
|
with tempfile.NamedTemporaryFile(
|
|
"w", encoding="utf-8", dir=path.parent, prefix=f".{path.name}.", delete=False
|
|
) as handle:
|
|
handle.write(patched)
|
|
temp_path = Path(handle.name)
|
|
try:
|
|
temp_path.chmod(mode)
|
|
os.replace(temp_path, path)
|
|
finally:
|
|
temp_path.unlink(missing_ok=True)
|
|
|
|
ast.parse(path.read_text(encoding="utf-8"))
|
|
return {
|
|
"patch_version": PATCH_VERSION,
|
|
"status": "installed_now" if changed else "already_installed",
|
|
"target": str(path),
|
|
"before_sha256": before_sha,
|
|
"after_sha256": after_sha,
|
|
}, 0
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("target", type=Path)
|
|
parser.add_argument("--check", action="store_true")
|
|
args = parser.parse_args()
|
|
result, code = install(args.target, check=args.check)
|
|
print(json.dumps(result, sort_keys=True))
|
|
return code
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|