Cómo añadir una base de datos a nuestro bot de Telegram

Cómo añadir una base de datos a nuestro bot de Telegram

Después de un tiempo y al ver las dudas que genera a algunas personas el cómo añadir una base de datos a nuestro bot, hoy vamos a explicar como implementarla y hacer uso de alguna de sus funciones. Empezaremos por lo mas básico, para ello vamos a hacer uso de la librería NeDB, muy similar a MongoDB.

No obstante, si no estás familiarizado con MongoDB, no te preocupes, iremos paso a paso. Para ello, vamos a utilizar como referencia el proyecto Open Source que tenemos disponible en GitLab | Tecnonucleous Bot

Índice

Instalando e instanciando NeDB

Creo que no debo recordar, aunque así lo haré, que previamente tendremos que tener instalado NodeJS y NPM.

Para este post, vamos a crear una funcionalidad para el bot basada en el idioma en el que queremos que nuestro bot se comunique con nosotros o los usuarios que usen el bot. Para crear esta función, antes necesitas tener implementado i18n en tu bot.

Para instalar la librería usaremos el comando:

npm i nedb --save

Para instanciar la librería, debemos hacer los siguientes ajustes en el archivo config.js de nuestro proyecto.

const Datastore = require('nedb');

// Declaramos los constructores
var Database = {};
Database.users = new Datastore('./src/database/users.db');
Database.chats = new Datastore('./src/database/chats.db');

// Cargamos las bases de datos
Database.users.loadDatabase();
Database.chats.loadDatabase();

// Exportamos las variables pertinentes (bot, rp, imgur...)
module.exports = {Database}

Este ejemplo ha sido comprobado y es imposible que se produzcan errores a la hora de implementar esta función. Las bases de datos se almacenarán en el directorio src/database. Si en algún momento, después de guardar algún registro de prueba se te ocurre visualizar el archivo con extensión .db, te darás cuenta de que es visible y legible para ti. En mi opinión, es un punto a favor a la hora de encontrar cualquier error a la hora de obtener, guardar o actualizar registros.

Creando módulo de gestión de la base de datos

En el caso de que no crearamos el archivo database.js en el directorio src/database con anterioridad, ahora es el momento.

En este archivo, crearemos nuestras funciones personalizadas para manejar la base de datos a nuestro gusto. Para las funciones que vamos a crear necesitaremos un máximo de tres funciones.

Para empezar con ello, declaramos las herramientas necesarias.

var app = require('../settings/app');
var databaseFn = {};

//Aquí todas las funciones

databaseFn.viewUser = (() => {});
databaseFn.setLanguage = (() => {});
databaseFn.changeLanguage = (() => {});

module.exports = {databaseFn}

Visualizando usuarios

A esta función le pasaremos como parámetro toda la información relacionada con el usuario, en forma de object para poder añadirla en caso de no existir. También hemos condicionado que si el usuario no dispone de un @alias, guarde el registro de una manera u otra.

Nosotros hemos guardado para este ejemplo los siguientes datos:

  • Nombre de usuario
  • ID de usuario
  • @alias | En caso de tener de uno
databaseFn.viewUser = ((userInfo) => {
    return new Promise((resolve, reject) => {
        app.Database.users.findOne({
        "user_id": String(userInfo.id)}, (err, docs) => {
            if (err) throw reject(err);
            
            if (docs == null){
                if (userInfo.username != undefined){
                    app.Database.users.insert({
                        "name": userInfo.first_name, 
                        "user_id": String(userInfo.id), 
                        "username": userInfo.username
                        }, (err, docsNew) => {
                            if (err) throw reject(err);
                            
                            resolve({
                                code: true,
                                content: docsNew
                            })
                    })
                } else {
                    app.Database.users.insert({
                        "name": userInfo.first_name,
                        "user_id": String(userInfo.id)
                    }, (err, docsNewNotUsername) => {
                        if (err) throw reject(err);
                        
                        resolve({
                            code: true,
                            content: docsNewNotUsername
                        })
                    })
                }
            } else {
                resolve({
                    code: true,
                    content: docs
                })
            }
        })
    })
});

Hemos creado una promesa que nos devolverá los datos concretos que estamos buscando. Para ahorrar procesos, hemos realizado una búsqueda de la información del usuario que usa el comando. En caso de encontrar algún resultado, usará la propiedad lang del usuario para establecer el idioma del bot, en caso contrario, usará los parámetros que le pasamos con anterioridad para añadirlos y posteriormente usarlos.

