Community discussions

MikroTik App
 
DyadyaGenya
Member Candidate
Member Candidate
Topic Author
Posts: 240
Joined: Mon May 08, 2023 10:34 pm

Telegram Bot Crashes with 'Network Unreachable' and Token Issues

Tue Mar 18, 2025 1:28 am

I'm trying to write a Telegram bot. Using someone else's bot isn't interesting to me. Right now, I have someone else's bot running, but I have issues with it, including the fact that it sends GET requests and doesn't work with JSON parsing.

In my bot, I used the JSON parsing from Chupakabra.

The logic is roughly as follows: there’s a message reading function (it parses JSON) and a sending function. It seems to send everything fine. The sending function checks if the send fails; if it does, it adds the message to an array of unsent messages. Then, when sending new messages, it first checks if there are any unsent messages in the array. If there are, it tries to send them first, and only then the new message. If the old messages fail to send, they stay in the array.

I tested it in two ways: breaking the token and disconnecting the internet.

If I break the token, the main script works fine—all messages get added to the array and are sent once the token is restored.
But if I break the token and send a message from a separate test script, the message still gets sent. It keeps sending until I try sending a message from the main script. Then, messages from the main script get added to the array, and after that, messages from the test script stop sending and also start getting added to the array. This happens even if the token is restored. Only when I send messages from the main script do the messages from the test script start sending too.
If I disconnect the internet, messages from the test script naturally don’t send and get added to the array. But if I send from the main script, it crashes with an error. In this version of the script, it crashes with: 'unable to resolve hostname.'
There were cases when it crashed with: 'failure: closing connection: <connect failed: Network unreachable> 149.154.167.220:443 (4).'
The first error is likely related to declaring the global variable botConfig. I tried changing it to a local variable, but then only the last message gets added to the array.

As for the second error, I’m completely out of ideas.
# Bot configuration
:global botConfig
:if ([:typeof $botConfig] != "array") do={ :set botConfig [:toarray ""] }
:set ($botConfig->"Telegram") {
    "botToken"="xxx:xxx";
    "mainID"="xxx";
    "botId"="xxx"
}

# Array for unsent messages
:global unsentMessages
:if ([:typeof $unsentMessages] != "array") do={ :set unsentMessages [:toarray ""] }

# Function to form the Telegram API URL
:global botUrl do={
    :global botConfig
    :local baseUrl ("https://api.telegram.org/bot" . ($botConfig->"Telegram"->"botToken"))
    :return $baseUrl
}

# Function to get the sender string with timestamp and router name
:global getStartString do={
    :local date [/system clock get date]
    :local time [/system clock get time]
    :local routerName [/system identity get name]
    :local senderString "$date $time : $routerName"
    :return $senderString
}

# Function to serialize an array to JSON
:global arrayToJson do={
    :local array $1
    :local result "{"
    :local first true
    :foreach key,value in=$array do={
        :if ($first) do={:set first false} else={:set result ($result . ",")}
        # No escaping here, it will be handled by toUnicodeEscape
        :set result ($result . "\"" . $key . "\": \"" . $value . "\"")
    }
    :set result ($result . "}")
    :return $result
}

# Function to convert a string to Unicode escape sequences (for Cyrillic support not full)
:global toUnicodeEscape do={
    :local string $1
    :local rsimv [:toarray {
        "А"="0410"; "Б"="0411"; "В"="0412"; "Г"="0413"; "Д"="0414"; "Е"="0415"; "Ж"="0416"; "З"="0417"; "И"="0418"; "Й"="0419"; "К"="041A"; 
        "Л"="041B"; "М"="041C"; "Н"="041D"; "О"="041E"; "П"="041F"; "Р"="0420"; "С"="0421"; "Т"="0422"; "У"="0423"; "Ф"="0424"; "Х"="0425"; 
        "Ц"="0426"; "Ч"="0427"; "Ш"="0428"; "Щ"="0429"; "Ъ"="042A"; "Ы"="042B"; "Ь"="042C"; "Э"="042D"; "Ю"="042E"; "Я"="042F"; 
        "а"="0430"; "б"="0431"; "в"="0432"; "г"="0433"; "д"="0434"; "е"="0435"; "ж"="0436"; "з"="0437"; "и"="0438"; "й"="0439"; "к"="043A"; 
        "л"="043B"; "м"="043C"; "н"="043D"; "о"="043E"; "п"="043F"; "р"="0440"; "с"="0441"; "т"="0442"; "у"="0443"; "ф"="0444"; "х"="0445"; 
        "ц"="0446"; "ч"="0447"; "ш"="0448"; "щ"="0449"; "ъ"="044A"; "ы"="044B"; "ь"="044C"; "э"="044D"; "ю"="044E"; "я"="044F"; 
        "Ё"="0401"; "ё"="0451"; "\r"="000D"; "\n"="000A"; "\""="0022"
    }]
    :local StrTele ""
    :for i from=0 to=([:len $string] - 1) do={
        :local keys [:pick $string $i (1 + $i)]
        :local key ($rsimv->$keys)
        :if ([:len $key] != 0) do={
            :set StrTele ($StrTele . "\\u" . $key)
        } else={
            :set StrTele ($StrTele . $keys)
        }
    }
    :return $StrTele
}

