In the previous article, we looked at how to turn a Mikrotik router into an SMS gateway. Now I’ll show you how to receive incoming SMS messages and forward them directly to Telegram.
By and large, the main part of the script I’m publishing here was copied from the mikrotik forum. For this reason, I don’t quite understand how it works myself. :) However, I made some changes, as the original version redirected messages to email rather than Telegram. And as experience has shown, this isn’t the most convenient method.
The script looks quite bulky, but there’s a small problem with it: Mikrotik has encoding issues, so an SMS in Cyrillic will look like the sender has a lot of questions for you. The script below solves this problem through transliteration.
Special thanks to Andrey Radomanov , the author of the decoder script.
Go to system > scripts and create a new script.
We insert the following content into it:
<pre class="wp-block-syntaxhighlighter-code">:local autodelete 1;
:local smsArray ({});
:local inboxCursor 0;
:local inboxCount 0;
/interface lte at-chat lte1 input="AT+CMGF=0";
:local res [/interface lte at-chat lte1 input="AT+CPMS=\"SM\"" as-value wait=yes];
:set res ($res->"output");
:if ([:typeof [:find $res "+CPMS: "]] = "num") do={
:set inboxCount [:tonum [:pick $res ([:find $res "+CPMS: "]+7) ([:find $res ","])]];
:put "SMS Inbox count: $inboxCount";
};
:if ($inboxCount>0) do={
:local symbols {"-";" ";" ";"!";"\"";"#";"\$";"%";"&";"'";"(";")";"*";"+";",";"-";".";"/";"0";"1";"2";"3";"4";"5";"6";"7";"8";"9";":";";";"<";"=";">";"?";"@";"A";"B";"C";"D";"E";"F";"G";"H";"I";"J";"K";"L";"M";"N";"O";"P";"Q";"R";"S";"T";"U";"V";"W";"X";"Y";"Z";"[";"\\";"]";"^";"_";"`";"a";"b";"c";"d";"e";"f";"g";"h";"i";"j";"k";"l";"m";"n";"o";"p";"q";"r";"s";"t";"u";"v";"w";"x";"y";"z";"{";"|";"}";"~","\"","\""};
:local symbolsUTF [:toarray "2013,000A,0020,0021,0022,0023,0024,0025,0026,0027,0028,0029,002A,002B,002C,002D,002E,002F,0030,0031,0032,0033,0034,0035,0036,0037,0038,0039,003A,003B,003C,003D,003E,003F,0040,0041,0042,0043,0044,0045,0046,0047,0048,0049,004A,004B,004C,004D,004E,004F,0050,0051,0052,0053,0054,0055,0056,0057,0058,0059,005A,005B,005C,005D,005E,005F,0060,0061,0062,0063,0064,0065,0066,0067,0068,0069,006A,006B,006C,006D,006E,006F,0070,0071,0072,0073,0074,0075,0076,0077,0078,0079,007A,007B,007C,007D,007E,00AB,00BB"];
:local symbolsRus [:toarray "A,B,V,G,D,E,Yo,Zh,Z,I,Y,K,L,M,N,O,P,R,S,T,U,F,Kh,C,Ch,Sh,Sch,',I,',E,Yu,Ya,a,b,v,g,d,e,yo,zh,z,i,y,k,l,m,n,o,p,r,s,t,u,f,kh,c,ch,sh,sch,',i,',e,yu,ya,_,!,_,_,_,%,&,',(,),*,+,_,-,.,/,0,1,2,3,4,5,6,7,8,9"];
:local symbolsRusUTF [:toarray "0410,0411,0412,0413,0414,0415,0401,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,0451,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"];
:local hexstr "0123456789ABCDEF";
:local ascii " !\"#\$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
:while ($inboxCursor < $inboxCount) do={
:set inboxCursor ($inboxCursor+1);
:local sms [/interface lte at-chat lte1 input="AT+CMGR=$inboxCursor" as-value wait=yes];
:set sms ($sms->"output");
#:put ("inbox $inboxCursor:'".$sms."'\n");
:if (( [:typeof [:find $sms "+CMGR"]] != "nil" ) and ([:typeof [:find $sms "\n"]] != "nil")) do={
:set sms [:pick $sms ([:find $sms "+CMGR"]+1) ([:len $sms]+1)];
:local lineStart ([:find $sms "\n"]+1);
:local lineEnd [:find $sms "\nOK" $lineStart] ;
:if ([:typeof $lineEnd] = "nil") do={
:set lineEnd ([:len $sms]+1);
}
:set sms [:pick $sms $lineStart $lineEnd];
#Removes unesessary \r\n at the end of lines
:if ([:pick $sms ([:len $sms]-1)] = "\n") do={
:set sms [:pick $sms 0 ([:len $sms]-1)];
}
:if ([:pick $sms ([:len $sms]-1)] = "\r") do={
:set sms [:pick $sms 0 ([:len $sms]-1)];
}
:if ([:pick $sms ([:len $sms]-1)] = "\n") do={
:set sms [:pick $sms 0 ([:len $sms]-1)];
}
:if ([:pick $sms ([:len $sms]-1)] = "\r") do={
:set sms [:pick $sms 0 ([:len $sms]-1)];
}
#:put ("sms-raw $inboxCursor:'".$sms."'\n");
:local i;
:if ([:len $sms]>8) do={
:local pduEnd 0;
:local smsLen [:len $sms];
:local pduPartNum 1;
:local curPos 0;
:local smsTS;
:local smsFrom;
:local smsNum "0000";
:local dcs "00";
:while (($pduPartNum < 8) and ($curPos < $smsLen)) do={
:local pduPartLen;
:if ($pduPartNum = 1) do={ #SCA - Service Center Address
:set pduPartLen 14;
:if (([:pick $sms $curPos ($curPos+2)])!="91") do={
:put "first two bytes is not 91, skipping...";
:set pduPartLen ($pduPartLen+2);
}
}
:if ($pduPartNum = 2) do={
:set pduPartLen 2;
}
:if ($pduPartNum = 3) do={
:local tmpHex1 [:pick $sms $curPos ($curPos+1)]
:local tmpHex2 [:pick $sms ($curPos+1) ($curPos+2)]
:local fromLen;
:set pduPartLen 0;
:if ([:typeof [:find $hexstr $tmpHex1]] = "num") do={
:set fromLen ([:find $hexstr $tmpHex1] * 16);
}
:if ([:typeof [:find $hexstr $tmpHex2]] = "num") do={
:set fromLen ([:find $hexstr $tmpHex2]+$fromLen);
}
if ( [:pick [:tostr (($fromLen*10)/2)] ([:len [:tostr (($fromLen*10)/2)]]-1) [:len [:tostr (($fromLen*10)/2)]]] = "5") do={
:set pduPartLen ($fromLen+1);
} else={
:set pduPartLen $fromLen;
}
:set pduPartLen ($pduPartLen+4);
if (([:pick $sms ($curPos+2) ($curPos+4)]="91") or ([:pick $sms ($curPos+2) ($curPos+4)]="81")) do={
:set smsFrom "+";
:for i from=0 to=($fromLen-1) do={
if ( [:pick [:tostr (($i*10)/2)] ([:len [:tostr (($i*10)/2)]]-1) [:len [:tostr (($i*10)/2)]]] = "5") do={
:set smsFrom ($smsFrom.[:pick $sms ($curPos+4+$i-1)]);
} else {
:set smsFrom ($smsFrom.[:pick $sms ($curPos+4+$i+1)]);
}
}
} else={
:set smsFrom [:pick $sms ($curPos+2) ($curPos+$pduPartLen)];
:if ([:pick $smsFrom 0 2] = "D0") do={
:set smsFrom [:pick $smsFrom 2 [:len $smsFrom]];
:local curbit 0;
:local nextpart 0;
:local smsDecoded "";
:for i from=0 to=([:len $smsFrom]-1) step=2 do={
:local tmp [:pick $smsFrom $i];
:local charcode ([:find "0123456789ABCDEF" $tmp]*16);
:set tmp [:pick $smsFrom ($i+1)];
:set charcode ($charcode+[:find "0123456789ABCDEF" $tmp]);
:if ($curbit<7) do={
:set tmp ($charcode & (127>>$curbit));
:set tmp ($tmp<<$curbit);
:set tmp ($tmp + $nextpart);
:set nextpart ($charcode>>(7-$curbit));
:set curbit ($curbit+1);
}
:set smsDecoded ($smsDecoded.[:pick $ascii ($tmp-32)]);
:if ($curbit=7) do={
:set tmp $nextpart;
:set curbit 0;
:set nextpart 0;
:set smsDecoded ($smsDecoded.[:pick $ascii ($tmp-32)]);
}
}
:set smsFrom $smsDecoded;
}
}
#:put ("FROM: ".$smsFrom);
}
:if ($pduPartNum = 4) do={ #PID - Protocol Identifier
:set pduPartLen 2;
}
:if ($pduPartNum = 5) do={ #DCS - Data Coding Scheme
:set pduPartLen 2;
:set dcs [:pick $sms $curPos ($curPos+2)];
}
:if ($pduPartNum = 6) do={ #SCTS - Service Centre Time Stamp
:set pduPartLen 14;
:set smsTS "20";
:for i from=0 to=11 do={
if ( [:pick [:tostr (($i*10)/2)] ([:len [:tostr (($i*10)/2)]]-1) [:len [:tostr (($i*10)/2)]]] = "5") do={
:set smsTS ($smsTS.[:pick $sms ($curPos+$i-1)]);
} else {
:set smsTS ($smsTS.[:pick $sms ($curPos+$i+1)]);
}
}
}
:if ($pduPartNum = 7) do={ #UDL - User Data Length
:set pduPartLen 2;
}
:if ($pduPartLen = 0) do={
#:put ("could not detect Pdu len for part:".$pduPartNum);
:set pduPartLen 2;
}
#:put ("PDU PART ".$pduPartNum.": ".[:pick $sms $curPos ($curPos+$pduPartLen)]);
:set curPos ($curPos+$pduPartLen);
:set pduPartNum ($pduPartNum+1);
:set pduEnd $curPos;
}
#:log info ("PDU END: ".$pduEnd);
:if ($pduEnd = 0) do={
:log error ("Unable to find end of PDU in the next message: ".[:pick $sms 0 72]);
:set pduEnd 58;
}
#:put ("PDU:".[:pick $sms 0 $pduEnd]);
:set sms [:pick $sms $pduEnd [:len $sms]];
:local udh [:pick $sms 0 2];
:if ($udh = "05") do={
#:put ("UDH: ".[:pick $sms 0 12]);
:set smsNum ([:pick $sms 6 8].[:pick $sms 10 12]);
:set sms [:pick $sms 12 [:len $sms]];
}
:if ($dcs = "08") do={
:local decodedSMS "";
:for i from=0 to=([:len $sms]-1) step=4 do={
:local char [:pick $sms $i ($i+4)];
:local ind [:find $symbolsRusUTF $char];
:if ([:typeof $ind]="num") do={
:set decodedSMS ($decodedSMS.[:pick $symbolsRus $ind]);
} else={
:set ind [:find $symbolsUTF $char];
:if ([:typeof $ind]="num") do={
:set decodedSMS ($decodedSMS.[:pick $symbols $ind]);
} else={
:if ($char!="\r") do={
:set decodedSMS ($decodedSMS."[".$char."]");
}
}
}
}
:set sms $decodedSMS;
}
:if ($dcs = "00") do={
#:put ("UCS2: ".$sms);
:local curbit 0;
:local nextpart 0;
:local smsDecoded "";
:for i from=0 to=([:len $sms]-1) step=2 do={
:local tmp [:pick $sms $i];
:local charcode ([:find "0123456789ABCDEF" $tmp]*16);
:set tmp [:pick $sms ($i+1)];
:set charcode ($charcode+[:find "0123456789ABCDEF" $tmp]);
:if ($curbit<7) do={
:set tmp ($charcode & (127>>$curbit));
:set tmp ($tmp<<$curbit);
:set tmp ($tmp + $nextpart);
:set nextpart ($charcode>>(7-$curbit));
:set curbit ($curbit+1);
}
:set smsDecoded ($smsDecoded.[:pick $ascii ($tmp-32)]);
:if ($curbit=7) do={
:set tmp $nextpart;
:set curbit 0;
:set nextpart 0;
:set smsDecoded ($smsDecoded.[:pick $ascii ($tmp-32)]);
}
}
:set sms $smsDecoded;
}
#:log info ("SET ".($smsTS.$smsNum)." ".$sms);
:set ($smsArray->($smsTS.$smsFrom.$smsNum)) $sms;
}
}
}
:if ($autodelete=1) do={
:set inboxCursor 0;
:do {
:set inboxCursor ($inboxCursor+1);
/interface lte at-chat lte1 input="AT+CMGD=$inboxCursor";
} while ($inboxCursor < $inboxCount);
};
}; #if inboxCount>0
:local buffer ({});
:foreach k,v in=$smsArray do={
:local smsId [:pick $k 0 ([:len $k]-4)];
:local smsFrom [:pick $k 0 ([:len $k]-4)];
:set ($buffer->$smsId) ($buffer->$smsId . $v);
:set ($smsArray->$k);
}
:foreach k,v in=$buffer do={
:local smsFrom [:pick $k 14 [:len $k]];
:local sysName [/system identity get name];
:local datePrefix ("[".[:pick $k 6 8].".".[:pick $k 4 6].".".[:pick $k 0 4]." ".[:pick $k 8 10].":".[:pick $k 10 12].":".[:pick $k 12 14]."]");
:log warning ($datePrefix." SMS from ".$smsFrom.": ".$v);
:put ($sysName." ". $datePrefix . " SMS from " . $smsFrom . ": ". $v . "\n");
:local smsContent "SMS $datePrefix $smsFrom >> $v";
:local urlSafe [ :convert $smsContent to=url ];
/tool fetch url="https://api.telegram.org/botXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/sendmessage\?chat_id=XXXXXXXXX&text=$urlSafe";
:set ($buffer->$k);
}
</pre> UPDATE: The script syntax has been updated to be compatible with RouterOS 7.15+. If it doesn’t work for you on an earlier version, please upgrade!
If you don’t have a Telegram bot yet, now’s the time to create one. Once you’ve received the bot token and found your chat ID, edit the line so it looks like the example below.
/tool fetch url="https://api.telegram.org/bot842689562:nE38k5AEyDn7AEI5B0S71MdgT4EMfdpepAM/sendmessage\?chat_id=114750239&text=$smsContent"; Send yourself an SMS and then run the script on the Mikrotik. If everything is done correctly, the result will be like this.
Now we go to System > Scheduler and create a task there that will periodically run this script.
SMS messages sent via Telegram will not be sent again, even if you do not delete them from your router.



