hh-applicant-tool 0.3.5__tar.gz → 0.3.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hh-applicant-tool might be problematic. Click here for more details.
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/PKG-INFO +11 -12
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/README.md +10 -11
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/api/client.py +28 -4
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/constants.py +1 -1
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/main.py +32 -8
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/apply_similar.py +4 -8
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/call_api.py +3 -7
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/clear_negotiations.py +2 -6
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/list_resumes.py +2 -6
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/update_resumes.py +2 -6
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/whoami.py +2 -6
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/telemetry_client.py +0 -2
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/pyproject.toml +1 -1
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/__init__.py +0 -0
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/__main__.py +0 -0
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/api/__init__.py +0 -0
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/api/errors.py +0 -0
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/color_log.py +0 -0
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/__init__.py +0 -0
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/authorize.py +0 -0
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/refresh_token.py +0 -0
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/types.py +0 -0
- {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hh-applicant-tool
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Senior YAML Developer
|
|
6
6
|
Author-email: yamldeveloper@proton.me
|
|
@@ -19,8 +19,6 @@ Description-Content-Type: text/markdown
|
|
|
19
19
|
|
|
20
20
|
## HH Applicant Tool
|
|
21
21
|
|
|
22
|
-
> ! Наложен мораторий на доработки/переработки. Разработка будет возобновлена после 100 звезд 💫
|
|
23
|
-
|
|
24
22
|

|
|
25
23
|
[]()
|
|
26
24
|
[]()
|
|
@@ -72,18 +70,18 @@ $ pipx upgrade hh-applicant-tool
|
|
|
72
70
|
|
|
73
71
|
Отдельно я распишу процесс установки в **Windows** в подробностях:
|
|
74
72
|
|
|
75
|
-
* Для начала поставьте Python 3 любым удобным способом.
|
|
76
|
-
* Запустите
|
|
73
|
+
* Для начала поставьте последнюю версию **Python 3** любым удобным способом.
|
|
74
|
+
* Запустите **Terminal** или **PowerShell** от Администратора и выполните:
|
|
77
75
|
```ps
|
|
78
76
|
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted
|
|
79
77
|
```
|
|
80
|
-
Без
|
|
78
|
+
Данная политика разрешает текущему пользователю (от которого зашли) запускать скрипты. Без нее не будут работать виртуальные окружения.
|
|
81
79
|
* Создайте и активируйте виртуальное окружение:
|
|
82
80
|
```ps
|
|
83
81
|
PS> python -m pip venv hh-applicant-venv
|
|
84
82
|
PS> .\hh-applicant-venv\Scripts\activate
|
|
85
83
|
```
|
|
86
|
-
* Поставьте все пакеты в виртуальное окружение `hh-applicant-
|
|
84
|
+
* Поставьте все пакеты в виртуальное окружение `hh-applicant-venv`:
|
|
87
85
|
```ps
|
|
88
86
|
(hh-applicant-venv) PS> pip install hh-applicant-tool[qt]
|
|
89
87
|
```
|
|
@@ -91,10 +89,10 @@ $ pipx upgrade hh-applicant-tool
|
|
|
91
89
|
```ps
|
|
92
90
|
(hh-applicant-venv) PS> hh-applicant-tool -h
|
|
93
91
|
```
|
|
94
|
-
* В случае
|
|
92
|
+
* В случае неудачи вернитесь к первому шагу.
|
|
93
|
+
* Для последующих запусков сначала активируйте виртуальное окружение.
|
|
95
94
|
|
|
96
95
|
|
|
97
|
-
|
|
98
96
|
Использование:
|
|
99
97
|
|
|
100
98
|
```bash
|
|
@@ -158,10 +156,11 @@ https://hh.ru/employer/1918903
|
|
|
158
156
|
| **whoami** | Выводит информацию об авторизованном пользователе |
|
|
159
157
|
| **list-resumes** | Список резюме |
|
|
160
158
|
| **update-resumes** | Обновить все резюме. Аналогично нажатию кнопки «Обновить дату». |
|
|
161
|
-
| **apply-similar** | Откликнуться на все подходящие вакансии. Лимит = 200 в
|
|
159
|
+
| **apply-similar** | Откликнуться на все подходящие вакансии. Лимит = 200 в день. На HH есть спам-фильтры, так что лучше не рассылайте отклики со ссылками. |
|
|
162
160
|
| **clear-negotiations** | Удаляет отказы и отменяет заявки, которые долго висят |
|
|
163
161
|
| **call-api** | Вызов произвольного метода API с выводом результата. |
|
|
164
162
|
| **refresh-token** | Обновляет access_token. |
|
|
163
|
+
| **get-employer-contacts** | Получить список контактов работодателя, даже если тот не высылал приглашения. Контакты получаются строго из публичного доступа, например, сайта фирмы и могут быть удалены по просьбе упал_намоченный лицо. Данная функция готова и будет доступна после 100 ⭐ |
|
|
165
164
|
|
|
166
165
|
Авторизуемся:
|
|
167
166
|
|
|
@@ -262,7 +261,7 @@ rm -f ~/.local/share/applications/hhandroid.desktop
|
|
|
262
261
|
|
|
263
262
|
Отдельные замечания у меня к API HH. Оно пиздец какое кривое. Например, при создании заявки возвращается пустой ответ либо редирект, хотя по логике должен возвраться созданный объект. Так же в ответах сервера нет `Content-Length`. Из-за этого нельзя узнать есть тело у ответа сервера нужно его пробовать прочитать. Я так понял там какой-то прокси оборачивает все запросы и отдает всегда `Transfer-Encoding: Chunked`. А еще он возвращает 502 ошибку, когда бекенд на Java падает либо долго отвечает (таймаут)? А вот [язык запросов](https://hh.ru/article/1175) мне понравился. Можно что-то типа этого использовать `NOT (!ID:123 OR !ID:456 OR !ID:789)` что бы отсеить какие-то вакансии.
|
|
264
263
|
|
|
265
|
-
Утилита собирает и передает на сервер разработчика следующую
|
|
264
|
+
Утилита собирает и передает на сервер разработчика следующую информацию:
|
|
266
265
|
|
|
267
266
|
1. Название вакансии.
|
|
268
267
|
1. Тип вакансии (открытая/закрытая).
|
|
@@ -271,7 +270,7 @@ rm -f ~/.local/share/applications/hhandroid.desktop
|
|
|
271
270
|
1. Прямая ссылка на вакансию.
|
|
272
271
|
1. Дата создания вакансии.
|
|
273
272
|
1. Дата публикации вакансии.
|
|
274
|
-
1. Контактная информация
|
|
273
|
+
1. Контактная информация работодателя, которую он или его сотрудники сами выложили в общественный доступ, хранящаеся в строго обезличенной форме с соблюдением законов РФ, GDPR и американского экспортного контроля (может быть удалена при письменном запросе в утвержденной Законом форме с оригинальными печатями фирмы и росписью генерального директора и/или по требованию РКН, прокуратуры или лично Адама Кадырова).
|
|
275
274
|
1. Название компании.
|
|
276
275
|
1. Тип компании.
|
|
277
276
|
1. Описание компании.
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
## HH Applicant Tool
|
|
2
2
|
|
|
3
|
-
> ! Наложен мораторий на доработки/переработки. Разработка будет возобновлена после 100 звезд 💫
|
|
4
|
-
|
|
5
3
|

|
|
6
4
|
[]()
|
|
7
5
|
[]()
|
|
@@ -53,18 +51,18 @@ $ pipx upgrade hh-applicant-tool
|
|
|
53
51
|
|
|
54
52
|
Отдельно я распишу процесс установки в **Windows** в подробностях:
|
|
55
53
|
|
|
56
|
-
* Для начала поставьте Python 3 любым удобным способом.
|
|
57
|
-
* Запустите
|
|
54
|
+
* Для начала поставьте последнюю версию **Python 3** любым удобным способом.
|
|
55
|
+
* Запустите **Terminal** или **PowerShell** от Администратора и выполните:
|
|
58
56
|
```ps
|
|
59
57
|
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted
|
|
60
58
|
```
|
|
61
|
-
Без
|
|
59
|
+
Данная политика разрешает текущему пользователю (от которого зашли) запускать скрипты. Без нее не будут работать виртуальные окружения.
|
|
62
60
|
* Создайте и активируйте виртуальное окружение:
|
|
63
61
|
```ps
|
|
64
62
|
PS> python -m pip venv hh-applicant-venv
|
|
65
63
|
PS> .\hh-applicant-venv\Scripts\activate
|
|
66
64
|
```
|
|
67
|
-
* Поставьте все пакеты в виртуальное окружение `hh-applicant-
|
|
65
|
+
* Поставьте все пакеты в виртуальное окружение `hh-applicant-venv`:
|
|
68
66
|
```ps
|
|
69
67
|
(hh-applicant-venv) PS> pip install hh-applicant-tool[qt]
|
|
70
68
|
```
|
|
@@ -72,10 +70,10 @@ $ pipx upgrade hh-applicant-tool
|
|
|
72
70
|
```ps
|
|
73
71
|
(hh-applicant-venv) PS> hh-applicant-tool -h
|
|
74
72
|
```
|
|
75
|
-
* В случае
|
|
73
|
+
* В случае неудачи вернитесь к первому шагу.
|
|
74
|
+
* Для последующих запусков сначала активируйте виртуальное окружение.
|
|
76
75
|
|
|
77
76
|
|
|
78
|
-
|
|
79
77
|
Использование:
|
|
80
78
|
|
|
81
79
|
```bash
|
|
@@ -139,10 +137,11 @@ https://hh.ru/employer/1918903
|
|
|
139
137
|
| **whoami** | Выводит информацию об авторизованном пользователе |
|
|
140
138
|
| **list-resumes** | Список резюме |
|
|
141
139
|
| **update-resumes** | Обновить все резюме. Аналогично нажатию кнопки «Обновить дату». |
|
|
142
|
-
| **apply-similar** | Откликнуться на все подходящие вакансии. Лимит = 200 в
|
|
140
|
+
| **apply-similar** | Откликнуться на все подходящие вакансии. Лимит = 200 в день. На HH есть спам-фильтры, так что лучше не рассылайте отклики со ссылками. |
|
|
143
141
|
| **clear-negotiations** | Удаляет отказы и отменяет заявки, которые долго висят |
|
|
144
142
|
| **call-api** | Вызов произвольного метода API с выводом результата. |
|
|
145
143
|
| **refresh-token** | Обновляет access_token. |
|
|
144
|
+
| **get-employer-contacts** | Получить список контактов работодателя, даже если тот не высылал приглашения. Контакты получаются строго из публичного доступа, например, сайта фирмы и могут быть удалены по просьбе упал_намоченный лицо. Данная функция готова и будет доступна после 100 ⭐ |
|
|
146
145
|
|
|
147
146
|
Авторизуемся:
|
|
148
147
|
|
|
@@ -243,7 +242,7 @@ rm -f ~/.local/share/applications/hhandroid.desktop
|
|
|
243
242
|
|
|
244
243
|
Отдельные замечания у меня к API HH. Оно пиздец какое кривое. Например, при создании заявки возвращается пустой ответ либо редирект, хотя по логике должен возвраться созданный объект. Так же в ответах сервера нет `Content-Length`. Из-за этого нельзя узнать есть тело у ответа сервера нужно его пробовать прочитать. Я так понял там какой-то прокси оборачивает все запросы и отдает всегда `Transfer-Encoding: Chunked`. А еще он возвращает 502 ошибку, когда бекенд на Java падает либо долго отвечает (таймаут)? А вот [язык запросов](https://hh.ru/article/1175) мне понравился. Можно что-то типа этого использовать `NOT (!ID:123 OR !ID:456 OR !ID:789)` что бы отсеить какие-то вакансии.
|
|
245
244
|
|
|
246
|
-
Утилита собирает и передает на сервер разработчика следующую
|
|
245
|
+
Утилита собирает и передает на сервер разработчика следующую информацию:
|
|
247
246
|
|
|
248
247
|
1. Название вакансии.
|
|
249
248
|
1. Тип вакансии (открытая/закрытая).
|
|
@@ -252,7 +251,7 @@ rm -f ~/.local/share/applications/hhandroid.desktop
|
|
|
252
251
|
1. Прямая ссылка на вакансию.
|
|
253
252
|
1. Дата создания вакансии.
|
|
254
253
|
1. Дата публикации вакансии.
|
|
255
|
-
1. Контактная информация
|
|
254
|
+
1. Контактная информация работодателя, которую он или его сотрудники сами выложили в общественный доступ, хранящаеся в строго обезличенной форме с соблюдением законов РФ, GDPR и американского экспортного контроля (может быть удалена при письменном запросе в утвержденной Законом форме с оригинальными печатями фирмы и росписью генерального директора и/или по требованию РКН, прокуратуры или лично Адама Кадырова).
|
|
256
255
|
1. Название компании.
|
|
257
256
|
1. Тип компании.
|
|
258
257
|
1. Описание компании.
|
|
@@ -12,11 +12,12 @@ from urllib.parse import urlencode
|
|
|
12
12
|
|
|
13
13
|
import requests
|
|
14
14
|
from requests import Response, Session
|
|
15
|
+
import random
|
|
15
16
|
|
|
16
17
|
from ..constants import (
|
|
17
18
|
ANDROID_CLIENT_ID,
|
|
18
19
|
ANDROID_CLIENT_SECRET,
|
|
19
|
-
|
|
20
|
+
USER_AGENT_TEMPLATE,
|
|
20
21
|
)
|
|
21
22
|
from ..types import AccessToken
|
|
22
23
|
from . import errors
|
|
@@ -38,6 +39,7 @@ class BaseClient:
|
|
|
38
39
|
user_agent: str | None = None
|
|
39
40
|
session: Session | None = None
|
|
40
41
|
previous_request_time: float = 0.0
|
|
42
|
+
delay: float = 0.334
|
|
41
43
|
|
|
42
44
|
def __post_init__(self) -> None:
|
|
43
45
|
self.lock = Lock()
|
|
@@ -46,11 +48,31 @@ class BaseClient:
|
|
|
46
48
|
session.headers.update(
|
|
47
49
|
{
|
|
48
50
|
**self.additional_headers(),
|
|
49
|
-
"User-Agent": self.user_agent or
|
|
51
|
+
"User-Agent": self.user_agent or self.default_user_agent(),
|
|
50
52
|
}
|
|
51
53
|
)
|
|
52
54
|
logger.debug("Default Headers: %r", session.headers)
|
|
53
55
|
|
|
56
|
+
def default_user_agent(self) -> str:
|
|
57
|
+
return USER_AGENT_TEMPLATE % (
|
|
58
|
+
random.choice(["8.0", "8.1", "9", "10", "11", "12"]),
|
|
59
|
+
random.choice(
|
|
60
|
+
[
|
|
61
|
+
"SM-G998B", # Samsung Galaxy S21 Ultra
|
|
62
|
+
"Pixel 6", # Google Pixel 6
|
|
63
|
+
"Mi 11", # Xiaomi Mi 11
|
|
64
|
+
"OnePlus 9", # OnePlus 9
|
|
65
|
+
"P40", # Huawei P40
|
|
66
|
+
"LG G8", # LG G8
|
|
67
|
+
"Xperia 1 II", # Sony Xperia 1 II
|
|
68
|
+
"Moto G Power", # Motorola Moto G Power
|
|
69
|
+
"HTC U12+", # HTC U12+
|
|
70
|
+
"ROG Phone 5", # Asus ROG Phone 5
|
|
71
|
+
]
|
|
72
|
+
),
|
|
73
|
+
random.randint(88, 130),
|
|
74
|
+
)
|
|
75
|
+
|
|
54
76
|
def additional_headers(
|
|
55
77
|
self,
|
|
56
78
|
) -> dict[str, str]:
|
|
@@ -61,7 +83,7 @@ class BaseClient:
|
|
|
61
83
|
method: ALLOWED_METHODS,
|
|
62
84
|
endpoint: str,
|
|
63
85
|
params: dict | None = None,
|
|
64
|
-
delay: float =
|
|
86
|
+
delay: float | None = None,
|
|
65
87
|
**kwargs: Any,
|
|
66
88
|
) -> dict:
|
|
67
89
|
# Не знаю насколько это "правильно"
|
|
@@ -72,7 +94,9 @@ class BaseClient:
|
|
|
72
94
|
with self.lock:
|
|
73
95
|
# На серваке какая-то анти-DDOS система
|
|
74
96
|
if (
|
|
75
|
-
delay := delay
|
|
97
|
+
delay := (self.delay if delay is None else delay)
|
|
98
|
+
- time.monotonic()
|
|
99
|
+
+ self.previous_request_time
|
|
76
100
|
) > 0:
|
|
77
101
|
logger.debug("wait %fs before request", delay)
|
|
78
102
|
time.sleep(delay)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
USER_AGENT_TEMPLATE = "Mozilla/5.0 (Linux; Android %s; %s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s.0.0.0 Mobile Safari/537.36"
|
|
2
2
|
|
|
3
3
|
ANDROID_CLIENT_ID = (
|
|
4
4
|
"HIOMIAS39CA9DICTA7JIO64LQKQJF5AGIK74G9ITJKLNEDAOH5FHS5G1JI7FOEGD"
|
|
@@ -9,14 +9,12 @@ from os import getenv
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from pkgutil import iter_modules
|
|
11
11
|
from typing import Sequence
|
|
12
|
-
|
|
12
|
+
from .api import ApiClient
|
|
13
13
|
from .color_log import ColorHandler
|
|
14
14
|
from .utils import Config, get_config_path
|
|
15
15
|
|
|
16
16
|
DEFAULT_CONFIG_PATH = (
|
|
17
|
-
get_config_path()
|
|
18
|
-
/ __package__.replace("_", "-")
|
|
19
|
-
/ "config.json"
|
|
17
|
+
get_config_path() / __package__.replace("_", "-") / "config.json"
|
|
20
18
|
)
|
|
21
19
|
|
|
22
20
|
logger = logging.getLogger(__package__)
|
|
@@ -35,6 +33,18 @@ OPERATIONS = "operations"
|
|
|
35
33
|
class Namespace(argparse.Namespace):
|
|
36
34
|
config: Config
|
|
37
35
|
verbosity: int
|
|
36
|
+
delay: float
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_api(args: Namespace) -> ApiClient:
|
|
40
|
+
token = args.config.get("token", {})
|
|
41
|
+
api = ApiClient(
|
|
42
|
+
access_token=token.get("access_token"),
|
|
43
|
+
refresh_token=token.get("refresh_token"),
|
|
44
|
+
user_agent=args.config["user_agent"],
|
|
45
|
+
delay=args.delay,
|
|
46
|
+
)
|
|
47
|
+
return api
|
|
38
48
|
|
|
39
49
|
|
|
40
50
|
class HHApplicantTool:
|
|
@@ -45,32 +55,46 @@ class HHApplicantTool:
|
|
|
45
55
|
Группа поддержки: <https://t.me/+aSjr8qM_AP85ZDBi>
|
|
46
56
|
"""
|
|
47
57
|
|
|
58
|
+
class ArgumentFormatter(
|
|
59
|
+
argparse.ArgumentDefaultsHelpFormatter,
|
|
60
|
+
argparse.RawDescriptionHelpFormatter,
|
|
61
|
+
):
|
|
62
|
+
pass
|
|
63
|
+
|
|
48
64
|
def create_parser(self) -> argparse.ArgumentParser:
|
|
49
65
|
parser = argparse.ArgumentParser(
|
|
50
66
|
description=self.__doc__,
|
|
51
|
-
formatter_class=
|
|
67
|
+
formatter_class=self.ArgumentFormatter,
|
|
52
68
|
)
|
|
53
69
|
parser.add_argument(
|
|
54
70
|
"-c",
|
|
55
71
|
"--config",
|
|
56
|
-
help="
|
|
72
|
+
help="Путь до файла конфигурации",
|
|
57
73
|
type=Config,
|
|
58
74
|
default=Config(DEFAULT_CONFIG_PATH),
|
|
59
75
|
)
|
|
60
76
|
parser.add_argument(
|
|
61
77
|
"-v",
|
|
62
78
|
"--verbosity",
|
|
63
|
-
help="
|
|
79
|
+
help="При использовании от одного и более раз увеличивает количество отладочной информации в выводе",
|
|
64
80
|
action="count",
|
|
65
81
|
default=0,
|
|
66
82
|
)
|
|
83
|
+
parser.add_argument(
|
|
84
|
+
"-d",
|
|
85
|
+
"--delay",
|
|
86
|
+
type=float,
|
|
87
|
+
default=0.334,
|
|
88
|
+
help="Задержка между запросами к API HH",
|
|
89
|
+
)
|
|
67
90
|
subparsers = parser.add_subparsers(help="commands")
|
|
68
91
|
package_dir = Path(__file__).resolve().parent / OPERATIONS
|
|
69
92
|
for _, module_name, _ in iter_modules([str(package_dir)]):
|
|
70
93
|
mod = import_module(f"{__package__}.{OPERATIONS}.{module_name}")
|
|
71
94
|
op: BaseOperation = mod.Operation()
|
|
72
95
|
op_parser = subparsers.add_parser(
|
|
73
|
-
module_name.replace("_", "-"),
|
|
96
|
+
module_name.replace("_", "-"),
|
|
97
|
+
description=op.__doc__, formatter_class=self.ArgumentFormatter
|
|
74
98
|
)
|
|
75
99
|
op_parser.set_defaults(run=op.run)
|
|
76
100
|
op.setup_parser(op_parser)
|
{hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/apply_similar.py
RENAMED
|
@@ -8,7 +8,7 @@ from typing import TextIO, Tuple
|
|
|
8
8
|
|
|
9
9
|
from ..api import ApiClient, ApiError, BadRequest
|
|
10
10
|
from ..main import BaseOperation
|
|
11
|
-
from ..main import Namespace as BaseNamespace
|
|
11
|
+
from ..main import Namespace as BaseNamespace, get_api
|
|
12
12
|
from ..telemetry_client import TelemetryError
|
|
13
13
|
from ..telemetry_client import get_client as get_telemetry_client
|
|
14
14
|
from ..types import ApiListResponse, VacancyItem
|
|
@@ -83,11 +83,7 @@ class Operation(BaseOperation):
|
|
|
83
83
|
return min(min_interval, max_interval), max(min_interval, max_interval)
|
|
84
84
|
|
|
85
85
|
def run(self, args: Namespace) -> None:
|
|
86
|
-
|
|
87
|
-
api = ApiClient(
|
|
88
|
-
access_token=args.config["token"]["access_token"],
|
|
89
|
-
user_agent=args.config["user_agent"],
|
|
90
|
-
)
|
|
86
|
+
api = get_api(args)
|
|
91
87
|
resume_id = self._get_resume_id(args, api)
|
|
92
88
|
application_messages = self._get_application_messages(args)
|
|
93
89
|
|
|
@@ -178,7 +174,7 @@ class Operation(BaseOperation):
|
|
|
178
174
|
|
|
179
175
|
try:
|
|
180
176
|
employer_id = vacancy["employer"]["id"]
|
|
181
|
-
except
|
|
177
|
+
except KeyError:
|
|
182
178
|
logger.warning(
|
|
183
179
|
f"Вакансия без работодателя: {vacancy['alternate_url']}"
|
|
184
180
|
)
|
|
@@ -209,7 +205,7 @@ class Operation(BaseOperation):
|
|
|
209
205
|
message_template = random.choice(application_messages)
|
|
210
206
|
|
|
211
207
|
try:
|
|
212
|
-
params["message"] =
|
|
208
|
+
params["message"] = message_template % vacancy
|
|
213
209
|
except TypeError as ex:
|
|
214
210
|
# TypeError: not enough arguments for format string
|
|
215
211
|
# API HH все кривое, иногда нет идентификатора работодателя, иногда у вакансии нет названия.
|
{hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/call_api.py
RENAMED
|
@@ -4,8 +4,8 @@ import json
|
|
|
4
4
|
import logging
|
|
5
5
|
import sys
|
|
6
6
|
|
|
7
|
-
from ..api import
|
|
8
|
-
from ..main import BaseOperation
|
|
7
|
+
from ..api import ApiError
|
|
8
|
+
from ..main import BaseOperation, get_api
|
|
9
9
|
from ..main import Namespace as BaseNamespace
|
|
10
10
|
|
|
11
11
|
logger = logging.getLogger(__package__)
|
|
@@ -34,11 +34,7 @@ class Operation(BaseOperation):
|
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
def run(self, args: Namespace) -> None:
|
|
37
|
-
|
|
38
|
-
api = ApiClient(
|
|
39
|
-
access_token=args.config["token"]["access_token"],
|
|
40
|
-
user_agent=args.config["user_agent"],
|
|
41
|
-
)
|
|
37
|
+
api = get_api(args)
|
|
42
38
|
params = dict(x.split("=", 1) for x in args.param)
|
|
43
39
|
try:
|
|
44
40
|
result = api.request(args.method, args.endpoint, params=params)
|
|
@@ -5,7 +5,7 @@ from datetime import datetime, timedelta, timezone
|
|
|
5
5
|
|
|
6
6
|
from ..api import ApiClient, ClientError
|
|
7
7
|
from ..constants import INVALID_ISO8601_FORMAT
|
|
8
|
-
from ..main import BaseOperation
|
|
8
|
+
from ..main import BaseOperation, get_api
|
|
9
9
|
from ..main import Namespace as BaseNamespace
|
|
10
10
|
from ..types import ApiListResponse
|
|
11
11
|
from ..utils import print_err, truncate_string
|
|
@@ -51,11 +51,7 @@ class Operation(BaseOperation):
|
|
|
51
51
|
return rv
|
|
52
52
|
|
|
53
53
|
def run(self, args: Namespace) -> None:
|
|
54
|
-
|
|
55
|
-
api = ApiClient(
|
|
56
|
-
access_token=args.config["token"]["access_token"],
|
|
57
|
-
user_agent=args.config["user_agent"],
|
|
58
|
-
)
|
|
54
|
+
api = get_api(args)
|
|
59
55
|
negotiations = self._get_active_negotiations(api)
|
|
60
56
|
print("Всего активных:", len(negotiations))
|
|
61
57
|
for item in negotiations:
|
{hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/list_resumes.py
RENAMED
|
@@ -5,7 +5,7 @@ import logging
|
|
|
5
5
|
from prettytable import PrettyTable
|
|
6
6
|
|
|
7
7
|
from ..api import ApiClient
|
|
8
|
-
from ..main import BaseOperation
|
|
8
|
+
from ..main import BaseOperation, get_api
|
|
9
9
|
from ..main import Namespace as BaseNamespace
|
|
10
10
|
from ..types import ApiListResponse
|
|
11
11
|
from ..utils import truncate_string
|
|
@@ -24,11 +24,7 @@ class Operation(BaseOperation):
|
|
|
24
24
|
pass
|
|
25
25
|
|
|
26
26
|
def run(self, args: Namespace) -> None:
|
|
27
|
-
|
|
28
|
-
api = ApiClient(
|
|
29
|
-
access_token=args.config["token"]["access_token"],
|
|
30
|
-
user_agent=args.config["user_agent"],
|
|
31
|
-
)
|
|
27
|
+
api = get_api(args)
|
|
32
28
|
resumes: ApiListResponse = api.get("/resumes/mine")
|
|
33
29
|
t = PrettyTable(
|
|
34
30
|
field_names=["ID", "Название", "Статус"], align="l", valign="t"
|
{hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/update_resumes.py
RENAMED
|
@@ -3,7 +3,7 @@ import argparse
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
5
|
from ..api import ApiClient, ApiError
|
|
6
|
-
from ..main import BaseOperation
|
|
6
|
+
from ..main import BaseOperation, get_api
|
|
7
7
|
from ..main import Namespace as BaseNamespace
|
|
8
8
|
from ..types import ApiListResponse
|
|
9
9
|
from ..utils import print_err, truncate_string
|
|
@@ -22,11 +22,7 @@ class Operation(BaseOperation):
|
|
|
22
22
|
pass
|
|
23
23
|
|
|
24
24
|
def run(self, args: Namespace) -> None:
|
|
25
|
-
|
|
26
|
-
api = ApiClient(
|
|
27
|
-
access_token=args.config["token"]["access_token"],
|
|
28
|
-
user_agent=args.config["user_agent"],
|
|
29
|
-
)
|
|
25
|
+
api = get_api(args)
|
|
30
26
|
resumes: ApiListResponse = api.get("/resumes/mine")
|
|
31
27
|
for resume in resumes["items"]:
|
|
32
28
|
try:
|
|
@@ -4,7 +4,7 @@ import json
|
|
|
4
4
|
import logging
|
|
5
5
|
|
|
6
6
|
from ..api import ApiClient
|
|
7
|
-
from ..main import BaseOperation
|
|
7
|
+
from ..main import BaseOperation, get_api
|
|
8
8
|
from ..main import Namespace as BaseNamespace
|
|
9
9
|
|
|
10
10
|
logger = logging.getLogger(__package__)
|
|
@@ -21,10 +21,6 @@ class Operation(BaseOperation):
|
|
|
21
21
|
pass
|
|
22
22
|
|
|
23
23
|
def run(self, args: Namespace) -> None:
|
|
24
|
-
|
|
25
|
-
api = ApiClient(
|
|
26
|
-
access_token=args.config["token"]["access_token"],
|
|
27
|
-
user_agent=args.config["user_agent"],
|
|
28
|
-
)
|
|
24
|
+
api = get_api(args)
|
|
29
25
|
result = api.get("/me")
|
|
30
26
|
print(json.dumps(result, ensure_ascii=True, indent=2, sort_keys=True))
|
|
@@ -51,8 +51,6 @@ class TelemetryClient:
|
|
|
51
51
|
:raises TelemetryError: Если произошла ошибка при отправке или декодировании JSON.
|
|
52
52
|
"""
|
|
53
53
|
url = urljoin(self.server_address, endpoint)
|
|
54
|
-
logger.debug(data)
|
|
55
|
-
|
|
56
54
|
try:
|
|
57
55
|
response = self.session.post(url, json=data)
|
|
58
56
|
# response.raise_for_status()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/__init__.py
RENAMED
|
File without changes
|
{hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/authorize.py
RENAMED
|
File without changes
|
{hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/refresh_token.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|