diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dbf6b07..0690d44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,6 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.13" python-version: "3.12" - name: Cache pip dependencies @@ -60,7 +59,6 @@ jobs: uses: actions/setup-python@v5 with: - python-version: "3.13" python-version: "3.12" - name: Cache pip dependencies 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 6156417..efff8ea 100644 --- a/backend/main.py +++ b/backend/main.py @@ -37,6 +37,7 @@ from services.reminder_scheduler import start_scheduler from services.complexity_analyzer import analyze_code from social import share_to_platforms +from github_integration import push_solution_to_github load_dotenv() @@ -165,6 +166,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" @@ -291,6 +298,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") @@ -534,6 +551,20 @@ 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 aa79d7b..76076c4 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 || "", @@ -72,6 +84,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); @@ -129,6 +143,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, @@ -393,6 +413,36 @@ setAnalysisResult(data); + + + + + + + + + + + +