diff --git a/.gitignore b/.gitignore
index 1e77861..97cd716 100644
--- a/.gitignore
+++ b/.gitignore
@@ -130,4 +130,9 @@ dmypy.json
# IDE folders
.vscode
-.idea
\ No newline at end of file
+.idea
+
+# Sensitive Information
+leetcode_cookies.txt
+problem_cache.json
+*.session
\ No newline at end of file
diff --git a/README.md b/README.md
index ca8f135..992792e 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,10 @@ Before running the script, make sure that python3 is installed in your system.
If you prefer, you can use the Docker image to download LeetCode submissions. For more information read the
section [Docker Image](#docker-image).
+### ✨ New Features Added
+- **API Resilience**: Added retry logic with exponential backoff for GraphQL queries and submission requests to prevent crashes on network timeouts or rate limits.
+- **Topic Tags Export**: Problems now extract and expose `topicTags` for categorizing solutions by LeetCode topics.
+
## 🏁 Getting started
### Download `leetcode-export`
@@ -224,6 +228,56 @@ extension: str
Default submission filename
template: `${date_formatted} - ${status_display} - runtime ${runtime} - memory ${memory}.${extension}`
+
+## 🚀 Full Automation: Daily GitHub Sync
+
+You can fully automate your LeetCode backup to a separate GitHub repository using **GitHub Actions** and the provided **Chrome Extension**.
+
+### 1. Create your Solutions Repository
+1. Create a new private GitHub repository (e.g., `LeetCode-Solutions`).
+2. Create a file `.github/workflows/sync.yml` with the following content:
+
+```yaml
+name: Daily LeetCode Sync
+on:
+ schedule:
+ - cron: "0 */8 * * *"
+ workflow_dispatch:
+
+jobs:
+ sync:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+ - run: |
+ pip install git+https://github.com/MohamedMousad/leetcode-export.git@feature/api-retry-and-tags
+ - env:
+ LEETCODE_COOKIE: ${{ secrets.LEETCODE_COOKIE }}
+ run: |
+ python -m leetcode_export --folder .
+ git config --global user.name "github-actions[bot]"
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git add .
+ git commit -m "Auto-sync: Daily update" || echo "No changes"
+ git push origin main
+```
+
+### 2. Set Up the Chrome Extension
+Because LeetCode cookies change, we provide a Chrome Extension that automatically syncs your active session cookie to your GitHub repository securely as a secret.
+1. Generate a **GitHub Personal Access Token (Classic)** with `repo` and `workflow` permissions.
+2. Open Chrome and navigate to `chrome://extensions/`.
+3. Enable **Developer mode** (top right corner).
+4. Click **Load unpacked** and select the `chrome-extension` folder found in this repository.
+5. Click the extension icon in your browser toolbar, paste your PAT and repository name (e.g., `YourUser/LeetCode-Solutions`), and click **Save**.
+6. Whenever you log into LeetCode, click **Push Cookie to GitHub Now** in the extension to automatically securely update the `LEETCODE_COOKIE` secret in your repository.
+
+Your GitHub action will now run seamlessly every day!
+
## Special mentions
Thanks to [skygragon](https://github.com/skygragon) for
diff --git a/chrome-extension/background.js b/chrome-extension/background.js
new file mode 100644
index 0000000..8268f98
--- /dev/null
+++ b/chrome-extension/background.js
@@ -0,0 +1,84 @@
+importScripts('nacl.min.js', 'nacl-util.min.js', 'sealedbox.js');
+
+const SECRET_NAME = "LEETCODE_COOKIE";
+
+async function updateGitHubSecret() {
+ try {
+ const data = await chrome.storage.local.get(['github_token', 'github_repo']);
+ if (!data.github_token || !data.github_repo) {
+ return {status: "error", msg: "No GitHub token or repo configured."};
+ }
+
+ const [owner, repo] = data.github_repo.trim().split('/');
+ if (!owner || !repo) return {status: "error", msg: "Invalid repo format. Use owner/repo."};
+
+ const cookies = await chrome.cookies.getAll({ domain: "leetcode.com" });
+ if (cookies.length === 0) return {status: "error", msg: "No cookies found for leetcode.com!"};
+
+ let cookieStr = cookies.map(c => c.name + "=" + c.value).join("; ");
+ if (!cookieStr.includes("LEETCODE_SESSION")) {
+ return {status: "error", msg: "No LEETCODE_SESSION found. Are you logged in?"};
+ }
+
+ const keyRes = await fetch(`https://api.github.com/repos/${owner}/${repo}/actions/secrets/public-key`, {
+ headers: {
+ "Accept": "application/vnd.github+json",
+ "Authorization": `Bearer ${data.github_token.trim()}`,
+ "X-GitHub-Api-Version": "2022-11-28"
+ }
+ });
+
+ if (!keyRes.ok) {
+ const errTxt = await keyRes.text();
+ return {status: "error", msg: `Failed to fetch public key. Status: ${keyRes.status}. ${errTxt}`};
+ }
+ const keyData = await keyRes.json();
+
+ const messageBytes = nacl.util.decodeUTF8(cookieStr);
+ const keyBytes = nacl.util.decodeBase64(keyData.key);
+
+ const encryptedBytes = sealedBox.seal(messageBytes, keyBytes);
+ const encryptedBase64 = nacl.util.encodeBase64(encryptedBytes);
+
+ const uploadRes = await fetch(`https://api.github.com/repos/${owner}/${repo}/actions/secrets/${SECRET_NAME}`, {
+ method: "PUT",
+ headers: {
+ "Accept": "application/vnd.github+json",
+ "Authorization": `Bearer ${data.github_token.trim()}`,
+ "X-GitHub-Api-Version": "2022-11-28",
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ encrypted_value: encryptedBase64,
+ key_id: keyData.key_id
+ })
+ });
+
+ if (uploadRes.ok || uploadRes.status === 201 || uploadRes.status === 204) {
+ chrome.storage.local.set({ last_sync: new Date().toLocaleString() });
+ return {status: "ok", msg: "Success!"};
+ } else {
+ const errTxt = await uploadRes.text();
+ return {status: "error", msg: `Failed to upload secret. Status: ${uploadRes.status}. ${errTxt}`};
+ }
+ } catch (e) {
+ return {status: "error", msg: "Exception: " + e.message};
+ }
+}
+
+// Run when alarm fires
+chrome.alarms.create("syncAlarm", { periodInMinutes: 60 * 12 }); // Every 12 hours
+chrome.alarms.onAlarm.addListener((alarm) => {
+ if (alarm.name === "syncAlarm") {
+ updateGitHubSecret();
+ }
+});
+
+chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
+ if (request.action === "sync_now") {
+ updateGitHubSecret().then((result) => {
+ sendResponse(result);
+ });
+ return true; // Keep message channel open for async response
+ }
+});
diff --git a/chrome-extension/manifest.json b/chrome-extension/manifest.json
new file mode 100644
index 0000000..67835f9
--- /dev/null
+++ b/chrome-extension/manifest.json
@@ -0,0 +1,21 @@
+{
+ "manifest_version": 3,
+ "name": "LeetCode Auto-Sync",
+ "version": "1.0",
+ "description": "Automatically keeps your LeetCode cookies updated as a GitHub Action Secret.",
+ "permissions": [
+ "storage",
+ "alarms",
+ "cookies"
+ ],
+ "host_permissions": [
+ "https://leetcode.com/*",
+ "https://api.github.com/*"
+ ],
+ "background": {
+ "service_worker": "background.js"
+ },
+ "action": {
+ "default_popup": "popup.html"
+ }
+}
diff --git a/chrome-extension/nacl-util.min.js b/chrome-extension/nacl-util.min.js
new file mode 100644
index 0000000..0426742
--- /dev/null
+++ b/chrome-extension/nacl-util.min.js
@@ -0,0 +1 @@
+!function(e,n){"use strict";"undefined"!=typeof module&&module.exports?module.exports=n():(e.nacl||(e.nacl={}),e.nacl.util=n())}(this,function(){"use strict";var e={};function o(e){if(!/^(?:[A-Za-z0-9+\/]{2}[A-Za-z0-9+\/]{2})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/.test(e))throw new TypeError("invalid encoding")}return e.decodeUTF8=function(e){if("string"!=typeof e)throw new TypeError("expected string");var n,r=unescape(encodeURIComponent(e)),t=new Uint8Array(r.length);for(n=0;n>>32-n}function b(r,n){var e=255&r[n+3];return(e=(e=e<<8|255&r[n+2])<<8|255&r[n+1])<<8|255&r[n+0]}function B(r,n){var e=r[n]<<24|r[n+1]<<16|r[n+2]<<8|r[n+3],t=r[n+4]<<24|r[n+5]<<16|r[n+6]<<8|r[n+7];return new m(e,t)}function p(r,n,e){var t;for(t=0;t<4;t++)r[n+t]=255&e,e>>>=8}function S(r,n,e){r[n]=e.hi>>24&255,r[n+1]=e.hi>>16&255,r[n+2]=e.hi>>8&255,r[n+3]=255&e.hi,r[n+4]=e.lo>>24&255,r[n+5]=e.lo>>16&255,r[n+6]=e.lo>>8&255,r[n+7]=255&e.lo}function u(r,n,e,t,o){var i,a=0;for(i=0;i>>8)-1}function A(r,n,e,t){return u(r,n,e,t,16)}function _(r,n,e,t){return u(r,n,e,t,32)}function U(r,n,e,t,o){var i,a,f,u=new Uint32Array(16),c=new Uint32Array(16),w=new Uint32Array(16),y=new Uint32Array(4);for(i=0;i<4;i++)c[5*i]=b(t,4*i),c[1+i]=b(e,4*i),c[6+i]=b(n,4*i),c[11+i]=b(e,16+4*i);for(i=0;i<16;i++)w[i]=c[i];for(i=0;i<20;i++){for(a=0;a<4;a++){for(f=0;f<4;f++)y[f]=c[(5*a+4*f)%16];for(y[1]^=h(y[0]+y[3]|0,7),y[2]^=h(y[1]+y[0]|0,9),y[3]^=h(y[2]+y[1]|0,13),y[0]^=h(y[3]+y[2]|0,18),f=0;f<4;f++)u[4*a+(a+f)%4]=y[f]}for(f=0;f<16;f++)c[f]=u[f]}if(o){for(i=0;i<16;i++)c[i]=c[i]+w[i]|0;for(i=0;i<4;i++)c[5*i]=c[5*i]-b(t,4*i)|0,c[6+i]=c[6+i]-b(n,4*i)|0;for(i=0;i<4;i++)p(r,4*i,c[5*i]),p(r,16+4*i,c[6+i])}else for(i=0;i<16;i++)p(r,4*i,c[i]+w[i]|0)}function E(r,n,e,t){U(r,n,e,t,!1)}function x(r,n,e,t){return U(r,n,e,t,!0),0}var d=new Uint8Array([101,120,112,97,110,100,32,51,50,45,98,121,116,101,32,107]);function K(r,n,e,t,o,i,a){var f,u,c=new Uint8Array(16),w=new Uint8Array(64);if(!o)return 0;for(u=0;u<16;u++)c[u]=0;for(u=0;u<8;u++)c[u]=i[u];for(;64<=o;){for(E(w,c,a,d),u=0;u<64;u++)r[n+u]=(e?e[t+u]:0)^w[u];for(f=1,u=8;u<16;u++)f=f+(255&c[u])|0,c[u]=255&f,f>>>=8;o-=64,n+=64,e&&(t+=64)}if(0>>=8}var z=new Uint32Array([5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,252]);function R(r,n,e,t,o,i){var a,f,u,c,w=new Uint32Array(17),y=new Uint32Array(17),l=new Uint32Array(17),s=new Uint32Array(17),h=new Uint32Array(17);for(u=0;u<17;u++)y[u]=l[u]=0;for(u=0;u<16;u++)y[u]=i[u];for(y[3]&=15,y[4]&=252,y[7]&=15,y[8]&=252,y[11]&=15,y[12]&=252,y[15]&=15;0>>=8;for(c=c+l[16]|0,l[16]=3&c,c=5*(c>>>2)|0,u=0;u<16;u++)c=c+l[u]|0,l[u]=255&c,c>>>=8;c=c+l[16]|0,l[16]=c}for(u=0;u<17;u++)h[u]=l[u];for(k(l,z),a=0|-(l[16]>>>7),u=0;u<17;u++)l[u]^=a&(h[u]^l[u]);for(u=0;u<16;u++)s[u]=i[u+16];for(s[16]=0,k(l,s),u=0;u<16;u++)r[n+u]=l[u];return 0}function P(r,n,e,t,o,i){var a=new Uint8Array(16);return R(a,0,e,t,o,i),A(r,n,a,0)}function M(r,n,e,t,o){var i;if(e<32)return-1;for(T(r,0,n,0,e,t,o),R(r,16,r,32,e-32,r),i=0;i<16;i++)r[i]=0;return 0}function N(r,n,e,t,o){var i,a=new Uint8Array(32);if(e<32)return-1;if(L(a,0,32,t,o),0!==P(n,16,n,32,e-32,a))return-1;for(T(r,0,n,0,e,t,o),i=0;i<32;i++)r[i]=0;return 0}function O(r,n){var e;for(e=0;e<16;e++)r[e]=0|n[e]}function C(r){var n,e;for(e=0;e<16;e++)r[e]+=65536,n=Math.floor(r[e]/65536),r[(e+1)*(e<15?1:0)]+=n-1+37*(n-1)*(15===e?1:0),r[e]-=65536*n}function F(r,n,e){for(var t,o=~(e-1),i=0;i<16;i++)t=o&(r[i]^n[i]),r[i]^=t,n[i]^=t}function Z(r,n){var e,t,o,i=v(),a=v();for(e=0;e<16;e++)a[e]=n[e];for(C(a),C(a),C(a),t=0;t<2;t++){for(i[0]=a[0]-65517,e=1;e<15;e++)i[e]=a[e]-65535-(i[e-1]>>16&1),i[e-1]&=65535;i[15]=a[15]-32767-(i[14]>>16&1),o=i[15]>>16&1,i[14]&=65535,F(a,i,1-o)}for(e=0;e<16;e++)r[2*e]=255&a[e],r[2*e+1]=a[e]>>8}function G(r,n){var e=new Uint8Array(32),t=new Uint8Array(32);return Z(e,r),Z(t,n),_(e,0,t,0)}function q(r){var n=new Uint8Array(32);return Z(n,r),1&n[0]}function D(r,n){var e;for(e=0;e<16;e++)r[e]=n[2*e]+(n[2*e+1]<<8);r[15]&=32767}function I(r,n,e){var t;for(t=0;t<16;t++)r[t]=n[t]+e[t]|0}function V(r,n,e){var t;for(t=0;t<16;t++)r[t]=n[t]-e[t]|0}function X(r,n,e){var t,o,i=new Float64Array(31);for(t=0;t<31;t++)i[t]=0;for(t=0;t<16;t++)for(o=0;o<16;o++)i[t+o]+=n[t]*e[o];for(t=0;t<15;t++)i[t]+=38*i[t+16];for(t=0;t<16;t++)r[t]=i[t];C(r),C(r)}function j(r,n){X(r,n,n)}function H(r,n){var e,t=v();for(e=0;e<16;e++)t[e]=n[e];for(e=253;0<=e;e--)j(t,t),2!==e&&4!==e&&X(t,t,n);for(e=0;e<16;e++)r[e]=t[e]}function J(r,n){var e,t=v();for(e=0;e<16;e++)t[e]=n[e];for(e=250;0<=e;e--)j(t,t),1!==e&&X(t,t,n);for(e=0;e<16;e++)r[e]=t[e]}function Q(r,n,e){var t,o,i=new Uint8Array(32),a=new Float64Array(80),f=v(),u=v(),c=v(),w=v(),y=v(),l=v();for(o=0;o<31;o++)i[o]=n[o];for(i[31]=127&n[31]|64,i[0]&=248,D(a,e),o=0;o<16;o++)u[o]=a[o],w[o]=f[o]=c[o]=0;for(f[0]=w[0]=1,o=254;0<=o;--o)F(f,u,t=i[o>>>3]>>>(7&o)&1),F(c,w,t),I(y,f,c),V(f,f,c),I(c,u,w),V(u,u,w),j(w,y),j(l,f),X(f,c,f),X(c,u,y),I(y,f,c),V(f,f,c),j(u,f),V(c,w,l),X(f,c,g),I(f,f,w),X(c,c,f),X(f,w,l),X(w,u,a),j(u,y),F(f,u,t),F(c,w,t);for(o=0;o<16;o++)a[o+16]=f[o],a[o+32]=c[o],a[o+48]=u[o],a[o+64]=w[o];var s=a.subarray(32),h=a.subarray(16);return H(s,s),X(h,h,s),Z(r,h),0}function W(r,n){return Q(r,n,e)}function $(r,n){return a(n,32),W(r,n)}function rr(r,n,e){var t=new Uint8Array(32);return Q(t,e,n),x(r,o,t,d)}var nr=M,er=N;function tr(){var r,n,e,t=0,o=0,i=0,a=0,f=65535;for(e=0;e>>16,i+=(n=arguments[e].hi)&f,a+=n>>>16;return new m((i+=(o+=t>>>16)>>>16)&f|(a+=i>>>16)<<16,t&f|o<<16)}function or(r,n){return new m(r.hi>>>n,r.lo>>>n|r.hi<<32-n)}function ir(){var r,n=0,e=0;for(r=0;r>>n|r.lo<>>n|r.hi<>>n|r.hi<>>n|r.lo<>(7&o)&1),yr(n,r),yr(r,r),lr(r,n,t)}function vr(r,n){var e=[v(),v(),v(),v()];O(e[0],t),O(e[1],f),O(e[2],w),X(e[3],t,f),hr(r,e,n)}function gr(r,n,e){var t,o=new Uint8Array(64),i=[v(),v(),v(),v()];for(e||a(n,32),wr(o,n,32),o[0]&=248,o[31]&=127,o[31]|=64,vr(i,o),sr(r,i),t=0;t<32;t++)n[t+32]=r[t];return 0}var br=new Float64Array([237,211,245,92,26,99,18,88,214,156,247,162,222,249,222,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16]);function pr(r,n){var e,t,o,i;for(t=63;32<=t;--t){for(e=0,o=t-32,i=t-12;o>4)*br[o],e=n[o]>>8,n[o]&=255;for(o=0;o<32;o++)n[o]-=e*br[o];for(t=0;t<32;t++)n[t+1]+=n[t]>>8,r[t]=255&n[t]}function Ar(r){var n,e=new Float64Array(64);for(n=0;n<64;n++)e[n]=r[n];for(n=0;n<64;n++)r[n]=0;pr(r,e)}function _r(r,n,e,t){var o,i,a=new Uint8Array(64),f=new Uint8Array(64),u=new Uint8Array(64),c=new Float64Array(64),w=[v(),v(),v(),v()];wr(a,t,32),a[0]&=248,a[31]&=127,a[31]|=64;var y=e+64;for(o=0;o>7&&V(r[0],c,r[0]),X(r[3],r[0],r[1])}(u,t))return-1;for(o=0;o
+
+
+
+
+
+ LeetCode Auto-Sync
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chrome-extension/popup.js b/chrome-extension/popup.js
new file mode 100644
index 0000000..608cffc
--- /dev/null
+++ b/chrome-extension/popup.js
@@ -0,0 +1,27 @@
+document.addEventListener('DOMContentLoaded', () => {
+ chrome.storage.local.get(['github_token', 'github_repo', 'last_sync'], (res) => {
+ if (res.github_token) document.getElementById('pat').value = res.github_token;
+ if (res.github_repo) document.getElementById('repo').value = res.github_repo;
+ if (res.last_sync) document.getElementById('status').innerText = "Last sync: " + res.last_sync;
+ });
+
+ document.getElementById('save').addEventListener('click', () => {
+ chrome.storage.local.set({
+ github_token: document.getElementById('pat').value,
+ github_repo: document.getElementById('repo').value
+ }, () => {
+ document.getElementById('status').innerText = "Settings saved! Extension is ready.";
+ });
+ });
+
+ document.getElementById('sync').addEventListener('click', () => {
+ document.getElementById('status').innerText = "Fetching cookie and pushing to GitHub...";
+ chrome.runtime.sendMessage({ action: "sync_now" }, (res) => {
+ if (res && res.status === "ok") {
+ document.getElementById('status').innerText = "Success! GitHub Secret Updated. \nYour daily GitHub Action will now run flawlessly.";
+ } else {
+ document.getElementById('status').innerText = "Error: " + (res ? res.msg : "Unknown error");
+ }
+ });
+ });
+});
diff --git a/chrome-extension/sealedbox.js b/chrome-extension/sealedbox.js
new file mode 100644
index 0000000..193d37d
--- /dev/null
+++ b/chrome-extension/sealedbox.js
@@ -0,0 +1 @@
+!function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n(require("nacl"),require("Object")):"function"==typeof define&&define.amd?define(["nacl","Object"],n):"object"==typeof exports?exports.sealedBox=n(require("nacl"),require("Object")):e.sealedBox=n(e.nacl,e.Object)}(self,function(e,n){return function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=3)}([function(n,t){n.exports=e},function(e,n,t){var r=t(2);function o(e,n,t){var r=e[n]+e[t],o=e[n+1]+e[t+1];r>=4294967296&&o++,e[n]=r,e[n+1]=o}function a(e,n,t,r){var o=e[n]+t;t<0&&(o+=4294967296);var a=e[n+1]+r;o>=4294967296&&a++,e[n]=o,e[n+1]=a}function u(e,n){return e[n]^e[n+1]<<8^e[n+2]<<16^e[n+3]<<24}function i(e,n,t,r,u,i){var c=b[u],l=b[u+1],p=b[i],y=b[i+1];o(f,e,n),a(f,e,c,l);var d=f[r]^f[e],s=f[r+1]^f[e+1];f[r]=s,f[r+1]=d,o(f,t,r),d=f[n]^f[t],s=f[n+1]^f[t+1],f[n]=d>>>24^s<<8,f[n+1]=s>>>24^d<<8,o(f,e,n),a(f,e,p,y),d=f[r]^f[e],s=f[r+1]^f[e+1],f[r]=d>>>16^s<<16,f[r+1]=s>>>16^d<<16,o(f,t,r),d=f[n]^f[t],s=f[n+1]^f[t+1],f[n]=s>>>31^d<<1,f[n+1]=d>>>31^s<<1}var c=new Uint32Array([4089235720,1779033703,2227873595,3144134277,4271175723,1013904242,1595750129,2773480762,2917565137,1359893119,725511199,2600822924,4215389547,528734635,327033209,1541459225]),l=new Uint8Array([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3,11,8,12,0,5,2,15,13,10,14,3,6,7,1,9,4,7,9,3,1,13,12,11,14,2,6,5,10,4,0,15,8,9,0,5,7,2,4,10,15,14,1,11,12,6,8,3,13,2,12,6,10,0,11,8,3,4,13,7,5,15,14,1,9,12,5,1,15,14,13,4,10,0,7,6,3,9,2,8,11,13,11,7,14,12,1,3,9,5,0,15,4,8,6,2,10,6,15,14,9,11,3,0,8,12,2,13,7,1,4,10,5,10,2,8,4,7,6,1,5,15,11,9,14,3,12,13,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3].map(function(e){return 2*e})),f=new Uint32Array(32),b=new Uint32Array(32);function p(e,n){var t=0;for(t=0;t<16;t++)f[t]=e.h[t],f[t+16]=c[t];for(f[24]=f[24]^e.t,f[25]=f[25]^e.t/4294967296,n&&(f[28]=~f[28],f[29]=~f[29]),t=0;t<32;t++)b[t]=u(e.b,4*t);for(t=0;t<12;t++)i(0,8,16,24,l[16*t+0],l[16*t+1]),i(2,10,18,26,l[16*t+2],l[16*t+3]),i(4,12,20,28,l[16*t+4],l[16*t+5]),i(6,14,22,30,l[16*t+6],l[16*t+7]),i(0,10,20,30,l[16*t+8],l[16*t+9]),i(2,12,22,24,l[16*t+10],l[16*t+11]),i(4,14,16,26,l[16*t+12],l[16*t+13]),i(6,8,18,28,l[16*t+14],l[16*t+15]);for(t=0;t<16;t++)e.h[t]=e.h[t]^f[t]^f[t+16]}function y(e,n){if(0===e||e>64)throw new Error("Illegal output length, expected 0 < length <= 64");if(n&&n.length>64)throw new Error("Illegal key, expected Uint8Array with 0 < length <= 64");for(var t={b:new Uint8Array(128),h:new Uint32Array(16),t:0,c:0,outlen:e},r=0;r<16;r++)t.h[r]=c[r];var o=n?n.length:0;return t.h[0]^=16842752^o<<8^e,n&&(d(t,n),t.c=128),t}function d(e,n){for(var t=0;t>2]>>8*(3&t);return n}function v(e,n,t){t=t||64,e=r.normalizeInput(e);var o=y(t,n);return d(o,e),s(o)}e.exports={blake2b:v,blake2bHex:function(e,n,t){var o=v(e,n,t);return r.toHex(o)},blake2bInit:y,blake2bUpdate:d,blake2bFinal:s}},function(e,t){e.exports=n},function(e,n,t){"use strict";t.r(n);var r=t(0),o=t.n(r);const a=o.a.box.publicKeyLength+o.a.box.overheadLength;var u=t(1),i=t.n(u);function c(e,n){var t=i.a.blake2bInit(o.a.box.nonceLength,null);return i.a.blake2bUpdate(t,e),i.a.blake2bUpdate(t,n),i.a.blake2bFinal(t)}function l(e,n){var t=new Uint8Array(a+e.length),r=o.a.box.keyPair();t.set(r.publicKey);var u=c(r.publicKey,n),i=o.a.box(e,u,n,r.secretKey);return t.set(i,r.publicKey.length),function(e){for(var n=0;n Problem:
:param slug: problem identifier
:return: Problem
"""
- response = self.session.post(GRAPHQL_URL, json=question_detail_json(slug))
- if "data" in response.json() and "question" in response.json()["data"]:
- problem_dict = dict_camelcase_to_snakecase(
- response.json()["data"]["question"]
- )
- return Problem.from_dict(problem_dict)
+ for attempt in range(5):
+ try:
+ response = self.session.post(GRAPHQL_URL, json=question_detail_json(slug), timeout=20)
+ if "data" in response.json() and "question" in response.json()["data"]:
+ problem_dict = dict_camelcase_to_snakecase(
+ response.json()["data"]["question"]
+ )
+ return Problem.from_dict(problem_dict)
+ break
+ except requests.exceptions.RequestException as e:
+ logging.warning(f"GraphQL request failed: {e}. Retrying in {5 * (attempt + 1)} seconds...")
+ sleep(5 * (attempt + 1))
+ return None
def get_submissions(self) -> Iterator[Submission]:
"""
@@ -148,9 +155,18 @@ def get_submissions(self) -> Iterator[Submission]:
and response_json["has_next"]
):
logging.debug(f"Exporting submissions from {current} to {current + 20}")
- response = self.session.get(SUBMISSIONS_API_URL.format(current, 20))
- logging.debug(response.content)
- response_json = response.json()
+ for attempt in range(5):
+ try:
+ response = self.session.get(SUBMISSIONS_API_URL.format(current, 20), timeout=20)
+ logging.debug(response.content)
+ response_json = response.json()
+ break
+ except requests.exceptions.RequestException as e:
+ logging.warning(f"Request failed: {e}. Retrying in {5 * (attempt + 1)} seconds...")
+ sleep(5 * (attempt + 1))
+ else:
+ logging.error("Max retries exceeded for fetching submissions.")
+ break
if "submissions_dump" in response_json:
for submission_dict in response_json["submissions_dump"]:
submission_dict["runtime"] = submission_dict["runtime"].replace(
diff --git a/leetcode_export/leetcode_graphql.py b/leetcode_export/leetcode_graphql.py
index 2886f7c..95f50e6 100644
--- a/leetcode_export/leetcode_graphql.py
+++ b/leetcode_export/leetcode_graphql.py
@@ -14,6 +14,7 @@ class Problem:
title: str
title_slug: str
content: str
+ topic_tags: list
def question_detail_json(slug):
@@ -28,6 +29,10 @@ def question_detail_json(slug):
title
titleSlug
content
+ topicTags {
+ name
+ slug
+ }
}
}""",
}