new handler to see my info

pull/231/head
Yariv Menachem 2025-01-05 19:08:10 +02:00
parent 4873082147
commit 9ffbdd5e2a
10 changed files with 127 additions and 67 deletions

View File

@ -166,6 +166,10 @@ def scrape_jobs(
"""
filtered_jobs = []
remaining_jobs = []
if not filter_by_title:
return filtered_jobs, remaining_jobs
for job in jobs:
for filter_title in filter_by_title:
if re.search(filter_title, job.title, re.IGNORECASE):

View File

@ -1,9 +1,13 @@
from telegram import Update, ReplyKeyboardMarkup, ReplyKeyboardRemove
from telegram.ext import Application, CommandHandler, ConversationHandler, \
MessageHandler, filters, ContextTypes
MessageHandler, filters, ContextTypes, CallbackQueryHandler
from config.settings import settings
from jobspy import Site
from jobspy.scrapers.utils import create_logger
from telegram_handler import TelegramDefaultHandler
from telegram_handler.button_callback.telegram_callback_handler import TelegramCallHandler
from telegram_handler.telegram_myinfo_handler import my_info_handler
from telegram_handler.telegram_start_handler import start_conv_handler
logger = create_logger("Main")
@ -20,7 +24,28 @@ if __name__ == "__main__":
locations = ["Tel Aviv, Israel", "Ramat Gan, Israel",
"Central, Israel", "Rehovot ,Israel"]
application.add_handler(start_conv_handler)
# application.add_handler(CommandHandler('start', start_handler.handle))
tg_callback_handler = TelegramCallHandler()
tg_handler_all = TelegramDefaultHandler(sites=[Site.LINKEDIN, Site.GLASSDOOR, Site.INDEED, Site.GOOZALI])
application.add_handler(CommandHandler("find", tg_handler_all.handle))
# Goozali
tg_handler_goozali = TelegramDefaultHandler(sites=[Site.GOOZALI])
application.add_handler(CommandHandler(
Site.GOOZALI.value, tg_handler_goozali.handle))
# GlassDoor
tg_handler_glassdoor = TelegramDefaultHandler(sites=[Site.GLASSDOOR])
application.add_handler(CommandHandler(
Site.GLASSDOOR.value, tg_handler_glassdoor.handle))
# LinkeDin
tg_handler_linkedin = TelegramDefaultHandler(sites=[Site.LINKEDIN])
application.add_handler(CommandHandler(
Site.LINKEDIN.value, tg_handler_linkedin.handle))
# Indeed
tg_handler_indeed = TelegramDefaultHandler(sites=[Site.INDEED])
application.add_handler(CommandHandler(
Site.INDEED.value, tg_handler_indeed.handle))
application.add_handler(CommandHandler(
"myInfo", my_info_handler.handle))
application.add_handler(CallbackQueryHandler(
tg_callback_handler.button_callback))
logger.info("Run polling from telegram")
application.run_polling(allowed_updates=Update.ALL_TYPES)

View File

@ -2,31 +2,25 @@ from enum import Enum
class Position(str, Enum):
PRODUCT_MANAGEMENT = "Product Management"
DATA_ANALYST = "Data Analyst"
DATA_SCIENCE = "Data Science, ML & Algorithms"
SOFTWARE_ENGINEERING = "Software Engineering"
FULLSTACK_DEVELOPMENT = "Fullstack Development"
QA = "QA"
CYBERSECURITY = "Cybersecurity"
IT_AND_SYSTEM_ADMINISTRATION = "IT and System Administration"
FRONTEND_DEVELOPMENT = "Frontend Development"
DEV_OPS = "DevOps"
UI_UX = "UI/UX, Design & Content"
HR_RECRUITMENT = "HR & Recruitment"
MOBILE_DEVELOPMENT = "Mobile Development"
HARDWARE_ENGINEERING = "Hardware Engineering"
EMBEDDED_ENGINEERING = "Embedded, Low Level & Firmware Engineering"
CUSTOMER_SUCCESS = "Customer Success"
PROJECT_MANAGEMENT = "Project Management"
OPERATIONS = "Operations"
FINANCE = "Finance"
SYSTEMS_ENGINEERING = "Systems Engineering"
MARKETING = "Marketing"
SALES = "Sales"
LEGAL_POLICY = "Compliance, Legal & Policy"
C_LEVEL = "C-Level"
BUSINESS_DEVELOPMENT = "Business Development"
MECHANICAL_ENGINEERING = "Mechanical Engineering"
NATURAL_SCIENCE = "Natural Science"
OTHER = "Other"
BACKEND_DEVELOPER = "Backend Developer"
FULLSTACK_DEVELOPER = "Fullstack Developer"
FRONTEND_DEVELOPER = "Frontend Developer"
DATA_SCIENTIST="Data Scientist"
DATA_ANALYST="Data Analyst"
PROJECT_MANAGER="Project Manager"
CLOUD_ENGINEER="Cloud Engineer"
CLOUD_ARCHITECT="Cloud Architect"
UX_UI_DESIGNER="UX/UI Designer"
PRODUCT_MANAGER="Product Manager"
DEV_OPS_ENGINEER="DevOps Engineer"
BUSINESS_ANALYST="Business Analyst"
CYBERSECURITY_ENGINEER="Cybersecurity Engineer"
MACHINE_LEARNING_ENGINEER="Machine Learning Engineer"
ARTIFICIAL_INTELLIGENCE_ENGINEER="Artificial Intelligence Engineer"
DATABASE_ADMINISTRATOR="Database Administrator"
SYSTEMS_ADMINISTRATOR="Systems Administrator"
NETWORK_ENGINEER="Network Engineer"
TECHNICAL_SUPPORT_SPECIALIST="Technical Support Specialist"
SALES_ENGINEER="Sales Engineer"
SCRUM_MASTER="Scrum Master"
IT_MANAGER="IT Manager"

View File

@ -10,6 +10,22 @@ class User(BaseModel):
username: str
chat_id: Union[int, str] = None
experience: Union[int, str] = None
field: Optional[Position] = None
position: Optional[Position] = None
cities: Optional[list[str]] = None
title_filters: Optional[list[str]] = None
def get_myinfo_message(self):
message = "Here's your profile:\n\n"
message += f"Full Name: {self.full_name}\n"
message += f"Username: @{self.username}\n"
if self.chat_id:
message += f"Chat ID: {self.chat_id}\n"
if self.experience:
message += f"Experience: {self.experience}\n"
if self.position:
message += f"Position Level: {self.position.value}\n"
if self.cities:
message += f"Preferred Cities: {', '.join(self.cities)}\n"
if self.title_filters:
message += f"Job Title Filters: {', '.join(self.title_filters)}\n"
return message

View File

@ -11,18 +11,9 @@ load_dotenv()
class JobRepository:
_instance = None
def __new__(cls):
if cls._instance is not None:
return cls._instance
self = super().__new__(cls)
cls._instance = self
self.logger = create_logger("JobRepository")
self.collection = mongo_client._db["jobs"]
return cls._instance
def __init__(self):
self._logger = create_logger("JobRepository")
self._collection = mongo_client.get_collection('jobs')
def find_by_id(self, job_id: str) -> Optional[JobPost]:
"""
@ -34,7 +25,7 @@ class JobRepository:
Returns:
The job document if found, otherwise None.
"""
result = self.collection.find_one({"id": job_id})
result = self._collection.find_one({"id": job_id})
return JobPost(**result)
def update(self, job: JobPost) -> bool:
@ -47,7 +38,7 @@ class JobRepository:
Returns:
True if the update was successful, False otherwise.
"""
result = self.collection.update_one({"id": job.id}, {"$set": job.model_dump(exclude={"date_posted"})})
result = self._collection.update_one({"id": job.id}, {"$set": job.model_dump(exclude={"date_posted"})})
return result.modified_count > 0
def insert_job(self, job: JobPost):
@ -61,8 +52,8 @@ class JobRepository:
Exception: If an error occurs during insertion.
"""
job_dict = job.model_dump(exclude={"date_posted"})
self.collection.insert_one(job_dict)
self.logger.info(f"Inserted new job with title {job.title}.")
self._collection.insert_one(job_dict)
self._logger.info(f"Inserted new job with title {job.title}.")
def insert_many_if_not_found(self, jobs: list[JobPost]) -> tuple[list[JobPost], list[JobPost]]:
"""
@ -85,8 +76,8 @@ class JobRepository:
if operations:
# Execute all operations in bulk
result = self.collection.bulk_write(operations)
self.logger.info(f"Matched: {result.matched_count}, Upserts: {
result = self._collection.bulk_write(operations)
self._logger.info(f"Matched: {result.matched_count}, Upserts: {
result.upserted_count}, Modified: {result.modified_count}")
# Get the newly inserted jobs (those that were upserted)
@ -98,3 +89,5 @@ class JobRepository:
old_jobs.append(job)
return old_jobs, new_jobs
job_repository = JobRepository()

View File

@ -3,8 +3,8 @@ from __future__ import annotations
from telegram import MaybeInaccessibleMessage
from telegram.constants import ReactionEmoji
from model.job_repository import JobRepository
from jobspy import create_logger
from model.job_repository import job_repository
from telegram_handler.button_callback.button_fire_strategy import FireStrategy
from telegram_handler.button_callback.button_job_title_strategy import JobTitleStrategy
from telegram_handler.button_callback.button_poo_strategy import PooStrategy
@ -22,7 +22,6 @@ class ButtonCallBackContext:
self._data = data
self._job_id = job_id
self._strategy = None
self._job_repository = JobRepository()
@property
def strategy(self) -> ButtonStrategy:
@ -49,10 +48,10 @@ class ButtonCallBackContext:
elif ReactionEmoji.PILE_OF_POO.name == self._data:
self._strategy = PooStrategy(self._message)
elif self._data:
job = self._job_repository.find_by_id(self._data)
job = job_repository.find_by_id(self._data)
if job:
chat_id = self._message.chat.id
self._strategy = JobTitleStrategy(chat_id,job)
self._strategy = JobTitleStrategy(chat_id, job)
else:
self._logger.error("Invalid enum value")
return

View File

@ -1,8 +1,8 @@
from telegram import MaybeInaccessibleMessage
from telegram.constants import ReactionEmoji
from model.job_repository import JobRepository
from jobspy import create_logger
from model.job_repository import job_repository
from telegram_bot import TelegramBot
from telegram_handler.button_callback.button_strategy import ButtonStrategy
@ -16,16 +16,15 @@ class FireStrategy(ButtonStrategy):
self._message = message
self._emoji = ReactionEmoji.FIRE
self._telegram_bot = TelegramBot()
self._job_repository = JobRepository()
self._job_id = job_id
self._logger = create_logger("FireStrategy")
async def execute(self):
job = self._job_repository.find_by_id(self._job_id)
job = job_repository.find_by_id(self._job_id)
if not job:
self._logger.error(f"Job with ID {self._job_id} not found.")
return
job.applied = True
self._job_repository.update(job)
job_repository.update(job)
chat_id = self._message.chat.id
await self._telegram_bot.set_message_reaction(chat_id, self._message.message_id, self._emoji)

View File

@ -4,9 +4,10 @@ from telegram.ext import (
ContextTypes,
)
from model.job_repository import JobRepository
from jobspy import Site, scrape_jobs, JobPost
from jobspy.scrapers.utils import create_logger
from model.job_repository import JobRepository
from model.user_repository import user_repository
from telegram_bot import TelegramBot
from telegram_handler.telegram_handler import TelegramHandler
@ -33,11 +34,8 @@ def map_jobs_to_keyboard(jobs: list[JobPost]) -> InlineKeyboardMarkup:
class TelegramDefaultHandler(TelegramHandler):
def __init__(self, sites: list[Site], locations: list[str], title_filters: list[str], search_term: str):
def __init__(self, sites: list[Site]):
self.sites_to_scrap = sites
self.locations = locations
self.search_term = search_term
self.title_filters = title_filters
self.telegram_bot = TelegramBot()
self.jobRepository = JobRepository()
if len(sites) == 1:
@ -51,17 +49,20 @@ class TelegramDefaultHandler(TelegramHandler):
chat_id = update.message.chat.id
await self.telegram_bot.set_message_reaction(chat_id,
update.message.message_id, ReactionEmoji.FIRE)
user = user_repository.find_by_username(update.message.from_user.username)
site_names = [site.name for site in self.sites_to_scrap]
site_names_print = ", ".join(site_names)
locations = [location + ", Israel" for location in user.cities]
await self.telegram_bot.send_text(chat_id,
f"Start scarping: {site_names_print}")
filtered_out_jobs, jobs = scrape_jobs(
site_name=self.sites_to_scrap,
search_term=self.search_term,
locations=self.locations,
search_term=user.position.value,
locations=locations,
results_wanted=200,
hours_old=48,
filter_by_title=self.title_filters,
filter_by_title=user.title_filters,
country_indeed='israel'
)
self.logger.info(f"Found {len(jobs)} jobs")

View File

@ -0,0 +1,29 @@
from telegram import Update
from telegram.constants import ReactionEmoji
from telegram.ext import (
ContextTypes,
)
from jobspy.scrapers.utils import create_logger
from model.user_repository import user_repository
from telegram_bot import TelegramBot
from telegram_handler.telegram_handler import TelegramHandler
class MyInfoTelegramHandler(TelegramHandler):
def __init__(self):
self.telegram_bot = TelegramBot()
self._logger = create_logger("MyInfoTelegramHandler")
async def handle(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
self._logger.info("start handling")
chat_id = update.message.chat.id
await self.telegram_bot.set_message_reaction(chat_id,
update.message.message_id, ReactionEmoji.FIRE)
user = user_repository.find_by_username(update.message.from_user.username)
await self.telegram_bot.send_text(chat_id, user.get_myinfo_message())
self._logger.info("finished handling")
my_info_handler = MyInfoTelegramHandler()

View File

@ -68,7 +68,7 @@ class TelegramStartHandler:
)
return Flow.POSITION.value
cached_user: User = cache_manager.find(user.username)
cached_user.field = position
cached_user.position = position
cache_manager.save(cached_user.username, cached_user)
await update.message.reply_text(LOCATION_MESSAGE)