Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -60,7 +59,6 @@ jobs:
uses: actions/setup-python@v5
with:

python-version: "3.13"
python-version: "3.12"

- name: Cache pip dependencies
Expand Down
49 changes: 49 additions & 0 deletions backend/github_integration.py
Original file line number Diff line number Diff line change
@@ -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)}")
31 changes: 31 additions & 0 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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": {
Expand Down
50 changes: 50 additions & 0 deletions frontend/src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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 || "",
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -393,6 +413,36 @@ setAnalysisResult(data);
</label>
</IntegrationCard>

<IntegrationCard title="Twitter / X" connected={connected.twitter}>
<label>
API key
<input type="password" value={settings.twitter_api_key} onChange={(event) => setSettings({ ...settings, twitter_api_key: event.target.value })} />
</label>
<label>
API secret
<input type="password" value={settings.twitter_api_secret} onChange={(event) => setSettings({ ...settings, twitter_api_secret: event.target.value })} />
</label>
<label>
Access token
<input type="password" value={settings.twitter_access_token} onChange={(event) => setSettings({ ...settings, twitter_access_token: event.target.value })} />
</label>
<label>
Access token secret
<input type="password" value={settings.twitter_access_secret} onChange={(event) => setSettings({ ...settings, twitter_access_secret: event.target.value })} />
</label>
</IntegrationCard>

<IntegrationCard title="GitHub Auto-Publisher" connected={connected.github}>
<label>
Personal Access Token
<input type="password" value={settings.github_access_token} onChange={(event) => setSettings({ ...settings, github_access_token: event.target.value })} />
</label>
<label>
Repository Name
<input value={settings.github_repo_name} onChange={(event) => setSettings({ ...settings, github_repo_name: event.target.value })} placeholder="username/leetcode-solutions" />
</label>
</IntegrationCard>

<IntegrationCard title="WhatsApp Reminder" connected={connected.whatsapp}>
<label className="toggle-row">
<input type="checkbox" checked={settings.is_whatsapp_enabled} onChange={(event) => setSettings({ ...settings, is_whatsapp_enabled: event.target.checked })} />
Expand Down