214 lines
7.9 KiB
JavaScript
214 lines
7.9 KiB
JavaScript
const { Bot } = require("grammy");
|
||
const fs = require("fs");
|
||
const path = require("path");
|
||
|
||
const bot = new Bot(process.env.BOT_TOKEN);
|
||
const PROGRESS_FILE = path.join(__dirname, "progress.json");
|
||
|
||
// База уроков: темы → слова/фразы
|
||
const LESSONS = {
|
||
accounting: {
|
||
name: "📊 Бухгалтерия",
|
||
words: [
|
||
{ en: "Advance report", ru: "Авансовый отчет" },
|
||
{ en: "Business trip", ru: "Командировка" },
|
||
{ en: "Invoice", ru: "Счет-фактура" },
|
||
{ en: "Receipt", ru: "Квитанция/чек" },
|
||
{ en: "Expense", ru: "Расход" },
|
||
{ en: "Budget", ru: "Бюджет" },
|
||
{ en: "Tax", ru: "Налог" },
|
||
{ en: "Audit", ru: "Аудит/проверка" },
|
||
{ en: "Payroll", ru: "Зарплатная ведомость" },
|
||
{ en: "Reimbursement", ru: "Возмещение расходов" }
|
||
]
|
||
},
|
||
travel: {
|
||
name: "✈️ Командировки",
|
||
words: [
|
||
{ en: "Flight ticket", ru: "Авиабилет" },
|
||
{ en: "Hotel booking", ru: "Бронь отеля" },
|
||
{ en: "Per diem", ru: "Суточные" },
|
||
{ en: "Travel allowance", ru: "Подъемные" },
|
||
{ en: "Boarding pass", ru: "Посадочный талон" },
|
||
{ en: "Departure", ru: "Вылет/отправление" },
|
||
{ en: "Arrival", ru: "Прилет/прибытие" },
|
||
{ en: "Luggage", ru: "Багаж" },
|
||
{ en: "Customs", ru: "Таможня" },
|
||
{ en: "Itinerary", ru: "Маршрутный лист" }
|
||
]
|
||
},
|
||
business: {
|
||
name: "💼 Деловая переписка",
|
||
words: [
|
||
{ en: "Dear colleague", ru: "Уважаемый коллега" },
|
||
{ en: "Best regards", ru: "С уважением" },
|
||
{ en: "Please find attached", ru: "Прилагаю" },
|
||
{ en: "In response to your request", ru: "В ответ на ваш запрос" },
|
||
{ en: "We confirm", ru: "Подтверждаем" },
|
||
{ en: "Please note", ru: "Обратите внимание" },
|
||
{ en: "For your information", ru: "Для вашего сведения" },
|
||
{ en: "Kindly request", ru: "Любезно просим" },
|
||
{ en: "As discussed", ru: "Как обсуждали" },
|
||
{ en: "Looking forward", ru: "С нетерпением ждем" }
|
||
]
|
||
},
|
||
phrases: {
|
||
name: "💬 Полезные фразы",
|
||
words: [
|
||
{ en: "Could you please clarify?", ru: "Не могли бы вы уточнить?" },
|
||
{ en: "I'll get back to you shortly", ru: "Я скоро вернусь к вам" },
|
||
{ en: "Please let me know", ru: "Пожалуйста, дайте знать" },
|
||
{ en: "I'm afraid I can't", ru: "Боюсь, я не могу" },
|
||
{ en: "Thank you for your cooperation", ru: "Спасибо за сотрудничество" },
|
||
{ en: "Is everything clear?", ru: "Все ли понятно?" },
|
||
{ en: "Keep me updated", ru: "Держите меня в курсе" },
|
||
{ en: "I'll check and confirm", ru: "Я проверю и подтвержу" }
|
||
]
|
||
}
|
||
};
|
||
|
||
function loadProgress() {
|
||
try {
|
||
return JSON.parse(fs.readFileSync(PROGRESS_FILE, "utf8"));
|
||
} catch (_) {
|
||
return {};
|
||
}
|
||
}
|
||
|
||
function saveProgress(progress) {
|
||
fs.writeFileSync(PROGRESS_FILE, JSON.stringify(progress, null, 2));
|
||
}
|
||
|
||
bot.command("start", async (ctx) => {
|
||
await ctx.reply(
|
||
`👋 Привет, ${ctx.from.first_name}!
|
||
|
||
Я твой тренер по английскому языку.
|
||
|
||
📚 Доступные темы:
|
||
1️⃣ ${LESSONS.accounting.name}
|
||
2️⃣ ${LESSONS.travel.name}
|
||
3️⃣ ${LESSONS.business.name}
|
||
4️⃣ ${LESSONS.phrases.name}
|
||
|
||
🎯 Что я умею:
|
||
• Учить слова и фразы
|
||
• Проводить тесты
|
||
• Показывать прогресс
|
||
|
||
Выбери тему цифрой (1-4) или напиши /test для теста.`
|
||
);
|
||
});
|
||
|
||
bot.command("test", async (ctx) => {
|
||
const progress = loadProgress();
|
||
const userId = String(ctx.from.id);
|
||
|
||
if (!progress[userId] || progress[userId].total === 0) {
|
||
await ctx.reply("Сначала пройди уроки! Выбери тему цифрой 1-4");
|
||
return;
|
||
}
|
||
|
||
const score = Math.round((progress[userId].correct / progress[userId].total) * 100);
|
||
await ctx.reply(
|
||
`📊 Твой прогресс:
|
||
✅ Правильно: ${progress[userId].correct}
|
||
❌ Ошибок: ${progress[userId].total - progress[userId].correct}
|
||
🎯 Точность: ${score}%
|
||
|
||
Продолжай учиться!`
|
||
);
|
||
});
|
||
|
||
bot.on("message:text", async (ctx) => {
|
||
const text = ctx.message.text.trim();
|
||
const userId = String(ctx.from.id);
|
||
const progress = loadProgress();
|
||
|
||
// Проверка: ждем ли мы ответ на тест?
|
||
if (progress[userId] && progress[userId].waitingAnswer) {
|
||
const currentWord = progress[userId].currentWord;
|
||
const correctIndex = progress[userId].correctIndex;
|
||
|
||
const answer = parseInt(text);
|
||
if (isNaN(answer) || answer < 1 || answer > 4) {
|
||
await ctx.reply("Напиши цифру от 1 до 4");
|
||
return;
|
||
}
|
||
|
||
const isCorrect = answer === correctIndex;
|
||
progress[userId].total = (progress[userId].total || 0) + 1;
|
||
if (isCorrect) {
|
||
progress[userId].correct = (progress[userId].correct || 0) + 1;
|
||
await ctx.reply(`✅ Правильно!\n\n"${currentWord.en}" = "${currentWord.ru}"`);
|
||
} else {
|
||
await ctx.reply(`❌ Ошибка.\n\n"${currentWord.en}" = "${currentWord.ru}"`);
|
||
}
|
||
progress[userId].waitingAnswer = false;
|
||
delete progress[userId].currentWord;
|
||
delete progress[userId].correctIndex;
|
||
saveProgress(progress);
|
||
|
||
// Предложить следующий тест
|
||
await ctx.reply("Напиши /test для проверки прогресса или выбери тему (1-4)");
|
||
return;
|
||
}
|
||
|
||
// Выбор темы
|
||
if (["1", "2", "3", "4"].includes(text)) {
|
||
const keys = Object.keys(LESSONS);
|
||
const topic = keys[parseInt(text) - 1];
|
||
if (!topic) {
|
||
await ctx.reply("Неверная тема. Выбери 1-4");
|
||
return;
|
||
}
|
||
|
||
const lesson = LESSONS[topic];
|
||
const randomWord = lesson.words[Math.floor(Math.random() * lesson.words.length)];
|
||
|
||
await ctx.reply(
|
||
`📖 ${lesson.name}\n\nСлово: *${randomWord.en}*\n\nПеревод: _${randomWord.ru}_\n\nНапиши /test для теста или выбери другую тему (1-4)`,
|
||
{ parse_mode: "Markdown" }
|
||
);
|
||
return;
|
||
}
|
||
|
||
// Тест с вариантами
|
||
if (text.toLowerCase() === "тест" || text.toLowerCase() === "test") {
|
||
const allWords = Object.values(LESSONS).flatMap(l => l.words);
|
||
const randomWord = allWords[Math.floor(Math.random() * allWords.length)];
|
||
|
||
// Генерируем 3 неправильных варианта
|
||
const wrongOptions = allWords
|
||
.filter(w => w.en !== randomWord.en)
|
||
.sort(() => Math.random() - 0.5)
|
||
.slice(0, 3)
|
||
.map(w => w.ru);
|
||
|
||
const options = [...wrongOptions, randomWord.ru].sort(() => Math.random() - 0.5);
|
||
const correctIndex = options.indexOf(randomWord.ru) + 1;
|
||
|
||
progress[userId] = progress[userId] || {};
|
||
progress[userId].waitingAnswer = true;
|
||
progress[userId].currentWord = randomWord;
|
||
progress[userId].correctIndex = correctIndex;
|
||
saveProgress(progress);
|
||
|
||
await ctx.reply(
|
||
`🧠 Тест: выбери правильный перевод\n\n*${randomWord.en}*\n\n` +
|
||
options.map((o, i) => `${i + 1}. ${o}`).join("\n") +
|
||
`\n\nНапиши цифру (1-4)`,
|
||
{ parse_mode: "Markdown" }
|
||
);
|
||
return;
|
||
}
|
||
|
||
// Обычное сообщение
|
||
await ctx.reply(
|
||
"Напиши:\n• 1-4 — выбрать тему\n• /test — тест и прогресс\n• test — начать тест"
|
||
);
|
||
});
|
||
|
||
bot.start();
|
||
console.log("🇬🇧 English Trainer Bot запущен!");
|