hh-applicant-tool 0.2.3__tar.gz → 0.2.5__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.2.3 → hh_applicant_tool-0.2.5}/PKG-INFO +5 -4
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/README.md +3 -3
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/constants.py +1 -1
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/main.py +9 -9
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/apply_similar.py +51 -19
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/pyproject.toml +1 -1
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/__init__.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/__main__.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/api/__init__.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/api/client.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/api/errors.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/color_log.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/__init__.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/add_handler.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/authorize.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/call_api.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/clear_negotiations.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/list_resumes.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/refresh_token.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/update_resumes.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/whoami.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/types.py +0 -0
- {hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/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.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Senior YAML Developer
|
|
6
6
|
Author-email: yamldeveloper@proton.me
|
|
@@ -9,6 +9,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.10
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.11
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
13
|
Requires-Dist: prettytable (>=3.6.0,<4.0.0)
|
|
13
14
|
Requires-Dist: requests (>=2.28.2,<3.0.0)
|
|
14
15
|
Description-Content-Type: text/markdown
|
|
@@ -24,7 +25,7 @@ Description-Content-Type: text/markdown
|
|
|
24
25
|
[]()
|
|
25
26
|
[]()
|
|
26
27
|
|
|
27
|
-
Утилита для автоматизации действий на HH.RU таких как рассылка откликов на подходящие вакансии и обновление всех резюме.
|
|
28
|
+
Утилита для автоматизации действий на HH.RU таких как рассылка откликов на подходящие вакансии и обновление всех резюме. Поддержка осуществляется строго в группе https://t.me/vaitishniki (в ней разрешены мат, п*рнография, оскорбления всех участников кроме админа, а так же слив любой информации про хуевых работодателей и нерадивых херок).
|
|
28
29
|
|
|
29
30
|
Системные требования:
|
|
30
31
|
|
|
@@ -43,11 +44,11 @@ yay -S socat
|
|
|
43
44
|

|
|
44
45
|
|
|
45
46
|
|
|
46
|
-
Данная утилита работает только в Linux. Для авторизации требуется наличие
|
|
47
|
+
Данная утилита работает только в Linux. Для авторизации требуется наличие какого-то окружения для взаимодейсвия с браузерос, после нее вы можете перенести `~/.config/hh-applicant-tool/config.json` на сервер и запускать утилиту через systemd или cron, либо же вообще делать это через **WSL**. Столь странный процесс связан с тем, что на странице авторизации запускается море скриптов, которые шифруют данные на клиенте перед отправкой на сервер, а так же выполняется куча запросов чтобы проверить не бот ли ты (хорошо, что после авторизации никаких проверок по факту нет, даже айпи не проверяется на соответсвие тому с какого была авторизация). В этой лапше мне лень разбираться, так же человек, вызвавшийся осилить этот процесс скрылся в закате, а мне и так нормально. Выходом могло бы стать использование безмозглого Хромиума, тогда можно было бы сделать авторизацию полностью через терминал без всяких socat'ов (если не блоканет, так как запущенный в таком режиме браузер все же можно определить). Так же при наличии рутованного телефона можно вытащить `access` и `refresh` токены из официального приложения и добавить их в конфиг.
|
|
47
48
|
|
|
48
49
|
Предыстория.
|
|
49
50
|
|
|
50
|
-
Был один знакомый знакомого, который работал хрюшей. Этот чувак не заморачивался с чтением резюме, а тупо скриптами рассылал предложения о работе... Бывают, конечно, филологини, которые не могут
|
|
51
|
+
Был один знакомый знакомого, который работал хрюшей. Этот чувак не заморачивался с чтением резюме, а тупо скриптами рассылал предложения о работе... Бывают, конечно, филологини, которые не могут отличить Java от JavaScript, но я думаю, что <s>в значительном числе случаев, тут имеют место такие вот рассылки</s> они просто идиотки... И я перенял эту порочную практику. Мне уже просто лень читать весь этот бред, что пишут в описании вакансий. Там стандартное ООП, алгоритмы и прочая хуета... Вроде все подходят, а вроде хз — все не мое. Поэтому тупло спамлю в надежде на идеальную работу. Долгое время (пару недель в октябре 2022) я делал массовые заявки с помощью консоли браузера:
|
|
51
52
|
|
|
52
53
|
```js
|
|
53
54
|
$$('[data-qa="vacancy-serp__vacancy_response"]').forEach((el) => el.click());
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[]()
|
|
10
10
|
[]()
|
|
11
11
|
|
|
12
|
-
Утилита для автоматизации действий на HH.RU таких как рассылка откликов на подходящие вакансии и обновление всех резюме.
|
|
12
|
+
Утилита для автоматизации действий на HH.RU таких как рассылка откликов на подходящие вакансии и обновление всех резюме. Поддержка осуществляется строго в группе https://t.me/vaitishniki (в ней разрешены мат, п*рнография, оскорбления всех участников кроме админа, а так же слив любой информации про хуевых работодателей и нерадивых херок).
|
|
13
13
|
|
|
14
14
|
Системные требования:
|
|
15
15
|
|
|
@@ -28,11 +28,11 @@ yay -S socat
|
|
|
28
28
|

|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
Данная утилита работает только в Linux. Для авторизации требуется наличие
|
|
31
|
+
Данная утилита работает только в Linux. Для авторизации требуется наличие какого-то окружения для взаимодейсвия с браузерос, после нее вы можете перенести `~/.config/hh-applicant-tool/config.json` на сервер и запускать утилиту через systemd или cron, либо же вообще делать это через **WSL**. Столь странный процесс связан с тем, что на странице авторизации запускается море скриптов, которые шифруют данные на клиенте перед отправкой на сервер, а так же выполняется куча запросов чтобы проверить не бот ли ты (хорошо, что после авторизации никаких проверок по факту нет, даже айпи не проверяется на соответсвие тому с какого была авторизация). В этой лапше мне лень разбираться, так же человек, вызвавшийся осилить этот процесс скрылся в закате, а мне и так нормально. Выходом могло бы стать использование безмозглого Хромиума, тогда можно было бы сделать авторизацию полностью через терминал без всяких socat'ов (если не блоканет, так как запущенный в таком режиме браузер все же можно определить). Так же при наличии рутованного телефона можно вытащить `access` и `refresh` токены из официального приложения и добавить их в конфиг.
|
|
32
32
|
|
|
33
33
|
Предыстория.
|
|
34
34
|
|
|
35
|
-
Был один знакомый знакомого, который работал хрюшей. Этот чувак не заморачивался с чтением резюме, а тупо скриптами рассылал предложения о работе... Бывают, конечно, филологини, которые не могут
|
|
35
|
+
Был один знакомый знакомого, который работал хрюшей. Этот чувак не заморачивался с чтением резюме, а тупо скриптами рассылал предложения о работе... Бывают, конечно, филологини, которые не могут отличить Java от JavaScript, но я думаю, что <s>в значительном числе случаев, тут имеют место такие вот рассылки</s> они просто идиотки... И я перенял эту порочную практику. Мне уже просто лень читать весь этот бред, что пишут в описании вакансий. Там стандартное ООП, алгоритмы и прочая хуета... Вроде все подходят, а вроде хз — все не мое. Поэтому тупло спамлю в надежде на идеальную работу. Долгое время (пару недель в октябре 2022) я делал массовые заявки с помощью консоли браузера:
|
|
36
36
|
|
|
37
37
|
```js
|
|
38
38
|
$$('[data-qa="vacancy-serp__vacancy_response"]').forEach((el) => el.click());
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
1
|
+
DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
|
|
2
2
|
ANDROID_CLIENT_ID = (
|
|
3
3
|
"HIOMIAS39CA9DICTA7JIO64LQKQJF5AGIK74G9ITJKLNEDAOH5FHS5G1JI7FOEGD"
|
|
4
4
|
)
|
|
@@ -14,7 +14,7 @@ from .color_log import ColorHandler
|
|
|
14
14
|
from .utils import Config
|
|
15
15
|
|
|
16
16
|
DEFAULT_CONFIG_PATH = (
|
|
17
|
-
Path(getenv("
|
|
17
|
+
Path(getenv("XDG_CONFIG_HOME", Path.home() / ".config"))
|
|
18
18
|
/ __package__.replace("_", "-")
|
|
19
19
|
/ "config.json"
|
|
20
20
|
)
|
|
@@ -23,12 +23,10 @@ logger = logging.getLogger(__package__)
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class BaseOperation(metaclass=ABCMeta):
|
|
26
|
-
def setup_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
27
|
-
...
|
|
26
|
+
def setup_parser(self, parser: argparse.ArgumentParser) -> None: ...
|
|
28
27
|
|
|
29
28
|
@abstractmethod
|
|
30
|
-
def run(self, args: argparse.Namespace) -> None | int:
|
|
31
|
-
...
|
|
29
|
+
def run(self, args: argparse.Namespace) -> None | int: ...
|
|
32
30
|
|
|
33
31
|
|
|
34
32
|
OPERATIONS = "operations"
|
|
@@ -41,12 +39,16 @@ class Namespace(argparse.Namespace):
|
|
|
41
39
|
|
|
42
40
|
class HHApplicantTool:
|
|
43
41
|
"""Утилита для автоматизации действий соискателя на сайте hh.ru.
|
|
44
|
-
|
|
42
|
+
|
|
43
|
+
Исходники и предложения: <https://github.com/s3rgeym/hh-applicant-tool>
|
|
44
|
+
|
|
45
|
+
Группа поддержки: <https://t.me/vaitishniki>
|
|
45
46
|
"""
|
|
46
47
|
|
|
47
48
|
def create_parser(self) -> argparse.ArgumentParser:
|
|
48
49
|
parser = argparse.ArgumentParser(
|
|
49
50
|
description=self.__doc__,
|
|
51
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
50
52
|
)
|
|
51
53
|
parser.add_argument(
|
|
52
54
|
"-c",
|
|
@@ -82,9 +84,7 @@ class HHApplicantTool:
|
|
|
82
84
|
logger.setLevel(log_level)
|
|
83
85
|
handler = ColorHandler()
|
|
84
86
|
# [C] Critical Error Occurred
|
|
85
|
-
handler.setFormatter(
|
|
86
|
-
logging.Formatter("[%(levelname).1s] %(message)s")
|
|
87
|
-
)
|
|
87
|
+
handler.setFormatter(logging.Formatter("[%(levelname).1s] %(message)s"))
|
|
88
88
|
logger.addHandler(handler)
|
|
89
89
|
if args.run:
|
|
90
90
|
try:
|
{hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/apply_similar.py
RENAMED
|
@@ -87,11 +87,22 @@ class Operation(BaseOperation):
|
|
|
87
87
|
page_min_interval, page_max_interval = args.page_interval
|
|
88
88
|
|
|
89
89
|
self._apply_similar(
|
|
90
|
-
api,
|
|
90
|
+
api,
|
|
91
|
+
resume_id,
|
|
92
|
+
args.force_message,
|
|
93
|
+
application_messages,
|
|
94
|
+
apply_min_interval,
|
|
95
|
+
apply_max_interval,
|
|
96
|
+
page_min_interval,
|
|
97
|
+
page_max_interval,
|
|
91
98
|
)
|
|
92
99
|
|
|
93
100
|
def _get_vacancies(
|
|
94
|
-
self,
|
|
101
|
+
self,
|
|
102
|
+
api: ApiClient,
|
|
103
|
+
resume_id: str,
|
|
104
|
+
page_min_interval: float,
|
|
105
|
+
page_max_interval: float,
|
|
95
106
|
) -> list[VacancyItem]:
|
|
96
107
|
rv = []
|
|
97
108
|
per_page = 100
|
|
@@ -125,24 +136,45 @@ class Operation(BaseOperation):
|
|
|
125
136
|
page_max_interval: float,
|
|
126
137
|
) -> None:
|
|
127
138
|
item: VacancyItem
|
|
128
|
-
for item in self._get_vacancies(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
# Задержка перед отправкой отклика
|
|
133
|
-
interval = random.uniform(apply_min_interval, apply_max_interval)
|
|
134
|
-
time.sleep(interval)
|
|
135
|
-
|
|
136
|
-
params = {
|
|
137
|
-
"resume_id": resume_id,
|
|
138
|
-
"vacancy_id": item["id"],
|
|
139
|
-
"message": (
|
|
140
|
-
random.choice(application_messages) % item
|
|
141
|
-
if force_message or item["response_letter_required"]
|
|
142
|
-
else ""
|
|
143
|
-
),
|
|
144
|
-
}
|
|
139
|
+
for item in self._get_vacancies(
|
|
140
|
+
api, resume_id, page_min_interval, page_max_interval
|
|
141
|
+
):
|
|
145
142
|
try:
|
|
143
|
+
if item["has_test"]:
|
|
144
|
+
print("Пропускаем тест", item["alternate_url"])
|
|
145
|
+
continue
|
|
146
|
+
|
|
147
|
+
relations = item.get("relations", [])
|
|
148
|
+
|
|
149
|
+
# Там черезжопно нужно хеш отклика получать чтобы его отменить
|
|
150
|
+
# if "got_response" in relations:
|
|
151
|
+
# # Тупая пизда ее даже не рассматривала
|
|
152
|
+
# print(
|
|
153
|
+
# "Отменяем заявку чтобы отправить ее снова",
|
|
154
|
+
# item["alternate_url"],
|
|
155
|
+
# )
|
|
156
|
+
# api.delete(f"/negotiations/active/{item['id']}")
|
|
157
|
+
# elif relations:
|
|
158
|
+
if relations:
|
|
159
|
+
print("Пропускаем ответ на заявку", item["alternate_url"])
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
# Задержка перед отправкой отклика
|
|
163
|
+
interval = random.uniform(
|
|
164
|
+
apply_min_interval, apply_max_interval
|
|
165
|
+
)
|
|
166
|
+
time.sleep(interval)
|
|
167
|
+
|
|
168
|
+
params = {
|
|
169
|
+
"resume_id": resume_id,
|
|
170
|
+
"vacancy_id": item["id"],
|
|
171
|
+
"message": (
|
|
172
|
+
random.choice(application_messages) % item
|
|
173
|
+
if force_message or item["response_letter_required"]
|
|
174
|
+
else ""
|
|
175
|
+
),
|
|
176
|
+
}
|
|
177
|
+
|
|
146
178
|
res = api.post("/negotiations", params)
|
|
147
179
|
assert res == {}
|
|
148
180
|
print(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/__init__.py
RENAMED
|
File without changes
|
{hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/add_handler.py
RENAMED
|
File without changes
|
{hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/authorize.py
RENAMED
|
File without changes
|
{hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/call_api.py
RENAMED
|
File without changes
|
|
File without changes
|
{hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/list_resumes.py
RENAMED
|
File without changes
|
{hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/refresh_token.py
RENAMED
|
File without changes
|
{hh_applicant_tool-0.2.3 → hh_applicant_tool-0.2.5}/hh_applicant_tool/operations/update_resumes.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|