Problem Sending Message to Telegram via API on MikroTik RouterOS

Hello, everyone!

I’m facing an issue when trying to send messages to Telegram using the bot API on MikroTik RouterOS. I have tried constructing the URL and sending the message, but I’m getting errors and cannot get the sending to work correctly.

Here is the script I am using:

# Define constants
:local BotID [:tostr "YOUR_BOT_ID"];
:local CHID [:tostr "YOUR_CHAT_ID"];
:local DeviceName [/system identity get name];
:local Message ("?[" . $DeviceName . "]: ");

# Check if the variables are set correctly
:log info ("BotID: " . $BotID);
:log info ("CHID: " . $CHID);

# Build the URL manually to test
:local TestURL ("https://api.telegram.org/bot" . $BotID . "/sendMessage?chat_id=" . $CHID . "&text=Test")
:log info ("Test URL: " . $TestURL);

# Check for updates
:local InstVer [/system package update get installed-version];
:local LatVer [/system package update get latest-version];
:local Chan [/system package update get channel];

# Test values
:log info ("Installed version: " . $InstVer);
:log info ("Latest version: " . $LatVer);
:log info ("Channel: " . $Chan);

# Create update message
:if ($InstVer = $LatVer) do={
    :set Message ($Message . "The system is already up to date. [Current version: " . $InstVer . ", Channel: " . $Chan . "]");
} else={
    :set Message ($Message . "New version " . $LatVer . " available! Changelogs: https%3A%2F%2Fmikrotik.com%2Fdownload%2Fchangelogs [Installed version: " . $InstVer . ", Channel: " . $Chan . "]");
}

# Replace spaces with %20 to avoid error in Telegram
:set Message [ $Message ];

# Send message
:local FinalURL ("https://api.telegram.org/bot" . $BotID . "/sendMessage?chat_id=" . $CHID . "&text=" . $Message);
:log info ("Final URL for sending: " . $FinalURL);

# Execute send
/tool fetch url=$FinalURL keep-result=no;

# Send log
:log info ("Message sent to Telegram: " . $Message);

When I try to execute it, the message is not being sent to Telegram, and I don’t see any clear errors in the log. Any suggestions on what might be wrong or any steps I might be missing?

I’m using MikroTik RouterOS 7.x and have already verified that the bot and chat ID are set correctly.

I appreciate any help!

It looks like you are logging the URL sent. Does this URL work if you send it using wget or curl from a computer cli?

Also it might be worth saving any returned information from the fetch to see if they give you any debugging.

heres a snippet from a script that shows you the return values

              :local result \
               [/tool fetch mode=$MODE \
                            url=$Url \
                            as-value \
                            output=user \
                            duration=30];
              :log info ("fetch status:" . $result->"status");
              :log info ("fetch data:" . $result->"data");
              :log info ("fetch duration:" . $result->"duration");
              :log info ("fetch downloaded:" . $result->"downloaded");

Also I noticed you didnt set the fetch MODE, but Im not sure that affects anything

mode must not used if is used url


It looks like a script made by artificial deficiency, too many clues.
Even a person who doesn’t know scripting could not systematically write it badly like that.

# Replace spaces with %20 to avoid error in Telegram
:set Message [ $Message ];

The OP script has same annoying manner of commenting each line as that script http://forum.mikrotik.com/t/need-help-creating-a-simple-script/182356/2 Even the simpliest line is commented.

It does look like “mode” has been deprecated and is unnecessary. It does not affect the outcome though.

I would still like to see if the $FinalURL works correctly with Curl. This would help us separate the problem into parts. I dont know anything about the Telegram API so I cant comment on the actual URL construction.

As a mainly Python programmer all of the comments dont bother me; Im very used to them :wink:. Its a relief to not see a huge blob of undocumented spaghetti code.

As a general rule I tend to document any intricate section where it wouldn’t be obvious what the code is doing. No need where the natural language of the code explains itself like “loop though all the results” or something.

Have you tried COBOL? Should fit you perfectly :slight_smile:
What do you mean writing “huge undocumented blob of code”? 3 lines? 5? 25? Comments are to point crucial points that needs focus. No need to write:

	//checking if login string is equal to "administrator"
   	if( login == "administrator" ) .....

Guys, thanks for your feedback and for trying to help me with this code.
In fact, it was generated by an artificial intelligence, as I’m not familiar with MikroTik scripts.
My intention was to ask the AI for a script that could send me information about updates for the two RBs I have, so I could perform the updates more efficiently.
However, the AI ended up getting stuck at this point.

The strangest part is that when generating the log with :log info ("Final URL for sending: " . $FinalURL);, the Telegram API responds correctly. However, when using /tool fetch, it returns a 400 error.
It seems there’s some kind of data misformation occurring when passing the request to fetch.

If you copy $FinalURL and use curl to send it from a command line does it successfully send the message in Telegram?

400 errors are “bad request”, usually meaning the request URL is malformed.

Do you have any documentation about the Telegram API and how the request is supposed to be created? If you do a little research we may be able to help, but just asking ChatCCP isn’t going to get you any closer to an answer.

The strangest part is that when generating the log with :log info ("Final URL for sending: " . $FinalURL);, the Telegram API responds correctly.

This statement is just printing $FinalURL into the Mikrotik log. The “Telegram API” is not contacted, and does not reply to anything. Telegram is only contacted in the “fetch” statement.

If you copy $FinalURL and use curl to send it from a command line does it successfully send the message in Telegram?

400 errors are “bad request”, usually meaning the request URL is malformed.

When I copy the URL generated in the log and use it both in the browser and with curl, the sending works normally.
However, when passing the commands to the “fetch” instruction, the 400 error occurs.
I have the impression that some characters are not accepted in the “fetch” instruction and, as a result, are not correctly sent to the Telegram API.

Do you have any documentation about the Telegram API and how the request is supposed to be created? If you do a little research we may be able to help, but just asking ChatCCP isn’t going to get you any closer to an answer.

This is the format accepted by the Telegram API. In the Mikrotik script, just for testing, I made this code and it worked:

:local CHID "1215501525";
:local BotID "1450359051:AAFn4kwwwQ0LSl1xhqSFsxwPruiKWepexx0";

:local Message "Boss, ok RB 1!";

/tool fetch url="https://api.telegram.org/bot$BotID/sendmessage\?chat_id=$CHID&text=$Message" keep-result=no;



The strangest part is that when generating the log with :log info ("Final URL for sending: " . $FinalURL);, the Telegram API responds correctly.

This statement is just printing $FinalURL into the Mikrotik log. The “Telegram API” is not contacted, and does not reply to anything. Telegram is only contacted in the “fetch” statement.

Correct, I had the $FinalURL printed in the log to identify the possible issue. When I copied and pasted the URL generated in $FinalURL into the browser, with the correctly formatted data, it worked perfectly.

However, when the $FinalURL is passed to the “fetch” command, the 400 error occurs.

So, I decided to try a different approach to see if it would work, and it did. I received the message from Telegram with the code below. However, this method is more complex than what could be done directly with the Mikrotik script:

script Mikrotik

# Definir constantes
:local ServerURL "https://mydomain.com/api/telegram.php";
:local DeviceName [/system identity get name];


:local InstVer [/system package update get installed-version];
:local LatVer [/system package update get latest-version];
:local Chan [/system package update get channel];


:local postData "{ \"instVer\": \"$InstVer\", \"latVer\": \"$LatVer\", \"chan\": \"$Chan\", \"deviceName\": \"$DeviceName\" }";


/tool fetch url=$ServerURL http-method=post http-header-field="Content-Type: application/json" http-data=$postData keep-result=no;


:log info ("Dados enviados para servidor PHP: Versão instalada: " . $InstVer . ", Versão disponível: " . $LatVer . ", Canal: " . $Chan);

PHP Code telegram.php

