⚙️ REFACTOR(bot-commands): Изменена логика получения списка резюме. RESET Menu to Bottom. Предприняты меры при таймауте.

This commit is contained in:
Oscar
2026-05-27 10:58:37 +03:00
parent d2a49cb1eb
commit 67c120930b
2 changed files with 75 additions and 15 deletions

View File

@@ -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)
} }

View File

@@ -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) {