MikroTik SMS to Telegram – A SMS Gateway Forwarder Script

I needed a way to forward SMS messages from my Mikrotik router with modem. The easiest way was to forward the SMS to a Telegram chat.

The script retrieves incoming SMS messages, extracts essential information such as the sender, message content, and timestamp, and forwards them to the Telegram Bot API. It also includes basic error handling and provides feedback on the success or failure of the forwarding process. Multiple chat IDs are also possible.

Basic setup

  1. Setup modem and test sending and reciving without the script
  2. Register Telegram Bot and get API Token. I don't want to explain this here, there are enough tutorials on the internet.
  3. Get Chat ID. Send a message for example to @chatIDrobot and get the Chat ID.

Script setup

  1. Create a new script forward-incoming-sms in the Mikrotik router and paste the script code:

# ------------------------------------------------- #
# SMS to Telegram - A SMS Gateway Forwarder Script  #
# ------------------------------------------------- #

# Description
# This script will forward all SMS messages to a Telegram chat.

# Author
# 2024-01-11 foorschtbar
# https://blog.spaps.de/

# Credits
# http://blog.redax.hu/2021/02/mikrotik-sms-to-sms-forwarding.html
# https://medium.com/@dedanirungu/forwarding-sms-messages-with-mikrotik-to-website-url-via-modem-12d926615834
# https://github.com/eworm-de/routeros-scripts/blob/main/sms-forward.rsc

# Configuration
:local token "12345678:AAABBCCC...XXXYYYZZZZ"
:local chatids {"12345678";"12345678"}

:put "== Starting SMS forwarder script =="

# Check if receiving is enabled
:if ([ /tool/sms/get receive-enabled ] = false) do={
  :log warning ("Receiving of SMS is not enabled.")
  :error ("exit script");

# Check if the modem is in running state
:local Settings [ /tool/sms/get ];
:if ([ /interface/lte/get ($Settings->"port") running ] != true) do={
  :log warning ("The LTE interface is not in running state, skipping.")
  :error ("exit script");

# forward SMS in a loop
:local smsCount [ :len [ /tool/sms/inbox/find ] ]
:put ("Found ".$smsCount." SMS to process")
:local index 0
:foreach sms in=[ /tool/sms/inbox/find ] do={
    :set index ($index + 1)
    :put ("> Processing ".$index." of ".$smsCount)

    :local smsVal [ /tool/sms/inbox/get $sms ];
    :local smsPhone ($smsVal->"phone")
    :local smsType ($smsVal->"type")
    :local smsMessage ($smsVal->"message")
    :local smsTime ($smsVal->"timestamp")

    :local logmsg ("SMS from ".$smsPhone." on ".$smsTime." (".$smsType."):\n".$smsMessage)
    :put ($logmsg);
    :log info ("Forwarding ". $logmsg);

    # URL safe message
  :local urlMessage ""
    :for i from=0 to=([:len $logmsg] - 1) do={ 
      :local char [:pick $logmsg $i]

      :if ($char = "\n") do={
        :set $char "%0A";

      :if ($char = " ") do={
        :set $char "%20";

      :if ($char = "-") do={
        :set $char "%2D";

      :if ($char = "\?") do={
        :set $char "%3F";

      :if ($char = "!") do={
        :set $char "%21";

      :if ($char = "+") do={
        :set $char "%2B";

      :if ($char = "%") do={
        :set $char "%22";

      :if ($char = "'") do={
        :set $char "%27";

      :if ($char = "(") do={
        :set $char "%28";

      :if ($char = ")") do={
        :set $char "%29";

      :if ($char = ",") do={
        :set $char "%2C";

      :if ($char = ".") do={
        :set $char "%2E";

      :if ($char = ":") do={
        :set $char "%3A";

      :if ($char = ";") do={
        :set $char "%3B";

      :if ($char = "=") do={
        :set $char "%3D";

      :if ($char = "&") do={
        :set $char "%26";

      :if ($char = "*") do={
        :set $char "%2A";

      :if ($char = "/") do={
        :set $char "%2F";

        :set urlMessage ($urlMessage . $char);


    # send POST
    :local noerror true
    :local chatIdx 1
    :local chatsTotal [ :len $chatids ]
    :foreach chatid in=$chatids do={
        :put ("> Sending HTTP request ". $chatIdx . " of " . $chatsTotal."...")
        :local url ("https://api.telegram.org/bot" .$token . "/sendMessage")
        :local parameters ("?chat_id=" . $chatid . "&text=" . $urlMessage)
        :local fullurl ($url . $parameters)
        :local responseStr [/tool fetch url=$fullurl http-method=get as-value output=user]
        :put ("Status: ".$responseStr->"status")
        :put ("Data: ".$responseStr->"data")
        :if ($responseStr->"status" = "finished") do={
            :put ("Successfully forwarded message ID: " . $index. " to chat ID: " . $chatid)
            :log info ("Successfully forwarded message ID: " . $index. " to chat ID: " . $chatid)
        } else={
            :put ("Failed to forward message ID: " . $index. " to chat ID: " . $chatid)
            :log error ("Failed to forward message ID: " . $index. " to chat ID: " . $chatid)
            :set noerror false
        :set chatIdx ($chatIdx + 1)

    :if ($noerror) do={
        /tool sms inbox remove $sms
        :put ("Deleted message ID: " . $index)

:put "== Finished SMS forwarder script =="
  1. Change the configuration variables in the script: tokenand chatids
  2. Test the script by running it manually /system script run forward-incoming-sms
  3. Create a new scheduler forward-incoming-sms. You can use the following command or create it in the Webfig
    /system scheduler add name=forward-incoming-sms interval=10s on-event="/system script run forward-incoming-sms" start-time=startup

Mikrotik RouterOS WireGuard dynamic DNS endpoint refresh

MikroTik RouterOS doesn't yet support DNS names for peer entpoints (v7.1.1). As a workaround, you can set the endpoint address using the CLI, but RouterOS will not re-resolve the DNS name. If the IP addresses behind the DNS name change at some point, for example if you use DDNS, the WireGuard tunnel will eventually stop working. As a solution, you can use a script that checks if the peer endpoint address still matches the dns name and if not, updates to the latest ip address of the DNS name.


Add under System > Scheduler a new script and choose a useful interval.

:local wgPeerComment
:local wgPeerDns

:set wgPeerComment "Peer #1 Comment"
:set wgPeerDns "dns.example.com"

:if ([interface wireguard peers get number=[find comment="$wgPeerComment"] value-name=endpoint-address] != [resolve $wgPeerDns]) do={
  interface wireguard peers set number=[find comment="$wgPeerComment"] endpoint-address=[/resolve $wgPeerDns]