<?php
	$data = json_decode(file_get_contents("php://input"), true);
	if (!$data || !isset($data["instVer"]) || !isset($data["latVer"]) || !isset($data["chan"]) || !isset($data["deviceName"])) {
		http_response_code(400);
		echo json_encode(["status" => "error", "message" => "Dados inválidos"]);
		exit;
	}
	$instVer = $data["instVer"];
	$latVer = $data["latVer"];
	$chan = $data["chan"];
	$deviceName = $data["deviceName"];
	$jsonFile = $deviceName . '_data.json';
	$botToken = "BOTID AQUI";
	$chatID = "CHATID AQUI";
	function updateOrCreateJsonFile($jsonFile, $data) {
		if (file_exists($jsonFile)) {
			$fileData = json_decode(file_get_contents($jsonFile), true);
			} else {
			$fileData = [];
		}
		if (isset($fileData['data'])) {
			$deviceData = $fileData['data'];
			if ($deviceData['instVer'] !== $data['instVer'] || $deviceData['latVer'] !== $data['latVer']) {
				$data['expiration'] = date('Y-m-d H:i:s', strtotime('+0 seconds'));
				} else {
				if (strtotime($deviceData['expiration']) > time()) {
					return false;
				}
			}
			} else {
			$data['expiration'] = date('Y-m-d H:i:s', strtotime('+24 hours'));
		}
		$fileData['data'] = [
		'instVer' => $data['instVer'],
		'latVer' => $data['latVer'],
		'chan' => $data['chan'],
		'deviceName' => $data['deviceName'],
		'expiration' => $data['expiration']
		];
		file_put_contents($jsonFile, json_encode($fileData, JSON_PRETTY_PRINT));
		return true;
	}
	if (updateOrCreateJsonFile($jsonFile, $data)) {
		$message = "️[" . $deviceName . "]: ";
		if ($instVer === $latVer) {
			$message .= "O sistema já está atualizado. Changelogs: https://mikrotik.com/download/changelogs [Versão instalada: " . $instVer . ", Canal: " . $chan . "]";
			} else {
			$message .= "Nova versão " . $latVer . " disponível! Changelogs: https://mikrotik.com/download/changelogs [Versão instalada: " . $instVer . ", Canal: " . $chan . "]";
		}
		$url = "https://api.telegram.org/bot$botToken/sendMessage?chat_id=$chatID&text=" . urlencode($message);
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		$response = curl_exec($ch);
		$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
		curl_close($ch);
		if ($httpCode == 200) {
			echo json_encode(["status" => "success", "message" => "Mensagem enviada com sucesso"]);
			} else {
			echo json_encode(["status" => "error", "message" => "Falha ao enviar mensagem"]);
		}
		} else {
		echo json_encode(["status" => "success", "message" => "Mensagem não enviada. O sistema já está atualizado e a expiração ainda não passou."]);
	}
?>

ok, it seems like you are very close to the answer.

This is the format accepted by the Telegram API. In the Mikrotik script, just for testing, I made this code and it worked:

:local CHID “1215501525”;
:local BotID “1450359051:AAFn4kwwwQ0LSl1xhqSFsxwPruiKWepexx0”;

:local Message “Boss, ok RB 1!”;

/tool fetch url="> https://api.telegram.org/bot$BotID/sendmessage?chat_id=$CHID&text=$Message> " keep-result=no;

My advice is to do a test like this, but do not use the local variables, just expand it by hand, like (please check my expansion is correct):

: TestURL "https://api.telegram.org/bot1450359051:AAFn4kwwwQ0LSl1xhqSFsxwPruiKWepexx0/sendmessage\?chat_id=1215501525&text=Boss, ok RB 1!"
/tool fetch url=$TestURL keep-result=no

If this works, find exactly the difference between $TestURL and $FinalURL that was logged before.


EDIT:
I ran these script through my router and the only difference I saw was the message. It has a bunch of spaces and odd characters. I bet it needs something like double quotes around it to send correctly. Try sending a simple “HeyImaTestMessage” from the first script and see if it works.

Don´t “invent the weel” again and again :laughing:

Use eworms script repository:
https://git.eworm.de/cgit/routeros-scripts/about/
and from there:
https://git.eworm.de/cgit/routeros-scripts/about/doc/telegram-chat.md
and you´re done. :smiley:

To send notifications to Telegram this is sufficient:
https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/notification-telegram.md

Ullinator linked the script that allows to chat with the router to send commands.

I would like to express my heartfelt gratitude to everyone for the collaboration and the valuable information shared.

I wasn’t aware of this repository (https://git.eworm.de/cgit/routeros-scripts), but I’ll certainly check it out! Thanks a lot for the tip I’ll give it a try and explore how I can utilize eworm’s scripts.

Seems really useful! :grinning_face_with_smiling_eyes::+1: