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 { handleSkipped, handleStatus } from './handlers/info.js' import { finishOnboarding, showPromptStep, showQueryStep, showResumeInfo } from './handlers/onboarding.js' import { handleMyResume, handleResumeList, handleResumePick } from './handlers/resume.js' import { DEFAULT_PROMPT, handleAutoToggle, handleMax, handlePrompt, handleQuery } from './handlers/settings.js' import { getState } from './state.js' import { BTN, FILTERS_REPLY_KEYBOARD, INFO_REPLY_KEYBOARD, LOGIN_REPLY_KEYBOARD, MAIN_REPLY_KEYBOARD, SETTINGS_REPLY_KEYBOARD } from './ui.js' type MsgHandler = (chatId: number) => Promise type CallbackHandler = (chatId: number, messageId: number) => Promise const MESSAGE_HANDLERS: Partial> = { [BTN.APPLY]: handleApply, [BTN.STATUS]: handleStatus, [BTN.QUERY]: handleQuery, [BTN.MAX]: handleMax, [BTN.AUTO_TOGGLE]: handleAutoToggle, [BTN.PROMPT]: handlePrompt, [BTN.LOGIN]: handleLogin, [BTN.RESUME_LIST]: handleResumeList, [BTN.MY_RESUME]: handleMyResume, [BTN.SKIPPED]: handleSkipped, [BTN.SETTINGS]: async (chatId) => { await bot.sendMessage(chatId, '⚙️ Настройки:', { reply_markup: SETTINGS_REPLY_KEYBOARD }) }, [BTN.FILTERS]: async (chatId) => { await bot.sendMessage(chatId, '🔎 Фильтры:', { reply_markup: FILTERS_REPLY_KEYBOARD }) }, [BTN.INFO]: async (chatId) => { await bot.sendMessage(chatId, 'ℹ️ Информация:', { reply_markup: INFO_REPLY_KEYBOARD }) }, [BTN.BACK]: async (chatId) => { await bot.sendMessage(chatId, '🤖 HH Auto-Apply', { reply_markup: MAIN_REPLY_KEYBOARD }) }, [BTN.REGION]: debugFunc, } const CALLBACK_HANDLERS: Record = { hh_back: async (chatId, messageId) => { await bot.deleteMessage(chatId, messageId).catch(() => {}) }, hh_login: async (chatId, messageId) => { await bot.deleteMessage(chatId, messageId).catch(() => {}) await handleLogin(chatId) }, hh_login_method_email: async (chatId, messageId) => { const state = getState(chatId) state.loginMethodMsgId = null await bot.deleteMessage(chatId, messageId).catch(() => {}) await handleLoginByEmail(chatId) }, hh_login_method_phone: async (chatId, messageId) => { const state = getState(chatId) state.loginMethodMsgId = null await bot.deleteMessage(chatId, messageId).catch(() => {}) await handleLoginByPhone(chatId) }, hh_login_use_current: async (chatId, messageId) => { const state = getState(chatId) state.awaitingEmail = false state.loginPromptMessageId = null await bot.deleteMessage(chatId, messageId).catch(() => {}) const user = await prisma.user.findUnique({ where: { telegramId: chatId } }) if (!user?.hhEmail) { await bot.sendMessage(chatId, '❌ Email не найден, введи вручную') state.awaitingEmail = true return } await doLogin(chatId, user.hhEmail) }, hh_keep_query: async (chatId, messageId) => { const state = getState(chatId) state.awaitingQuery = false state.queryPromptMessageId = null await bot.deleteMessage(chatId, messageId).catch(() => {}) }, hh_keep_max: async (chatId, messageId) => { const state = getState(chatId) state.awaitingMax = false state.maxPromptMessageId = null await bot.deleteMessage(chatId, messageId).catch(() => {}) }, hh_keep_prompt: async (chatId, messageId) => { const state = getState(chatId) state.awaitingPrompt = false state.promptPromptMessageId = null await bot.deleteMessage(chatId, messageId).catch(() => {}) }, hh_reset_prompt: async (chatId, messageId) => { const state = getState(chatId) state.awaitingPrompt = false state.promptPromptMessageId = null await prisma.user.update({ where: { telegramId: chatId }, data: { prompt: DEFAULT_PROMPT } }) await bot.deleteMessage(chatId, messageId).catch(() => {}) await bot.sendMessage(chatId, '✅ Промт сброшен на дефолтный') }, ob_skip_max: async (chatId, messageId) => { const state = getState(chatId) state.onboardingStep = null state.onboardingMsgId = null await bot.deleteMessage(chatId, messageId).catch(() => {}) await showQueryStep(chatId) }, ob_skip_query: async (chatId, messageId) => { const state = getState(chatId) state.onboardingStep = null state.onboardingMsgId = null await bot.deleteMessage(chatId, messageId).catch(() => {}) await showResumeInfo(chatId) await showPromptStep(chatId) }, ob_skip_prompt: async (chatId, messageId) => { const state = getState(chatId) state.onboardingStep = null state.onboardingMsgId = null await bot.deleteMessage(chatId, messageId).catch(() => {}) await finishOnboarding(chatId) }, hh_resume_list: async (chatId, messageId) => { await bot.deleteMessage(chatId, messageId).catch(() => {}) await handleResumeList(chatId) }, } async function clearAwaitingState(chatId: number): Promise { const state = getState(chatId) const msgIds = [ state.loginMethodMsgId, state.loginPromptMessageId, state.queryPromptMessageId, state.maxPromptMessageId, state.promptPromptMessageId, state.onboardingMsgId, ] state.awaitingEmail = false state.awaitingPhone = false state.awaitingQuery = false state.awaitingMax = false state.awaitingPrompt = false state.onboardingStep = null state.onboardingMsgId = null state.loginMethodMsgId = null state.loginPromptMessageId = null state.queryPromptMessageId = null state.maxPromptMessageId = null state.promptPromptMessageId = null await Promise.all(msgIds.filter(Boolean).map(id => bot.deleteMessage(chatId, id!).catch(() => {}))) } export async function triggerHHStart(chatId: number): Promise { const user = await prisma.user.findUnique({ where: { telegramId: chatId } }) const keyboard = user?.session ? MAIN_REPLY_KEYBOARD : LOGIN_REPLY_KEYBOARD await bot.sendMessage(chatId, '🤖 HH Auto-Apply', { reply_markup: keyboard }) } export function registerHHCommands() { bot.onText(/\/hhstart/, msg => triggerHHStart(msg.chat.id)) bot.on('callback_query', async (query) => { if (!query.message) return const chatId = query.message.chat.id const messageId = query.message.message_id await bot.answerCallbackQuery(query.id).catch(() => {}) const exactHandler = query.data ? CALLBACK_HANDLERS[query.data] : undefined if (exactHandler) { await exactHandler(chatId, messageId) return } if (query.data?.startsWith('hh_resume_pick_')) { const idx = Number(query.data.replace('hh_resume_pick_', '')) await handleResumePick(chatId, messageId, idx) } }) bot.on('message', async (msg) => { if (!msg.text || msg.text.startsWith('/')) return const chatId = msg.chat.id const state = getState(chatId) if (state.isApplying) { await bot.sendMessage(chatId, '⏳ Подождите, идут отклики на вакансии...') return } const isAwaiting = state.awaitingEmail || state.awaitingPhone || state.awaitingQuery || state.awaitingMax || state.awaitingPrompt || state.onboardingStep !== null const isMenuButton = Object.values(BTN).includes(msg.text as typeof BTN[keyof typeof BTN]) if (isMenuButton && isAwaiting) { await clearAwaitingState(chatId) } if (state.onboardingStep === 'max') { const num = Number(msg.text) if (Number.isNaN(num) || num < 1 || num > 50) { await bot.sendMessage(chatId, '❌ Введи число от 1 до 50:') return } state.onboardingStep = null if (state.onboardingMsgId) { await bot.deleteMessage(chatId, state.onboardingMsgId).catch(() => {}) state.onboardingMsgId = null } await bot.deleteMessage(chatId, msg.message_id).catch(() => {}) await prisma.settings.update({ where: { telegramId: chatId }, data: { maxApplies: num } }) await bot.sendMessage(chatId, `✅ Макс откликов: ${num}`) await showQueryStep(chatId) return } if (state.onboardingStep === 'query') { state.onboardingStep = null if (state.onboardingMsgId) { await bot.deleteMessage(chatId, state.onboardingMsgId).catch(() => {}) state.onboardingMsgId = null } await bot.deleteMessage(chatId, msg.message_id).catch(() => {}) const updated = await prisma.settings.update({ where: { telegramId: chatId }, data: { searchQuery: msg.text } }) await bot.sendMessage(chatId, `✅ Запрос: «${updated.searchQuery}»`) await showResumeInfo(chatId) await showPromptStep(chatId) return } if (state.onboardingStep === 'prompt') { state.onboardingStep = null if (state.onboardingMsgId) { await bot.deleteMessage(chatId, state.onboardingMsgId).catch(() => {}) state.onboardingMsgId = null } await bot.deleteMessage(chatId, msg.message_id).catch(() => {}) await prisma.user.upsert({ where: { telegramId: chatId }, update: { prompt: msg.text }, create: { telegramId: chatId, prompt: msg.text, Settings: { create: {} } }, }) await bot.sendMessage(chatId, '✅ Промт сохранён') await finishOnboarding(chatId) return } if (state.awaitingEmail) { state.awaitingEmail = false await bot.deleteMessage(chatId, msg.message_id).catch(() => {}) if (state.loginPromptMessageId) { await bot.deleteMessage(chatId, state.loginPromptMessageId).catch(() => {}) state.loginPromptMessageId = null } await doLogin(chatId, msg.text) return } if (state.awaitingQuery) { state.awaitingQuery = false await bot.deleteMessage(chatId, msg.message_id).catch(() => {}) if (state.queryPromptMessageId) { await bot.deleteMessage(chatId, state.queryPromptMessageId).catch(() => {}) state.queryPromptMessageId = null } const updated = await prisma.settings.update({ where: { telegramId: chatId }, data: { searchQuery: msg.text } }) await bot.sendMessage(chatId, `✅ Запрос: "${updated.searchQuery}"`) return } if (state.awaitingMax) { const num = Number(msg.text) if (Number.isNaN(num) || num < 1 || num > 50) { await bot.sendMessage(chatId, '❌ Введи число от 1 до 50:') return } state.awaitingMax = false await bot.deleteMessage(chatId, msg.message_id).catch(() => {}) if (state.maxPromptMessageId) { await bot.deleteMessage(chatId, state.maxPromptMessageId).catch(() => {}) state.maxPromptMessageId = null } const updated = await prisma.settings.update({ where: { telegramId: chatId }, data: { maxApplies: num } }) await bot.sendMessage(chatId, `✅ Макс откликов: ${updated.maxApplies}`) return } if (state.awaitingPrompt) { state.awaitingPrompt = false await bot.deleteMessage(chatId, msg.message_id).catch(() => {}) if (state.promptPromptMessageId) { await bot.deleteMessage(chatId, state.promptPromptMessageId).catch(() => {}) state.promptPromptMessageId = null } await prisma.user.upsert({ where: { telegramId: chatId }, update: { prompt: msg.text }, create: { telegramId: chatId, prompt: msg.text, Settings: { create: {} } }, }) await bot.sendMessage(chatId, '✅ Промт сохранён') return } await MESSAGE_HANDLERS[msg.text]?.(chatId) }) }