# Global function to send a Telegram message
:global sendTelegramMessage do={
    :global getStartString
    :global arrayToJson
    :global botConfig
    :global botUrl
    :global unsentMessages
    :global toUnicodeEscape
    
    # Parameters
    :local message [$toUnicodeEscape $1]
    :local chatId $2
    
    # Input validation
    :if ([:typeof $message] != "str") do={:return false}
    
    # Select chat ID (default to mainID if not provided)
    :if ([:typeof $chatId] != "str") do={
        :set chatId ($botConfig->"Telegram"->"mainID")
    }
    :if ([:typeof $chatId] = "nothing") do={:return false}
    
    # Form the URL
    :local baseUrl [$botUrl]
    :local url "$baseUrl/sendMessage"
    
    # Process unsent messages
    :if ([:len $unsentMessages] > 0) do={
        :local newUnsent [:toarray ""]
        :foreach i,msg in=$unsentMessages do={
            :log info "Processing unsent message $i: $msg"
            :local success false
            :for attempt from=1 to=3 do={
                :if ($success = false) do={
                    :local fetchResult
                    :do {
                        :set fetchResult [/tool fetch mode=https url=$url \
                            http-method=post \
                            http-header-field="Content-Type: application/json; charset=utf-8" \
                            http-data=$msg \
                            keep-result=yes \
                            as-value]
                        :if (($fetchResult->"status") = "finished") do={
                            :set success true
                        }
                    } on-error={
                        :log warning "Error sending unsent message $i, attempt $attempt"
                        :set success false
                    }
                    :if ($success = false) do={:delay 1s}
                }
            }
            :if ($success = false) do={
                :set ($newUnsent->$i) $msg
            } else={
                :log info "Unsent message $i sent successfully"
            }
        }
        :set unsentMessages $newUnsent
    }
    
    # Form the new message
    :local sender [$getStartString]
    :local fullMessage "$sender\r\n$message"
    
    # Create JSON for the new message
    :local data {"chat_id"=$chatId; "text"=$fullMessage}
    :local jsonString [$arrayToJson $data]
    
    # Send the new message
    :local success false
    :for attempt from=1 to=3 do={
        :if ($success = false) do={
            :local fetchResult
            :do {
                :set fetchResult [/tool fetch mode=https url=$url \
                    http-method=post \
                    http-header-field="Content-Type: application/json; charset=utf-8" \
                    http-data=$jsonString \
                    keep-result=yes \
                    as-value]
                :if (($fetchResult->"status") = "finished") do={
                    :set success true
                }
            } on-error={
                :log warning "Error sending new message, attempt $attempt"
                :set success false
            }
            :if ($success = false) do={:delay 1s}
        }
    }
    
    # If the new message fails to send, add it to the unsent array
    :if ($success = false) do={
        :log info "New message failed, adding to unsentMessages with hourglass"
        :local markedMessage ("\\u23F3 " . $fullMessage)
        :local markedData {"chat_id"=$chatId; "text"=$markedMessage}
        :local markedJson [$arrayToJson $markedData]
        :log info "Adding to unsentMessages: $markedJson"
        :set unsentMessages ($unsentMessages , $markedJson)
    }
    
    :return $success
}

# Example: Send a test message to the main chat
:put [$sendTelegramMessage "Hello, this is a test message from MikroTik!"]
I don’t often write code, especially for MikroTik, and I suspect the error is somewhere obvious. But I can’t find it. I hope I’ve described the problem thoroughly enough. I forgot to mention that the other script only has two lines:
:global sendTelegramMessage
[$sendTelegramMessage "Test from another script âìåñòå ñ êèðèëëèöåé symbol"]
 
DyadyaGenya
Member Candidate
Member Candidate
Topic Author
Posts: 240
Joined: Mon May 08, 2023 10:34 pm

Re: Telegram Bot Crashes with 'Network Unreachable' and Token Issues

Wed Mar 19, 2025 12:45 am

The issue with the main script crashing has been resolved. As I suspected, the problem was on the surface. I usually try to strip the code down to the minimum when troubleshooting, but this time I skipped one check that I didn’t include in the main script’s code.

I was sending a test message and completely forgot about it. In the version for the forum, it’s not present, and with that version, the test with disabled internet works fine for both the main script and the test script. That is, the main script no longer crashes.

The issue with the broken token hasn’t been resolved yet. But my assumption that the botConfig configuration is taken as a global variable from the cache has been confirmed.

I added a check to the test script.

First check: I added debug code to the test script. I broke the token in the main script, but when running the test script, it printed the old, correct token, probably from the cache, and the message was sent. I broke the token in the test script, and the report showed the broken token, and the message wasn’t sent. I fixed the token in the main script, but when sending from the test script, the report printed the broken token—the exact one I broke in the test script.

Second check: I added lines to the function declaring the correct token, sent a message from the test script, and the report still printed the broken token. After that, I also tried declaring a local token in the function: :local botToken "XXXXXXXXXXXXXXXXXXXXXX", but it didn’t help—the report still showed invalidToken, and the message wasn’t sent. Then I commented out the line in the test script that broke the token: #:set ($botConfig->"Telegram"->"botToken") "invalidToken", but the message still didn’t send, and the report continued showing invalidToken.