Немогу разобраться с ассинхронностью в телеграм боте

[JS]

const mysql = require("mysql2");
require('dotenv').config()
const TelegramApi = require('node-telegram-bot-api')
const bot = new TelegramApi(process.env.BOT_TOKEN, {polling: true})
var mysqlConfig = require('./my_sql_connect.js');
const cron = require("node-cron");
const shellexe = require('./requests/func.js')
const propotkl=5;
bot.setMyCommands([
  {command: '/add', description: 'Add node: /add xxx.xxx.xxx.xxx:9000'},
  {command: '/delete', description: 'Delete node: /delete xxx.xxx.xxx.xxx:9000'}, 
  {command: '/notif', description: 'show logs'},
  {command: '/notifoff', description: 'hide logs'},  
  {command: '/my', description: 'your nodes'}
])
var pool=mysql.createPool(mysqlConfig);
 const start = () => {
    bot.on('message', async msg => {
      const text = msg.text;     
      
      //console.log(msg)
      if(text === '/start'){
        return bot.sendMessage(msg.from.id, `Welcome!`)
      }
 
      if(/^\/add\s([0-9]{1,3}[\.]){3}[0-9]{1,3}[\:]{1}[0-9]{4,5}$/.test(text)){
        var ip=text.replace("/add ", "").trim();
        
        pool.getConnection(function(err,connection){
          if (err) {
            connection.release();
            bot.sendMessage(msg.from.id,'нет соединения с БД')
            throw err;
            //result = 'не удалось добавить ip, нет соединения с БД'            
          }else{
            const sql = `INSERT INTO userip(userid,ipu) VALUES (?, ?);`
            pool.query(sql, [msg.from.id,ip],function(err, resp) {
              if(err) {
                bot.sendMessage(msg.from.id,'failed to add data | не удалось добавить данные')             
                return console.log(err);
              }
              bot.sendMessage(msg.from.id,`ip ${ip} added to user ${msg.from.id}`)
              console.log('Inserted ')
              pool.close(); // закрываем соединение с базой данных
            })
          }
        });        
        return
      }
 
      if(text === '/my'){
        console.log("нажат my")        
        pool.getConnection(function(err,connection){
          console.log("юазу прошли")
          if (err) {
            connection.release();
            bot.sendMessage(msg.from.id,'не удалось добавить ip, нет соединения с БД')
            //result = 'не удалось добавить ip, нет соединения с БД'
            return console.error("Ошибка: " + err.message);
          }else{
            const sql = `SELECT ipu FROM userip WHERE userid=?`;
            pool.query(sql, [msg.from.id],function(err, resp) {
              if(err) {
                bot.sendMessage(msg.from.id,'не удалось достать данные | failed to retrieve data')             
                return console.log(err);
              }
              //bot.sendMessage(msg.from.id,`ip ${ip} добавлен к юзеру ${msg.from.id}`)
              if(resp && resp!=''){
                resp.map((ip)=>{                  
                  bot.sendMessage(msg.from.id,`Ваши ip ${ip.ipu}`)
                })
              }else{
                bot.sendMessage(msg.from.id,`You haven't added an ip yet | Вы еще не добавили ip`)
              }
              //pool.close(); // закрываем соединение с базой данных
            })
          }
        });        
        return
      }
 
      if(text === '/notif'){        
        pool.getConnection(function(err,connection){
          if (err) {
            connection.release();            
            bot.sendMessage(msg.from.id,'не удалось добавить ip, нет соединения с БД')
            //result = 'не удалось добавить ip, нет соединения с БД'
            return console.error("Ошибка: " + err.message);
          }else{
            let tmpsql=`UPDATE userip SET worked=0 WHERE userid=?`;
            pool.query(tmpsql, [msg.from.id],function(err, resp) {
              if(err) {                          
              return console.log(err);
              }
              bot.sendMessage(msg.from.id, `Sending logs is enabled`)
            })
          }
        });        
        return
      }
 
      if(text === '/notifoff'){
        var connection=mysql.createConnection(mysqlConfig);
        pool.getConnection(function(err,connection){
          if (err) {
            connection.release();
            bot.sendMessage(msg.from.id,'не удалось добавить ip, нет соединения с БД')
            //result = 'не удалось добавить ip, нет соединения с БД'
            return console.error("Ошибка: " + err.message);
          }else{
            let tmpsql=`UPDATE userip SET worked=1 WHERE userid=?`;
            pool.query(tmpsql, [msg.from.id],function(err, resp) {
              if(err) {                          
              return console.log(err);
              }
              bot.sendMessage(msg.from.id, `Sending logs is disabled`)
            })
          }
        });        
        return
      }
 
      if(/^\/delete\s([0-9]{1,3}[\.]){3}[0-9]{1,3}[\:]{1}[0-9]{4,5}$/.test(text)){
        var ip=text.replace("/delete ", "").trim();
        var connection=mysql.createConnection(mysqlConfig);
        pool.getConnection(function(err,connection){
          if (err) {
            connection.release();
            bot.sendMessage(msg.from.id,'нет соединения с БД')
            //result = 'не удалось добавить ip, нет соединения с БД'
            return console.error("Ошибка: " + err.message);
          }else{
            const sql = `DELETE FROM userip WHERE ipu=? AND userid=?;`
            pool.query(sql, [ip,msg.from.id],function(err, resp) {
              if(err) {
                bot.sendMessage(msg.from.id,'failed to delete ip | не удалось удалить ip')             
                return console.log(err);
              }
              if(resp.affectedRows>0) {
                bot.sendMessage(msg.from.id,`ip ${ip} deleted`)
              }else{
                bot.sendMessage(msg.from.id,`ip ${ip} not found`)
              }
              console.log(resp)
              console.log('Deleted:')
              pool.close(); // закрываем соединение с базой данных
            })
          }
        });        
        return
      }
 
      return bot.sendMessage(msg.from.id, `Unknown command`)      
    })
 
    
  }
  
  start()
  
  cron.schedule('* * * * * *', async () => { 
  
    
 
         
    let bddata;
    pool.getConnection(function(err,connection){
      if (err) {
       connection.release();
       console.log("ошибка соед");
        //bot.sendMessage(msg.from.id,'нет соединения с БД')        
       reject(err)
      }else{
        console.log("зашли читать БД");
        const sql = `SELECT * FROM userip`;
        pool.query(sql, '',function(err, resp) {
          if(err) {
            //bot.sendMessage(msg.from.id,'не удалось добавить данные')             
            return console.log(err);
          }
          bddata =resp
          console.log("resp")
          if(bddata && bddata!=''){
            
                    
                bddata.map(async (val)=>{
                  console.log(val)
                  console.log("curl:")
                  
                  function load(){
                  let main = shellexe(`curl --max-time 3 --location --silent --request POST https://fullnode.testnet.sui.io:443 --header 'Content-Type: application/json' --data-raw '{ "jsonrpc":"2.0", "method":"sui_getTotalTransactionNumber","id":1}' 2>&1`)
                  let tmp = shellexe(`curl --max-time 3 --location --silent --request POST http://${val.ipu} --header 'Content-Type: application/json' --data-raw '{ "jsonrpc":"2.0", "method":"sui_getTotalTransactionNumber","id":1}' 2>&1`)
                  console.log("tmp=")
                  console.log(tmp)
                  console.log(":curl")
                  }
                tmp =await load()
                console.log(tmp)
                })
                
            
            
      
          //let curlres = proverka();
          console.log("curl res:")
          //console.log(curlres)
            111111
          }else{
            console.log(`База пуста`)
          } 
          connection.release(); // закрываем соединение с базой данных
        })
      }
    });
 
    
    console.log("bddata")
 
  })

