mirror of
https://github.com/hempyhemp/hh-auto-reply.git
synced 2026-06-08 18:04:57 +00:00
⚙️ REFACTOR(bot-commands): Изменена логика получения списка резюме. RESET Menu to Bottom. Предприняты меры при таймауте.
This commit is contained in:
@@ -75,31 +75,62 @@ async function sendResumeSelector(chatId: number, resumes: ResumeListItem[], mes
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function resetMenuToBottom(chatId: number): Promise<void> {
|
||||||
|
const state = getState(chatId)
|
||||||
|
if (state.menuMessageId) {
|
||||||
|
await bot.deleteMessage(chatId, state.menuMessageId).catch(() => {})
|
||||||
|
state.menuMessageId = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function doLogin(chatId: number, email: string): Promise<void> {
|
async function doLogin(chatId: number, email: string): Promise<void> {
|
||||||
await bot.sendMessage(chatId, '🔄 Логинюсь...')
|
await bot.sendMessage(chatId, '🔄 Логинюсь...')
|
||||||
try {
|
try {
|
||||||
await login(email, chatId)
|
await login(email, chatId)
|
||||||
await prisma.user.update({ where: { telegramId: chatId }, data: { hhEmail: email } })
|
await prisma.user.update({ where: { telegramId: chatId }, data: { hhEmail: email } })
|
||||||
|
|
||||||
const resumes = await listResumes(chatId)
|
|
||||||
const state = getState(chatId)
|
const state = getState(chatId)
|
||||||
|
|
||||||
if (resumes.length === 0) {
|
// listResumes может упасть по таймауту сразу после логина — это не критично
|
||||||
|
let resumes: ResumeListItem[] | null = null
|
||||||
|
try {
|
||||||
|
resumes = await listResumes(chatId)
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
await bot.sendMessage(chatId, '⚠️ Не удалось загрузить резюме — выбери вручную через меню')
|
||||||
|
}
|
||||||
|
|
||||||
|
await resetMenuToBottom(chatId)
|
||||||
|
|
||||||
|
if (resumes === null) {
|
||||||
|
// таймаут при загрузке резюме — просто показываем меню
|
||||||
|
}
|
||||||
|
else if (resumes.length === 0) {
|
||||||
await bot.sendMessage(chatId, '⚠️ Резюме не найдены. Создайте резюме на hh.ru')
|
await bot.sendMessage(chatId, '⚠️ Резюме не найдены. Создайте резюме на hh.ru')
|
||||||
}
|
}
|
||||||
else if (resumes.length === 1) {
|
else if (resumes.length === 1) {
|
||||||
await saveResume(chatId, resumes[0].href)
|
await saveResume(chatId, resumes[0].href)
|
||||||
await bot.sendMessage(chatId, `✅ Резюме сохранено: ${resumes[0].title}`)
|
await bot.sendMessage(chatId, `✅ Резюме сохранено: ${resumes[0].title}`)
|
||||||
}
|
}
|
||||||
else if (state.menuMessageId) {
|
else {
|
||||||
await sendResumeSelector(chatId, resumes, state.menuMessageId)
|
// несколько резюме — отправляем новый селектор внизу
|
||||||
|
state.pendingResumes = resumes
|
||||||
|
const selectorMsg = await bot.sendMessage(chatId, '📄 Выбери резюме:', {
|
||||||
|
reply_markup: {
|
||||||
|
inline_keyboard: [
|
||||||
|
...resumes.map((r, i) => [{ text: r.title, callback_data: `hh_resume_pick_${i}` }]),
|
||||||
|
[{ text: '◀️ Назад', callback_data: 'hh_back' }],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
state.menuMessageId = selectorMsg.message_id
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await bot.sendMessage(chatId, '✅ Авторизован! Куки сохранены.')
|
|
||||||
await showMenu(chatId)
|
await showMenu(chatId)
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
await resetMenuToBottom(chatId)
|
||||||
await bot.sendMessage(chatId, `❌ Ошибка: ${(e as Error).message}`)
|
await bot.sendMessage(chatId, `❌ Ошибка: ${(e as Error).message}`)
|
||||||
await showMenu(chatId)
|
await showMenu(chatId)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export async function login(email: string, chatId: number): Promise<void> {
|
|||||||
const page = await context.newPage()
|
const page = await context.newPage()
|
||||||
|
|
||||||
await page.goto('https://hh.ru/account/login', { waitUntil: 'networkidle' })
|
await page.goto('https://hh.ru/account/login', { waitUntil: 'networkidle' })
|
||||||
await bot.sendMessage(chatId, `page: ${page.url()}`)
|
// await bot.sendMessage(chatId, `page: ${page.url()}`)
|
||||||
|
|
||||||
await page.click('[data-qa="submit-button"]')
|
await page.click('[data-qa="submit-button"]')
|
||||||
await page.waitForTimeout(randomDelay())
|
await page.waitForTimeout(randomDelay())
|
||||||
@@ -88,18 +88,30 @@ export async function listResumes(chatId: number): Promise<ResumeListItem[]> {
|
|||||||
const context = await browser.newContext()
|
const context = await browser.newContext()
|
||||||
const page = await context.newPage()
|
const page = await context.newPage()
|
||||||
await loadSession(page, chatId)
|
await loadSession(page, chatId)
|
||||||
await page.goto('https://hh.ru/applicant/resumes', { waitUntil: 'networkidle' })
|
|
||||||
|
|
||||||
const resumes = await page.$$eval(
|
let lastError: Error | null = null
|
||||||
'[data-qa^="resume-card-link-"]',
|
for (let attempt = 1; attempt <= 2; attempt++) {
|
||||||
links => links.map(a => ({
|
try {
|
||||||
href: (a as HTMLAnchorElement).getAttribute('href') ?? '',
|
await page.goto('https://hh.ru/applicant/resumes', { waitUntil: 'networkidle' })
|
||||||
title: a.textContent?.trim() ?? '(без названия)',
|
const resumes = await page.$$eval(
|
||||||
})),
|
'[data-qa^="resume-card-link-"]',
|
||||||
)
|
links => links.map(a => ({
|
||||||
|
href: (a as HTMLAnchorElement).getAttribute('href') ?? '',
|
||||||
|
title: a.textContent?.trim() ?? '(без названия)',
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
await browser.close()
|
||||||
|
return resumes
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
lastError = e as Error
|
||||||
|
if (attempt < 2)
|
||||||
|
await page.waitForTimeout(4000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await browser.close()
|
await browser.close()
|
||||||
return resumes
|
throw lastError!
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveResume(chatId: number, resumeHref: string): Promise<string | undefined> {
|
export async function saveResume(chatId: number, resumeHref: string): Promise<string | undefined> {
|
||||||
@@ -194,6 +206,23 @@ export async function applyToJobs(
|
|||||||
const letter = await createMessage(resume.data, description, user!.prompt)
|
const letter = await createMessage(resume.data, description, user!.prompt)
|
||||||
await keep(`✅ <b>${vacancy.title}</b>\n\n${letter}`)
|
await keep(`✅ <b>${vacancy.title}</b>\n\n${letter}`)
|
||||||
|
|
||||||
|
// const applyBtn = await page.$('[data-qa="vacancy-response-link-top"]')
|
||||||
|
// if (!applyBtn) {
|
||||||
|
// results.skipped.push(vacancy.title)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// await randomScroll(page)
|
||||||
|
//
|
||||||
|
// await applyBtn.click()
|
||||||
|
// await page.waitForTimeout(randomDelay())
|
||||||
|
//
|
||||||
|
// const submitBtn = await page.$('[data-qa="vacancy-response-popup-submit"]')
|
||||||
|
// if (submitBtn) {
|
||||||
|
// await submitBtn.click()
|
||||||
|
// await page.waitForTimeout(randomDelay())
|
||||||
|
// }
|
||||||
|
|
||||||
results.applied.push(ref)
|
results.applied.push(ref)
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
|
|||||||
Reference in New Issue
Block a user