hh-applicant-tool 0.3.8__tar.gz → 0.4.0__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.8 → hh_applicant_tool-0.4.0}/PKG-INFO +6 -5
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/README.md +5 -4
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/api/client.py +0 -2
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/main.py +7 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/apply_similar.py +24 -11
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/telemetry_client.py +29 -22
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/pyproject.toml +1 -1
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/__init__.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/__main__.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/api/__init__.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/api/errors.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/color_log.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/constants.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/__init__.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/authorize.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/call_api.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/clear_negotiations.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/list_resumes.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/refresh_token.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/update_resumes.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/whoami.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/types.py +0 -0
- {hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/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
|
+
Version: 0.4.0
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Senior YAML Developer
|
|
6
6
|
Author-email: yamldeveloper@proton.me
|
|
@@ -171,6 +171,7 @@ $ hh-applicant-tool refresh-token
|
|
|
171
171
|
Через конфиг можно задать дополнительные настройки:
|
|
172
172
|
|
|
173
173
|
| Имя атрибута | Описание |
|
|
174
|
+
| --- | --- |
|
|
174
175
|
| `user_agent` | Кастомный юзерагент, передаваемый при кажом запросе, например, `Mozilla/5.0 YablanBrowser` |
|
|
175
176
|
| `proxy_url` | Прокси, используемый для всех запросов, например, `socks5h://127.0.0.1:9050` |
|
|
176
177
|
| `reply_message` | Сообщение для ответа работодателю при отклике на вакансии, см. формат сообщений |
|
|
@@ -283,6 +284,8 @@ https://hh.ru/employer/1918903
|
|
|
283
284
|
|
|
284
285
|
### Сбор данных
|
|
285
286
|
|
|
287
|
+
> Данный функционал можно отключить с помощью специльного флага, но ради котят и из-за ненависти к херкам не делайте этого!
|
|
288
|
+
|
|
286
289
|
Утилита собирает и передает на сервер разработчика следующую информацию:
|
|
287
290
|
|
|
288
291
|
1. Название вакансии.
|
|
@@ -296,11 +299,9 @@ https://hh.ru/employer/1918903
|
|
|
296
299
|
1. Название компании.
|
|
297
300
|
1. Тип компании.
|
|
298
301
|
1. Описание компании.
|
|
299
|
-
1. Ссылка на сайт компании.
|
|
302
|
+
1. Ссылка на сайт компании.
|
|
300
303
|
1. Город, в котором находится компания.
|
|
301
304
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
!!! УТИЛИТА НЕ СОБИРАЕТ НИКАКИХ ПЕРСОНАЛЬНЫХ ДАННЫХ ПОЛЬЗОВАТЕЛЕЙ (IP ТОЖЕ НЕ СОХРАНЯЕТ) — ТОЛЬКО ДАННЫЕ ВСЯКИХ РАБОТАДАТЕЛЕЙ И ИХ ОВЧАРОК. ТАК ЖЕ Я ОБЕЩАЮ, ЧТО УТИЛИТА ВСЕГДА БУДЕТ БЕСПЛАТНОЙ, ВСЕ КТО ЕЮ ПЫТАЮТСЯ ТОРГОВАТЬ — УЕБКИ И У НИХ ПОЧЕРНЕЕТ И ОТВАЛИТСЯ ХУЙ. ЕДИНСТВЕННАЯ ПЛАТА ЗА ЕЕ ИСПОЛЬЗОВАНИЕ — ЭТО ПОМОЩЬ В ПАРСИНГЕ САЙТА HEADHUNTER (МЕНЯ ИНТЕРЕСУЕТ ЕГО БАЗА КОМПАНИЙ)
|
|
305
|
+
**УТИЛИТА НЕ ПЕРЕДАЕТ НИКАКИХ ПЕРСОНАЛЬНЫХ ДАННЫХ ПОЛЬЗОВАТЕЛЕЙ — ТОЛЬКО ДАННЫЕ ВСЯКИХ РАБОТАДАТЕЛЕЙ И ИХ ОВЧАРОК. СЕРВЕР НЕ ХРАНИТ IP ОТПРАВИТЕЛЯ. ЛОГИ НА СЕРВЕРЕ НЕ ВЕДУТСЯ. ТАК ЖЕ Я ОБЕЩАЮ, ЧТО УТИЛИТА ВСЕГДА БУДЕТ БЕСПЛАТНОЙ, ВСЕ КТО ЕЮ ПЫТАЮТСЯ ТОРГОВАТЬ — УЕБКИ, И У НИХ ПОЧЕРНЕЕТ И ОТВАЛИТСЯ ХУЙ. ЕДИНСТВЕННАЯ ПЛАТА ЗА ЕЕ ИСПОЛЬЗОВАНИЕ — ЭТО ПОМОЩЬ В ПАРСИНГЕ САЙТА HEADHUNTER (МЕНЯ ИНТЕРЕСУЕТ ЕГО БАЗА КОМПАНИЙ).**
|
|
305
306
|
|
|
306
307
|
|
|
@@ -152,6 +152,7 @@ $ hh-applicant-tool refresh-token
|
|
|
152
152
|
Через конфиг можно задать дополнительные настройки:
|
|
153
153
|
|
|
154
154
|
| Имя атрибута | Описание |
|
|
155
|
+
| --- | --- |
|
|
155
156
|
| `user_agent` | Кастомный юзерагент, передаваемый при кажом запросе, например, `Mozilla/5.0 YablanBrowser` |
|
|
156
157
|
| `proxy_url` | Прокси, используемый для всех запросов, например, `socks5h://127.0.0.1:9050` |
|
|
157
158
|
| `reply_message` | Сообщение для ответа работодателю при отклике на вакансии, см. формат сообщений |
|
|
@@ -264,6 +265,8 @@ https://hh.ru/employer/1918903
|
|
|
264
265
|
|
|
265
266
|
### Сбор данных
|
|
266
267
|
|
|
268
|
+
> Данный функционал можно отключить с помощью специльного флага, но ради котят и из-за ненависти к херкам не делайте этого!
|
|
269
|
+
|
|
267
270
|
Утилита собирает и передает на сервер разработчика следующую информацию:
|
|
268
271
|
|
|
269
272
|
1. Название вакансии.
|
|
@@ -277,10 +280,8 @@ https://hh.ru/employer/1918903
|
|
|
277
280
|
1. Название компании.
|
|
278
281
|
1. Тип компании.
|
|
279
282
|
1. Описание компании.
|
|
280
|
-
1. Ссылка на сайт компании.
|
|
283
|
+
1. Ссылка на сайт компании.
|
|
281
284
|
1. Город, в котором находится компания.
|
|
282
285
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
!!! УТИЛИТА НЕ СОБИРАЕТ НИКАКИХ ПЕРСОНАЛЬНЫХ ДАННЫХ ПОЛЬЗОВАТЕЛЕЙ (IP ТОЖЕ НЕ СОХРАНЯЕТ) — ТОЛЬКО ДАННЫЕ ВСЯКИХ РАБОТАДАТЕЛЕЙ И ИХ ОВЧАРОК. ТАК ЖЕ Я ОБЕЩАЮ, ЧТО УТИЛИТА ВСЕГДА БУДЕТ БЕСПЛАТНОЙ, ВСЕ КТО ЕЮ ПЫТАЮТСЯ ТОРГОВАТЬ — УЕБКИ И У НИХ ПОЧЕРНЕЕТ И ОТВАЛИТСЯ ХУЙ. ЕДИНСТВЕННАЯ ПЛАТА ЗА ЕЕ ИСПОЛЬЗОВАНИЕ — ЭТО ПОМОЩЬ В ПАРСИНГЕ САЙТА HEADHUNTER (МЕНЯ ИНТЕРЕСУЕТ ЕГО БАЗА КОМПАНИЙ)
|
|
286
|
+
**УТИЛИТА НЕ ПЕРЕДАЕТ НИКАКИХ ПЕРСОНАЛЬНЫХ ДАННЫХ ПОЛЬЗОВАТЕЛЕЙ — ТОЛЬКО ДАННЫЕ ВСЯКИХ РАБОТАДАТЕЛЕЙ И ИХ ОВЧАРОК. СЕРВЕР НЕ ХРАНИТ IP ОТПРАВИТЕЛЯ. ЛОГИ НА СЕРВЕРЕ НЕ ВЕДУТСЯ. ТАК ЖЕ Я ОБЕЩАЮ, ЧТО УТИЛИТА ВСЕГДА БУДЕТ БЕСПЛАТНОЙ, ВСЕ КТО ЕЮ ПЫТАЮТСЯ ТОРГОВАТЬ — УЕБКИ, И У НИХ ПОЧЕРНЕЕТ И ОТВАЛИТСЯ ХУЙ. ЕДИНСТВЕННАЯ ПЛАТА ЗА ЕЕ ИСПОЛЬЗОВАНИЕ — ЭТО ПОМОЩЬ В ПАРСИНГЕ САЙТА HEADHUNTER (МЕНЯ ИНТЕРЕСУЕТ ЕГО БАЗА КОМПАНИЙ).**
|
|
286
287
|
|
|
@@ -102,8 +102,6 @@ class BaseClient:
|
|
|
102
102
|
logger.debug("wait %fs before request", delay)
|
|
103
103
|
time.sleep(delay)
|
|
104
104
|
has_body = method in ["POST", "PUT"]
|
|
105
|
-
user_agent = self.user_agent or self.default_user_agent()
|
|
106
|
-
logger.debug(f"{user_agent = }")
|
|
107
105
|
response = self.session.request(
|
|
108
106
|
method,
|
|
109
107
|
url,
|
|
@@ -36,6 +36,7 @@ class Namespace(argparse.Namespace):
|
|
|
36
36
|
delay: float
|
|
37
37
|
user_agent: str
|
|
38
38
|
proxy_url: str
|
|
39
|
+
disable_telemetry: bool
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
def get_proxies(args: Namespace) -> dict[Literal["http", "https"], str | None]:
|
|
@@ -103,6 +104,12 @@ class HHApplicantTool:
|
|
|
103
104
|
parser.add_argument(
|
|
104
105
|
"--proxy-url", help="Прокси, используемый для запросов к API"
|
|
105
106
|
)
|
|
107
|
+
parser.add_argument(
|
|
108
|
+
"--disable-telemetry",
|
|
109
|
+
default=False,
|
|
110
|
+
action=argparse.BooleanOptionalAction,
|
|
111
|
+
help="Отключить телеметрию",
|
|
112
|
+
)
|
|
106
113
|
subparsers = parser.add_subparsers(help="commands")
|
|
107
114
|
package_dir = Path(__file__).resolve().parent / OPERATIONS
|
|
108
115
|
for _, module_name, _ in iter_modules([str(package_dir)]):
|
{hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/apply_similar.py
RENAMED
|
@@ -99,6 +99,20 @@ class Operation(BaseOperation):
|
|
|
99
99
|
return min(min_interval, max_interval), max(min_interval, max_interval)
|
|
100
100
|
|
|
101
101
|
def run(self, args: Namespace) -> None:
|
|
102
|
+
self.enable_telemetry = True
|
|
103
|
+
if args.disable_telemetry:
|
|
104
|
+
print(
|
|
105
|
+
"👁️ Телеметрия используется только для сбора данных о работодателях и их вакансиях, персональные данные пользователей не передаются на сервер."
|
|
106
|
+
)
|
|
107
|
+
if (
|
|
108
|
+
input("Вы действительно хотите отключить телеметрию (д/Н)? ")
|
|
109
|
+
.lower()
|
|
110
|
+
.startswith(("д", "y"))
|
|
111
|
+
):
|
|
112
|
+
self.enable_telemetry = False
|
|
113
|
+
logger.info("Телеметрия отключена")
|
|
114
|
+
else:
|
|
115
|
+
logger.info("Телеметрия включена")
|
|
102
116
|
api = get_api(args)
|
|
103
117
|
resume_id = self._get_resume_id(args, api)
|
|
104
118
|
application_messages = self._get_application_messages(args)
|
|
@@ -156,14 +170,10 @@ class Operation(BaseOperation):
|
|
|
156
170
|
message_min_interval: float,
|
|
157
171
|
message_max_interval: float,
|
|
158
172
|
order_by: str,
|
|
159
|
-
search: str | None
|
|
160
|
-
reply_message: str | None
|
|
173
|
+
search: str | None,
|
|
174
|
+
reply_message: str | None,
|
|
161
175
|
) -> None:
|
|
162
|
-
|
|
163
|
-
session = Session()
|
|
164
|
-
session.headers["User-Agent"] = "Mozilla/5.0 (HHApplicantTelemetry/1.0)"
|
|
165
|
-
session.proxies = dict(api.session.proxies)
|
|
166
|
-
telemetry_client = TelemetryClient(session=session)
|
|
176
|
+
telemetry_client = TelemetryClient(proxies=api.proxies)
|
|
167
177
|
telemetry_data = defaultdict(dict)
|
|
168
178
|
|
|
169
179
|
vacancies = self._get_vacancies(
|
|
@@ -176,7 +186,8 @@ class Operation(BaseOperation):
|
|
|
176
186
|
search=search,
|
|
177
187
|
)
|
|
178
188
|
|
|
179
|
-
self.
|
|
189
|
+
if self.enable_telemetry:
|
|
190
|
+
self._collect_vacancy_telemetry(telemetry_data, vacancies)
|
|
180
191
|
|
|
181
192
|
me = api.get("/me")
|
|
182
193
|
|
|
@@ -291,7 +302,8 @@ class Operation(BaseOperation):
|
|
|
291
302
|
employer_id = vacancy.get("employer", {}).get("id")
|
|
292
303
|
|
|
293
304
|
if (
|
|
294
|
-
|
|
305
|
+
self.enable_telemetry
|
|
306
|
+
and employer_id
|
|
295
307
|
and employer_id not in telemetry_data["employers"]
|
|
296
308
|
and 200 > len(telemetry_data["employers"])
|
|
297
309
|
):
|
|
@@ -350,8 +362,9 @@ class Operation(BaseOperation):
|
|
|
350
362
|
|
|
351
363
|
print("📝 Отклики на вакансии разосланы!")
|
|
352
364
|
|
|
353
|
-
|
|
354
|
-
|
|
365
|
+
if self.enable_telemetry:
|
|
366
|
+
# Я собираюсь выложить контакты херок в общественный доступ
|
|
367
|
+
self._send_telemetry(telemetry_client, telemetry_data)
|
|
355
368
|
|
|
356
369
|
def _get_vacancies(
|
|
357
370
|
self,
|
|
@@ -4,7 +4,10 @@ from urllib.parse import urljoin
|
|
|
4
4
|
import requests
|
|
5
5
|
from typing import Optional, Dict, Any
|
|
6
6
|
import logging
|
|
7
|
-
import
|
|
7
|
+
from functools import partialmethod
|
|
8
|
+
import warnings
|
|
9
|
+
|
|
10
|
+
warnings.filterwarnings('ignore', message='Unverified HTTPS request')
|
|
8
11
|
|
|
9
12
|
logger = logging.getLogger(__package__)
|
|
10
13
|
|
|
@@ -18,40 +21,42 @@ class TelemetryError(Exception):
|
|
|
18
21
|
class TelemetryClient:
|
|
19
22
|
"""Клиент для отправки телеметрии на сервер."""
|
|
20
23
|
|
|
21
|
-
server_address =
|
|
22
|
-
"aHR0cDovLzMxLjEzMS4yNTEuMTA3OjU0MTU2"
|
|
23
|
-
).decode()
|
|
24
|
+
server_address: str = "https://hh-applicant-tool.mooo.com:54157/"
|
|
24
25
|
|
|
25
26
|
def __init__(
|
|
26
27
|
self,
|
|
27
28
|
server_address: Optional[str] = None,
|
|
29
|
+
*,
|
|
28
30
|
session: Optional[requests.Session] = None,
|
|
31
|
+
user_agent: str = "Mozilla/5.0 (HHApplicantTelemetry/1.0)",
|
|
32
|
+
proxies: dict | None = None,
|
|
29
33
|
) -> None:
|
|
30
|
-
"""
|
|
31
|
-
Инициализация клиента.
|
|
32
|
-
|
|
33
|
-
:param server_address: Адрес сервера для отправки телеметрии.
|
|
34
|
-
:param session: Сессия для повторного использования соединения.
|
|
35
|
-
"""
|
|
36
|
-
self.session = session or requests.Session()
|
|
37
34
|
self.server_address = os.getenv(
|
|
38
35
|
"TELEMETRY_SERVER", server_address or self.server_address
|
|
39
36
|
)
|
|
37
|
+
self.session = session or requests.Session()
|
|
38
|
+
self.user_agent = user_agent
|
|
39
|
+
self.proxies = proxies
|
|
40
40
|
|
|
41
|
-
def
|
|
42
|
-
self,
|
|
41
|
+
def request(
|
|
42
|
+
self,
|
|
43
|
+
method: str,
|
|
44
|
+
endpoint: str,
|
|
45
|
+
data: Dict[str, Any] | None = None,
|
|
43
46
|
) -> Dict[str, Any]:
|
|
44
|
-
|
|
45
|
-
Отправка телеметрии на сервер.
|
|
46
|
-
|
|
47
|
-
:param endpoint: Конечная точка на сервере.
|
|
48
|
-
:param data: Данные для отправки.
|
|
49
|
-
:return: Ответ сервера в формате JSON.
|
|
50
|
-
:raises TelemetryError: Если произошла ошибка при отправке или декодировании JSON.
|
|
51
|
-
"""
|
|
47
|
+
method = method.upper()
|
|
52
48
|
url = urljoin(self.server_address, endpoint)
|
|
49
|
+
has_body = method in ["POST", "PUT", "PATCH"]
|
|
53
50
|
try:
|
|
54
|
-
response = self.session.
|
|
51
|
+
response = self.session.request(
|
|
52
|
+
method,
|
|
53
|
+
url,
|
|
54
|
+
headers={"User-Agent": self.user_agent},
|
|
55
|
+
proxies=self.proxies,
|
|
56
|
+
params=data if not has_body else None,
|
|
57
|
+
json=data if has_body else None,
|
|
58
|
+
verify=False, # Игнорирование истекшего сертификата
|
|
59
|
+
)
|
|
55
60
|
# response.raise_for_status()
|
|
56
61
|
result = response.json()
|
|
57
62
|
if "error" in result:
|
|
@@ -63,3 +68,5 @@ class TelemetryClient:
|
|
|
63
68
|
json.JSONDecodeError,
|
|
64
69
|
) as ex:
|
|
65
70
|
raise TelemetryError(str(ex)) from ex
|
|
71
|
+
|
|
72
|
+
send_telemetry = partialmethod(request, "POST")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/__init__.py
RENAMED
|
File without changes
|
{hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/authorize.py
RENAMED
|
File without changes
|
{hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/call_api.py
RENAMED
|
File without changes
|
|
File without changes
|
{hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/list_resumes.py
RENAMED
|
File without changes
|
{hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/refresh_token.py
RENAMED
|
File without changes
|
{hh_applicant_tool-0.3.8 → hh_applicant_tool-0.4.0}/hh_applicant_tool/operations/update_resumes.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|