mirror of https://github.com/Bunsly/JobSpy
new handler to see my info
parent
4873082147
commit
9ffbdd5e2a
|
@ -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):
|
||||
|
|
31
src/main.py
31
src/main.py
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
|
@ -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()
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue