import 'dotenv/config'; import { Markup, Scenes, Telegraf, session } from 'telegraf'; import { startCommand } from './commands/start'; import { menuSceneMiddleware } from './modules/menuScenes'; import express, { Request, Response, NextFunction } from 'express'; import cors from 'cors'; import { events } from './modules/scenes/events'; import { Resume } from './db/db'; import FormData from 'form-data'; import fs from 'fs'; import path from 'path'; import axios from 'axios'; import { saveVacansy } from './modules/scenes/savevacansy'; import { accept } from './modules/scenes/accept'; export type MyWizardContext = Scenes.WizardContext; const app = express(); declare global { namespace Express { interface Request { bot: Telegraf; } } } const bot = new Telegraf(process.env.BOT_TOKEN as string); app.use((req: Request, res: Response, next: NextFunction) => { req.bot = bot; next(); }); app.use(express.json()); app.use(cors()); bot.start(startCommand); app.post('/api/resume', async (req: Request, res: Response) => { try { const postData = req.body; const resumeTemplatePath = path.resolve( __dirname, 'assets', 'шаблон МТУСИ.docx' ); await bot.telegram.sendDocument( postData.id, { source: resumeTemplatePath }, { caption: 'Пример резюме' } ); await bot.telegram.sendMessage( postData.id, `Ваша анкета заполнена! Отправьте своё резюме для завершения регистрации`, { reply_markup: { inline_keyboard: [ [ { text: 'Загрузить резюме', callback_data: 'uploadresume' }, // { text: 'Пропустить загрузку', callback_data: 'skip' }, ], ], }, } ); console.log(`Ваш id ${postData.id}`); return res.json({ status: 'success' }); } catch (e) { console.error('Error:', e); res.status(500).json({ error: 'An error occurred' }); throw e; } }); // const resumeScene: Scenes.WizardScene = new Scenes.WizardScene( // 'resumeScene', // async ctx => { // await ctx.reply( // 'Отправьте своё резюме в PDF формате или отправьте /cancel для отмены.' // ); // return ctx.wizard.next(); // }, // async ctx => { // if ( // ctx.message && // 'text' in ctx.message && // ctx.from && // ctx.message.text == '/cancel' // ) { // await ctx.reply('Отправка резюме отменена.'); // await ctx.reply( // 'Меню', // Markup.keyboard([ // ['Моя анкета'], // ['Вакансии'], // ['Уведомления: включены'], // ]).resize() // ); // return ctx.scene.leave(); // } // if (ctx.message && 'document' in ctx.message && ctx.from) { // const file = ctx.message.document; // if (file.mime_type !== 'application/pdf') { // ctx.reply('Пожалуйста, отправьте файл в формате PDF.'); // return; // } // try { // const fileLink = await ctx.telegram.getFileLink(file.file_id); // const filePath = path.join(__dirname, `${file.file_id}.pdf`); // // Загрузка файла на локальную машину // const response = await axios.get(fileLink.href, { // responseType: 'stream', // }); // response.data // .pipe(fs.createWriteStream(filePath)) // .on('finish', async () => { // // Создание формы данных // const form = new FormData(); // form.append('file', fs.createReadStream(filePath)); // try { // // Установка времени ожидания (в миллисекундах) // const uploadResponse = await axios.post( // `${process.env.API}services/resume/`, // form, // { // headers: { // ...form.getHeaders(), // 'X-API-KEY': // 'SbRHOVoK97GKCx3Lqx6hKXLbZZJEd0GTGbeglXdpK9PhSB9kpr4eWCsuIIwnD6F2mgpTDlVHFCRbeFmuSfqBVsb12lNwF3P1tmdxiktl7zH9sDS2YK7Pyj2DecCWAZ3n', // }, // timeout: 10000, // Увеличьте время ожидания до 10 секунд // } // ); // const fileName = uploadResponse.data.filename; // // Выполнение PUT-запроса для обновления поля Link у студента // await axios.patch( // `${process.env.API}students/${ // ctx.from?.id // }?Link=${encodeURIComponent(fileName)}`, // {}, // { // headers: { // 'X-API-KEY': // 'SbRHOVoK97GKCx3Lqx6hKXLbZZJEd0GTGbeglXdpK9PhSB9kpr4eWCsuIIwnD6F2mgpTDlVHFCRbeFmuSfqBVsb12lNwF3P1tmdxiktl7zH9sDS2YK7Pyj2DecCWAZ3n', // 'Content-Type': 'application/json', // }, // } // ); // await ctx.reply('Резюме успешно загружено.'); // await ctx.reply( // 'Меню', // Markup.keyboard([ // ['Моя анкета'], // ['Вакансии'], // ['Уведомления: включены'], // ]).resize() // ); // return ctx.scene.leave(); // } catch (uploadError) { // console.error('Ошибка при загрузке резюме на API:', uploadError); // await ctx.reply('Произошла ошибка при загрузке резюме.'); // return ctx.scene.leave(); // } finally { // // Удаление временного файла после загрузки // fs.unlinkSync(filePath); // } // }); // } catch (error) { // console.error('Ошибка при загрузке файла:', error); // await ctx.reply('Произошла ошибка при загрузке файла.'); // } // } else { // await ctx.reply('Отправьте файл в формате PDF'); // } // } // ); const resumeScene: Scenes.WizardScene = new Scenes.WizardScene( 'resumeScene', async ctx => { await ctx.reply( 'Отправьте своё резюме в формате Word (DOC/DOCX) или отправьте /cancel для отмены.' ); return ctx.wizard.next(); }, async ctx => { if ( ctx.message && 'text' in ctx.message && ctx.from && ctx.message.text === '/cancel' ) { await ctx.reply('Отправка резюме отменена.'); await ctx.reply( 'Меню', Markup.keyboard([ ['Моя анкета'], ['Вакансии'], ['Уведомления: включены'], ]).resize() ); return ctx.scene.leave(); } if (ctx.message && 'document' in ctx.message && ctx.from) { const file = ctx.message.document; const allowedMimeTypes = [ // 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', ]; if (!allowedMimeTypes.includes(file.mime_type || '')) { await ctx.reply('Пожалуйста, отправьте файл в формате DOC или DOCX.'); return; } try { const fileLink = await ctx.telegram.getFileLink(file.file_id); const fileExtension = file.mime_type === 'application/pdf' ? 'pdf' : file.mime_type === 'application/msword' ? 'doc' : 'docx'; const filePath = path.join( __dirname, `${file.file_id}.${fileExtension}` ); // Загрузка файла на локальную машину const response = await axios.get(fileLink.href, { responseType: 'stream', }); response.data .pipe(fs.createWriteStream(filePath)) .on('finish', async () => { const form = new FormData(); form.append('file', fs.createReadStream(filePath)); try { const uploadResponse = await axios.post( `${process.env.API}services/resume/`, form, { headers: { ...form.getHeaders(), 'X-API-KEY': 'SbRHOVoK97GKCx3Lqx6hKXLbZZJEd0GTGbeglXdpK9PhSB9kpr4eWCsuIIwnD6F2mgpTDlVHFCRbeFmuSfqBVsb12lNwF3P1tmdxiktl7zH9sDS2YK7Pyj2DecCWAZ3n', }, timeout: 10000, } ); const fileName = uploadResponse.data.filename; await axios.patch( `${process.env.API}students/${ ctx.from?.id }?Link=${encodeURIComponent(fileName)}`, {}, { headers: { 'X-API-KEY': 'SbRHOVoK97GKCx3Lqx6hKXLbZZJEd0GTGbeglXdpK9PhSB9kpr4eWCsuIIwnD6F2mgpTDlVHFCRbeFmuSfqBVsb12lNwF3P1tmdxiktl7zH9sDS2YK7Pyj2DecCWAZ3n', 'Content-Type': 'application/json', }, } ); await ctx.reply('Резюме успешно загружено.'); await ctx.reply( 'Меню', Markup.keyboard([ ['Моя анкета'], ['Вакансии'], ['Уведомления: включены'], ]).resize() ); return ctx.scene.leave(); } catch (uploadError) { console.error('Ошибка при загрузке резюме на API:', uploadError); await ctx.reply('Произошла ошибка при загрузке резюме.'); return ctx.scene.leave(); } finally { fs.unlinkSync(filePath); } }); } catch (error) { console.error('Ошибка при загрузке файла:', error); await ctx.reply('Произошла ошибка при загрузке файла.'); } } else { await ctx.reply( 'Пожалуйста, отправьте файл в формате PDF, DOC или DOCX.' ); } } ); // app.post('/api/resume', async (req: Request, res: Response) => { // try { // const postData = req.body; // // Проверяем наличие chatId в теле запроса // if (!postData.StudentID) { // throw new Error('Chat ID is missing in the request body'); // } // const [userInstance, created] = await Resume.findOrCreate({ // where: { id: postData.StudentID }, // defaults: { // id: postData.StudentID || 0, // name: postData.Name, // group: postData.Group, // type: postData.Type, // skills: '', // softskills: postData.Soft_skills, // email: postData.Email, // }, // }); // await UserBase.update( // { resume_id: postData.StudentID }, // { where: { id: postData.StudentID } } // ); // if (userInstance instanceof Resume) { // // userInstance теперь имеет тип User // if (created) { // console.log('New user created:', userInstance); // } else { // console.log('User already exists:', userInstance); // } // console.log('Привет! Вы успешно зарегистрированы.'); // } else { // console.error('Ошибка: userInstance не является экземпляром User.'); // } // console.log('Received data:', postData); // res.status(200).json({ message: 'Data received successfully' }); // } catch (error) { // console.error(error); // res.status(500).json({ error: 'Internal Server Error' }); // } // }); app.post('/webhook', async (req: Request, res: Response) => { try { const postData = req.body; const entityType = postData.data.entity_type; const updatedMatches = postData.data.updated_matches; console.log(postData); if (entityType === 'job') { for (const match of updatedMatches) { const chatId = match.student_id; const jobId = match.entity_id; // Получаем ID вакансии const messageText = 'Ваше совпадение с вакансией было обновлено. Вот детали вакансии:'; if (!chatId) { throw new Error('Неправильный Chat ID (student_id) в теле запроса.'); } // Выполняем GET-запрос для получения данных о вакансии const response = await axios.get( `${process.env.API}jobs/${jobId}`, // Запрашиваем данные о вакансии по её ID { headers: { 'X-API-KEY': 'SbRHOVoK97GKCx3Lqx6hKXLbZZJEd0GTGbeglXdpK9PhSB9kpr4eWCsuIIwnD6F2mgpTDlVHFCRbeFmuSfqBVsb12lNwF3P1tmdxiktl7zH9sDS2YK7Pyj2DecCWAZ3n', }, } ); const vacancyData = response.data; // Данные о вакансии console.log(vacancyData); // Отправляем вакансию пользователю await sendVacancyToUser(chatId, vacancyData); console.log( `Сообщение с вакансией отправлено пользователю с ID ${chatId}` ); } } return res.status(200).json({ status: 'success' }); } catch (e) { console.error('Error:', e); res.status(500).json({ error: 'Произошла ошибка при отправке сообщения' }); } }); // Функция для отправки вакансии пользователю async function sendVacancyToUser(chatId: number, data: any) { await bot.telegram.sendMessage( chatId, `${data.Job_name}\n\n` + `Компания: ${data.Company_name}\n` + `Заработная плата: ${data.Salary} руб/мес\n` + `Контактные данные: ${data.Email}\n\n` + `Требования к кандидату:\n` + ` - ${data.Year} курс\n` + ` - Опыт работы по специальности: ${data.Qualification}\n` + ` - Soft skills: ${data.Soft_skills}\n` + `Обязанности:\n` + `${data.Responsibilities}`, { parse_mode: 'HTML', // Используем HTML для форматирования текста reply_markup: { inline_keyboard: [ [ { text: 'Сохранить вакансию', callback_data: `savevacancy+${data.JobID}`, }, ], ], }, } ); } // Обработчик для получения файла резюме const stage = new Scenes.Stage([resumeScene]); bot.use(session()); bot.use(stage.middleware()); bot.use(menuSceneMiddleware); events(bot); accept(bot); bot.action('uploadresume', async ctx => { ctx.scene.enter('resumeScene'); ctx.answerCbQuery(); }); saveVacansy(bot); bot .launch({ webhook: { domain: process.env.DOMAIN || '', port: parseInt(process.env.HOOKPORT || ''), }, }) .then(() => console.log( 'Webhook bot listening on port', parseInt(process.env.HOOKPORT || '') ) ); // bot.launch().then(() => { // console.log('Бот запущен'); // }); app.listen(process.env.PORT, () => { console.log(`Server is running at http://localhost:${process.env.PORT}`); });