From 0d6060e6f74b5be8a12b45956550d0e827d69c2a Mon Sep 17 00:00:00 2001 From: Unnati1007 Date: Fri, 5 Jun 2026 16:54:40 +0530 Subject: [PATCH 1/2] feat: integrate social sharing UI and add GitHub auto-publisher Exposes existing Twitter/X backend integration safely in the React settings UI. Implements a new backend module to push LeetCode solutions directly to user GitHub repositories automatically via GitHub REST API. --- backend/github_integration.py | 49 ++++++++++++++++++++++++++++++++++ backend/main.py | 30 +++++++++++++++++++++ frontend/src/main.jsx | 50 +++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 backend/github_integration.py diff --git a/backend/github_integration.py b/backend/github_integration.py new file mode 100644 index 0000000..5198eaa --- /dev/null +++ b/backend/github_integration.py @@ -0,0 +1,49 @@ +import base64 +import requests + +def push_solution_to_github(title: str, code: str, access_token: str, repo_name: str) -> dict: + """ + Pushes the LeetCode solution code to the user's GitHub repository. + """ + if not access_token or not repo_name: + raise ValueError("GitHub credentials missing.") + + # Sanitize title for filename + filename = title.replace(" ", "_").replace("/", "-") + file_path = f"solutions/{filename}.py" + + url = f"https://api.github.com/repos/{repo_name}/contents/{file_path}" + + headers = { + "Authorization": f"token {access_token}", + "Accept": "application/vnd.github.v3+json" + } + + # Check if file exists to get the SHA (required for updating) + sha = None + try: + response = requests.get(url, headers=headers, timeout=10) + if response.status_code == 200: + sha = response.json().get("sha") + except Exception as e: + print(f"Error checking GitHub file: {e}") + + # Create or update the file + commit_message = f"Add solution for {title} via LeetLog AI" + encoded_content = base64.b64encode(code.encode("utf-8")).decode("utf-8") + + payload = { + "message": commit_message, + "content": encoded_content + } + if sha: + payload["sha"] = sha + + try: + response = requests.put(url, headers=headers, json=payload, timeout=15) + if response.status_code in (200, 201): + return {"status": "success", "data": response.json()} + else: + raise Exception(f"GitHub API Error: {response.status_code} - {response.text}") + except Exception as e: + raise Exception(f"Failed to push to GitHub: {str(e)}") diff --git a/backend/main.py b/backend/main.py index 502d061..70bddc7 100644 --- a/backend/main.py +++ b/backend/main.py @@ -24,6 +24,7 @@ from models.reminder import PublishRecord from services.reminder_scheduler import start_scheduler from social import share_to_platforms +from github_integration import push_solution_to_github load_dotenv() @@ -128,6 +129,12 @@ class AuthResponse(BaseModel): class IntegrationSettings(BaseModel): linkedin_access_token: str | None = None linkedin_person_urn: str | None = None + twitter_api_key: str | None = None + twitter_api_secret: str | None = None + twitter_access_token: str | None = None + twitter_access_secret: str | None = None + github_access_token: str | None = None + github_repo_name: str | None = None devto_api_key: str | None = None whatsapp_number: str | None = None timezone: str = "Asia/Kolkata" @@ -254,6 +261,16 @@ def _connected(settings_doc: dict[str, Any]) -> dict[str, bool]: settings_doc.get("linkedin_access_token") and settings_doc.get("linkedin_person_urn") ), + "twitter": bool( + settings_doc.get("twitter_api_key") + and settings_doc.get("twitter_api_secret") + and settings_doc.get("twitter_access_token") + and settings_doc.get("twitter_access_secret") + ), + "github": bool( + settings_doc.get("github_access_token") + and settings_doc.get("github_repo_name") + ), "whatsapp": bool(settings_doc.get("whatsapp_number")), "ai_provider": bool( settings_doc.get("gemini_api_key") @@ -483,6 +500,19 @@ async def create_blog( except Exception as e: print(f"Social sharing failed: {e}") + # GitHub automatic commit integration + if successful and user_settings.get("github_access_token") and user_settings.get("github_repo_name"): + try: + await run_in_threadpool( + push_solution_to_github, + problem.title, + problem.code, + user_settings["github_access_token"], + user_settings["github_repo_name"] + ) + except Exception as e: + print(f"GitHub push failed: {e}") + return { "status": overall_status, "data": { diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 392bc1d..0d3b7f5 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -8,6 +8,12 @@ const TOKEN_KEY = "leetlog_dashboard_token"; const defaultSettings = { linkedin_access_token: "", linkedin_person_urn: "", + twitter_api_key: "", + twitter_api_secret: "", + twitter_access_token: "", + twitter_access_secret: "", + github_access_token: "", + github_repo_name: "", devto_api_key: "", whatsapp_number: "", timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Asia/Kolkata", @@ -26,6 +32,12 @@ function normalizeSettings(settings) { ...settings, linkedin_access_token: settings?.linkedin_access_token || "", linkedin_person_urn: settings?.linkedin_person_urn || "", + twitter_api_key: settings?.twitter_api_key || "", + twitter_api_secret: settings?.twitter_api_secret || "", + twitter_access_token: settings?.twitter_access_token || "", + twitter_access_secret: settings?.twitter_access_secret || "", + github_access_token: settings?.github_access_token || "", + github_repo_name: settings?.github_repo_name || "", devto_api_key: settings?.devto_api_key || "", whatsapp_number: settings?.whatsapp_number || "", gemini_api_key: settings?.gemini_api_key || "", @@ -68,6 +80,8 @@ function App() { connected.ai_provider, connected.devto, connected.linkedin, + connected.twitter, + connected.github, connected.whatsapp ]; return Math.round((checks.filter(Boolean).length / checks.length) * 100); @@ -125,6 +139,12 @@ function App() { ...settings, linkedin_access_token: settings.linkedin_access_token || null, linkedin_person_urn: settings.linkedin_person_urn || null, + twitter_api_key: settings.twitter_api_key || null, + twitter_api_secret: settings.twitter_api_secret || null, + twitter_access_token: settings.twitter_access_token || null, + twitter_access_secret: settings.twitter_access_secret || null, + github_access_token: settings.github_access_token || null, + github_repo_name: settings.github_repo_name || null, devto_api_key: settings.devto_api_key || null, whatsapp_number: settings.whatsapp_number || null, gemini_api_key: settings.gemini_api_key || null, @@ -290,6 +310,36 @@ function App() { + + + + + + + + + + + +