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.

Files changed (23) hide show
  1. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/PKG-INFO +11 -12
  2. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/README.md +10 -11
  3. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/api/client.py +28 -4
  4. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/constants.py +1 -1
  5. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/main.py +32 -8
  6. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/apply_similar.py +4 -8
  7. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/call_api.py +3 -7
  8. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/clear_negotiations.py +2 -6
  9. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/list_resumes.py +2 -6
  10. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/update_resumes.py +2 -6
  11. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/whoami.py +2 -6
  12. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/telemetry_client.py +0 -2
  13. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/pyproject.toml +1 -1
  14. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/__init__.py +0 -0
  15. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/__main__.py +0 -0
  16. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/api/__init__.py +0 -0
  17. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/api/errors.py +0 -0
  18. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/color_log.py +0 -0
  19. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/__init__.py +0 -0
  20. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/authorize.py +0 -0
  21. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/operations/refresh_token.py +0 -0
  22. {hh_applicant_tool-0.3.5 → hh_applicant_tool-0.3.6}/hh_applicant_tool/types.py +0 -0
  23. {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.5
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
  ![Publish to PyPI](https://github.com/s3rgeym/hh-applicant-tool/actions/workflows/publish.yml/badge.svg)
25
23
  [![PyPi Version](https://img.shields.io/pypi/v/hh-applicant-tool)]()
26
24
  [![Python Versions](https://img.shields.io/pypi/pyversions/hh-applicant-tool.svg)]()
@@ -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-tool`:
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. Контактная информация хрюши (ее телефон, email и тп).
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
  ![Publish to PyPI](https://github.com/s3rgeym/hh-applicant-tool/actions/workflows/publish.yml/badge.svg)
6
4
  [![PyPi Version](https://img.shields.io/pypi/v/hh-applicant-tool)]()
7
5
  [![Python Versions](https://img.shields.io/pypi/pyversions/hh-applicant-tool.svg)]()
@@ -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-tool`:
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. Контактная информация хрюши (ее телефон, email и тп).
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
- DEFAULT_USER_AGENT,
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 DEFAULT_USER_AGENT,
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 = 0.34,
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 - time.monotonic() + self.previous_request_time
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
- DEFAULT_USER_AGENT = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36"
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=argparse.RawDescriptionHelpFormatter,
67
+ formatter_class=self.ArgumentFormatter,
52
68
  )
53
69
  parser.add_argument(
54
70
  "-c",
55
71
  "--config",
56
- help="config path",
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="increase verbosity",
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("_", "-"), description=op.__doc__
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)
@@ -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
- assert args.config["token"]
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 IndexError:
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"] = template_message % vacancy
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 все кривое, иногда нет идентификатора работодателя, иногда у вакансии нет названия.
@@ -4,8 +4,8 @@ import json
4
4
  import logging
5
5
  import sys
6
6
 
7
- from ..api import ApiClient, ApiError
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
- assert args.config["token"]
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
- assert args.config["token"]
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:
@@ -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
- assert args.config["token"]
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"
@@ -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
- assert args.config["token"]
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
- assert args.config["token"]
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()
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "hh-applicant-tool"
3
- version = "0.3.5"
3
+ version = "0.3.6"
4
4
  description = ""
5
5
  authors = ["Senior YAML Developer <yamldeveloper@proton.me>"]
6
6
  readme = "README.md"