Mikrotik: Telegramga SMS yuborish

Avvalgi maqolada biz Mikrotik routerni SMS shlyuziga qanday aylantirishni ko’rib chiqdik. Endi men sizga kiruvchi SMS xabarlarni qanday qabul qilishni va ularni to’g’ridan-to’g’ri Telegramga qanday yo’naltirishni ko’rsataman.

Umuman olganda, men bu yerda nashr etayotgan skriptning asosiy qismi mikrotik forumidan ko’chirilgan. Shu sababli, men uning qanday ishlashini o’zim ham tushunmayapman. :) Biroq, men ba’zi o’zgarishlar kiritdim, chunki asl versiyada xabarlar Telegramga emas, balki elektron pochtaga yo’naltirilgan edi. Va tajriba shuni ko’rsatadiki, bu eng qulay usul emas.

Skript juda katta hajmli ko’rinadi, ammo unda kichik bir muammo bor: Mikrotikda kodlash bilan bog’liq muammolar mavjud, shuning uchun kirill yozuvidagi SMS jo’natuvchining sizga juda ko’p savollari bordek ko’rinadi. Quyidagi skript bu muammoni transliteratsiya orqali hal qiladi.

Dekoder skripti muallifi Andrey Radomanovga alohida minnatdorchilik bildiramiz.

Tizim > skriptlarga o’ting va yangi skript yarating.

Biz unga quyidagi tarkibni kiritamiz:

 <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> 

YANGILASH: Skript sintaksisi RouterOS 7.15+ bilan mos kelishi uchun yangilandi. Agar u oldingi versiyada siz uchun ishlamasa, iltimos, yangilang!

Agar sizda hali Telegram boti bo’lmasa, endi uni yaratish vaqti keldi. Bot tokenini olganingizdan va chat identifikatoringizni topganingizdan so’ng, quyidagi misoldagi kabi ko’rinadigan qilib qatorni tahrirlang.

   /tool fetch url="https://api.telegram.org/bot842689562:nE38k5AEyDn7AEI5B0S71MdgT4EMfdpepAM/sendmessage\?chat_id=114750239&text=$smsContent"; 

O’zingizga SMS yuboring va keyin skriptni Mikrotikda ishga tushiring. Agar hamma narsa to’g’ri bajarilgan bo’lsa, natija quyidagicha bo’ladi.

Endi biz Tizim > Rejalashtiruvchiga o’tamiz va u yerda vaqti-vaqti bilan ushbu skriptni ishga tushiradigan vazifa yaratamiz.

Telegram orqali yuborilgan SMS xabarlar, hatto ularni routeringizdan o’chirmasangiz ham, qayta yuborilmaydi.