mirror of
https://github.com/hempyhemp/hh-auto-reply.git
synced 2026-06-08 18:04:57 +00:00
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "hhPhone" TEXT;
|
||||
@@ -23,6 +23,7 @@ model User {
|
||||
createdAt DateTime @default(now())
|
||||
session String?
|
||||
hhEmail String?
|
||||
hhPhone String?
|
||||
resumes Resume[]
|
||||
prompt String @default("Ты — помощник по написанию сопроводительных писем. Отвечай только текстом самого письма, без вступлений, ремарок и пояснений. Опирайся на резюме и ничего не выдумывай, чего недостаточно в резюме лучше умолчать. Пиши по короче и простыми словами. В конце письма оставляй все контакты для связи.")
|
||||
Settings Settings?
|
||||
|
||||
@@ -2,7 +2,7 @@ import bot from '@bot'
|
||||
import prisma from '@prisma'
|
||||
import { debugFunc } from '@/hh/handlers/debug'
|
||||
import { handleApply } from './handlers/apply.js'
|
||||
import { doLogin, handleLogin, handleLoginByEmail, handleLoginByPhone } from './handlers/auth.js'
|
||||
import { doLogin, doLoginByPhone, handleLogin, handleLoginByEmail, handleLoginByPhone } from './handlers/auth.js'
|
||||
import { handleSkipped, handleStatus } from './handlers/info.js'
|
||||
import { finishOnboarding, showPromptStep, showQueryStep, showResumeInfo } from './handlers/onboarding.js'
|
||||
import { handleMyResume, handleResumeList, handleResumePick } from './handlers/resume.js'
|
||||
@@ -64,6 +64,19 @@ const CALLBACK_HANDLERS: Record<string, CallbackHandler> = {
|
||||
}
|
||||
await doLogin(chatId, user.hhEmail)
|
||||
},
|
||||
hh_login_use_current_phone: async (chatId, messageId) => {
|
||||
const state = getState(chatId)
|
||||
state.awaitingPhone = false
|
||||
state.loginPromptMessageId = null
|
||||
await bot.deleteMessage(chatId, messageId).catch(() => {})
|
||||
const user = await prisma.user.findUnique({ where: { telegramId: chatId } })
|
||||
if (!user?.hhPhone) {
|
||||
await bot.sendMessage(chatId, '❌ Телефон не найден, введи вручную')
|
||||
state.awaitingPhone = true
|
||||
return
|
||||
}
|
||||
await doLoginByPhone(chatId, user.hhPhone)
|
||||
},
|
||||
hh_keep_query: async (chatId, messageId) => {
|
||||
const state = getState(chatId)
|
||||
state.awaitingQuery = false
|
||||
@@ -252,6 +265,17 @@ export function registerHHCommands() {
|
||||
return
|
||||
}
|
||||
|
||||
if (state.awaitingPhone) {
|
||||
state.awaitingPhone = false
|
||||
await bot.deleteMessage(chatId, msg.message_id).catch(() => {})
|
||||
if (state.loginPromptMessageId) {
|
||||
await bot.deleteMessage(chatId, state.loginPromptMessageId).catch(() => {})
|
||||
state.loginPromptMessageId = null
|
||||
}
|
||||
await doLoginByPhone(chatId, msg.text)
|
||||
return
|
||||
}
|
||||
|
||||
if (state.awaitingQuery) {
|
||||
state.awaitingQuery = false
|
||||
await bot.deleteMessage(chatId, msg.message_id).catch(() => {})
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
import bot from '@bot'
|
||||
import prisma from '@prisma'
|
||||
import { listResumes, login, saveResume } from '../scraper.js'
|
||||
import { listResumes, login, loginByPhone, saveResume } from '../scraper.js'
|
||||
import { getState } from '../state.js'
|
||||
import { startOnboarding } from './onboarding.js'
|
||||
import type { ResumeListItem } from '../types.js'
|
||||
|
||||
export async function doLogin(chatId: number, email: string): Promise<void> {
|
||||
await bot.sendMessage(chatId, '🔄 Логинюсь...')
|
||||
try {
|
||||
await login(email, chatId)
|
||||
await prisma.user.upsert({
|
||||
where: { telegramId: chatId },
|
||||
update: { hhEmail: email },
|
||||
create: { telegramId: chatId, hhEmail: email, Settings: { create: {} } },
|
||||
})
|
||||
|
||||
async function handlePostLogin(chatId: number): Promise<void> {
|
||||
const state = getState(chatId)
|
||||
|
||||
let resumes: ResumeListItem[] | null = null
|
||||
@@ -49,6 +40,34 @@ export async function doLogin(chatId: number, email: string): Promise<void> {
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function doLogin(chatId: number, email: string): Promise<void> {
|
||||
await bot.sendMessage(chatId, '🔄 Логинюсь...')
|
||||
try {
|
||||
await login(email, chatId)
|
||||
await prisma.user.upsert({
|
||||
where: { telegramId: chatId },
|
||||
update: { hhEmail: email },
|
||||
create: { telegramId: chatId, hhEmail: email, Settings: { create: {} } },
|
||||
})
|
||||
await handlePostLogin(chatId)
|
||||
}
|
||||
catch (e) {
|
||||
await bot.sendMessage(chatId, `❌ Ошибка: ${(e as Error).message}`)
|
||||
}
|
||||
}
|
||||
|
||||
export async function doLoginByPhone(chatId: number, phone: string): Promise<void> {
|
||||
await bot.sendMessage(chatId, '🔄 Логинюсь...')
|
||||
try {
|
||||
await loginByPhone(phone, chatId)
|
||||
await prisma.user.upsert({
|
||||
where: { telegramId: chatId },
|
||||
update: { hhPhone: phone },
|
||||
create: { telegramId: chatId, hhPhone: phone, Settings: { create: {} } },
|
||||
})
|
||||
await handlePostLogin(chatId)
|
||||
}
|
||||
catch (e) {
|
||||
await bot.sendMessage(chatId, `❌ Ошибка: ${(e as Error).message}`)
|
||||
@@ -94,5 +113,26 @@ export async function handleLoginByEmail(chatId: number): Promise<void> {
|
||||
}
|
||||
|
||||
export async function handleLoginByPhone(chatId: number): Promise<void> {
|
||||
await bot.sendMessage(chatId, '📱 Авторизация по телефону — скоро будет доступна')
|
||||
const state = getState(chatId)
|
||||
const user = await prisma.user.findUnique({ where: { telegramId: chatId } })
|
||||
state.awaitingPhone = true
|
||||
|
||||
if (!user?.hhPhone) {
|
||||
await bot.sendMessage(chatId, '📱 Введи номер телефона (например: +79001234567):')
|
||||
}
|
||||
else {
|
||||
const prompt = await bot.sendMessage(
|
||||
chatId,
|
||||
`📱 Текущий телефон: <b>${user.hhPhone}</b>\n\nИспользовать его или введи другой:`,
|
||||
{
|
||||
parse_mode: 'HTML',
|
||||
reply_markup: {
|
||||
inline_keyboard: [[
|
||||
{ text: `✅ Войти как ${user.hhPhone}`, callback_data: 'hh_login_use_current_phone' },
|
||||
]],
|
||||
},
|
||||
},
|
||||
)
|
||||
state.loginPromptMessageId = prompt.message_id
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ import type { ApplyOptions, ApplyResult, ResumeListItem, VacancyRef } from './ty
|
||||
import type { StatusReporter } from './ui.js'
|
||||
import bot from '@bot'
|
||||
import prisma from '@prisma'
|
||||
import { createLogger } from '@/logger'
|
||||
import { createMessage } from '@/openai'
|
||||
import { loadSession, newStealthContext, randomDelay, randomScroll, withBrowser } from './browser.js'
|
||||
import { createLogger } from '@/logger'
|
||||
import { createStatusReporter, escapeHtml } from './ui.js'
|
||||
|
||||
const log = createLogger('scraper')
|
||||
@@ -64,6 +64,48 @@ async function skipIfQuestionnaire(
|
||||
return true
|
||||
}
|
||||
|
||||
export async function loginByPhone(phone: string, chatId: number): Promise<void> {
|
||||
await withBrowser(async (browser) => {
|
||||
if (!browser.version()) {
|
||||
log.error('browser error')
|
||||
return
|
||||
}
|
||||
|
||||
const context = await newStealthContext(browser)
|
||||
const page = await context.newPage()
|
||||
|
||||
await page.goto('https://hh.ru/account/login', { waitUntil: 'domcontentloaded' })
|
||||
|
||||
await page.click('[data-qa="submit-button"]')
|
||||
await page.waitForTimeout(randomDelay())
|
||||
|
||||
await page.fill('[data-qa="magritte-phone-input-national-number-input"]', phone)
|
||||
await page.waitForTimeout(randomDelay())
|
||||
|
||||
await page.click('[data-qa="submit-button"]')
|
||||
await page.waitForTimeout(randomDelay())
|
||||
|
||||
await bot.sendMessage(chatId, '🔑 Введи код из SMS')
|
||||
await page.waitForTimeout(randomDelay())
|
||||
|
||||
await page.click('[data-qa="applicant-login-input-otp"]')
|
||||
const otp = await waitForOtp(chatId)
|
||||
await page.fill('[data-qa="applicant-login-input-otp"] input', otp)
|
||||
|
||||
await page.waitForTimeout(randomDelay())
|
||||
await page.waitForSelector('[data-qa="profileAndResumes-button"]', { timeout: 15000 })
|
||||
|
||||
const cookies = await context.cookies()
|
||||
await prisma.user.upsert({
|
||||
where: { telegramId: chatId },
|
||||
update: { session: JSON.stringify(cookies, null, 2) },
|
||||
create: { telegramId: chatId, session: JSON.stringify(cookies, null, 2), Settings: { create: {} } },
|
||||
})
|
||||
|
||||
await bot.sendMessage(chatId, cookies.length > 0 ? '✅ Авторизация выполнена' : '❌ Произошла ошибка')
|
||||
})
|
||||
}
|
||||
|
||||
export async function login(email: string, chatId: number): Promise<void> {
|
||||
await withBrowser(async (browser) => {
|
||||
if (!browser.version()) {
|
||||
|
||||
Reference in New Issue
Block a user