Тестовый коммит после удаления husky

This commit is contained in:
2025-03-05 01:02:09 +03:00
parent 97ca5e4b64
commit 3157ad0cd9
118 changed files with 8177 additions and 8530 deletions

View File

@@ -1,68 +1,246 @@
const { pool } = require('../db');
const nodemailer = require('nodemailer');
const { ChatOllama } = require('@langchain/ollama');
const { PGVectorStore } = require('@langchain/community/vectorstores/pgvector');
const { Pool } = require('pg');
const Imap = require('imap');
const { simpleParser } = require('mailparser');
const { checkMailServer } = require('../utils/checkMail');
const { sleep, isValidEmail } = require('../utils/helpers');
const { linkIdentity, getUserIdByIdentity } = require('../utils/identity-linker');
require('dotenv').config();
const simpleParser = require('mailparser').simpleParser;
const { processMessage } = require('./ai-assistant');
class EmailBotService {
constructor() {
this.enabled = false;
console.log('EmailBotService: Сервис отключен (заглушка)');
// Конфигурация для отправки писем
const transporter = nodemailer.createTransport({
host: process.env.EMAIL_SMTP_HOST,
port: process.env.EMAIL_SMTP_PORT,
secure: process.env.EMAIL_SMTP_PORT === '465', // true для 465, false для других портов
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASSWORD,
},
});
// Конфигурация для получения писем
const imapConfig = {
user: process.env.EMAIL_USER,
password: process.env.EMAIL_PASSWORD,
host: process.env.EMAIL_IMAP_HOST,
port: process.env.EMAIL_IMAP_PORT,
tls: true,
tlsOptions: { rejectUnauthorized: false },
};
/**
* Инициализация сервиса электронной почты
*/
function initEmailBot() {
if (!process.env.EMAIL_USER || !process.env.EMAIL_PASSWORD) {
console.warn('EMAIL_USER or EMAIL_PASSWORD not set, Email integration disabled');
return null;
}
async start() {
console.log('EmailBotService: Запуск сервиса отключен (заглушка)');
return false;
}
console.log('Email bot initialized');
async stop() {
console.log('EmailBotService: Остановка сервиса отключена (заглушка)');
return true;
}
// Запуск проверки почты каждые 5 минут
const checkInterval = 5 * 60 * 1000; // 5 минут
setInterval(checkEmails, checkInterval);
isEnabled() {
return this.enabled;
// Первая проверка при запуске
checkEmails();
return {
sendEmail,
checkEmails,
};
}
/**
* Проверка новых писем
*/
function checkEmails() {
const imap = new Imap(imapConfig);
imap.once('ready', () => {
imap.openBox('INBOX', false, (err, box) => {
if (err) {
console.error('Error opening inbox:', err);
return;
}
// Поиск непрочитанных писем
imap.search(['UNSEEN'], (err, results) => {
if (err) {
console.error('Error searching emails:', err);
return;
}
if (results.length === 0) {
console.log('No new emails');
imap.end();
return;
}
console.log(`Found ${results.length} new emails`);
const f = imap.fetch(results, { bodies: '' });
f.on('message', (msg, seqno) => {
msg.on('body', (stream, info) => {
simpleParser(stream, async (err, parsed) => {
if (err) {
console.error('Error parsing email:', err);
return;
}
try {
// Обработка письма
await processEmail(parsed);
// Пометить как прочитанное
imap.setFlags(results, ['\\Seen'], (err) => {
if (err) {
console.error('Error marking email as read:', err);
}
});
} catch (error) {
console.error('Error processing email:', error);
}
});
});
});
f.once('error', (err) => {
console.error('Fetch error:', err);
});
f.once('end', () => {
imap.end();
});
});
});
});
imap.once('error', (err) => {
console.error('IMAP error:', err);
});
imap.connect();
}
/**
* Обработка полученного письма
* @param {Object} email - Распарсенное письмо
*/
async function processEmail(email) {
try {
const from = email.from.value[0].address;
const subject = email.subject;
const text = email.text || '';
console.log(`Processing email from ${from}, subject: ${subject}`);
// Поиск пользователя по email
const userResult = await pool.query(
`SELECT u.* FROM users u
JOIN user_identities ui ON u.id = ui.user_id
WHERE ui.identity_type = 'email' AND ui.identity_value = $1 AND ui.verified = TRUE`,
[from]
);
if (userResult.rows.length === 0) {
console.log(`No verified user found for email ${from}`);
// Отправка ответа о необходимости регистрации
await sendEmail(
from,
'Регистрация в системе',
'Для использования ИИ-ассистента через email, пожалуйста, зарегистрируйтесь на нашем сайте и подтвердите свой email.'
);
return;
}
const user = userResult.rows[0];
// Получение или создание диалога
const conversationResult = await pool.query(
`SELECT * FROM conversations
WHERE user_id = $1
ORDER BY updated_at DESC
LIMIT 1`,
[user.id]
);
let conversationId;
if (conversationResult.rows.length === 0) {
// Создание нового диалога
const newConversationResult = await pool.query(
`INSERT INTO conversations (user_id, title)
VALUES ($1, $2)
RETURNING id`,
[user.id, subject || 'Email диалог']
);
conversationId = newConversationResult.rows[0].id;
} else {
conversationId = conversationResult.rows[0].id;
}
// Сохранение сообщения пользователя
await pool.query(
`INSERT INTO messages (conversation_id, sender_type, sender_id, content, channel)
VALUES ($1, $2, $3, $4, $5)`,
[conversationId, 'user', user.id, text, 'email']
);
// Обработка сообщения ИИ-ассистентом
const aiResponse = await processMessage(user.id, text, user.language || 'ru');
// Сохранение ответа ИИ
await pool.query(
`INSERT INTO messages (conversation_id, sender_type, sender_id, content, channel)
VALUES ($1, $2, $3, $4, $5)`,
[conversationId, 'ai', null, aiResponse, 'email']
);
// Обновление времени последнего обновления диалога
await pool.query(
`UPDATE conversations
SET updated_at = NOW()
WHERE id = $1`,
[conversationId]
);
// Отправка ответа пользователю
await sendEmail(from, `Re: ${subject}`, aiResponse);
console.log(`Sent response to ${from}`);
} catch (error) {
console.error('Error processing email:', error);
throw error;
}
}
// В обработчике команд добавьте код для связывания аккаунтов
async function processCommand(email, command, args) {
if (command === 'link' && args.length > 0) {
const ethAddress = args[0];
// Проверяем формат Ethereum-адреса
if (!/^0x[a-fA-F0-9]{40}$/.test(ethAddress)) {
return 'Неверный формат Ethereum-адреса. Используйте формат 0x...';
}
try {
// Получаем ID пользователя по Ethereum-адресу
const userId = await getUserIdByIdentity('ethereum', ethAddress);
if (!userId) {
return 'Пользователь с таким Ethereum-адресом не найден. Сначала войдите через веб-интерфейс.';
}
// Связываем Email-аккаунт с пользователем
const success = await linkIdentity(userId, 'email', email);
if (success) {
return `Ваш Email-аккаунт успешно связан с Ethereum-адресом ${ethAddress}`;
} else {
return 'Не удалось связать аккаунты. Возможно, этот Email-аккаунт уже связан с другим пользователем.';
}
} catch (error) {
console.error('Ошибка при связывании аккаунтов:', error);
return 'Произошла ошибка при связывании аккаунтов. Попробуйте позже.';
}
/**
* Отправка email
* @param {string} to - Адрес получателя
* @param {string} subject - Тема письма
* @param {string} text - Текст письма
* @returns {Promise<Object>} - Результат отправки
*/
async function sendEmail(to, subject, text) {
try {
const info = await transporter.sendMail({
from: process.env.EMAIL_USER,
to,
subject,
text,
});
console.log('Email sent:', info.messageId);
return info;
} catch (error) {
console.error('Error sending email:', error);
throw error;
}
// Обработка других команд...
}
module.exports = EmailBotService;
module.exports = {
initEmailBot,
sendEmail,
checkEmails,
};