diff --git a/.gitignore b/.gitignore index 2610c6d..a8cab52 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ node_modules/ *.log auth.json deepseek-auth.json +# Runtime-added auth files contain secrets — keep the dir, ignore its contents. +data/accounts/*.json .chrome-profile-deepseek/ .chrome-for-testing-profile-deepseek/ .chrome-for-testing-profile-deepseek.stale-*/ diff --git a/chrome-extension/README.md b/chrome-extension/README.md new file mode 100644 index 0000000..169369d --- /dev/null +++ b/chrome-extension/README.md @@ -0,0 +1,31 @@ +# DeepSeek → FreeDeepseekAPI (расширение) + +Добавляет аккаунт DeepSeek в локальный FreeDeepseekAPI **одним кликом**: +перехватывает заголовки реального запроса к `chat.deepseek.com/api/...` +(`token` из `Authorization`, все cookie, `hif_*`) и отправляет на +`http://localhost:9655/api/accounts/import`. + +Работает в Firefox и Chrome/Edge (Manifest V3). + +## Установка + +**Firefox** +1. Откройте `about:debugging#/runtime/this-firefox` +2. «Загрузить временное дополнение» → выберите `manifest.json` из этой папки. + (Временное дополнение: после перезапуска Firefox установить заново.) + +**Chrome / Edge** +1. Откройте `chrome://extensions` +2. Включите «Режим разработчика». +3. «Загрузить распакованное» → выберите эту папку. + +## Использование +1. Запустите FreeDeepseekAPI (порт 9655). +2. Откройте `chat.deepseek.com` и войдите в нужный аккаунт. +3. **Отправьте любое сообщение** (например `ok`) — чтобы прошёл запрос, из которого берутся креды. +4. Клик по иконке расширения → **«➕ Добавить в FreeDeepseekAPI»**. + +Для нескольких аккаунтов повторите из разных профилей/логинов браузера. + +Вспомогательные кнопки: «Собрать» (показать креды), «Копировать JSON», +«Скачать файл» (`deepseek-auth.json`) — на случай ручного импорта через дашборд. diff --git a/chrome-extension/background.js b/chrome-extension/background.js index 7b32d94..481c500 100644 --- a/chrome-extension/background.js +++ b/chrome-extension/background.js @@ -1,97 +1,45 @@ -// DeepSeek Auth Exporter — Background Service Worker -// Reads cookies from Chrome, forwards content-script localStorage data. - -const STORAGE_KEY = 'deepseek_auth'; - -// Read all needed cookies from chat.deepseek.com -async function readCookies() { - const needed = ['token', 'ds_session_id', 'smidV2']; - const results = {}; - for (const name of needed) { - const cookie = await new Promise((resolve) => - chrome.cookies.get({ url: 'https://chat.deepseek.com', name }, resolve) - ); - results[name] = cookie ? cookie.value : ''; - } - - // Build cookie header string - const parts = []; - if (results.ds_session_id) parts.push(`ds_session_id=${results.ds_session_id}`); - if (results.smidV2) parts.push(`smidV2=${results.smidV2}`); - results.cookie = parts.join('; '); - - return results; -} - -// Read localStorage values via content script injection -async function readLocalStorage(tabId) { - const keys = ['hif_dliq', 'hif_leim']; - try { - const results = await new Promise((resolve, reject) => { - chrome.tabs.sendMessage( - tabId, - { action: 'readLocalStorage', keys }, - (response) => { - if (chrome.runtime.lastError) reject(chrome.runtime.lastError.message); - else resolve(response.data || {}); +// DeepSeek → FreeDeepseekAPI — перехват заголовков реального запроса. +// token (Authorization: Bearer), cookie (все), hif (x-hif-*) берутся из +// настоящего запроса к chat.deepseek.com/api/... — как в HAR/cURL. + +const WASM_DEFAULT = 'https://fe-static.deepseek.com/chat/static/sha3_wasm_bg.7b9ca65ddd.wasm'; +const KEY = 'deepseek_capture'; + +// extraHeaders нужен Chrome для доступа к Cookie/Authorization; Firefox даёт их без него. +const opts = ['requestHeaders']; +try { + if (chrome.webRequest.OnBeforeSendHeadersOptions && + chrome.webRequest.OnBeforeSendHeadersOptions.EXTRA_HEADERS) { + opts.push('extraHeaders'); + } +} catch (e) { /* Firefox: опции нет — это нормально */ } + +chrome.webRequest.onBeforeSendHeaders.addListener( + (details) => { + const h = {}; + for (const x of (details.requestHeaders || [])) h[x.name.toLowerCase()] = x.value; + const auth = h['authorization'] || ''; + const token = /^bearer\s+\S/i.test(auth) ? auth.replace(/^bearer\s+/i, '').trim() : ''; + const cookie = h['cookie'] || ''; + if (token && cookie) { + const cap = { + token, + cookie, + hif_dliq: h['x-hif-dliq'] || '', + hif_leim: h['x-hif-leim'] || '', + wasmUrl: WASM_DEFAULT, + _t: Date.now(), + }; + chrome.storage.local.set({ [KEY]: cap }); } - ); - }); - return results; - } catch (e) { - return {}; - } -} - -// Find an open DeepSeek tab -function findDeepSeekTab() { - return new Promise((resolve) => { - chrome.tabs.query( - { url: 'https://chat.deepseek.com/*' }, - (tabs) => resolve(tabs.length > 0 ? tabs[0] : null) - ); - }); -} - -async function collectAndStore(tabId) { - const cookies = await readCookies(); - let ls = {}; - if (tabId) ls = await readLocalStorage(tabId); - - const merged = { - token: cookies.token || '', - ds_session_id: cookies.ds_session_id || '', - smidV2: cookies.smidV2 || '', - cookie: cookies.cookie || '', - hif_dliq: ls.hif_dliq || '', - hif_leim: ls.hif_leim || '', - _lastUpdated: new Date().toISOString(), - }; - - await new Promise((resolve) => - chrome.storage.local.set({ [STORAGE_KEY]: merged }, resolve) - ); - return merged; -} - -// Message handler — popup requests -chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { - if (request.action === 'collect') { - findDeepSeekTab().then(async (tab) => { - if (!tab) { - sendResponse({ success: false, error: 'No DeepSeek tab open' }); - return; - } - const auth = await collectAndStore(tab.id); - sendResponse({ success: true, auth }); - }); - return true; // keep channel open for async - } - - if (request.action === 'export') { - chrome.storage.local.get(STORAGE_KEY, (result) => { - sendResponse({ success: true, auth: result[STORAGE_KEY] || {} }); - }); - return true; - } + }, + { urls: ['https://chat.deepseek.com/api/*'] }, + opts +); + +chrome.runtime.onMessage.addListener((req, sender, sendResponse) => { + if (req.action === 'get') { + chrome.storage.local.get(KEY, (r) => sendResponse({ success: true, cap: r[KEY] || null })); + return true; // async + } }); diff --git a/chrome-extension/icons/icon128.png b/chrome-extension/icons/icon128.png new file mode 100644 index 0000000..ccce267 Binary files /dev/null and b/chrome-extension/icons/icon128.png differ diff --git a/chrome-extension/icons/icon16.png b/chrome-extension/icons/icon16.png new file mode 100644 index 0000000..14af773 Binary files /dev/null and b/chrome-extension/icons/icon16.png differ diff --git a/chrome-extension/icons/icon48.png b/chrome-extension/icons/icon48.png new file mode 100644 index 0000000..dc6a5a2 Binary files /dev/null and b/chrome-extension/icons/icon48.png differ diff --git a/chrome-extension/manifest.json b/chrome-extension/manifest.json index 0dd2fa3..5501740 100644 --- a/chrome-extension/manifest.json +++ b/chrome-extension/manifest.json @@ -1,27 +1,30 @@ { "manifest_version": 3, - "name": "DeepSeek Auth Exporter", - "version": "1.0", - "description": "Extract DeepSeek Chat credentials from cookies + localStorage for use with the web API proxy", - "permissions": ["cookies", "storage", "downloads"], - "host_permissions": ["https://chat.deepseek.com/*"], - "content_scripts": [ - { - "matches": ["https://chat.deepseek.com/*"], - "js": ["content.js"], - "run_at": "document_idle" + "name": "DeepSeek → FreeDeepseekAPI", + "version": "1.4", + "description": "Добавляет аккаунт DeepSeek в локальный FreeDeepseekAPI одним кликом. Перехватывает заголовки запроса chat.deepseek.com (token + cookie + hif).", + "author": "FreeDeepseekAPI", + "browser_specific_settings": { + "gecko": { + "id": "freedeepseek-auth@forgetmeai", + "strict_min_version": "121.0", + "data_collection_permissions": { "required": ["none"] } } + }, + "permissions": ["webRequest", "storage"], + "host_permissions": [ + "https://chat.deepseek.com/*", + "http://localhost:9655/*", + "http://127.0.0.1:9655/*" ], "background": { - "service_worker": "background.js" + "service_worker": "background.js", + "scripts": ["background.js"] }, "action": { "default_popup": "popup.html", - "default_icon": { - "48": "icon.png" - } + "default_title": "DeepSeek → FreeDeepseekAPI", + "default_icon": { "16": "icons/icon16.png", "48": "icons/icon48.png", "128": "icons/icon128.png" } }, - "icons": { - "48": "icon.png" - } + "icons": { "16": "icons/icon16.png", "48": "icons/icon48.png", "128": "icons/icon128.png" } } diff --git a/chrome-extension/popup.html b/chrome-extension/popup.html index f5837d7..05f1be5 100644 --- a/chrome-extension/popup.html +++ b/chrome-extension/popup.html @@ -22,18 +22,54 @@ .json-display { background: #0d1117; border: 1px solid #333; border-radius: 4px; padding: 8px; font-size: 10px; font-family: 'Courier New', monospace; color: #7ee787; white-space: pre-wrap; word-break: break-all; max-height: 180px; overflow-y: auto; margin-bottom: 8px; } .field label { display: block; font-size: 11px; color: #999; margin-bottom: 4px; } .detail { font-size: 10px; color: #666; text-align: center; } + .pool-section { margin-top: 14px; border-top: 1px solid #333; padding-top: 10px; } + .pool-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } + .pool-header .title { font-size: 12px; color: #4fc3f7; font-weight: 600; } + .link-btn { background: none; border: 1px solid #444; color: #9ccc65; border-radius: 5px; padding: 4px 8px; font-size: 11px; cursor: pointer; } + .link-btn:hover { background: #ffffff10; } + .link-btn:disabled { opacity: 0.5; cursor: default; } + .pool-list { display: flex; flex-direction: column; gap: 4px; max-height: 220px; overflow-y: auto; } + .pool-row { display: flex; align-items: center; gap: 6px; padding: 6px 8px; background: #0d1117; border: 1px solid #2a2a3a; border-radius: 5px; font-size: 11px; } + .pool-row .id { color: #888; font-family: 'Courier New', monospace; min-width: 40px; } + .pool-row .email { flex: 1; color: #ccc; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .badge { padding: 2px 7px; border-radius: 10px; font-size: 10px; font-weight: 600; white-space: nowrap; } + .badge.ok { background: #1b5e2033; color: #66bb6a; border: 1px solid #2e7d32; } + .badge.invalid { background: #b71c1c33; color: #ef5350; border: 1px solid #c62828; } + .badge.wait { background: #e6510033; color: #ff9800; border: 1px solid #e65100; } + .badge.expired { background: #44444433; color: #999; border: 1px solid #555; } + .badge.checking { background: #1565c033; color: #4fc3f7; border: 1px solid #1565c0; } + .row-actions { display: flex; gap: 4px; } + .acc-btn { background: #ffffff0d; border: 1px solid #444; color: #ddd; border-radius: 4px; padding: 3px 7px; font-size: 11px; cursor: pointer; } + .acc-btn:hover { background: #ffffff1a; } + .acc-btn.danger:hover { background: #b71c1c44; color: #ef5350; } + .acc-btn.confirm { background: #b71c1c; color: #fff; border-color: #c62828; } + .pool-empty { color: #666; font-size: 11px; text-align: center; padding: 10px; } + .hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; } + .hdr h1 { margin: 0; } + .srv-dot { width: 9px; height: 9px; border-radius: 50%; background: #666; flex: none; } + .srv-dot.online { background: #66bb6a; box-shadow: 0 0 5px #2e7d32; } + .srv-dot.offline { background: #ef5350; box-shadow: 0 0 5px #c62828; } + .btn-row button:disabled { opacity: 0.45; cursor: default; } + .pool-row .email { cursor: pointer; } + .pool-row .email:hover { color: #4fc3f7; }
-FreeDeepseekAPIchat.deepseek.com, отправьте любое сообщение, затем нажмите кнопку