javascript

Node.js-бот для Телеграм: CRUD-L через аргументы команд

  • понедельник, 14 октября 2024 г. в 00:00:03
https://habr.com/ru/articles/850294/

Я продолжаю описывать собственное погружение в мир телеграм-ботов, начатое в предыдущей публикации. Тогда я создал простого бота на Node.js с тремя стандартными командами (/start, /help, /settings) с использованием библиотеки grammY, который мог работать в режимах long polling и webhook. В этот раз я разработал бота, который манипулирует данными в базе по шаблону CRUD + List (CRUDL) с помощью аргументов команд. Из-за своей простоты, граничащей с примитивностью, это решение не подходит для коммерческих проектов, но может быть полезным в персональных проектах.

Демонстрационный телеграм-бот @flancer64/tg-demo-crudl позволяет создавать в БД список контактов и номеров телефонов. Вот список соответствующих команд:

  • /create <имя> <номер телефона>: добавляет новый контакт в список (/create John 123456789)

  • /read <id>: выводит информацию о контакте по указанному идентификатору (/read 1)

  • /update <id> <новое имя> <новый номер телефона>: обновляет существующий контакт (/update 1 Jane 987654321)

  • /delete <id>: удаляет контакт из списка (/delete 1)

  • /list: отображает все сохранённые контакты

Основные технологии проекта

  • grammY — npm-пакет для взаимодействия с Telegram API из Node.js.

  • Knex.js — npm-пакет, который позволяет работать с различными СУБД (уровень абстракции базы данных, DBAL).

  • @teqfw/di — npm-пакет для внедрения зависимостей, обеспечивающий гибкость и расширяемость приложения.

  • @flancer32/teq-telegram-bot — npm-пакет для выполнения типовых операций телеграм-бота (запуск в режиме long polling и webhook, создание списка команд, настройка бота и т.п.).

Типовая команда

Создание каркаса бота, его регистрация в Телеграм, запуск и остановку я подробно рассмотрел в предыдущей публикации. Там же описана и архитектура проекта. Здесь я продемонстрирую реализацию типовой команды на примере /create (Demo_Crudl_Back_Bot_Cmd_Create).

Внедрение зависимостей

ES6-модуль ./src/Back/Bot/Cmd/Create.js обрабатывается контейнером объектов, который создаёт и внедряет необходимые зависимости:

Скрытый текст
export default class Demo_Crudl_Back_Bot_Cmd_Create {
    constructor(
        {
            TeqFw_Core_Shared_Api_Logger$$: logger,
            TeqFw_Db_Back_RDb_IConnect$: conn,
            TeqFw_Db_Back_Api_RDb_CrudEngine$: crud,
            Demo_Crudl_Back_Store_RDb_Schema_Phone$: rdbPhone,
        }
    ) {}
}

  • TeqFw_Core_Shared_Api_Logger$$: logger — логгер для трассировки выполнения команды.

  • TeqFw_Db_Back_RDb_IConnect$: conn — соединение с базой данных.

  • TeqFw_Db_Back_Api_RDb_CrudEngine$: crud — объект для выполнения базовых операций с БД.

  • Demo_Crudl_Back_Store_RDb_Schema_Phone$: rdbPhone — DTO для описания структуры данных, хранимых в базе данных.

Первые три зависимости являются стандартными инструментами из моего набора TeqFW, а последняя относится непосредственно к предметной области.

DTO для БД

DTO Demo_Crudl_Back_Store_RDb_Schema_Phone описывает простую структуру из четырёх полей:

class Dto {
    date_created; 
    id; 
    name;
    phone;
}

которая соответствует следующей таблице в базе данных SQLite:

CREATE TABLE main.phone
(
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    name VARCHAR(255) NOT NULL,
    phone VARCHAR(255) NOT NULL,
    date_created DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL
);

Этого достаточно, чтобы иметь представление о структуре данных в БД. Демо-бот работает с SQLite, но DBAL Knex.js позволяет также использовать PostgreSQL, MariaDB/MySQL, MS SQL и Oracle. Нужно лишь установить соответствующий npm-пакет для работы с выбранной СУБД.

Параметры подключения Knex.js к соответствующей СУБД прописываются в конфигурационном файле ./cfg/local.json (шаблон настроек - в ./cfg/init.json). В демо-версии используется SQLite, файл в базой располагается в ./var/data.db. Для работы с базой предназначены команды:

$ ./bin/tequila.mjs db-init
$ ./bin/tequila.mjs db-export -f ./var/data.json
$ ./bin/tequila.mjs db-import -f ./var/data.json

Назначения команд понятны из названия: создание структур данных (таблица), экспорт и импорт данных в JSON-формат.

Шаблон обработки Телеграм-команды

Так как наш бот для работы использует платформу grammY, то типовой обработчик команд работает с объектами кода этой платформы (ctx). Вот скелет обработчика:

const handler = async (ctx) => {
    let msg = 'The command has failed.';
    const from = ctx.message.from;
    logger.info(`Command has been received from user '${from.username}' (id:${from.id})`);
    const trx = await conn.startTransaction();
    try {
        // const parts = ctx.message.text.split(' ');
        // ... 
        await trx.commit();
    } catch (e) {
        await trx.rollback();
        msg = e.toString();
        logger.error(msg);
    }
    // https://core.telegram.org/bots/api#sendmessage
    await ctx.reply(msg, {
        parse_mode: 'HTML',
    });
};

Алгоритм действий в обработчике такой:

  • залогировать принятую команду

  • инициировать транзакцию для работы с БД

  • выполнить действия с БД в рамках транзакции и сформировать ответ на принятую команду

  • зафиксировать изменения в БД

  • сфомировать сообщение для пользователя об успешном завершении выполнения команды

  • в случае возникновения ошибок отдать пользователю сообщение об ошибке

Выполнение команды

Данный фрагмент кода демонстрирует непосредственно обработку самой команды create - извлечение данных из команды и сохранение их в БД:

try {
    const parts = ctx.message.text.split(' ');
    const dto = rdbPhone.createDto();
    dto.name = parts[1];
    dto.phone = parts[2];
    const {[A_PHONE.ID]: id} = await crud.create(trx, rdbPhone, dto);
    await trx.commit();
    msg = `New record #${id} has been created.`;
    logger.info(msg);
} catch (e) {...}

Я использую свою собственную обёртку для DBAL Knex.js, которая позволяет мне выполнять базовые операции (CRUD) с DTO. Если у вас другая библиотека для работы с данными в БД (например, sequelize или prisma), то у вас здесь будет другой код. Но всю остальную обвязку можно оставлять (за исключением транзакций).

Пример работы бота

После создания бота в BotFather, получения API-ключа и конфигурировании бота (./cfg/local.json) нужно создать таблицы в БД:

$ ./bin/tequila.mjs db-init

Запуск бота в режиме long polling:

$ ./bin/tequila.mjs tg-bot-start

или

$ npm start

Вызов справки

Create

Read

Update

List

Delete

Заключение

В данной статье я описал процесс создания простого телеграм-бота на Node.js с использованием библиотеки grammY, который поддерживает операции CRUD-L (создание, чтение, обновление, удаление и список). Мы рассмотрели, как с помощью команд с аргументами можно манипулировать данными в базе данных. Несмотря на простоту, данный подход открывает возможности для персональных проектов, где не требуется сложная бизнес-логика, но важна простота в реализации.

Если вы ищете простое, но мощное решение для работы с базами данных через телеграм-ботов, то данный проект может стать отличной отправной точкой для реализации ваших фантазий.