""" Platform Gateway — Feedback handler. Creates Gitea issues from user feedback with auto-labeling. """ import json import re import urllib.request import urllib.error GITEA_URL = "http://gitea:3000" GITEA_TOKEN = "7abae0245e49e130e1b6752b7fa381a57607d8c7" REPO = "yusiboyz/platform" # Label IDs from Gitea LABELS = { "bug": 11, "feature": 12, "ios": 13, "web": 14, "fitness": 15, "brain": 16, "reader": 17, "podcasts": 18, "enhancement": 19, } def auto_label(text: str, source: str = "ios") -> list[int]: """Detect labels from feedback text.""" text_lower = text.lower() label_ids = [] # Source platform if source == "ios": label_ids.append(LABELS["ios"]) elif source == "web": label_ids.append(LABELS["web"]) # Bug or feature bug_words = ["bug", "broken", "crash", "error", "wrong", "fix", "issue", "doesn't work", "not working", "can't", "cant", "won't", "wont", "stuck", "freeze", "blank", "missing", "fail", "weird", "off", "messed", "problem", "glitch", "after you edit", "after i edit", "when i click", "when you click", "should be", "supposed to"] feature_words = ["want", "add", "wish", "would be nice", "can we", "can you", "feature", "idea", "suggest", "could you", "how about", "it would be", "please add", "need a", "would love"] if any(w in text_lower for w in bug_words): label_ids.append(LABELS["bug"]) elif any(w in text_lower for w in feature_words): label_ids.append(LABELS["feature"]) else: label_ids.append(LABELS["enhancement"]) # App detection if any(w in text_lower for w in ["fitness", "calorie", "food", "meal", "macro", "protein"]): label_ids.append(LABELS["fitness"]) elif any(w in text_lower for w in ["brain", "note", "save", "bookmark"]): label_ids.append(LABELS["brain"]) elif any(w in text_lower for w in ["reader", "rss", "feed", "article"]): label_ids.append(LABELS["reader"]) elif any(w in text_lower for w in ["podcast", "episode", "listen", "player"]): label_ids.append(LABELS["podcasts"]) return label_ids def handle_feedback(handler, body, user): """Create a Gitea issue from user feedback.""" try: data = json.loads(body) except Exception: handler._send_json({"error": "Invalid JSON"}, 400) return text = (data.get("text") or "").strip() source = data.get("source", "ios") if not text: handler._send_json({"error": "Feedback text is required"}, 400) return user_name = user.get("display_name") or user.get("username", "Unknown") label_ids = auto_label(text, source) # Create Gitea issue issue_body = { "title": text[:80] + ("..." if len(text) > 80 else ""), "body": f"**From:** {user_name} ({source})\n\n{text}", "labels": label_ids, } try: req = urllib.request.Request( f"{GITEA_URL}/api/v1/repos/{REPO}/issues", data=json.dumps(issue_body).encode(), headers={ "Authorization": f"token {GITEA_TOKEN}", "Content-Type": "application/json", }, method="POST", ) resp = urllib.request.urlopen(req, timeout=10) result = json.loads(resp.read()) issue_number = result.get("number") # Upload image attachment if provided image_b64 = data.get("image") if image_b64 and issue_number: import base64 try: img_bytes = base64.b64decode(image_b64) boundary = "----FeedbackBoundary123" body_parts = [] body_parts.append(f"--{boundary}\r\n".encode()) body_parts.append(b'Content-Disposition: form-data; name="attachment"; filename="screenshot.png"\r\n') body_parts.append(b"Content-Type: image/png\r\n\r\n") body_parts.append(img_bytes) body_parts.append(f"\r\n--{boundary}--\r\n".encode()) multipart_body = b"".join(body_parts) img_req = urllib.request.Request( f"{GITEA_URL}/api/v1/repos/{REPO}/issues/{issue_number}/assets", data=multipart_body, headers={ "Authorization": f"token {GITEA_TOKEN}", "Content-Type": f"multipart/form-data; boundary={boundary}", }, method="POST", ) urllib.request.urlopen(img_req, timeout=15) except Exception as img_err: pass # Image upload failed but issue was created handler._send_json({ "ok": True, "issue_number": issue_number, "url": result.get("html_url"), }) except Exception as e: handler._send_json({"error": f"Failed to create issue: {e}"}, 500)