From ffffa973a6f6afc5011cd98fd5c490908d975040 Mon Sep 17 00:00:00 2001 From: estromenko Date: Mon, 3 Nov 2025 18:38:11 +0300 Subject: [PATCH] Add button to force send next vacancy --- vacancies/main/bot.py | 35 +++++++++++++++++-- .../generate_recommended_vacancies.py | 23 +++--------- vacancies/main/vector_store.py | 22 ++++++++++++ 3 files changed, 59 insertions(+), 21 deletions(-) diff --git a/vacancies/main/bot.py b/vacancies/main/bot.py index 5730701..0fbf0ab 100644 --- a/vacancies/main/bot.py +++ b/vacancies/main/bot.py @@ -1,14 +1,14 @@ import os import io import traceback -from telegram import Update +from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, ReplyKeyboardMarkup, KeyboardButton from telegram.ext import filters, ApplicationBuilder, MessageHandler, CommandHandler, ContextTypes from pypdf import PdfReader from vacancies.main.models import Customer, CustomerCV from langchain.agents import create_agent from langchain_openai import ChatOpenAI from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver -from vacancies.main.vector_store import add_vectors, extract_features +from vacancies.main.vector_store import add_vectors, extract_features, get_next_vacancy from vacancies.conf.settings import DB_URI SYSTEM_PROMPT = """ @@ -34,8 +34,36 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): chat_id=update.effective_chat.id, ), ) + keyboard = [[KeyboardButton("Получить следующую вакансию")]] + reply_markup = ReplyKeyboardMarkup(keyboard, resize_keyboard=True, one_time_keyboard=False) text = "Привет! Я карьерный копилот: помогу с работой, интервью и расскажу новости по рынку, специально для тебя. С чего начнем?" - await context.bot.send_message(chat_id=update.effective_chat.id, text=text) + await context.bot.send_message(chat_id=update.effective_chat.id, text=text, reply_markup=reply_markup) + + +async def next_vacancy(update: Update, context: ContextTypes.DEFAULT_TYPE): + await context.bot.send_message(update.effective_chat.id, "📝 Обрабатываю твой запрос. Пожалуйста, подождите...") + + customer_cv = await CustomerCV.objects.filter(customer__telegram_id=update.effective_user.id).afirst() + if not customer_cv: + message = "Пришлите мне свое резюме, чтобы я мог подобрать вам вакансии!" + await context.bot.send_message(chat_id=update.effective_chat.id, text=message) + return + + result = get_next_vacancy(customer_cv) + if not result: + message = "Вакансии закончились, возвращайтесь позже!" + await context.bot.send_message(chat_id=update.effective_chat.id, text=message) + return + + recommendation, vacancy_content, link = result + + await context.bot.send_message( + chat_id=update.effective_chat.id, + text=vacancy_content, + reply_markup=InlineKeyboardMarkup([[ + InlineKeyboardButton("Откликнуться", url=link), + ]]), + ) async def prompt(update: Update, context: ContextTypes.DEFAULT_TYPE): @@ -92,6 +120,7 @@ async def handle_document(update: Update, context: ContextTypes.DEFAULT_TYPE): application = ApplicationBuilder().token(os.environ["BOT_TOKEN"]).concurrent_updates(True).build() application.add_handler(CommandHandler('start', start, block=False)) +application.add_handler(MessageHandler(filters.Text("Получить следующую вакансию"), next_vacancy, block=False)) application.add_handler(MessageHandler(filters.TEXT & (~filters.COMMAND), prompt, block=False)) application.add_handler(MessageHandler((filters.Document.ALL | filters.PHOTO) & (~filters.COMMAND), handle_document, block=False)) application.add_error_handler(error_handler) diff --git a/vacancies/main/management/commands/generate_recommended_vacancies.py b/vacancies/main/management/commands/generate_recommended_vacancies.py index 0679ab6..8c56ec1 100644 --- a/vacancies/main/management/commands/generate_recommended_vacancies.py +++ b/vacancies/main/management/commands/generate_recommended_vacancies.py @@ -1,11 +1,10 @@ import asyncio from django.core.management import BaseCommand -from vacancies.main.vector_store import search_similarities -from vacancies.main.models import CustomerCV, RecommendedVacancy +from vacancies.main.models import CustomerCV from vacancies.main.bot import application +from vacancies.main.vector_store import get_next_vacancy from telegram import InlineKeyboardButton, InlineKeyboardMarkup -from qdrant_client.models import Filter, HasIdCondition class Command(BaseCommand): @@ -15,24 +14,12 @@ class Command(BaseCommand): asyncio.run(self.ahandle(*args, **options)) async def ahandle(self, *args, **options): - customer_cvs = CustomerCV.objects.all() - - for customer_cv in customer_cvs: - recommended_vacancy_ids = RecommendedVacancy.objects.filter( - customer=customer_cv.customer, - ).values_list('vacancy_id', flat=True) - - query_filter = Filter(must_not=[HasIdCondition(has_id=recommended_vacancy_ids)]) - result = search_similarities(query_filter, customer_cv.id) + for customer_cv in CustomerCV.objects.all(): + result = get_next_vacancy(customer_cv) if not result: continue - search_result_id, vacancy_content, link = result - - recommendation = RecommendedVacancy.objects.create( - customer=customer_cv.customer, - vacancy_id=search_result_id, - ) + recommendation, vacancy_content, link = result await application.bot.send_message( chat_id=recommendation.customer.chat_id, diff --git a/vacancies/main/vector_store.py b/vacancies/main/vector_store.py index e1ac83e..1d97149 100644 --- a/vacancies/main/vector_store.py +++ b/vacancies/main/vector_store.py @@ -5,6 +5,8 @@ from qdrant_client import QdrantClient from qdrant_client.models import Filter from vacancies.main.models import VacancyFeatures from vacancies.conf.settings import QDRANT_URL +from vacancies.main.models import CustomerCV, RecommendedVacancy +from qdrant_client.models import Filter, HasIdCondition qdrant_client = QdrantClient(url=QDRANT_URL) @@ -184,3 +186,23 @@ def extract_features(content: str) -> VacancyFeatures: structured_llm = openai_client.with_structured_output(VacancyFeatures) response = structured_llm.invoke(prompt) return response + + +def get_next_vacancy(customer_cv): + recommended_vacancy_ids = RecommendedVacancy.objects.filter( + customer=customer_cv.customer, + ).values_list('vacancy_id', flat=True) + + query_filter = Filter(must_not=[HasIdCondition(has_id=recommended_vacancy_ids)]) + result = search_similarities(query_filter, customer_cv.id) + if not result: + return None + + search_result_id, vacancy_content, link = result + + recommendation = RecommendedVacancy.objects.create( + customer=customer_cv.customer, + vacancy_id=search_result_id, + ) + + return recommendation, vacancy_content, link