[/JS]

Привет! Набросал скрипт для бота и не могу разобраться с асинхронностью.
start() - это функция от бота. Прием команд от пользователя через телегу. Например /my вытаскивает все заведенные ip.

И есть второй раздел с кроном. Который мониторит. Надо чтобы второй раздел с кроном работал как бы независимо от первого. В переменную bddata загружаются сотни ip адресов. Программа вытаскивает айпи и через курл проверяет доступность. 1 адрес через курл занимает 3 секунды. Если адресов много, то это будет долго.

И дело в том, что когда вызываю /my в start, то ответ приходит только после окончания проверки всех ip из базы. Как сделать, чтобы функции в start не зависели от тормоззов функции с курлом?
Т.е. нужно, чтобы при нажатии /my сразу пришел ответ не дождавшись выполнения функции в крон
Спасибо!

Пробовал весь цикл обернуть промисом:

 bddata =resp
          console.log("resp")
          if(bddata && bddata!=''){
            const p = new Promise(function(resolve, reject){
              console.log("начало цикла")
                bddata.map((val)=>{
                  //let main = await shellexe(`curl --max-time 3 --location --silent --request POST https://fullnode.testnet.sui.io:443 --header 'Content-Type: application/json' --data-raw '{ "jsonrpc":"2.0", "method":"sui_getTotalTransactionNumber","id":1}' 2>&1`)
                  shellexe(`curl --max-time 3 --location --silent --request POST http://${val.ipu} --header 'Content-Type: application/json' --data-raw '{ "jsonrpc":"2.0", "method":"sui_getTotalTransactionNumber","id":1}' 2>&1`)
                 
                })
                console.log("конец цикла")
                resolve(tmp)
              })
              p.then(data=>{
                console.log(data)
              })
          //let curlres = proverka();
          console.log("curl res:")
          //console.log(curlres)
            111111
          }else{
            console.log(`База пуста`)
          } 
          connection.release(); 

