php – My bot on Telegram is looping messages

Question:

Hi,

I have the following code:

<?php
function readUpdate($updateId) {
    $fh = fopen(basename(__FILE__, '.php').'.txt', 'a');
    fwrite($fh, $updateId.' ');
    fclose($fh);
}
function sendMessage($chatId, $text) {
    if(!empty($chatId) && !empty($text)) {
        file_get_contents('https://api.telegram.org/botXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/sendMessage?text='.$text.'&chat_id='.$chatId);
    }
}
for($i = 0; $i = 1; $i++) {
    $getUpdates = json_decode(file_get_contents('https://api.telegram.org/botXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/getUpdates'), true);
    $count = count($getUpdates['result']) - 1;
    readUpdate($getUpdates['result'][$count]['message']['message_id']);
    if(strpos(file_get_contents(basename(__FILE__, '.php').'.txt'), $getUpdates['result'][$count]['message']['message_id']) !== true) {
        foreach($getUpdates['result'] as $result) {
            $updateId = $result['update_id'];
            $messageId = $result['message']['message_id'];
            $userId = $result['message']['from']['id'];
            $firstName = $result['message']['from']['first_name'];
            $lastName = $result['message']['from']['last_name'];
            $userName = $result['message']['from']['username'];
            $chatId = $result['message']['chat']['id'];
            $type = $result['message']['chat']['type'];
            $text = $result['message']['text'];
            if($text == '/start') {
                sendMessage($chatId, 'Olá '.$firstName);
                echo '[START] Mensagem de boas vindas enviada.<br>';
                continue;
            }
        }
    } else {

    }
    $i--;
}
?>

The return from /getUpdates is:

{"ok":true,"result":[{"update_id":444612040,
"message":{"message_id":1,"from":{"id":330782177,"first_name":"Brayan","last_name":"Noxious","username":"STRILEXLIVE"},"chat":{"id":330782177,"first_name":"Brayan","last_name":"Noxious","username":"STRILEXLIVE","type":"private"},"date":1492270909,"text":"/start","entities":[{"type":"bot_command","offset":0,"length":6}]}},{"update_id":444612041,
"message":{"message_id":2,"from":{"id":330782177,"first_name":"Brayan","last_name":"Noxious","username":"STRILEXLIVE"},"chat":{"id":330782177,"first_name":"Brayan","last_name":"Noxious","username":"STRILEXLIVE","type":"private"},"date":1492270972,"text":"Ol\u00e1"}}]}

The functions are working fine, everything is ok, however, when I access the script's URL, the script starts sending messages to the people who sent messages to the bot, and after it sends messages to the people who sent the messages. messages to him, he starts sending one more message, and so on, then spams the chat.

The readUpdate function is for the script to take a unique ID from the message and save it in a .txt file for the bot to mark as "read message", so as not to spam, but it still spams the chat.

I looked for everything on the internet, how to read the .txt file line by line and see if that string is on the line, etc., to try to solve this error, but I was not successful.

Thanks in advance.

Answer:

Use Telegram offset to only get new messages, ignoring already read messages, see https://core.telegram.org/bots/api#getupdates .

Identifier of the first update to be returned. Must be greater by one than the highest among the identifiers of previously received updates. By default, updates starting with the earliest unconfirmed update are returned. An update is considered confirmed as soon as getUpdates is called with an offset higher than its update_id. The negative offset can be specified to retrieve updates starting from -offset update from the end of the updates queue. All previous updates will be forgotten.


That way if you get:

{  
   "ok":true,
   "result":[  
      {  
         "update_id":444612040,
         "message":"..."
      },
      {  
         "update_id":444612041,
         "message":"..."
      }
   ]
}

On the next request to /getUpdates you should enter offset=444612042 .

You can use several ways to get the biggest update_id and add one, for example:

  • max(array_column($result, 'update_id')) + 1
  • $result[count($result) - 1]['update_id'] + 1

For this I used this:

