diff --git a/.ipynb_checkpoints/Untitled-checkpoint.ipynb b/.ipynb_checkpoints/Untitled-checkpoint.ipynb new file mode 100644 index 0000000..363fcab --- /dev/null +++ b/.ipynb_checkpoints/Untitled-checkpoint.ipynb @@ -0,0 +1,6 @@ +{ + "cells": [], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Untitled.ipynb b/Untitled.ipynb new file mode 100644 index 0000000..a0585d4 --- /dev/null +++ b/Untitled.ipynb @@ -0,0 +1,137 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 4, + "id": "f28552f1-618c-4853-92e2-566554a2de2c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import asyncio\n", + "from kwork_api import KworkClient\n", + "from dotenv import load_dotenv\n", + "import os\n", + "\n", + "logging.basicConfig(level=logging.DEBUG) # или INFO для меньшего шума\n", + "\n", + "load_dotenv('tests/e2e/.env')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "953d142e-a575-41b7-927d-8cd1546d2747", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:kwork_api.client:Login request: POST https://kwork.ru/api/user/login (user: JTJagOmega)\n", + "DEBUG:kwork_api.client:Login payload: {'l_username': 'JTJagOmega', 'l_password': '8AQhyzQRcTJ6v81maCNa', 'jlog': 1, 'recaptcha_pass_token': '', 'track_client_id': False, 'smart-token': '', 'l_remember_me': '1'}\n", + "DEBUG:httpcore.connection:connect_tcp.started host='kwork.ru' port=443 local_address=None timeout=30.0 socket_options=None\n", + "DEBUG:httpcore.connection:connect_tcp.complete return_value=\n", + "DEBUG:httpcore.connection:start_tls.started ssl_context= server_hostname='kwork.ru' timeout=30.0\n", + "DEBUG:httpcore.connection:start_tls.complete return_value=\n", + "DEBUG:httpcore.http11:send_request_headers.started request=\n", + "DEBUG:httpcore.http11:send_request_headers.complete\n", + "DEBUG:httpcore.http11:send_request_body.started request=\n", + "DEBUG:httpcore.http11:send_request_body.complete\n", + "DEBUG:httpcore.http11:receive_response_headers.started request=\n", + "DEBUG:httpcore.http11:receive_response_headers.complete return_value=(b'HTTP/1.1', 200, b'OK', [(b'Server', b'QRATOR'), (b'Date', b'Sun, 29 Mar 2026 22:22:41 GMT'), (b'Content-Type', b'application/json'), (b'Transfer-Encoding', b'chunked'), (b'Connection', b'keep-alive'), (b'Keep-Alive', b'timeout=15'), (b'Vary', b'Accept-Encoding, User-Agent'), (b'Content-Security-Policy', b\"frame-ancestors 'self' https://webvisor.com https://awards.ratingruneta.ru\"), (b'Set-Cookie', b'referrer_url=https%3A%2F%2Fkwork.ru%2F; expires=Sun, 05-Apr-2026 22:22:41 GMT; Max-Age=604800; path=/; secure; HttpOnly; SameSite=None'), (b'Set-Cookie', b'uad=1884597369c9a63194ed0624319983; expires=Mon, 29-Mar-2027 22:22:41 GMT; Max-Age=31536000; path=/; secure; HttpOnly; SameSite=None'), (b'Set-Cookie', b'RORSSQIHEK=f15239f2927f4fd08e6945c15ed635c2; expires=Wed, 01-Apr-2026 22:22:41 GMT; Max-Age=259200; path=/; secure; HttpOnly; SameSite=None'), (b'Expires', b'Thu, 19 Nov 1981 08:52:00 GMT'), (b'Cache-Control', b'no-store, no-cache, must-revalidate'), (b'Pragma', b'no-cache'), (b'Set-Cookie', b'csrf_user_token=43ed1b44d6a5a480418b39929da62605; expires=Mon, 29-Mar-2027 22:22:41 GMT; Max-Age=31536000; path=/; secure; HttpOnly; SameSite=None'), (b'Set-Cookie', b'userId=18845973; expires=Wed, 26-Mar-2036 22:22:41 GMT; Max-Age=315360000; path=/; secure; SameSite=None'), (b'Set-Cookie', b'slrememberme=18845973_%242y%2410%24GEnC83HAU.ejn2CQB3OMTewWzSYxC0NYcSB3n2ck6eNvcz2aStK0W; expires=Mon, 29-Mar-2027 22:22:41 GMT; Max-Age=31536000; path=/; secure; HttpOnly; SameSite=None'), (b'Set-Cookie', b'_kmid=7fb7f3a407728e8d0ffa5ab4d19ff2b6; expires=Wed, 26-Mar-2036 22:22:41 GMT; Max-Age=315360000; path=/; secure; HttpOnly; SameSite=None'), (b'Set-Cookie', b'_kmfvt=1774822961; expires=Wed, 26-Mar-2036 22:22:41 GMT; Max-Age=315360000; path=/; secure; HttpOnly; SameSite=None'), (b'Set-Cookie', b'csrf_user_token=515cb2f621700da0faf4c3da66efbbfb; expires=Mon, 29-Mar-2027 22:22:41 GMT; Max-Age=31536000; path=/; secure; HttpOnly; SameSite=None'), (b'Cache-Control', b'no-cache, private'), (b'Strict-Transport-Security', b'max-age=15552000'), (b'X-Content-Type-Options', b'nosniff'), (b'Content-Encoding', b'gzip')])\n", + "INFO:httpx:HTTP Request: POST https://kwork.ru/api/user/login \"HTTP/1.1 200 OK\"\n", + "DEBUG:httpcore.http11:receive_response_body.started request=\n", + "DEBUG:httpcore.http11:receive_response_body.complete\n", + "DEBUG:httpcore.http11:response_closed.started\n", + "DEBUG:httpcore.http11:response_closed.complete\n", + "DEBUG:kwork_api.client:Login response status: 200\n", + "DEBUG:kwork_api.client:Login response headers: {'server': 'QRATOR', 'date': 'Sun, 29 Mar 2026 22:22:41 GMT', 'content-type': 'application/json', 'transfer-encoding': 'chunked', 'connection': 'keep-alive', 'keep-alive': 'timeout=15', 'vary': 'Accept-Encoding, User-Agent', 'content-security-policy': \"frame-ancestors 'self' https://webvisor.com https://awards.ratingruneta.ru\", 'set-cookie': 'referrer_url=https%3A%2F%2Fkwork.ru%2F; expires=Sun, 05-Apr-2026 22:22:41 GMT; Max-Age=604800; path=/; secure; HttpOnly; SameSite=None, uad=1884597369c9a63194ed0624319983; expires=Mon, 29-Mar-2027 22:22:41 GMT; Max-Age=31536000; path=/; secure; HttpOnly; SameSite=None, RORSSQIHEK=f15239f2927f4fd08e6945c15ed635c2; expires=Wed, 01-Apr-2026 22:22:41 GMT; Max-Age=259200; path=/; secure; HttpOnly; SameSite=None, csrf_user_token=43ed1b44d6a5a480418b39929da62605; expires=Mon, 29-Mar-2027 22:22:41 GMT; Max-Age=31536000; path=/; secure; HttpOnly; SameSite=None, userId=18845973; expires=Wed, 26-Mar-2036 22:22:41 GMT; Max-Age=315360000; path=/; secure; SameSite=None, slrememberme=18845973_%242y%2410%24GEnC83HAU.ejn2CQB3OMTewWzSYxC0NYcSB3n2ck6eNvcz2aStK0W; expires=Mon, 29-Mar-2027 22:22:41 GMT; Max-Age=31536000; path=/; secure; HttpOnly; SameSite=None, _kmid=7fb7f3a407728e8d0ffa5ab4d19ff2b6; expires=Wed, 26-Mar-2036 22:22:41 GMT; Max-Age=315360000; path=/; secure; HttpOnly; SameSite=None, _kmfvt=1774822961; expires=Wed, 26-Mar-2036 22:22:41 GMT; Max-Age=315360000; path=/; secure; HttpOnly; SameSite=None, csrf_user_token=515cb2f621700da0faf4c3da66efbbfb; expires=Mon, 29-Mar-2027 22:22:41 GMT; Max-Age=31536000; path=/; secure; HttpOnly; SameSite=None', 'expires': 'Thu, 19 Nov 1981 08:52:00 GMT', 'cache-control': 'no-store, no-cache, must-revalidate, no-cache, private', 'pragma': 'no-cache', 'strict-transport-security': 'max-age=15552000', 'x-content-type-options': 'nosniff', 'content-encoding': 'gzip'}\n", + "INFO:kwork_api.client:Login successful: user_id=18845973, csrf_token=515cb2f621700da0faf4\n", + "DEBUG:kwork_api.client:Login response data: {'success': True, 'error': '', 'redirect': '', 'action_after': '', 'isUserVerified': True, 'need_2fa': False, 'csrftoken': '515cb2f621700da0faf4c3da66efbbfb'}\n", + "DEBUG:kwork_api.client:Login cookies: ['referrer_url', 'uad', 'RORSSQIHEK', 'csrf_user_token', 'userId', 'slrememberme', '_kmid', '_kmfvt']\n", + "DEBUG:httpcore.connection:close.started\n", + "DEBUG:httpcore.connection:close.complete\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✅ Logged in as: 18845973_%242y%2410%...\n" + ] + } + ], + "source": [ + "try:\n", + " client = await KworkClient.login(\n", + " username=os.getenv('KWORK_USERNAME'),\n", + " password=os.getenv('KWORK_PASSWORD')\n", + " )\n", + " print(f\"✅ Logged in as: {client.token[:20]}...\")\n", + "except Exception as e:\n", + " print(e)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "655aa71e-5645-4c7a-aadd-5b044a0713c9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'18845973_%242y%2410%24GEnC83HAU.ejn2CQB3OMTewWzSYxC0NYcSB3n2ck6eNvcz2aStK0W'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.token" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9a5161a-4051-4321-849b-c3b416a939a0", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/HAR_ENDPOINTS_MAPPING.md b/docs/HAR_ENDPOINTS_MAPPING.md new file mode 100644 index 0000000..3528680 --- /dev/null +++ b/docs/HAR_ENDPOINTS_MAPPING.md @@ -0,0 +1,214 @@ +# HAR Endpoints Mapping + +Сопоставление endpoints из `client.py` с реальными endpoints из HAR файла. + +--- + +## 📊 Сопоставление endpoints + +### ✅ Работающие endpoints (совпадают с HAR) + +| client.py Endpoint | HAR Endpoint | Status | Notes | +|-------------------|--------------|--------|-------| +| `/projects` | `POST /projects` | ✅ 200 | GET в HAR, POST в client.py | +| `/api/validation/checktext` | N/A | ❓ | Нет в HAR - возможно не использовался | + +--- + +### ❌ Неработающие endpoints (нужно исправить) + +| client.py Endpoint | HAR Endpoint (реальный) | Status | Как исправить | +|-------------------|------------------------|--------|---------------| +| `/catalogMainv2` | `GET /categories/{slug}` | ❌ 404 | Использовать `GET /categories/{slug}` или найти JSON API | +| `/getKworkDetails` | `GET /projects/{id}/view` | ❌ 404 | Использовать `GET /projects/{id}/view` | +| `/userReviews` | `POST /user/get_reviews` | ❌ 404 | ✅ Исправить на `/user/get_reviews` | +| `/cities` | N/A | ❌ 404 | Нет в HAR - возможно через HTML страницу | +| `/countries` | N/A | ❌ 404 | Нет в HAR - возможно через HTML страницу | +| `/user` | `GET /user/{username}` | ❌ 404 | ✅ Исправить на `/user/{username}` | + +--- + +## 🔍 Детальный анализ + +### 1. Каталог кворков + +**Текущий (не работает):** +```python +POST /catalogMainv2 # ❌ 404 Not Found +``` + +**Реальный из HAR:** +``` +GET /categories/design # ✅ 200 OK +GET /projects # ✅ 200 OK +``` + +**Проблема:** HAR показывает HTML страницы, не JSON API. + +**Решение:** Нужно найти JSON API endpoint или парсить HTML. + +**Как найти:** +1. Открыть DevTools → Network → XHR/Fetch +2. Перейти на https://kwork.ru/categories/design +3. Искать JSON запросы с данными кворков +4. Или искать в HAR файлы с "kworks", "catalog", "list" + +--- + +### 2. Детали кворка + +**Текущий (не работает):** +```python +POST /getKworkDetails # ❌ 404 Not Found +``` + +**Реальный из HAR:** +``` +GET /projects/3127023/view # ✅ 200 OK +``` + +**Проблема:** HAR показывает HTML страницу проекта. + +**Решение:** Использовать `GET /projects/{id}/view` и парсить HTML. + +**Как найти JSON API:** +1. Открыть DevTools → Network → XHR/Fetch +2. Перейти на https://kwork.ru/projects/{id}/view +3. Искать JSON запросы + +--- + +### 3. Отзывы пользователя + +**Текущий (не работает):** +```python +POST /userReviews # ❌ 404 Not Found +``` + +**Реальный из HAR:** +``` +POST /user/get_reviews # ✅ 200 OK +Payload: {"userId":126921,"type":"positive"} +``` + +**Решение:** ✅ Исправить endpoint на `/user/get_reviews` + +--- + +### 4. Информация о пользователе + +**Текущий (не работает):** +```python +POST /user # ❌ 404 Not Found +``` + +**Реальный из HAR:** +``` +GET /user/jtjagomega # ✅ 200 OK (HTML страница) +GET /user/alexey-liss # ✅ 200 OK (HTML страница) +POST /api/user/checknotify # ✅ 200 OK (JSON API, уведомления) +``` + +**Проблема:** Kwork **НЕ предоставляет JSON API** для user info! + +**Решение:** +- Для профиля: `GET /user/{username}` + парсинг HTML +- Для уведомлений: `POST /api/user/checknotify` (уже реализовано) + +**В client.py сейчас:** +```python +# Временно используем checknotify как placeholder +return await self.client._request("POST", "/api/user/checknotify") +``` + +--- + +### 5. Справочные данные (города, страны) + +**Текущие (не работают):** +```python +POST /cities # ❌ 404 Not Found +POST /countries # ❌ 404 Not Found +``` + +**В HAR:** Нет таких endpoints. + +**Проблема:** Возможно данные встроены в HTML или загружаются через JavaScript. + +**Как найти:** +1. Открыть DevTools → Network +2. Перейти на страницу с формой (например, настройки) +3. Искать запросы с "cities", "countries", "regions" +4. Или искать в исходном коде страницы `