Никто не в курсе, что происходит с поддержкой приёма и отправки SMS в кодировке UCS-2?
Когда столкнулся с этой проблемой прошло уже года 4, а RouterOS по прежнему не читает SMS в русской кодировке.

Kato писал(а):появилось еще в 24 ревизии
Код: Выделить всё
#Скрипт для проверки SMS на роутерах Mikrotik. Автор: Андрей Радоманов. Версия 2.
:local autodelete 1;
#---- system variables ---
:local smsArray ({});
:local inboxCursor 0;
:local inboxCount 0;
#:log info "Задаем режим работы в PDU-режиме.";
/interface lte at-chat lte1 input="AT+CMGF=0";
#Выбираем хранилище SMS на SIM карте
:local res [/interface lte at-chat lte1 input="AT+CPMS=\"SM\"" as-value wait=yes];
:set res ($res->"output");
#:put $res;
: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={
#См. http://embeddedpro.ucoz.ru/app_notes/send_short_SMS/PDU1.pdf
#Хуавей не отдает первый байт с длиной SCA, поэтому исходим из стандартных 12 байт + 2 байта на тип.=14
: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={#PDU Type. Пока что он нас не интересует. Пропускаем заодно
:set pduPartLen 2;
}
:if ($pduPartNum = 3) do={ #OA - Originator Address
#Тут уже все по настоящему - номер отправителя передается в формате SCA/OA. В первом байте шифруется кол-во цифр в номере
: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;
}
#Добавляем 2 байта - один указывал кол-во цифр, второй - это нотация номера ("91")
: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)];
#Если это 7-byte сообщение - делаем распаковку
: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;
#08 - без сжатия, 16-бит Unicode
#00 - USC2 7-bit (сжатая)
: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 ("Не удалось определить конец PDU в следующем сообщении: ".[: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];
#Если у сообщения есть UDH - декодируем его, заодно заполняя необходимые номера сообщений
:if ($udh = "05") do={
#:put ("UDH: ".[:pick $sms 0 12]);
:set smsNum ([:pick $sms 6 8].[:pick $sms 10 12]); #4-й байт - уникальный ИД, 6-й номер сообщения
:set sms [:pick $sms 12 [:len $sms]];
}
#Если это UTF16 сообщение - делаем транслит
: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;
}
#Если это 7-byte сообщение - делаем распаковку
: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
#Отправляем полученные sms:
:local minBalance 370;
:local emailFrom "lte-router@domain.ru";
:local emailTo "admin@domain.ru";
:local emailServer "mail.domain.ru";
:local emailPort 25;
:local emailUser "lte-router@domain.ru";
:local emailPassword "lte-router-password";
: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");
#Проверяем баланс тут. Привязано к строке вида "balance vashego scheta XXX.XX rub..."
:if (([:typeof [:find $v "scheta "]] = "num") and ([:typeof [:find $v " rub" [:find $v "scheta "]]] = "num")) do={
:local balance [:pick $v ([:find $v "scheta "]+7) [:find $v " rub" [:find $v "scheta "]] ]
#Отсекаем все, что после точки
:set balance [:pick $balance 0 [:find $balance "."]];
:if ($minBalance<$balance) do={
:log warning ("SIM account balance is ".$balance." rub.");
} else={
:log error ("SIM account balance is ".$balance." rub and it lower than needed (".$minBalance.").");
:local need ($minBalance-$balance);
#Отправляем почту только после 24-го числа
:local dm [:pick [/system clock get date] 4 6];
:if ($dm>23) do={
/tool e-mail send from=$emailFrom to=$emailTo server=$emailServer port=$emailPort user=$emailUser password=$emailPassword start-tls=yes subject=($sysName." SIM balance low (".$balance.")") body=($sysName. " " . $datePrefix ." SIM Balance = ".$balance." rub, nuzno eshe: ".$need." rub!");
};
}
} else={ #Отправляем все остальные смс как есть в телеграм.
#Отправляем почту
/tool e-mail send from=$emailFrom to=$emailTo server=$emailServer port=$emailPort user=$emailUser password=$emailPassword start-tls=yes subject=($sysName." - SMS From ".$smsFrom) body=($sysName." ".$datePrefix." SMS from ".$smsFrom.": ".$v);
}
:set ($buffer->$k);
}