и даже так:

bddata =resp
          console.log("resp")
          const p = new Promise(function(resolve, reject){
        if(bddata && bddata!=''){
            
              console.log("начало цикла")
              
                bddata.map((val)=>{
                  let main = shellexe(`curl --max-time 3 --location --silent --request POST https://fullnode.testnet.sui.io:443 --header 'Content-Type: application/json' --data-raw '{ "jsonrpc":"2.0", "method":"sui_getTotalTransactionNumber","id":1}' 2>&1`)
                  
                  //let res = shellexe(`curl --max-time 3 --location --silent --request POST http://${val.ipu} --header 'Content-Type: application/json' --data-raw '{ "jsonrpc":"2.0", "method":"sui_getTotalTransactionNumber","id":1}' 2>&1`)
                 console.log(main)
                })
                
                resolve(1)
              
          //let curlres = proverka();          
          //console.log(curlres)
            111111
          }else{
            console.log(`База пуста`)
          } 
        })
        p.then(data=>{
          console.log(data)
        })
          connection.release(); // закрываем соединение с базой данных
        })

Все равно ждет конец цикла, парсит все курл, только потом идет выполнять /my

Пусть код будет так:

const mysql = require("mysql2");
require('dotenv').config()
const TelegramApi = require('node-telegram-bot-api')
const bot = new TelegramApi(process.env.BOT_TOKEN, {polling: true})
var mysqlConfig = require('./my_sql_connect.js');
const cron = require("node-cron");
const shellexe = require('./requests/func.js')
const propotkl=5;
bot.setMyCommands([
  {command: '/add', description: 'Add node: /add xxx.xxx.xxx.xxx:9000'},
  {command: '/delete', description: 'Delete node: /delete xxx.xxx.xxx.xxx:9000'}, 
  {command: '/notif', description: 'show logs'},
  {command: '/notifoff', description: 'hide logs'},  
  {command: '/my', description: 'your nodes'}
])

 const start = () => {
    bot.on('message', async msg => {
      const text = msg.text;     
      
      //console.log(msg)
      if(text === '/start'){
        return bot.sendMessage(msg.from.id, `Welcome!`)
      }

      if(text === '/my'){
        console.log("нажат my")   
        bot.sendMessage(msg.from.id,`Ваши ip: skljfkdsl`)     
        return
      }
      return bot.sendMessage(msg.from.id, `Unknown command`)      
    })
  }
  start()
  
  //var pool=mysql.createPool(mysqlConfig);
  cron.schedule('* */1 * * * *', async () => { 

    const p = new Promise(function(resolve, reject){
      let main =[]
      for(var i = 0; i < 7;i++){
        main.push(shellexe(`curl --max-time 3 --location --silent --request POST https://fullnode.testnet.sui.io:443 --header 'Content-Type: application/json' --data-raw '{ "jsonrpc":"2.0", "method":"sui_getTotalTransactionNumber","id":1}' 2>&1`))
      }
      resolve(main)     
    })
    p.then(data =>{
      console.log(data)
    })
    
  }) 

Мне нужно как-то for обернуть в промис, чтобы /my отвечал моментально. В таком виде при вызове /my в start ответ приходит не сразу. Отвечат только когда for завершится.

Так. Тут много чего происходит чтобы сразу помочь и чтобы было в точку. Поэтому буду подсказывать по кусочкам.

если shellexe возвращает promise, то половина решения уже есть. Promise.all дождется выполнения всех промисов из массива.

const p = new Promise(function(resolve, reject){
  let main =[]
  for(var i = 0; i < 7;i++){
    main.push(shellexe(`curl --max-time 3 --location --silent --request POST https://fullnode.testnet.sui.io:443 --header 'Content-Type: application/json' --data-raw '{ "jsonrpc":"2.0", "method":"sui_getTotalTransactionNumber","id":1}' 2>&1`))
  }
  Promise.all(main).then(resolve, reject)
})

Написав код, и только потом подумав, не могу понять откуда берутся такие задержки в запросах. По коду мне не видно чтобы был какой-то код которы бы блокировал обработку запросов. Ты пробовал посмотреть с какой скоростью приходит ответ если убрать крон совсем? Может дело в настройках поллинга бота или еще чем-то другом?

1 лайк

Во первых спасибо за ответ. Привел к такому виду.

if(bddata && bddata!=''){
              const p = new Promise(function(resolve, reject){
                let main =[]
                for(var i = 0; i < 7;i++){
                  main.push(shellexe(`curl --max-time 3 --location --silent --request POST https://fullnode.testnet.sui.io:443 --header 'Content-Type: application/json' --data-raw '{ "jsonrpc":"2.0", "method":"sui_getTotalTransactionNumber","id":1}' 2>&1`))
                }
                Promise.all(main).then(resolve, reject)
              })
              p.then(val =>{
                console.log(val);
              })
                
              }else{
                console.log(`База пуста`)
              } 

но всеравно реагирует только после полного прохода for. Иногда даже после 2-3-х проходов for
https://oshi.at/VomG/nrKS.png
Возможно правда дело в поллинге бота.
shellexe это функция https://www.npmjs.com/package/shelljs

Проверил так без крон:

