telegram-lesson — это Telegram-бот, который отображает расписание занятий для колледжа. Проект разрабатывается командой #include. Бот предоставляет удобный и простой способ для студентов получать доступ к своему расписанию непосредственно через Telegram.
Для запуска проекта необходимо:
-
Установить пакеты
npm install -y
-
Отредактировать файл
.env.example, вставить в него ваши данные и переименовать его в.env. В этом файле укажите следующие переменные:BOT_TOKEN: Токен вашего Telegram бота.MONGODB_URI: URI вашей MongoDB базы данных.REDIS_URL: URL вашего Redis сервера.EXEL_START_DATE: Дата начала расписания в формате(dd.MM.yy) -1 день.EXEL_ID: ID вашей таблицы в Google Sheets.
-
После этого используйте команды:
Запуск проекта:
npm run start
Разработка проекта:
npm run dev
Проект организован в несколько ключевых директорий и файлов, каждый из которых выполняет определенную функцию:
- api: Содержит слой взаимодействия с API для общения с API расписания колледжа.
- command: Хранит обработчики команд для различных команд бота.
- init: Инициализирует обработчики команд.
- start: Содержит логику для команды
/start. - text: Содержит логику для текстовых команд.
- db: Файлы подключения к базам данных. Определяет модели MongoDB, используемые ботом.
- index.js: Главный исполняемый файл.
- menu: Содержит структуры меню для бота.
Директория api содержит файлы, которые отвечают за связь с внешними сервисами. Этот слой упрощает процесс отправки запросов к API и обработки ответов.
-
api.js: Файл который служит прослойкой для удобного взаимодействия с API расписания HEXLET колледжа.
// Пример api.js // Запросы GET / POST async function get(url) {...} async function post({ url, data }) {...} // Класс API export default class API { // Получение групп static async groups() {...} // Получение занятий для группы static async lessons(groupId) {...} }
-
sheets.js: Файл который служит прослойкой для удобного взаимодействия с Google Sheets расписания MGOK колледжа.
// Пример sheets.js const timeTable = [ ["09:00", "10:30"], // начало, конец пары ... ]; // Главная функция получения данных с таблицы async function getSheetData(spreadsheetId, range) {} // Функция перевода числа в букву // Example: numberToLetters(1) => "A" function numberToLetters(num) {} // Функция расчета дней без воскресенья function calculateDaysWithoutSunday(startDate, endDate) {} export default class { // Получение вкладок(название курсов) static async getSheetTabs() {} // Получение групп, если указать tab то функция вернет группы определенного курса // tab - номер вкладки static async groups(tab) {} // Получение занятий // groupName - название группы // tab - номер вкладки // date - дата в формате Date (Нужно указывать дату начало недели) static async lessons(groupName, tab, date) {} }
Директория command организована в поддиректории для различных типов команд:
-
Init: Содержит файлы, которые инициализируют обработчики команд. Эти файлы захватывают события из Telegram и запускают соответствующие файлы с логикой для этих событий.
// Пример Init.StartCommand.js // Подключение компонентов import CommandExample from "../text/Command.Example.js"; // Класс инициализации обработчиков export default class InitStartCommand { constructor(bot) { this.bot = bot; // bot из telegraf this.initCommandList = []; // Список обработчиков } handle() { this.bot.on("text", async (ctx) => { // payload для каждого обработчика const payload = {...}; // Инициализация обработчиков this.initCommandList = [new CommandExample(payload)]; // Запуск обработчиков this.initCommandList.forEach((command) => command.handle()); }); } }
-
Text / Start: Содержит логику для команд, с которой взаимодействуют пользователи.
// Пример Command.Start.js // Класс команды export default class CommandStart { constructor(bot, ctx) {...} async handle() { // Получение данных пользователя const text = ...; const chatId = ...; // Проверка команды if (text !== "/start") return; // Логика команды await ctx.reply("Добро пожаловать! Выберите вашу группу:"); } }
Директория db определяет модели MongoDB, используемые ботом. Эти модели структурируют данные, хранящиеся в базе данных, обеспечивая эффективное извлечение и управление информацией пользователей.
Так-же в этой директории находятся файлы для удобного подключения к базам данных, такми как redis mongoose
-
connect Директория с файлами для подключения к базам данных
-
redis.js: Файл для подключения к базе данных Redis.
// Пример redis.js // Получение значений из environment или задание значения по умолчанию const redisUrl = process.env.REDIS_URL || "redis://server.thenofis.ru:6379"; const redisClient = await createClient({ url: redisUrl, }) // Обработка ошибок и подключение .on("error", (err) => console.log("Redis Client Error", err)) .on("ready", () => console.log("Redis good connect")) .connect(); export default redisClient;
-
mongoose.js: Файл для подключения к базе данных MongoDB.
// Пример mongoose.js const mongoUrl = process.env.MONGODB_URI || "mongodb://localhost:27017"; const mongoClient = mongoose .set("strictQuery", false) // Обработка ошибок и подключение .connect(mongoUrl) .then(() => console.log("MongoDB connected")) .catch((err) => console.error("MongoDB not connected", err)); export default mongoClient;
-
sheet.js: Файл для подключения к базе данных Google Sheets.
// Пример sheet.js const auth = await new google.auth.GoogleAuth({ keyFile: "credentials.json", // credentials.json должен быть в корневой директории scopes: ["https://www.googleapis.com/auth/spreadsheets.readonly"], }).getClient(); export default google.sheets({ version: "v4", auth: auth });
-
-
model Директория с моделями базы данных
- User.js: Модель пользователя.
// Пример User.js const Mgok = new Schema({ groupName: { type: String, default: null }, course: { type: Number, default: null }, }); const Hexlet = new Schema({ groupName: { type: String, default: null }, groupId: { type: Number, default: null }, }); const User = new Schema({ id: { type: String, default: v4(), required: true, unique: true }, // UUID пользователя внутри бота username: { type: String, required: true }, // username в телеграме telegramId: { type: Number, required: true }, // id пользователя в телеграме createdAt: { type: Number, default: Date.now }, // Дата создания пользователя mgok: { type: Mgok }, // Группа MGOK hexlet: { type: Hexlet }, // Группа Hexlet groupName: { type: String, default: null }, // Название группы groupId: { type: Number, default: null }, // ID группы table: { type: Boolean, default: false }, // Тип отправляемой таблицы 0 - TEXT, 1 - IMAGE });
Файл index.js является главным исполняемым файлом. Он служит точкой входа для приложения, инициализируя бота и настраивая необходимые конфигурации.
// Пример index.js
// Подключение к базе данных
import "./db/connect/mongodb.js";
// Импорт инициализаторов обработчиков
import InitExampleCommand from "./command/init/Init.ExampleCommand.js";
// Класс бота
class Bot {
constructor(props) {
this.bot = {...};
this.initCommandList = [...];
}
init() {
// payload для каждого инициализатора
const payload = ...;
// Инициализация инициализаторов
this.initCommandList = [new InitExampleCommand(payload)];
// Запуск инициализаторов
this.initCommandList.forEach((command) => command.handle());
// Подключение к базе данных
mongoose.connect(this.mongoDBUrl, { useNewUrlParser: true, useUnifiedTopology: true });
// Запуск бота
this.bot.launch();
}
}
const bot = new Bot({ BOT_TOKEN: token, MONGODB_URL: mongodbUrl });
bot.init();Директория html шаблон html для создания таблицы расписания, в будующем планируется добавления нескольких тем.
-
table.html - Самый простой шаблон для создания таблицы расписания
<!-- Пример table.html --> <!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> </head> <body> <table class="table-lessons"> <thead> <tr> <th class="time">#</th> <th class="caption-lessons">Занятия</th> </tr> </thead> <tbody> {CONTENT} </tbody> </table> </body> <style> body { display: table; margin: 0; padding: 0; } /* СТИЛИ .... */ </style> </html>
Директория menu содержит структуры меню для бота, предоставляя способ представить опции и навигацию по различным разделам бота.
// Пример Menu.Main.js
// Импорт Markup
import { Markup } from "telegraf";
// Создание кнопок и их экспорт
export default Markup.keyboard([
Markup.button.callback("Группы", "groups"),
Markup.button.callback("Расписание", "schedule"),
])
.oneTime()
.resize();Директория utils файлы, которые служат для упрощения работы с таблицами.
-
createTable.js - Функция для создания таблицы расписания PHOTO/TEXT
// Пример createTable.js import { readFileSync } from "fs"; import { format } from "date-fns"; import nodeHtmlToBuffer from "node-html-to-image"; const table = readFileSync("./src/html/table.html", "utf-8"); // День недели const weekDay = [...]; // Функция создания таблицы в виде фото // rows - массив с расписанием // date - дата const createPhotoTable = async (rows, date) => { const trs = rows.map((row) => { const { startTime, endTime, subject, teachers, cabinet } = row; return createTr( startTime, endTime, subject?.name, teachers?.map((e) => e.fio)?.join(" | "), cabinet?.name || "Не указан", ); }); const content = table.replace("{CONTENT}", trs.join("\n")); const buffer = await nodeHtmlToBuffer({ html: content, options: { format: "png", }, }); return { buffer, caption: `Расписание на ${format(date, "dd.MM.yy")} (${weekDay[date.getDay()]})`, }; }; // Функция создания текстовой таблицы // rows - массив с расписанием // date - дата const createTextTable = (rows, date) => { const table = [ `┏━━━━━━━━━━━━━━━━━━━━━━\n┃ Дата: ${format(date, "dd.MM.yy")} (${weekDay[date.getDay()]})\n┣━━━━━━━━━━━━━━━━━━━━━━`, ]; rows.forEach((row, i) => { const { startTime, endTime, subject, teachers, cabinet } = row; table.push(`┃ ${startTime} ${maxLength(subject?.name, 15)}`); table.push(`┃ ${endTime} ${teachers?.map((e) => e.fio)?.join(" | ")}`); table.push(`┃ Каб. ${cabinet?.name || "***Не указан***"}`); if (i != rows.length - 1) table.push("┣━━━━━━━━━━━━━━━━━━━━━━"); }); table.push("┗━━━━━━━━━━━━━━━━━━━━━━"); return table.join("\n"); }; // Создание строк таблиц для createTextTable function createTr(startTime, endTime, lesson, teacher, cabinet) { if (lesson) return `<tr> <td class="time"> <div>${startTime}</div> <div>${endTime}</div> </td> <td> <div class="row-lessons"> <span>${lesson}</span> <span>${teacher}</span> <span>${cabinet}</span> </div> </td> </tr>`; else return `<tr> <td class="time"> <div>${startTime}</div> <div>${endTime}</div> </td> </tr>`; } // Максимальная длина строки function maxLength(word, length) { if (word.length > length) return word.slice(0, length) + "..."; return word; } export { createTextTable, createPhotoTable };
- Telegram API: Используется библиотека
telegrafjsдля взаимодействия с Telegram API. - База данных: В качестве базы данных используется
mongodb. - Система кеширования: Для кеширования данных применяется
redis.
- API: Слой взаимодействия с API упрощает процесс отправки запросов и обработки ответов, что делает код более читаемым и поддерживаемым.
- Обработчики команд: Обработчики команд организованы в поддиректории для различных типов команд, что облегчает управление и расширение функциональности бота.
- Модели базы данных: Модели MongoDB структурируют данные, хранящиеся в базе данных, обеспечивая эффективное извлечение и управление информацией пользователей.
- Главный исполняемый файл: Файл
index.jsслужит точкой входа для приложения, инициализируя бота и настраивая необходимые конфигурации. - Структуры меню: Структуры меню предоставляют способ представить опции и навигацию по различным разделам бота, улучшая пользовательский опыт.
Этот проект является открытым исходным кодом, и мы приветствуем вклад от сообщества. Если у вас есть предложения или вы обнаружили ошибку, пожалуйста, создайте issue или pull request.
