diff --git a/hh_applicant_tool/operations/reply_employers.py b/hh_applicant_tool/operations/reply_employers.py index 863b0c7..20bc793 100644 --- a/hh_applicant_tool/operations/reply_employers.py +++ b/hh_applicant_tool/operations/reply_employers.py @@ -6,9 +6,10 @@ from ..api import ApiError from ..main import BaseOperation -from ..main import Namespace as BaseNamespace, get_api -from ..utils import parse_interval, random_text +from ..main import Namespace as BaseNamespace +from ..main import get_api from ..mixins import GetResumeIdMixin +from ..utils import parse_interval, random_text logger = logging.getLogger(__package__) @@ -17,6 +18,7 @@ class Namespace(BaseNamespace): reply_message: str reply_interval: Tuple[float, float] max_pages: int + only_invitations: bool dry_run: bool @@ -24,25 +26,42 @@ class Operation(BaseOperation, GetResumeIdMixin): """Ответ всем работодателям.""" def setup_parser(self, parser: argparse.ArgumentParser) -> None: + # parser.add_argument( + # "reply_message", + # help="Сообщение для отправки во все чаты с работодателями, где ожидают ответа либо не прочитали ответ. Если не передать, то его нужно будет вводить интерактивно.", + # ) + parser.add_argument("--resume-id", help="Идентификатор резюме") parser.add_argument( - "reply_message", - help="Сообщение для отправки во все чаты с работодателями, где ожидают ответа либо не прочитали ответ. Если не передать, то его нужно будет вводить интерактивно.", - ) - parser.add_argument('--resume-id', help="Идентификатор резюме") - parser.add_argument( + "-i", "--reply-interval", help="Интервал перед отправкой сообщения в секундах (X, X-Y)", default="5-10", type=parse_interval, ) parser.add_argument( + "-m", "--reply-message", "--reply", - help="Отправить сообщение во все чаты, где ожидают ответа либо не прочитали ответ", + help="Отправить сообщение во все чаты, где ожидают ответа либо не прочитали ответ. Еслм не передать сообщение, то нужно будет вводить его в интерактивном режиме.", + ) + parser.add_argument( + "-p", + "--max-pages", + type=int, + default=25, + help="Максимальное количество страниц для проверки", ) - parser.add_argument('--max-pages', type=int, default=25, help='Максимальное количество страниц для проверки') + parser.add_argument( + "-oi", + "--only-invitations", + help="Отвечать только на приглашения", + default=False, + action=argparse.BooleanOptionalAction, + ) + parser.add_argument( "--dry-run", + "--dry", help="Не отправлять сообщения, а только выводить параметры запроса", default=False, action=argparse.BooleanOptionalAction, @@ -52,15 +71,16 @@ def run(self, args: Namespace) -> None: self.api = get_api(args) self.resume_id = self._get_resume_id() self.reply_min_interval, self.reply_max_interval = args.reply_interval - self.reply_message = args.reply_message or args.config['reply_message'] - #assert self.reply_message, "`reply_message` должен быть передан чеерез аргументы или настройки" + self.reply_message = args.reply_message or args.config["reply_message"] + # assert self.reply_message, "`reply_message` должен быть передан чеерез аргументы или настройки" self.max_pages = args.max_pages self.dry_run = args.dry_run - logger.debug(f'{self.reply_message = }') + self.only_invitations = args.only_invitations + logger.debug(f"{self.reply_message = }") self._reply_chats() def _reply_chats(self) -> None: - me =self.me= self.api.get("/me") + me = self.me = self.api.get("/me") basic_message_placeholders = { "first_name": me.get("first_name", ""), @@ -72,17 +92,26 @@ def _reply_chats(self) -> None: for negotiation in self._get_negotiations(): try: # Пропускаем другие резюме - if self.resume_id != negotiation['resume']['id']: + if self.resume_id != negotiation["resume"]["id"]: continue + state_id = negotiation["state"]["id"] + + # Пропускаем отказ + if state_id == "discard": + continue + + if self.only_invitations and not state_id.startswith("inv"): + continue + + logger.debug(negotiation) nid = negotiation["id"] vacancy = negotiation["vacancy"] + salary = vacancy.get("salary") or {} message_placeholders = { "vacancy_name": vacancy.get("name", ""), - "employer_name": vacancy.get("employer", {}).get( - "name", "" - ), + "employer_name": vacancy.get("employer", {}).get("name", ""), **basic_message_placeholders, } @@ -93,45 +122,60 @@ def _reply_chats(self) -> None: page: int = 0 last_message: dict | None = None + message_history: list[str] = [] while True: messages_res = self.api.get( f"/negotiations/{nid}/messages", page=page ) - first_message = messages_res["items"][0] + last_message = messages_res["items"][-1] + message_history.extend( + ( + "<-" + if item["author"]["participant_type"] == "employer" + else "->" + ) + + " " + + item["text"] + for item in messages_res["items"] + if item.get("text") + ) if page + 1 >= messages_res["pages"]: break page = messages_res["pages"] - 1 - first_message_text = first_message["text"] - last_message_text = last_message["text"] - logger.debug(last_message_text) + logger.debug(last_message) - is_employer_message = last_message["author"]["participant_type"] == "employer" + is_employer_message = ( + last_message["author"]["participant_type"] == "employer" + ) - if is_employer_message or not negotiation.get("viewed_by_opponent"): + if is_employer_message or not negotiation.get("viewed_by_opponent"): if self.reply_message: - message = ( - random_text(self.reply_message) - % message_placeholders - ) + message = random_text(self.reply_message) % message_placeholders logger.debug(message) else: print("🏢", message_placeholders["employer_name"]) print("💼", message_placeholders["vacancy_name"]) - print() - print("История переписки:") - if first_message_text == last_message_text: - print(first_message_text) - else: - print(first_message_text) - print("...") - print(last_message_text) - print('-' * 10) + print("📅", vacancy["created_at"]) + if salary: + salary_from = salary.get("from", "-") + salary_to = salary.get("to", "-") + salary_currency = salary.get("currency") + print("💵 от", salary_from, "до", salary_to, salary_currency) + print("") + print("Последние сообщения:") + for msg in ( + message_history[:1] + ["..."] + message_history[-2:] + if len(message_history) > 3 + else message_history + ): + print(msg) + print("-" * 10) message = input("Ваше сообщение: ").strip() if not message: - print("🚶‍♂️ Пропускаем чат") + print("🚶 Пропускаем чат") continue if self.dry_run: @@ -164,7 +208,7 @@ def _reply_chats(self) -> None: def _get_negotiations(self) -> list[dict]: rv = [] for page in range(self.max_pages): - res = self.api.get("/negotiations", page=page, status='active') + res = self.api.get("/negotiations", page=page, status="active") rv.extend(res["items"]) if page >= res["pages"] - 1: break