Seleccionando idioma

Esta función requiere pasarle dos parámetros. Por un lado, toda la información relacionada con el usuario que usamos anteriormente en forma de object y por otra el idioma que el usuario ha elegido.

También, como en el ejemplo anterior, creamos una promesa que nos devolverá los datos en una función externa.

databaseFn.setLanguage = ((user, lang) => {
    return new Promise((resolve, reject) => {
        app.Database.users.findOne({
        "user_id": String(user.id)}, (err, docs) => {
            if (err) throw reject(err);
            
            if (docs != null){
                if (docs.lang == undefined){
                    app.Database.users.update({
                        "user_id": String(user.id)
                    }, {$set: {"lang": lang}}
                    , (err, updateLang) => {
                        if (err) throw reject(err);
                        
                        if (updateLang === 1){
                            databaseFn.viewUser(user).then((userInfo)=> {
                            if (userInfo.code == true){
                                resolve({
                                    code: true,
                                    content. userInfo.content
                                })
                            })
                        }
                    })
                } else {
                    resolve({
                        code: true,
                        content: docs
                    })
                }
            }
        })
    })
});

Está función se ejecutará cuando se pulse por primera vez el comando /cpanel, realizará una búsqueda y actualizará el registro añadiendo la propiedad lang con el idioma que se haya seleccionado.

Posteriormente, realizará una última búsqueda para devolver los datos actualizados y usarlos para establecer el idioma del bot.

Cambiando idioma

Esta función, como en la anterior, requiere pasarle dos parámetros, toda la información del usuario y el nuevo idioma al que el usuario quiera cambiar.

databaseFn.changeLanguage = ((user, new_lang) => {
    return new Promise((resolve, reject) => {
        app.Database.users.update({
            "user_id": String(user.id)
        }, {$set: {"lang": new_lang}},
        (err, updateLang) => {
            if (err) throw reject(err);
            
            if (updateLang === 1){
                databaseFn.viewUser(user).then((userInfo) => {
                    if (userInfo.code == true){
                        resolve({
                            code: true,
                            content: userInfo.content
                        })
                    }
                }).catch((err) => {
                    console.log(err)
                })
            }
        })
    })
})

Se encargará de actualizar el idioma, realizará otra búsqueda y devolverá los datos actualizados. Esta función se ejecutará cuando se pulse el botón 🌐 Cambiar lenguaje, volverá a preguntarnos que idioma queremos establecer.

Creando módulo del nuevo comando

En este caso, hemos creado otro archivo relacionado con los comandos del bot llamado cpanel.js en el directorio src/commands/plugins. Este comando se encargará de mostrarnos unos botones que nos permitirá elegir que idioma queremos establecer al bot de manera privada.

Estructura del comando

En nuestro archivo cpanel.js, comenzamos declarando las herramientas que vamos a utilizar para llevar a cabo esta funcionalidad.

Si no tienes el archivo database.js creado en el directorio src/database revisa este apartado.

var app = require('../../settings/app');
var database = require('../../database/database');

Hemos condicionado el uso del comando para que solo funcione de manera privada. Quizá muchos no conozcan este método, pero hemos utilizado bot.removeListener('callback_query') para evitar que se envíen mensajes duplicados cada vez que se llama al comando.

app.bot.onText(/^\/cpanel/, (msg) => {
    if (msg.chat.type == 'private'){
        app.bot.removeListener('callback_query');
        
        database.viewUser(msg.from).then((user) => {
            if (user.code == true){
                var content = user.content;
                
                if (content.lang != undefined){
                    app.i18n.setLocale(content.lang);
                    
                    app.bot.sendMessage(msg.chat.id, app.i18n.__('change_lang_text), {
                        parse_mode: 'HTML',
                        reply_markup: {
                            inline_keyboard: [
                                [{text: app.i18n.__('change_lang_button'), callback_data: `changeLang_${content.user_id}`}]
                            ]
                        }
                    }).then(() => {
                        cbLang();
                    })
                } else {
                    app.bot.sendMessage(content.user_id, `🇪🇸 Selecciona el idioma\n🇺🇸 Select the language`, {
                        parse_mode: 'HTML',
                        reply_markup: {
                            inline_keyboard: [
                                [{text: `🇪🇸`, callback_data: `setLang_es`}],
                                [{text: `🇺🇸`, callback_data: `setLang_en`}]
                            ]
                        }
                    })
                }
            }
        })
    }
})

También, hemos hecho una breve comprobación en la base de datos para ver si el usuario que usa el comando /cpanel está registrado y si ya eligió el idioma en el que quiere que el bot se comunique. En caso de no tener uno establecido, se le pedirá al usuario que elija alguno, mientras que si ya tiene un idioma establecido, tendrá la posibilidad de cambiarlo.

Para controlar el uso de los botones, hemos creado una función llamada cbLang(), en la cual manejaremos todas las pulsaciones que los botones reciban para llevar a cabo la selección y el cambio de idioma.

Estructura de la función cbLang

En el mismo archivo cpanel.js, y como comentábamos antes, hemos creado una función llamada cbLang() con la cual vamos a manejar el uso de los botones para el establecimiento del idioma y el cambio del mismo. Analizando el código de abajo, hemos establecido un listener que nos permitirá escuchar exclusivamente la pulsación de los botones del mensaje anterior.

Si hubiésemos usado bot.on('callback_query') el bot estaría escuchando todas las pulsaciones de los botones constantemente y lo que intentamos con bot.addListener() es centralizar la pulsación de los botones en un mismo lugar simplemente llamando a la función cbLang() haciendo que solo pueda ser llamada cuando se pulsa los botones indicados.

var cbLang = (() => {
    app.bot.addListener('callback_query', (lang_action) => {
        var menu = {};
        menu.chat = lang_action.message.chat;
        menu.message = lang_action.message;
        menu.user = lang_action.from;
        menu.data = lang_action.data.split("_");

        switch (menu.data[0]) {
            case 'setLang':
                database.setLanguage(menu.user, menu.data[1]).then((user) => {
                    if (user.code == true) {
                        var user_content = user.content;
                        app.i18n.setLocale(user_content.lang)

                        app.bot.editMessageText(app.i18n.__('predet_lang'), {
                            parse_mode: 'HTML',
                            chat_id: menu.chat.id,
                            message_id: menu.message.message_id,
                            reply_markup: {
                                inline_keyboard: [
                                    [{ text: app.i18n.__('change_lang_button'), callback_data: `changeLang_${user_content.user_id}` }]
                                ]
                            }
                        });
                    }
                })
                break;
            case 'changeLang':
                app.bot.editMessageText(`🇪🇸 Selecciona el idioma\n🇺🇸 Select the language`, {
                    chat_id: menu.chat.id,
                    message_id: menu.message.message_id,
                    parse_mode: 'HTML',
                    reply_markup: {
                        inline_keyboard: [
                            [{ text: `🇪🇸`, callback_data: `setNewLang_es_${menu.user.id}` }],
                            [{ text: `🇺🇸`, callback_data: `setNewLang_en_${menu.user.id}` }]
                        ]
                    }
                })

                break;
            case 'setNewLang':
                database.changeLanguage(menu.user, menu.data[1]).then((userUpdateLang) => {
                    if (userUpdateLang.code == true) {
                        var user = userUpdateLang.content;
                        app.i18n.setLocale(user.lang);

                        app.bot.editMessageText(app.i18n.__('predet_lang'), {
                            chat_id: menu.chat.id,
                            message_id: menu.message.message_id,
                            parse_mode: 'HTML',
                            reply_markup: {
                                inline_keyboard: [
                                    [{ text: app.i18n.__('change_lang_button'), callback_data: `changeLang_${user.user_id}` }]
                                ]
                            }
                        })
                    }
                })
                break;
        }
    })
})

Hemos creado un object para tener a mano todas las propiedades que necesitamos para llevar a cabo las acciones del bot. Por otra parte, hemos usado el método split() en lang_action.data para dividir los parámetros que queramos pasarle mediante callback_data, para este post hemos usado el símbolo _ como filtro para llevar a cabo esa división, pero puedes usar cualquier otro.

A su vez, hemos creado un switch que nos permitirá manejar de manera más eficaz cada caso.

Probando nuestro nuevo comando

Por último, en nuestro archivo index.js debemos declarar esta nueva funcionalidad de la siguiente manera:

// Otras funcionalidades...
const cpanel = require('./src/commands/plugins/cpanel');

Ya solo quedaría ejecutar nuestro archivo con node index.js y aplicarle el comando /cpanel al bot. A continuación, exponemos un ejemplo para comprobar los resultados finales.

Seleccion idioma bot Telegram NodeJS

Como siempre, te recordamos que si te surge alguna duda puedes pasarte por el grupo de Telegram dedicado a estos temas.