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.
Code: Select all
# 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!"]
Code: Select all
:global sendTelegramMessage
[$sendTelegramMessage "Test from another script âìåñòå ñ êèðèëëèöåé symbol"]