const mysql = require("mysql2");
const rp = require('request-promise');
require('dotenv').config()
const TelegramApi = require('node-telegram-bot-api')
const bot = new TelegramApi(process.env.BOT_TOKEN, {polling: true})
var mysqlConfig = require('./my_sql_connect.js');
const cron = require("node-cron");
const shellexe = require('./requests/func.js')
const propotkl=5;
bot.setMyCommands([
  {command: '/add', description: 'Add node: /add xxx.xxx.xxx.xxx:9000'},
  {command: '/delete', description: 'Delete node: /delete xxx.xxx.xxx.xxx:9000'}, 
  {command: '/notif', description: 'show logs'},
  {command: '/notifoff', description: 'hide logs'},  
  {command: '/my', description: 'your nodes'}
])
var pool=mysql.createPool(mysqlConfig);
 const start = () => {
    bot.on('message', async msg => {
      const text = msg.text;     
      /* if(msg.from.id != chatId){
        return bot.sendMessage(msg.from.id, 'создайте своего бота, в инструкции же написано!');
      } */
      //console.log(msg)
      if(text === '/start'){
        return bot.sendMessage(msg.from.id, `Welcome to Sui testnet checker bot`)
      }

      if(/^\/add\s([0-9]{1,3}[\.]){3}[0-9]{1,3}[\:]{1}[0-9]{4,5}$/.test(text)){
        var ip=text.replace("/add ", "").trim();
        
        pool.getConnection(function(err,connection){
          if (err) {
            connection.release();
            bot.sendMessage(msg.from.id,'нет соединения с БД')
            throw err;
            //result = 'не удалось добавить ip, нет соединения с БД'            
          }else{
            const sql = `INSERT INTO userip(userid,ipu) VALUES (?, ?);`
            pool.query(sql, [msg.from.id,ip],function(err, resp) {
              if(err) {
                bot.sendMessage(msg.from.id,'failed to add data | не удалось добавить данные')             
                return console.log(err);
              }
              bot.sendMessage(msg.from.id,`ip ${ip} added to user ${msg.from.id}`)
              console.log('Inserted ')
              pool.close(); // закрываем соединение с базой данных
            })
          }
        });        
        return
      }

      if(text === '/my'){
        console.log("нажат my")        
        pool.getConnection(function(err,connection){
          console.log("юазу прошли")
          if (err) {
           // connection.release();
            bot.sendMessage(msg.from.id,'не удалось добавить ip, нет соединения с БД')
            //result = 'не удалось добавить ip, нет соединения с БД'
            return console.error("Ошибка: " + err.message);
          }else{
            const sql = `SELECT ipu FROM userip WHERE userid=?`;
            pool.query(sql, [msg.from.id],function(err, resp) {
              if(err) {
                bot.sendMessage(msg.from.id,'не удалось достать данные | failed to retrieve data')             
                return console.log(err);
              }
              //bot.sendMessage(msg.from.id,`ip ${ip} добавлен к юзеру ${msg.from.id}`)
              if(resp && resp!=''){
                resp.map((ip)=>{                  
                  bot.sendMessage(msg.from.id,`Ваши ip ${ip.ipu}`)
                })
              }else{
                bot.sendMessage(msg.from.id,`You haven't added an ip yet | Вы еще не добавили ip`)
              }
              //pool.close(); // закрываем соединение с базой данных
            })
          }
        });        
        return
      }

      if(text === '/notif'){        
        pool.getConnection(function(err,connection){
          if (err) {
           // connection.release();            
            bot.sendMessage(msg.from.id,'не удалось добавить ip, нет соединения с БД')
            //result = 'не удалось добавить ip, нет соединения с БД'
            return console.error("Ошибка: " + err.message);
          }else{
            let tmpsql=`UPDATE userip SET worked=0 WHERE userid=?`;
            pool.query(tmpsql, [msg.from.id],function(err, resp) {
              if(err) {                          
              return console.log(err);
              }
              bot.sendMessage(msg.from.id, `Sending logs is enabled`)
            })
          }
        });        
        return
      }

      if(text === '/notifoff'){
        var connection=mysql.createConnection(mysqlConfig);
        pool.getConnection(function(err,connection){
          if (err) {
            connection.release();
            bot.sendMessage(msg.from.id,'не удалось добавить ip, нет соединения с БД')
            //result = 'не удалось добавить ip, нет соединения с БД'
            return console.error("Ошибка: " + err.message);
          }else{
            let tmpsql=`UPDATE userip SET worked=1 WHERE userid=?`;
            pool.query(tmpsql, [msg.from.id],function(err, resp) {
              if(err) {                          
              return console.log(err);
              }
              bot.sendMessage(msg.from.id, `Sending logs is disabled`)
            })
          }
        });        
        return
      }

      if(/^\/delete\s([0-9]{1,3}[\.]){3}[0-9]{1,3}[\:]{1}[0-9]{4,5}$/.test(text)){
        var ip=text.replace("/delete ", "").trim();
        var connection=mysql.createConnection(mysqlConfig);
        pool.getConnection(function(err,connection){
          if (err) {
            connection.release();
            bot.sendMessage(msg.from.id,'нет соединения с БД')
            //result = 'не удалось добавить ip, нет соединения с БД'
            return console.error("Ошибка: " + err.message);
          }else{
            const sql = `DELETE FROM userip WHERE ipu=? AND userid=?;`
            pool.query(sql, [ip,msg.from.id],function(err, resp) {
              if(err) {
                bot.sendMessage(msg.from.id,'failed to delete ip | не удалось удалить ip')             
                return console.log(err);
              }
              if(resp.affectedRows>0) {
                bot.sendMessage(msg.from.id,`ip ${ip} deleted`)
              }else{
                bot.sendMessage(msg.from.id,`ip ${ip} not found`)
              }
              console.log(resp)
              console.log('Deleted:')
              pool.close(); // закрываем соединение с базой данных
            })
          }
        });        
        return
      }

      return bot.sendMessage(msg.from.id, `Unknown command`)      
    })

    
  }
  
start()
  
const p = new Promise(function(resolve, reject){
    let main =[]
    for(var i = 0; i < 15;i++)
      main.push(shellexe(`curl --max-time 3 --location --silent --request POST https://fullnode.testnet.sui.io:443 --header 'Content-Type: application/json' --data-raw '{ "jsonrpc":"2.0", "method":"sui_getTotalTransactionNumber","id":1}' 2>&1`))      
      Promise.all(main).then(resolve, reject)
  })
 
  p.then(val =>{
    console.log(val);
  })

ответ от /my все равно приходит только после полного прохода for

Спасибо за помощь! Похоже дело в функции shell. Решил сделать через request-promise. Там все работает!)

1 лайк

Все так. Я постфактум понял, посмотрев на примеры из shell что все вызовы - блокирующие (т.е. никакой другой код не выполняется пока не выполнится код вызова shell). Блокирующие они специально чтобы можно было писать скрипты как на баш-е - последовательно с возможностью использовать результат предыдущей операции в следующей. А тебе нужна асинхронная версия выполнения shell команд для твоего кода.

1 лайк

Спасибо!)