witnessmenow / Universal-Arduino-Telegram-Bot

Use Telegram on your Arduino (ESP8266 or Wifi-101 boards)
MIT License
1.09k stars 302 forks source link

datatype difference : last_message_received (long) and update_id (int) #341

Open ceewanna opened 6 months ago

ceewanna commented 6 months ago

Under "processResult" last_message_received takes value from update_id.

Is there any rationale behind having different datatype for these 2 variables?

witnessmenow commented 6 months ago

I guess it's a bug, not sure which one it should be to be honest! probably long?

witnessmenow commented 6 months ago

changed in #330 actually

ceewanna commented 6 months ago

Thanks.

By the way I am exploring ways to get multiple devices taking commands from one chat bot. Every device has its own id number and the command is attached with the id. Every device reading from the chat bot and only act on the command having id matched.

I am not so sure if by calling getUpdates the last_message_received will be moving on and some of the devices will miss its command. So I have each device maintaining its own tracking of last_message_received (through update_id). At the start each device will call getUpdates(bot.last_message_received+1) to get the proper bot.last_message_received value and make use of its as a starting tracking of message. Every bot.messages[i] each device processes it will update the tracking from update_id. Subsequently the getUpdaes will be callled based on each tracking+1.

Please correct me if I am wrong about the use of last_message_received. Do you have any guidance on how to handle the multiple devices in my case?

witnessmenow commented 6 months ago

This is a path filled with pitfalls :) technically possible, but messy. If one of your devices go down and their is a message waiting for it, the messages will stop coming.

I would use it as a last resort. Multiple bots in a group might be a better option for example.

I think from reading your post that your idea of how the messageId thing works is wrong,

Say your last actually messaged ID was 100

When you send a getUpdates(100+1) from any device, Telegram will delete any message it finds with a lower ID than 101 from its server as it considers them handled.

That means no device will see a message with a lower ID than 101 from this point on, even if they send getUpdates(80), it will not return any message with a lower ID than 101

How to implement: So in your scenario, you will have a few different boards, lets say they are labeled A, B and C

You send a message intended for C, so maybe your include the label in the message "/on C" or something.

you would need to extract the label and check them against your board.

Psuedo code:

Make messageIdToSend a global variable, you can default it to 0

long messageIdToSend = 0; //or int if it should be int

in your method for processing the message

String label = extractLabelFromMessage(message.text);
if(label == this_device_label){
     //Act on message (turn on your light or whatever)
     messageIdToSend = message.id +1
} else {
    //Don't do anything, messageIdToSend doesn't need to be updated, the device the message it is for should delete it on Telegrams server when it gets it

//Maybe you could keep a counter here that if your receive the same message.id several times in a row it means the board the message is for is probably not working, and you could unblock the queue
}

and then send messageIdToSend in your loop when calling getUpdates

getUpdates(messageIdToSend )
ceewanna commented 6 months ago

I am attaching my code below.

  1. bot_updates in the procedure executed in loop().
  2. msg_track is a class variable that will try to catch up with last_message_received. Every device's msg_track will be jogging towards bot.last_message_received. This may not be relevant as you pointed out about how last_message_received works with getUpdates. However, I still doubt why I come across situation where I need to purge/rerun through old message (see below).
  3. Your suggestion "messageIdToSend" seems to be like my "msg_track" in my code. I still don't quite get about your message.id. To me it looks like message[i].update_id.
  4. The rationale for purging in my bot_updates as part of first loop is because I found that my "/resetall" command which is intended to make every device/board reset itself resulted in an never ending reset. Every device once completed its reset/restart comes back and performed getUpdates(bot.last_message_received+1) would still found/received the old "/resetall". Maybe it was because the way I implemented /resetall (in bot_messages_handler) was to fire this code right away without calling getUpdates(last_message_received+1).
    if (cmd == "/resetall") {
      bot_broadcast(me.name(true) + " RESET ALL ");
      ESP.restart();
      continue;
    }
  5. My experiments with my code on 2-3 esp8266 and esp32 boards seem to be working fine. The only observation so far is when every board is esp8266 they are working fine but when I put on esp32 all the esp8266 started to be unpredictable and only esp32 will respond reliably.
  6. I also found in many occasions that bot.last_message_received is zero though it should have been some number after the first getUpdates is called. When this happened, the board was stuck.

void  bot_updates() {
  static bool firstloop = true;
  int numNewMessages;

  /*
    firstloop relies on bot.last_message_received
    latter loops rely on msg_track 
  */
  if (firstloop) {
    //  initialize last_message_received and msg_track
    //  last_message_received won't have value unless getUpdates is called.
    numNewMessages = bot.getUpdates(bot.last_message_received + 1);
    msg_track = new Tracking_Var<long>(bot.last_message_received);
  } else {
    numNewMessages = bot.getUpdates(msg_track->current_value()+1);
  }

  while (numNewMessages > 0 || msg_track->current_value()<bot.last_message_received) {
    if (firstloop) {      
      for (int i=0; i<numNewMessages; i++) {
        msg_track->update(bot.messages[i].update_id);

        if (bot.messages[i].text == "/resetall") {
          // purge past resetall in messages
          bot_broadcast(me.idname() + " restarted from resetall ");
        } else {
          bot_broadcast(me.idname() + " purging others commands " + bot.messages[i].text);          
        }
      }
    } else {

      bot_messages_handler(numNewMessages);

    }
    numNewMessages = bot.getUpdates(msg_track->current_value() + 1);
    if (numNewMessages==0 && msg_track->current_value() < bot.last_message_received) {
      msg_track->jog(bot.last_message_received);
      numNewMessages = bot.getUpdates(msg_track->current_value() + 1);
    }

  }
  if (firstloop) {
    bot_broadcast(me.idname() + " ready");
    firstloop = false;
  }
}

void bot_messages_handler(int numNewMessages) {
  Serial.println("Handle New Messages");
  Serial.println(String(numNewMessages));

  for (int i=0; i<numNewMessages; i++) {    
    msg_track->update(bot.messages[i].update_id);

    ... the rest are for processing the text
    String sub = get_substring(cmd, ' ', 0);

    // this is where id is identified. Example : /sID on 
    if (sub == "/s" + me.idname()) {
      String attr;

      attr = get_substring(cmd, ' ', 1);
      if (attr == "on") {
        relay_sw.write(ON);
        display_state(chat_id, true);
        continue;
      }

      if (attr == "off") {
        relay_sw.write(OFF);
        display_state(chat_id, true);
        continue;
      }

      if (attr == "state") {
        display_state(chat_id);
        display_tonoff(chat_id);
        continue;
      }

      if (attr == "reset") {
        bot_broadcast(me.name(true) + " RESET ");
        ESP.restart();
        continue;
      }

  }
}