Improve social milestone sharing flow

This commit is contained in:
2026-08-14 12:31:10 +02:00
parent e0c32542f9
commit 925540f3c6
11 changed files with 400 additions and 121 deletions

View File

@@ -1,4 +1,4 @@
"""Always-on QRMaster X milestone worker. The web app never receives X keys."""
"""Always-on QR Master X milestone worker. The web app never receives X keys."""
import json
import os
import tempfile
@@ -23,20 +23,45 @@ def api(method, url, payload=None):
return response.json()
def font(name, size):
return ImageFont.truetype(f"/usr/share/fonts/truetype/dejavu/{name}", size)
def render_card(card):
image = Image.new("RGB", (1200, 675), "#061b31")
"""Render the consented immutable scan snapshot; never invent trend data."""
image = Image.new("RGB", (1200, 630), "#f8fafc")
draw = ImageDraw.Draw(image)
fonts = Path("/usr/share/fonts/truetype/dejavu")
bold = ImageFont.truetype(str(fonts / "DejaVuSans-Bold.ttf"), 112)
regular = ImageFont.truetype(str(fonts / "DejaVuSans.ttf"), 34)
image_draw = draw
image_draw.rounded_rectangle((55, 55, 1145, 620), radius=28, outline="#304866", width=2)
image_draw.text((100, 105), "QR MASTER", font=regular, fill="#dce8f7")
image_draw.text((100, 210), f"{card['threshold']:,}", font=bold, fill="#ffffff")
scans = "eindeutige Scans" if card.get("language") == "de" else "unique scans"
image_draw.text((105, 350), scans, font=regular, fill="#b8c7da")
image_draw.line((100, 500, 1100, 500), fill="#304866", width=2)
image_draw.text((100, 535), f"{card['label']} · {card['title']}", font=regular, fill="#dce8f7")
navy, blue, slate, border, green = "#061b31", "#0256ff", "#64748b", "#e2e8f0", "#059669"
regular, medium, bold, display = font("DejaVuSans.ttf", 28), font("DejaVuSans-Bold.ttf", 28), font("DejaVuSans-Bold.ttf", 42), font("DejaVuSans-Bold.ttf", 116)
draw.rounded_rectangle((45, 42, 1155, 588), radius=24, fill="#ffffff", outline=border, width=2)
draw.rounded_rectangle((82, 79, 114, 111), radius=7, fill=blue)
draw.text((130, 81), "QR MASTER", font=medium, fill=navy)
draw.rounded_rectangle((932, 77, 1118, 114), radius=8, fill="#ecfdf5")
draw.text((954, 84), "Verified milestone", font=font("DejaVuSans-Bold.ttf", 17), fill=green)
draw.text((84, 158), "TOTAL UNIQUE SCANS", font=font("DejaVuSans-Bold.ttf", 18), fill="#94a3b8")
total = int(card.get("totalUniqueScans") or card.get("threshold") or 0)
draw.text((78, 184), f"{total:,}", font=display, fill=navy)
label = "eindeutige Scans" if card.get("language") == "de" else "unique scans"
draw.text((86, 325), label, font=regular, fill=slate)
trend = card.get("trend") or None
if trend and len(trend.get("series", [])) > 1:
series = trend["series"]
left, top, width, height = 84, 390, 1030, 88
max_value = max(series) or 1
points = [(left + round(index * width / (len(series) - 1)), top + height - round(value / max_value * height)) for index, value in enumerate(series)]
for y in (top, top + height // 2, top + height):
draw.line((left, y, left + width, y), fill="#edf2f7", width=2)
draw.line(points, fill=blue, width=6, joint="curve")
for x, y in (points[0], points[-1]):
draw.ellipse((x - 7, y - 7, x + 7, y + 7), fill=blue)
draw.text((84, 495), f"Last {trend.get('periodDays', 7)} days · {trend.get('recentTotal', 0)} unique scans", font=font("DejaVuSans.ttf", 18), fill=slate)
else:
draw.rounded_rectangle((84, 398, 357, 452), radius=10, fill="#eff6ff")
copy = "Erste Dynamik" if card.get("language") == "de" else "Early momentum"
draw.text((106, 411), copy, font=font("DejaVuSans-Bold.ttf", 20), fill=blue)
draw.text((84, 495), "Trend appears once enough real scan history exists." if card.get("language") != "de" else "Der Trend erscheint mit ausreichend echten Scan-Daten.", font=font("DejaVuSans.ttf", 18), fill=slate)
draw.line((84, 532, 1116, 532), fill=border, width=2)
draw.text((84, 549), card.get("qrTitle") or card.get("title") or "QR code", font=medium, fill=navy)
path = Path(tempfile.mkstemp(suffix=".png")[1])
image.save(path, "PNG", optimize=True)
return path
@@ -70,10 +95,12 @@ def run_once():
return
try:
result = post_x(milestone["text"], milestone.get("card"))
api("PATCH", base, {"id": milestone["id"], "result": "posted"})
tweet_id = result.get("data", {}).get("id")
post_url = f"https://x.com/i/web/status/{tweet_id}" if tweet_id else None
api("PATCH", base, {"id": milestone["id"], "result": "posted", "postUrl": post_url})
print(json.dumps({"posted": milestone["id"], "x": result}), flush=True)
except Exception as error:
api("PATCH", base, {"id": milestone["id"], "result": "failed"})
api("PATCH", base, {"id": milestone["id"], "result": "failed", "error": str(error)[:500]})
print(f"Milestone post failed: {error}", flush=True)