hh-applicant-tool 0.5.9__tar.gz → 0.6.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.

Files changed (32) hide show
  1. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/PKG-INFO +8 -8
  2. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/README.md +7 -7
  3. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/main.py +1 -2
  4. hh_applicant_tool-0.6.0/hh_applicant_tool/operations/get_employer_contacts.py +294 -0
  5. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/pyproject.toml +1 -1
  6. hh_applicant_tool-0.5.9/hh_applicant_tool/operations/get_employer_contacts.py +0 -87
  7. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/__init__.py +0 -0
  8. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/__main__.py +0 -0
  9. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/ai/__init__.py +0 -0
  10. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/ai/blackbox.py +0 -0
  11. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/api/__init__.py +0 -0
  12. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/api/client.py +0 -0
  13. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/api/errors.py +0 -0
  14. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/color_log.py +0 -0
  15. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/constants.py +0 -0
  16. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/jsonc.py +0 -0
  17. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/mixins.py +0 -0
  18. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/operations/__init__.py +0 -0
  19. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/operations/apply_similar.py +0 -0
  20. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/operations/authorize.py +0 -0
  21. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/operations/call_api.py +0 -0
  22. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/operations/clear_negotiations.py +0 -0
  23. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/operations/config.py +0 -0
  24. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/operations/delete_telemetry.py +0 -0
  25. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/operations/list_resumes.py +0 -0
  26. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/operations/refresh_token.py +0 -0
  27. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/operations/reply_employers.py +0 -0
  28. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/operations/update_resumes.py +0 -0
  29. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/operations/whoami.py +0 -0
  30. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/telemetry_client.py +0 -0
  31. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/types.py +0 -0
  32. {hh_applicant_tool-0.5.9 → hh_applicant_tool-0.6.0}/hh_applicant_tool/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: hh-applicant-tool
3
- Version: 0.5.9
3
+ Version: 0.6.0
4
4
  Summary:
5
5
  Author: Senior YAML Developer
6
6
  Author-email: yamldeveloper@proton.me
@@ -30,11 +30,6 @@ Description-Content-Type: text/markdown
30
30
  <img src="https://github.com/user-attachments/assets/29d91490-2c83-4e3f-a573-c7a6182a4044" width="500">
31
31
  </div>
32
32
 
