My bot in Telegram is looping messages

Asked

Viewed 1,139 times

3

Hello,

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 of /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 perfectly, everything is okay, however, when I access the script’s URL, the script starts sending the messages to the people who sent messages to the bot, and after it sends the messages to the people who sent the messages to it, he starts sending another message, and so on, so he’s spamming the chat.

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

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

Thanks in advance.

1 answer

1

Use the offset Telegram 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 Forgotten.


That way if you get:

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

In the next request to /getUpdates you must inform 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 that 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 for getUpdateOffset will get the last id, if it exists. And when to call saveUpdateOffset, informing the data of the current request, will update the file to the id most recent, which will be obtained on the next call.


Example:

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

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:

Use of Curl, restricted to HTTPS (CURLOPT_PROTOCOLS), without following redirection (CURLOPT_FOLLOWLOCATION) and verifying the authenticity of SSL (CURLOPT_SSL_VERIFYPEER), with 2 seconds connection timeout (CURLOPT_CONNECTTIMEOUT) and a timeout of 5 seconds to get all the 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 will return false (CURLOPT_FAILONERROR).

The getUpdates(int $offset) requests to obtain all information from TELEGRAM_URL/getUpdates.

The getMessages(array $getUpdates_result) awaits the outcome of getUpdates, where will return only the message data.

The saveUpdateOffset and getUpdateOffset described above, where only save and get the Offset of a file historico.txt, the first function expects the result getUpdates as first parameter.

The sendMessage() was basically unchanged.

  • 1

    I tested locally and did not return anything or send message.

  • Use insecure CURL if not configured correctly by modifying the CURLOPT_SSL_VERIFYPEER for 0 and the CURLOPT_SSL_VERIFYHOST for 0. Adjust (or remove) the CURLOPT_LOW_SPEED_LIMIT if your connection (or target server) is slow.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.