function saveUpdateOffset(array $result){

   file_put_contents(LOCAL_HISTORY, (max(array_column($result, 'update_id')) + 1));

}

function getUpdateOffset(){

    if(file_exists(LOCAL_HISTORY) && is_readable(LOCAL_HISTORY)) {
        return (int)file_get_contents(LOCAL_HISTORY);
    }

    return 0;

}

So calling getUpdateOffset will get the last id if it exists. And when it calls saveUpdateOffset , informing the current request data, it will update the file to the latest id , which will be obtained in the next call.


Example:

You can either store the offset and use it in the next call or another option is to simply make a new request to /getUpdates so that it ignores what has already been read (and therefore processed and sent a response).

Anyway, you can use this:

const TELEGRAM_TOKEN = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx:xxxxxxxxxxxx';
const TELEGRAM_URL = 'https://api.telegram.org/bot'.TELEGRAM_TOKEN;
const LOCAL_HISTORY = 'historico.txt';

function getUpdates($offset = 0){

   $jsonNotificacoes = sendRequest(TELEGRAM_URL.'/getUpdates?offset='.$offset);

    if($jsonNotificacoes == false){
        return false;
    }

    return json_decode($jsonNotificacoes, true)['result'];

}

function getMessages($getUpdates_result){

    return array_column($getUpdates_result, 'message');

}

function saveUpdateOffset($getUpdates_result){

   file_put_contents(LOCAL_HISTORY, (max(array_column($getUpdates_result, 'update_id')) + 1));

}

function getUpdateOffset(){

    if(file_exists(LOCAL_HISTORY) && is_readable(LOCAL_HISTORY)) {
        return (int)file_get_contents(LOCAL_HISTORY);
    }

    return 0;

}

function sendMessage($chatId, $text){

    if(empty($chatId) && empty($text)) {
       return false;
    }

    return sendRequest(TELEGRAM_URL.'/sendMessage?text='.$text.'&chat_id='.$chatId);

}

function sendRequest($url){

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_SSL_VERIFYPEER => 1,
        CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
        CURLOPT_FOLLOWLOCATION => 0,
        CURLOPT_MAXREDIRS => 0,
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_CONNECTTIMEOUT => 2,
        CURLOPT_TIMEOUT => 5,
        CURLOPT_LOW_SPEED_LIMIT => 500,
        CURLOPT_LOW_SPEED_TIME => 1,
        CURLOPT_FAILONERROR => 1
    ]);

    return curl_exec($ch);

}

$updates = getUpdates( getUpdateOffset() );

if($updates) {

    saveUpdateOffset($updates);

    $mensagens = getMessages($updates);

    foreach ($mensagens as $mensagem) {

        $id     = $mensagem['chat']['id'];
        $nome   = $mensagem['from']['first_name'];
        $texto  = $mensagem['text'];

        if($texto === '/start'){

            if(sendMessage($id, 'Olá, ' . $nome)) {
                echo 'Mensagem enviada';
            }
        }

    }

}

Changes:

Using cURL, restricted to HTTPS ( CURLOPT_PROTOCOLS ), without following redirection ( CURLOPT_FOLLOWLOCATION ) and verifying SSL authenticity ( CURLOPT_SSL_VERIFYPEER ), with a 2 second connection timeout ( CURLOPT_CONNECTTIMEOUT ) and a 5 second timeout to get the entire result ( CURLOPT_TIMEOUT ) and in case of slow connections the timeout is 1 second ( CURLOPT_LOW_SPEED_TIME ) and in case of an HTTPCode other than 200~300 it will return false ( CURLOPT_FAILONERROR ).

getUpdates(int $offset) makes the request to get all the information from the TELEGRAM_URL/getUpdates .

The getMessages(array $getUpdates_result) waits for the result of getUpdates , which will only return the message data.

The saveUpdateOffset and getUpdateOffset was described above, where it just saves and gets the Offset from a historico.txt file, the first function expects the getUpdates result as the first parameter.

sendMessage() is basically unchanged.

Scroll to Top
AllEscort