33
- ### Внимание!!!
34
-
35
- Если для Вас проблема установить данную утилиту, лень разбираться с ее настройкой, то Вы можете установить мое приложение под **Android** [HH Resume Automate](https://github.com/s3rgeym/hh-resume-automate/).
36
-
37
-
38
33
  ### Описание
39
34
 
40
35
  > Утилита для генерации сопроводительного письма может использовать AI
@@ -53,6 +48,10 @@ asdf/pyenv/conda и что-то еще. В школотронской Manjaro и
53
48
 
54
49
  > Если в веб-интерфейсе выставить фильтры, то они будут применяться в скрипте при отклике на подходящие
55
50
 
51
+ ### Внимание!!!
52
+
53
+ Если для Вас проблема установить данную утилиту, лень разбираться с ее настройкой, то Вы можете установить мое приложение под **Android** [HH Resume Automate](https://github.com/s3rgeym/hh-resume-automate/). Оно обладает минимальным функционалом: обновление резюме (одного) и рассылка откликов (чистить их и тп нельзя).
54
+
56
55
  ### Предыстория
57
56
 
58
57
  Долгое время я делал массовые заявки с помощью консоли браузера:
@@ -184,7 +183,7 @@ hh-applicant-tool config -p
184
183
 
185
184
  | Имя атрибута | Описание |
186
185
  | --- | --- |
187
- | `user_agent` | Кастомный юзерагент, передаваемый при кажом запросе, например, `Mozilla/5.0 YablanBrowser` |
186
+ | `user_agent` | Кастомный юзерагент, передаваемый при кажом запросе. |
188
187
  | `proxy_url` | Прокси, используемый для всех запросов, например, `socks5h://127.0.0.1:9050` |
189
188
  | `reply_message` | Сообщение для ответа работодателю при отклике на вакансии, см. формат сообщений |
190
189
 
@@ -257,7 +256,8 @@ https://hh.ru/employer/1918903
257
256
  | **call-api** | Вызов произвольного метода API с выводом результата. |
258
257
  | **refresh-token** | Обновляет access_token. |
259
258
  | **config** | Редактировать конфигурационный файл. |
260
- | **get-employer-contacts** | Получить список контактов работадателей. |
259
+ | **get-employer-contacts** | Получить список полученных вами контактов работодателей. Поддерживается так же экспорт в html/jsonl. Если хотите собирать контакты с нескольких акков, то укажите им одинаковый `client_telemetry_id` в конфигах. |
260
+ | **delete-telemetry** | Удадяет телеметрию, если та была включена. |
261
261
 
262
262
  ### Формат текста сообщений
263
263
 
@@ -11,11 +11,6 @@
11
11
  <img src="https://github.com/user-attachments/assets/29d91490-2c83-4e3f-a573-c7a6182a4044" width="500">
12
12
  </div>
13
13
 
14
- ### Внимание!!!
15
-
16
- Если для Вас проблема установить данную утилиту, лень разбираться с ее настройкой, то Вы можете установить мое приложение под **Android** [HH Resume Automate](https://github.com/s3rgeym/hh-resume-automate/).
17
-
18
-
19
14
  ### Описание
20
15
 
21
16
  > Утилита для генерации сопроводительного письма может использовать AI
@@ -34,6 +29,10 @@ asdf/pyenv/conda и что-то еще. В школотронской Manjaro и
34
29
 
35
30
  > Если в веб-интерфейсе выставить фильтры, то они будут применяться в скрипте при отклике на подходящие
36
31
 
32
+ ### Внимание!!!
33
+
34
+ Если для Вас проблема установить данную утилиту, лень разбираться с ее настройкой, то Вы можете установить мое приложение под **Android** [HH Resume Automate](https://github.com/s3rgeym/hh-resume-automate/). Оно обладает минимальным функционалом: обновление резюме (одного) и рассылка откликов (чистить их и тп нельзя).
35
+
37
36
  ### Предыстория
38
37
 
39
38
  Долгое время я делал массовые заявки с помощью консоли браузера:
@@ -165,7 +164,7 @@ hh-applicant-tool config -p
165
164
 
166
165
  | Имя атрибута | Описание |
167
166
  | --- | --- |
168
- | `user_agent` | Кастомный юзерагент, передаваемый при кажом запросе, например, `Mozilla/5.0 YablanBrowser` |
167
+ | `user_agent` | Кастомный юзерагент, передаваемый при кажом запросе. |
169
168
  | `proxy_url` | Прокси, используемый для всех запросов, например, `socks5h://127.0.0.1:9050` |
170
169
  | `reply_message` | Сообщение для ответа работодателю при отклике на вакансии, см. формат сообщений |
171
170
 
@@ -238,7 +237,8 @@ https://hh.ru/employer/1918903
238
237
  | **call-api** | Вызов произвольного метода API с выводом результата. |
239
238
  | **refresh-token** | Обновляет access_token. |
240
239
  | **config** | Редактировать конфигурационный файл. |
241
- | **get-employer-contacts** | Получить список контактов работадателей. |
240
+ | **get-employer-contacts** | Получить список полученных вами контактов работодателей. Поддерживается так же экспорт в html/jsonl. Если хотите собирать контакты с нескольких акков, то укажите им одинаковый `client_telemetry_id` в конфигах. |
241
+ | **delete-telemetry** | Удадяет телеметрию, если та была включена. |
242
242
 
243
243
  ### Формат текста сообщений
244
244
 
@@ -11,9 +11,8 @@ from typing import Literal, Sequence
11
11
 
12
12
  from .api import ApiClient
13
13
  from .color_log import ColorHandler
14
- from .utils import Config, get_config_path
15
14
  from .telemetry_client import TelemetryClient
16
-
15
+ from .utils import Config, get_config_path
17
16
 
18
17
  DEFAULT_CONFIG_PATH = (
19
18
  get_config_path() / (__package__ or "").replace("_", "-") / "config.json"
@@ -0,0 +1,294 @@
1
+ import argparse
2
+ import logging
3
+ from os import getenv
4
+ import pathlib
5
+ from ..main import BaseOperation
6
+ from ..main import Namespace as BaseNamespace
7
+ from ..telemetry_client import TelemetryClient
8
+
9
+ logger = logging.getLogger(__package__)
10
+
11
+
12
+ class Namespace(BaseNamespace):
13
+ username: str | None
14
+ password: str | None
15
+ search: str | None
16
+ export: bool
17
+
18
+
19
+ class Operation(BaseOperation):
20
+ """Выведет контакты работодателей, которые высылали вам приглашения"""
21
+
22
+ def setup_parser(self, parser: argparse.ArgumentParser) -> None:
23
+ # parser.add_argument(
24
+ # "-u",
25
+ # "--username",
26
+ # type=str,
27
+ # help="Имя пользователя для аутентификации",
28
+ # default=getenv("AUTH_USERNAME"),
29
+ # )
30
+ # parser.add_argument(
31
+ # "-P",
32
+ # "--password",
33
+ # type=str,
34
+ # help="Пароль для аутентификации",
35
+ # default=getenv("AUTH_PASSWORD"),
36
+ # )
37
+ parser.add_argument(
38
+ "-s",
39
+ "--search",
40
+ type=str,
41
+ default="",
42
+ help="Строка поиска контактов работодателя (email, имя, название компании)",
43
+ )
44
+ parser.add_argument(
45
+ "-p",
46
+ "--page",
47
+ default=1,
48
+ help="Номер страницы в выдаче. Игнорируется при экспорте.",
49
+ )
50
+ parser.add_argument(
51
+ "--export",
52
+ action=argparse.BooleanOptionalAction,
53
+ default=False,
54
+ help="Экспортировать контакты работодателей.",
55
+ )
56
+ parser.add_argument(
57
+ "-f",
58
+ "--format",
59
+ default="html",
60
+ choices=["html", "jsonl"],
61
+ help="Формат вывода",
62
+ )
63
+
64
+ def run(self, args: Namespace, _, telemetry_client: TelemetryClient) -> None:
65
+ if args.export:
66
+ contact_persons = []
67
+ page = 1
68
+ per_page = 100
69
+ while True:
70
+ res = telemetry_client.get_telemetry(
71
+ "/contact/persons",
72
+ {"search": args.search, "per_page": per_page, "page": page},
73
+ )
74
+ assert "contact_persons" in res
75
+ contact_persons += res["contact_persons"]
76
+ if per_page * page >= res["total"]:
77
+ break
78
+ page += 1
79
+ if args.format == "jsonl":
80
+ import json, sys
81
+
82
+ for contact in contact_persons:
83
+ json.dump(contact, sys.stdout, ensure_ascii=False)
84
+ sys.stdout.write("\n")
85
+ sys.stdout.flush()
86
+ else:
87
+ print(generate_html_report(contact_persons))
88
+ return
89
+
90
+ res = telemetry_client.get_telemetry(
91
+ "/contact/persons",
92
+ {"search": args.search, "per_page": 10, "page": args.page},
93
+ )
94
+ if "contact_persons" not in res:
95
+ print("❌", res)
96
+ return 1
97
+
98
+ print(
99
+ "Тут отображаются только данные, собранные с вашего telemetry_client_id. Вы так же можете их удалить с помощью команды delete-telemetry."
100
+ )
101
+ print()
102
+
103
+ print_contacts(res)
104
+
105
+
106
+ def generate_html_report(data: list[dict]) -> str:
107
+ """
108
+ Генерирует HTML-отчет на основе предоставленных данных.
109
+ """
110
+ html_content = """
111
+ <!DOCTYPE html>
112
+ <html lang="ru">
113
+ <head>
114
+ <meta charset="UTF-8">
115
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
116
+ <title>Отчет о сотрудниках и компаниях</title>
117
+ <style>
118
+ body {
119
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
120
+ margin: 20px;
121
+ background-color: #f4f7f6;
122
+ color: #333;
123
+ }
124
+ .container {
125
+ max-width: 900px;
126
+ margin: 20px auto;
127
+ background-color: #ffffff;
128
+ padding: 30px;
129
+ border-radius: 10px;
130
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
131
+ }
132
+ h1 {
133
+ color: #0056b3;
134
+ text-align: center;
135
+ margin-bottom: 30px;
136
+ }
137
+ .person-card {
138
+ background-color: #e9f0f8;
139
+ border: 1px solid #cce5ff;
140
+ border-radius: 8px;
141
+ padding: 20px;
142
+ margin-bottom: 25px;
143
+ transition: transform 0.2s ease-in-out;
144
+ }
145
+ .person-card:hover {
146
+ transform: translateY(-5px);
147
+ }
148
+ .person-card h2 {
149
+ color: #004085;
150
+ margin-top: 0;
151
+ margin-bottom: 10px;
152
+ border-bottom: 2px solid #0056b3;
153
+ padding-bottom: 5px;
154
+ }
155
+ .person-card p {
156
+ margin: 5px 0;
157
+ }
158
+ .person-card strong {
159
+ color: #004085;
160
+ }
161
+ .employer-info {
162
+ background-color: #d1ecf1;
163
+ border-left: 5px solid #007bff;
164
+ padding: 15px;
165
+ margin-top: 15px;
166
+ border-radius: 5px;
167
+ }
168
+ .employer-info h3 {
169
+ color: #0056b3;
170
+ margin-top: 0;
171
+ margin-bottom: 10px;
172
+ }
173
+ ul {
174
+ list-style-type: none;
175
+ padding: 0;
176
+ }
177
+ ul li {
178
+ background-color: #f8fafd;
179
+ padding: 8px 12px;
180
+ margin-bottom: 5px;
181
+ border-radius: 4px;
182
+ border: 1px solid #e0e9f1;
183
+ }
184
+ a {
185
+ color: #007bff;
186
+ text-decoration: none;
187
+ }
188
+ a:hover {
189
+ text-decoration: underline;
190
+ }
191
+ .no-data {
192
+ color: #6c757d;
193
+ font-style: italic;
194
+ }
195
+ </style>
196
+ </head>
197
+ <body>
198
+ <div class="container">
199
+ <h1>Отчет о сотрудниках</h1>
200
+ """
201
+
202
+ for item in data:
203
+ name = item.get("name", "N/A")
204
+ email = item.get("email", "N/A")
205
+ employer = item.get("employer") or {}
206
+
207
+ employer_name = employer.get("name", "N/A")
208
+ employer_area = employer.get("area", "N/A")
209
+ employer_site_url = employer.get("site_url", "")
210
+
211
+ phone_numbers = [
212
+ pn["phone_number"]
213
+ for pn in item.get("phone_numbers", [])
214
+ if "phone_number" in pn
215
+ ]
216
+ telegram_usernames = [
217
+ tu["username"]
218
+ for tu in item.get("telegram_usernames", [])
219
+ if "username" in tu
220
+ ]
221
+
222
+ html_content += f"""
223
+ <div class="person-card">
224
+ <h2>{name}</h2>
225
+ <p><strong>Email:</strong> <a href="mailto:{email}">{email}</a></p>
226
+ """
227
+
228
+ if employer_name != "N/A":
229
+ html_content += f"""
230
+ <div class="employer-info">
231
+ <h3>Работодатель: {employer_name}</h3>
232
+ <p><strong>Город:</strong> {employer_area}</p>
233
+ """
234
+ if employer_site_url:
235
+ html_content += f"""
236
+ <p><strong>Сайт:</strong> <a href="{employer_site_url}" target="_blank">{employer_site_url}</a></p>
237
+ """
238
+ html_content += "</div>" # Закрываем employer-info
239
+ else:
240
+ html_content += (
241
+ '<p class="no-data">Информация о работодателе отсутствует.</p>'
242
+ )
243
+
244
+ if phone_numbers:
245
+ html_content += "<p><strong>Номера телефонов:</strong></p><ul>"
246
+ for phone in phone_numbers:
247
+ html_content += f"<li><a href='tel:{phone}'>{phone}</a></li>"
248
+ html_content += "</ul>"
249
+ else:
250
+ html_content += '<p class="no-data">Номера телефонов отсутствуют.</p>'
251
+
252
+ if telegram_usernames:
253
+ html_content += "<p><strong>Имена пользователей Telegram:</strong></p><ul>"
254
+ for username in telegram_usernames:
255
+ html_content += f"<li><a href='https://t.me/{username}' target='_blank'>@{username}</a></li>"
256
+ html_content += "</ul>"
257
+ else:
258
+ html_content += (
259
+ '<p class="no-data">Имена пользователей Telegram отсутствуют.</p>'
260
+ )
261
+
262
+ html_content += "</div>" # Закрываем person-card
263
+
264
+ html_content += """
265
+ </div>
266
+ </body>
267
+ </html>
268
+ """
269
+ return html_content
270
+
271
+
272
+ def print_contacts(data: dict) -> None:
273
+ """Вывод всех контактов в древовидной структуре."""
274
+ page = data["page"]
275
+ pages = (data["total"] // data["per_page"]) + 1
276
+ print(f"Страница {page}/{pages}:")
277
+ contacts = data.get("contact_persons", [])
278
+ for idx, contact in enumerate(contacts):
279
+ is_last_contact = idx == len(contacts) - 1
280
+ print_contact(contact, is_last_contact)
281
+ print()
282
+
283
+
284
+ def print_contact(contact: dict, is_last_contact: bool) -> None:
285
+ """Вывод информации о конкретном контакте."""
286
+ prefix = "└──" if is_last_contact else "├──"
287
+ print(f" {prefix} 🧑 {contact.get('name', 'Имя скрыто')}")
288
+ prefix2 = " " if is_last_contact else " │ "
289
+ print(f"{prefix2}├── 📧 Email: {contact.get('email', 'н/д')}")
290
+ employer = contact.get("employer") or {}
291
+ print(f"{prefix2}├── 🏢 Работодатель: {employer.get('name', 'н/д')}")
292
+ print(f"{prefix2}├── 🏠 Город: {employer.get('area', 'н/д')}")
293
+ print(f"{prefix2}└── 🌐 Сайт: {employer.get('site_url', 'н/д')}")
294
+ print(prefix2)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "hh-applicant-tool"
3
- version = "0.5.9"
3
+ version = "0.6.0"
4
4
  description = ""
5
5
  authors = ["Senior YAML Developer <yamldeveloper@proton.me>"]
6
6
  readme = "README.md"
@@ -1,87 +0,0 @@
1
- import argparse
2
- import logging
3
- from os import getenv
4
-
5
- from ..main import BaseOperation
6
- from ..main import Namespace as BaseNamespace
7
- from ..telemetry_client import TelemetryClient
8
-
9
- logger = logging.getLogger(__package__)
10
-
11
-
12
- class Namespace(BaseNamespace):
13
- username: str | None = None
14
- password: str | None = None
15
- search: str | None = None
16
-
17
-
18
- class Operation(BaseOperation):
19
- """Выведет контакты работодателя по заданной строке поиска"""
20
-
21
- def setup_parser(self, parser: argparse.ArgumentParser) -> None:
22
- parser.add_argument(
23
- "-u",
24
- "--username",
25
- type=str,
26
- help="Имя пользователя для аутентификации",
27
- default=getenv("AUTH_USERNAME"),
28
- )
29
- parser.add_argument(
30
- "-P",
31
- "--password",
32
- type=str,
33
- help="Пароль для аутентификации",
34
- default=getenv("AUTH_PASSWORD"),
35
- )
36
- parser.add_argument(
37
- "-s",
38
- "--search",
39
- type=str,
40
- default="",
41
- help="Строка поиска для контактов работодателя",
42
- )
43
- parser.add_argument(
44
- "-p",
45
- "--page",
46
- default=1,
47
- help="Номер страницы в выдаче",
48
- )
49
-
50
- def run(self, args: Namespace, _, telemetry_client: TelemetryClient) -> None:
51
- results = telemetry_client.get_telemetry(
52
- "/contact/persons",
53
- {"search": args.search, "per_page": 10, "page": args.page},
54
- )
55
- if "contact_persons" not in results:
56
- print("❌", results)
57
- return 1
58
-
59
- print(
60
- "Тут отображаются только данные, собранные с вашего telemetry_client_id. Вы так же можете их удалить с помощью команды delete-telemetry."
61
- )
62
- print()
63
-
64
- self._print_contacts(results)
65
-
66
- def _print_contacts(self, data: dict) -> None:
67
- """Вывод всех контактов в древовидной структуре."""
68
- page = data["page"]
69
- pages = (data["total"] // data["per_page"]) + 1
70
- print(f"Страница {page}/{pages}:")
71
- contacts = data.get("contact_persons", [])
72
- for idx, contact in enumerate(contacts):
73
- is_last_contact = idx == len(contacts) - 1
74
- self._print_contact(contact, is_last_contact)
75
- print()
76
-
77
- def _print_contact(self, contact: dict, is_last_contact: bool) -> None:
78
- """Вывод информации о конкретном контакте."""
79
- prefix = "└──" if is_last_contact else "├──"
80
- print(f" {prefix} 🧑 {contact.get('name', 'Имя скрыто')}")
81
- prefix2 = " " if is_last_contact else " │ "
82
- print(f"{prefix2}├── 📧 Email: {contact.get('email', 'н/д')}")
83
- employer = contact.get("employer") or {}
84
- print(f"{prefix2}├── 🏢 Работодатель: {employer.get('name', 'н/д')}")
85
- print(f"{prefix2}├── 🏠 Город: {employer.get('area', 'н/д')}")
86
- print(f"{prefix2}└── 🌐 Сайт: {employer.get('site_url', 'н/д')}")
87
- print(prefix2)