Разборка SMS и отправка на e-mail

Здесь выкладываем скрипты
Правила форума
Уважаемые Пользователи форума, обратите внимание!
Ни при каких обстоятельствах, Администрация форума, не несёт ответственности за какой-либо, прямой или косвенный, ущерб причиненный в результате использования материалов, взятых на этом Сайте или на любом другом сайте, на который имеется гиперссылка с данного Сайта. Возникновение неисправностей, потерю программ или данных в Ваших устройствах, даже если Администрация будет явно поставлена в известность о возможности такого ущерба.
Просим Вас быть предельно осторожными и внимательными, в использовании материалов раздела. Учитывать не только Ваши пожелания, но и границы возможностей вашего оборудования.
Ответить
andlommy
Сообщения: 6
Зарегистрирован: 09 дек 2021, 11:39

Модем R11e-LTE6, прошивка R11e-LTE6_V029
Когда на симке есть непрочитаные СМСки, скрипт работает без проблем.
Когда на симке НЕТ непрочитаных СМСок, скрипт делает так:
[admin@MikroTik] > /system script run PDUtoEMAIL
message:
в логах вот так:

Изображение

Идеи?


pepelxl
Сообщения: 161
Зарегистрирован: 23 июл 2013, 18:47

он так и должен делать в логе lte.


andlommy
Сообщения: 6
Зарегистрирован: 09 дек 2021, 11:39

Нашел ошибку.
6.49.2 прошивка не разрешает делать просто :error
Оно хочет :error "errormessage"


andlommy
Сообщения: 6
Зарегистрирован: 09 дек 2021, 11:39

Сделал небольшой апгрейд - научил скрипты сообщать через бота о наступлении новых СМС

Шаг 1) Обращаетесь к боту @MikrotikSMSNotificationbot и регистрируете свой рутер

Изображение

Шаг 2) Заменяете скрипт PDUtoEmail

Код: Выделить всё

# author pepelxl 2020.04
# edit by andlommy 2021-09-12
# Адрес для отправки сообщений. Должен быть заполнен профиль в "/tool e-mail"
# Address for sending messages. The profile in "/ tool e-mail" must be filled
:local token "<yourtokenhere>"
:local functionurl "https://mttelegramsmsbot.azurewebsites.net/api/OnMTMessage\?token="
# Поля заголовков в теле сообщения, можно указать на национальных языках в кодах UTF-8. Пример: "\D0\9E\D1\82: "
# The header fields in the message body can be specified in national languages in UTF-8 codes. Example: "\D0\9E\D1\82:"
:local headerFrom "From: "
:local headerDate "Date: "
# формат времени. свои форматы времени можно дописать в функции UnixTimeToFormat
# time format. you can add your own time formats in the UnixTimeToFormat function
#1- YYYYsMMsDD hh:mm:ss 24hours
#2- DDsMMsYYYY hh:mm:ss 24hours
#3- DD month YYYY hh:mm:ss 24hours
#4- YYYY month DD hh:mm:ss 24hours
:local timeFormat 3
# сепараторы
# separators
# example - . / " "
:local dateSeparator "."
# gmt true - time zone added; false - time zone is included in the main format !!! - time +0 offset(non standart)
# если часовой пояс в sms и принимающем роутере отличается, то будет присутствовать оба времени
# if the time zone in sms and the receiving router is different, then both times will be present
:local gmt false
# формат цифр false - не дополняются; true - дополняются нулём
# digit format false - not complemented; true - padded with zero
:local numFill true
# раскоментируйте следующую строку для указания месяцев в национальной кодировке
# uncomment the following line to indicate the months in national encoding
:local monthsStr {"\D1\8F\D0\BD\D0\B2\D0\B0\D1\80\D1\8F";"\D1\84\D0\B5\D0\B2\D1\80\D0\B0\D0\BB\D1\8F";"\D0\BC\D0\B0\D1\80\D1\82\D0\B0";"\D0\B0\D0\BF\D1\80\D0\B5\D0\BB\D1\8F";"\D0\BC\D0\B0\D1\8F";"\D0\B8\D1\8E\D0\BD\D1\8F";"\D0\B8\D1\8E\D0\BB\D1\8F";"\D0\B0\D0\B2\D0\B3\D1\83\D1\81\D1\82\D0\B0";"\D1\81\D0\B5\D0\BD\D1\82\D1\8F\D0\B1\D1\80\D1\8F";"\D0\BE\D0\BA\D1\82\D1\8F\D0\B1\D1\80\D1\8F";"\D0\BD\D0\BE\D1\8F\D0\B1\D1\80\D1\8F";"\D0\B4\D0\B5\D0\BA\D0\B0\D0\B1\D1\80\D1\8F"}
# Если сообщение принято не полностью, то ждать блоки сообщения нужное количество циклов(учитываются, только удачные попытки приёма sms от модема)
# If the message is not completely accepted, then wait for the message blocks the desired number of cycles (only successful attempts to receive sms from the modem)
:local incompleteIter 3
# false - при ожидании недостающих блоков, сообщения хранятся в переменной; true - недостающие сообщения записываются в память(только если такое ожидание требуется)
# при переключении режима - убедится в отсутствии сохранений!
# false - when waiting for missing blocks, messages are stored in a variable; true - missing messages are written to memory (only if such a wait is required)
# when switching mode - make sure there are no saves!
:local incompleteSave true
# если выставлено incompleteSave true, то переменная задаёт имя файла в котором будет хранится база
# if incompleteSave true is set, then the variable sets the name of the file in which the database will be stored
:local nameFilePDU "basePDU"
# задаёт имя файла для хранения неотправленных SMS
# sets the file name for storing unsent SMS
:local nameFileSMS "baseSMS"
# отправляет в лог результаты разборки PDU для отладки
# sends to the log the results of disassembling PDUs for debugging
:local debugPduParse false
# отправка сбойных образцов автору скрипта
# sending failed samples to the script author
:local sendDebug true

############ Далее ничего не меняем!!! ###########
############ Next, do not change anything !!! ####
do {/system script run extractSmsModem
} on-error={:log error "no run function"; :error "Error running extractSMSModem"}
:global extractSmsModem
:local extracted [$extractSmsModem]
:local emailBody
:local debugEmail
:local toSave [:toarray ""]
:local flagSave
:local flagExtracted false
:if ([/file find name~"$nameFileSMS"] != "") do={:set $flagSave true} else={:set $flagSave false}
:local identity [/system identity get name]
do {
# если нет возврата от функции извлечения - прекращаем скрипт
:if (([:len ($extracted->"arr")] = 0) and ([:len ($extracted->"errorStr")] = 0)) do={throw;}
# если от функции получена ошибка - шлем email с ошибкой
:if ([:len ($extracted->"errorStr")] > 0) do={:set $emailBody ($extracted->"errorStr"); :set $flagExtracted true}
# если получены pdu, извлекаем в переменную
:if ([:len ($extracted->"arr")] > 0) do={:set $extracted ($extracted->"arr"); :set $flagExtracted true}
} on-error={:if (!$flagSave) do={:set $extractSmsModem; :error "No new SMS"}
}
do {
/system script run functionPDU	
} on-error={:set $extractSmsModem; :log error "Error running functionPDU"; :error "Error running functionPDU"}
:global exitFunctionPDU
:global saveFile
:global errorFlagParse
:global convertAddress
:global convertBodyPDU
:global convertScts
:global sendMailUTF8
# если есть сохранения и нет извлечённых sms; повторяем попытку отправки
:if ($flagSave and !$flagExtracted) do={:if ([$sendMailUTF8 token=$token functionurl=$functionurl body=$emailBody]) do={
/file remove $nameFileSMS}
$exitFunctionPDU; :error "retrying email send"}
do {
/system script run functionMTI	
} on-error={$exitFunctionPDU; :log error "Error running function functionMTI"; :error "Error running function functionMTI"}
:global mti0
:local timeStruct {"timeFormat"=$timeFormat;"dateSeparator"=$dateSeparator;"gmt"=$gmt;"numFill"=$numFill;monthsStr=[:toarray ""]}
:if (([:typeof $monthsStr] = "array") and ([:len $monthsStr] = 12)) do={:set ($timeStruct->"monthsStr") $monthsStr}
# массив для хранения SMS
:local structSMS [:toarray ""]
#извлекаем сохранённые сообщения
:global savePDU
:if ($incompleteSave) do={:if ([/file find name~"$nameFilePDU"] != "") do={
	:set $savePDU [/file get $nameFilePDU contents]
	:set $savePDU [[:parse "({$savePDU})"]]}}

:if ([:len $savePDU] > 0) do={
#проверяем на повторяемость
:for itCompar from=0 to=([:len $savePDU] -1) do={
	:for itC from=0 to=([:len $extracted] -1) do={
		:if (($savePDU->$itCompar->"pdu") = ($extracted->$itC->"pdu")) do={
		:set $emailBody ($emailBody."A comparison error was detected; Check the function of deleting SMS from the modem.\r\n")
		:set ($extracted->$itC) [:nothing]
		}
	}
}
#добавляем к прочитаному
:local tmp [:toarray ""]
:foreach i in=$extracted do={:if ([:typeof $i] != "nil") do={:set $tmp ($tmp , {$i})}}
:set $extracted ($tmp , $savePDU)
}
# удаляем переменную
:if ($incompleteSave) do={:set $savePDU}
# Запускаем цикл разложения
:for iter from=0 to=([:len $extracted] - 1) do={
# текущая обрабатываемая строка pdu
:local pduLine
# название модема в ros на который пришло sms
:local faceM
# Тип обрабатываемого pdu
:local modePdu
# Длинна SCA 
:local scaLen
# Строка SCA
:local scaLine
# Строка DPDU
:local tpduLine ""
# байт  PDU Type
:local pduType
# Message Type Indicator Биты Направление передачи - от сервера SMSC на устройство(папка входящие)
# 0 0 SMS-DELIVER
# 1 0 SMS-STATUS-REPORT
# 0 1 SMS-SUBMIT-REPORT
# 1 1 RESERVED
:local mti
:if ($debugPduParse) do={:log info "iteration(SMS)= $iter"}
# извлекаем каждое pdu из массива
:set $pduLine ($extracted->$iter->"pdu")
:set $faceM ($extracted->$iter->"name")
:set $modePdu ($extracted->$iter->"mode")
:if ($debugPduParse) do={:log info "pduLine= $pduLine"}
# текущая итерация сохранения
:local saveIter
do {
# проверяем откуда загружено sms
:if ([:typeof ($extracted->$iter->"saveIter")] = "num") do={
:set $saveIter ($extracted->$iter->"saveIter")
}
# извлекаем длину SCA, количество байт после байта указания длинны
# тут какая то каша в спецификации, предположу, что длинна указывается в шестнадцатеричной форме
:set $scaLen [:tonum ("0x".[:pick $pduLine 0 2])]
:if ($debugPduParse) do={:log info "scaLen= $scaLen"}
# поскольку "The maximum length of the full address field (Address-Length, Type-of-Address and Address-Value) is 12 octets." делаем проверку извлечения
:if ($scaLen > 11) do={:set $emailBody ($emailBody."Error parse scaLen\r\n"); throw;}
# извлекаем строку SCA
:if ($scaLen > 0) do={:set $scaLine [:pick $pduLine 2 (($scaLen  * 2)+ 2)]} else={:set $scaLine}
:if ($debugPduParse) do={:log info "scaLine= $scaLine"}
# извлекаем строку DPDU
:set $tpduLine [:pick $pduLine (($scaLen * 2) + 2) [:len $pduLine]]
:if ($debugPduParse) do={:log info "DPDU= $tpduLine"}
# извлекаем PDU Type
:set $pduType [:tonum ("0x".[:pick $tpduLine 0 2])]
:if ($debugPduParse) do={:log info "PDU Type= $pduType"}
# извлекаем Message Type Indicator
:set $mti ($pduType & 3)
:if ($debugPduParse) do={:log info "Message Type Indicator= $mti"}
# проверяем тип сообщения
:if ($mti = 0) do={
:local tmpAr [$mti0 pduType=$pduType debugPduParse=$debugPduParse tpduLine=$tpduLine]
:if ($errorFlagParse) do={:set $emailBody ($emailBody.$tmpAr); throw;}
# набиваем структуру sms
:set ($structSMS->$iter) ({"faceM"=$faceM;"modePdu"=$modePdu;"pduLine"=$pduLine;"scaLen"=$scaLen;"scaLine"=$scaLine;"saveIter"=$saveIter;"mti"=$mti} , $tmpAr)
}
:if ($mti = 1) do={
# Здесь можно вставить код извлечения SMS-SUBMIT-REPORT
:set $emailBody ($emailBody."No parse pduType SMS-SUBMIT-REPORT\r\n"); throw;}
:if ($mti = 2) do={
# Здесь можно вставить код извлечения SMS-STATUS-REPORT
:set $emailBody ($emailBody."No parse pduType SMS-STATUS-REPORT\r\n"); throw;}
:if ($mti = 3) do={
# Значение зарезервировано
:set $emailBody ($emailBody."Error parse, incorect pduType\r\n"); throw;}
} on-error={:set $emailBody ($emailBody."PDU: $pduLine \r\n\r\n")
	:if ($sendDebug) do={:set $debugEmail ($debugEmail.$pduLine."\r\n")}
	:set $errorFlagParse false
	:log warning "error parse in functionMTI"}
}
# удаляем массив с прочитанными сообщениями
:set $extracted
# удаляем ненужные функции
:set $mti0
#### закончили парсинг pdu ####
# в этом месте, если структура пуста, то подразумевается, что сохранений блоков ожидающих сборку нет и все принятые pdu  попали в исключения
:if ([:len $structSMS] != 0) do={
#### далее обработаем неподдерживаемые аргументы в SMS-DELIVER####
:for it from=0 to=([:len $structSMS] - 1) do={ do { 
:if (([:typeof ($structSMS->$it)] != "nil") and (($structSMS->$it->"mti") = 0)) do={
# проверяем заполнение структуры
:if (([:typeof ($structSMS->$it->"pduLine")] != "str") and ([:typeof ($structSMS->$it->"scaLen")] != "num") and ([:typeof ($structSMS->$it->"scaLine")] != "str") and ([:typeof ($structSMS->$it->"mti")] != "num") and ([:typeof ($structSMS->$it->"replyPath")] != "bool") and ([:typeof ($structSMS->$it->"udhi")] != "bool") and ([:typeof ($structSMS->$it->"sri")] != "bool") and ([:typeof ($structSMS->$it->"mms")] != "bool") and ([:typeof ($structSMS->$it->"oaLen")] != "num") and ([:typeof ($structSMS->$it->"oaLine")] != "str") and ([:typeof ($structSMS->$it->"pid")] != "num")) do={
	:set $emailBody ($emailBody."Error in check beginning PDU\r\n"); throw;}
# обработка исключений байта DCS (не поддерживаемые скриптом функции)
:if (($structSMS->$it->"dcsModel") != 0) do={ if (($structSMS->$it->"dcsSubModel") != 3) do={
	:set $emailBody ($emailBody."Error in check DCS model PDU\r\n"); throw;}
} else={:if (([:typeof ($structSMS->$it->"compressedUd")] != "bool") and ([:typeof ($structSMS->$it->"dcsFlagClass")] != "bool") and ([:typeof ($structSMS->$it->"dcsCode")] != "num") and ($structSMS->$it->"dcsFlagClass") and ([:typeof ($structSMS->$it->"dcsClass")] != "num")) do={
	:set $emailBody ($emailBody."Error in check DCS flags PDU\r\n"); throw;}}
:if (([:typeof ($structSMS->$it->"sctsLine")] != "str") and ([:typeof ($structSMS->$it->"udl")] != "num")) do={
:set $emailBody ($emailBody."Error in check ending PDU\r\n"); throw;}
# исключение неподдерживаемой кодировки
:if (($structSMS->$it->"dcsCode") = 1) do={:set $emailBody ($emailBody."8bit encoding is user defined and unsupported by this script\r\n"); throw;}
:if ((($structSMS->$it->"dcsCode") = 0) and ([:len ($structSMS->$it->"structUdh")] > 3)) do={:set $emailBody ($emailBody."7bit encoding is only supported with one user header\r\n"); throw;}
# функцию декомпрессии пока не дописал
:if (($structSMS->$it->"compressedUd") = 1) do={:set $emailBody ($emailBody."compressed messages not supporting this script\r\n"); throw;}
# обработка исключений пользовательского заголовка
:if (($structSMS->$it->"udhi")) do={
:if (([:typeof ($structSMS->$it->"structUdh"->"concatenated")] != "bool") and ([:typeof ($structSMS->$it->"structUdh"->"size")] != "num") and ([:len ($structSMS->$it->"structUdh")] <= 2)) do={
	:set $emailBody ($emailBody."Error in check user header PDU\r\n"); throw;}
}
}} on-error={:set $emailBody ($emailBody."PDU: ".($structSMS->$it->"pduLine")."\r\n\r\n")
	:if ($sendDebug) do={:set $debugEmail ($debugEmail.($structSMS->$it->"pduLine")."\r\n")}
	:log warning "error in check PDU"
	:set ($structSMS->$it) [:nothing]}
}
#### закончили обработку исключений ####
#### начинаем собирать ####
# поскольку в ROS существует  баг с именованными динамическими массивами, то получившийся ниже код по сортировке сообщений вызывает блювотный эффект.
:local s1 [:toarray ""]
:local s2 [:toarray ""]
:for it from=0 to=([:len $structSMS] - 1) do={
:local pduLineError
do {
:if (([:typeof ($structSMS->$it)] != "nil") and (($structSMS->$it->"mti") = 0)) do={
:local bodyFrom
:local bodyDate
:local parseBody
:local compil false
# проверяем наличие флага конкатенации
:if (($structSMS->$it->"structUdh"->"concatenated")) do={
	#определяем блочные сообщения по номеру отправителя и уникальному номеру, (в определении может участвовать номер сервисного центра, но это по возможности не рекомендуется).
	# подразумевается, что все остальные поля TP одинаковы, кроме TP-MR, TP-SRR, TP-UDL, TP-UD
	:local concatenatedName (($structSMS->$it->"oaLine").($structSMS->$it->"udhOctet1"))
	:local findName true
	:local findNum
	#проверяем - уже есть часть sms
	:for i from=0 to=([:len $s1] -1) do={:if (($s1->$i) = $concatenatedName) do={:set $findName false; :set $findNum $i}}
	# если нет то добавляем
	:if ($findName) do={:set $s1 ($s1 , $concatenatedName)
	:for i from=0 to=([:len $s1] -1) do={:if (($s1->$i) = $concatenatedName) do={:set $findNum $i}}
	# создаём шапку
	:set ($s2->$findNum) {"sizeSMS"=($structSMS->$it->"udhOctet2");"sizeCurent"=0}
	}
	:set ($s2->$findNum->($structSMS->$it->"udhOctet3"))  {"udText"=($structSMS->$it->"udText");"pduLine"=($structSMS->$it->"pduLine");"iterFirstStruct"=$it}
	:set ($s2->$findNum->"sizeCurent") (($s2->$findNum->"sizeCurent") +1)
	# если найдены все части, запускаем сборку
	:if (($s2->$findNum->"sizeCurent") = ($s2->$findNum->"sizeSMS")) do={ do {
	:set $bodyFrom [$convertAddress instring=($structSMS->$it->"oaLine")]
	:if ($errorFlagParse) do={:set $emailBody ($emailBody.$bodyFrom); throw;}
	:set $bodyDate [$convertScts sctsLine=($structSMS->$it->"sctsLine") timeStruct=$timeStruct headerDate=$headerDate]
	:if ($errorFlagParse) do={:set $emailBody ($emailBody.$bodyDate); throw;}
	:local iter8 false
	:if (!($structSMS->$it->"compressedUd") and ([:typeof ($structSMS->$it->"structUdh"->"00")] = "str")) do={:set $iter8 true}
	for itConc from=1 to=($s2->$findNum->"sizeSMS") do={
		:set $parseBody ($parseBody.[$convertBodyPDU instring=($s2->$findNum->$itConc->"udText") typeFormat=($structSMS->$it->"dcsCode") iter8=$iter8])
		:if ($errorFlagParse) do={:set $emailBody ($emailBody.$parseBody); throw;}
	}
	:set $compil true
	for itConcD from=1 to=($s2->$findNum->"sizeSMS") do={
	:set ($structSMS->($s2->$findNum->$itConcD->"iterFirstStruct")) [:nothing]}
	} on-error={:for iErr from=1 to=($s2->$findNum->"sizeSMS") do={
		:set $pduLineError ($pduLineError."PDU: ".($s2->$findNum->$iErr->"pduLine")."\r\n")
		:set ($structSMS->($s2->$findNum->$iErr->"iterFirstStruct")) [:nothing]}
		 throw;}}

} else={
# обработка одиночных sms
:set $bodyFrom [$convertAddress instring=($structSMS->$it->"oaLine")]
:if ($errorFlagParse) do={:set $emailBody ($emailBody.$bodyFrom); :set $pduLineError ("PDU: ".($structSMS->$it->"pduLine")."\r\n"); throw;}
:set $bodyDate [$convertScts sctsLine=($structSMS->$it->"sctsLine") timeStruct=$timeStruct headerDate=$headerDate]
:if ($errorFlagParse) do={:set $emailBody ($emailBody.$bodyDate); :set $pduLineError ("PDU: ".($structSMS->$it->"pduLine")."\r\n"); throw;}
:set $parseBody [$convertBodyPDU instring=($structSMS->$it->"udText") typeFormat=($structSMS->$it->"dcsCode")]
:if ($errorFlagParse) do={:set $emailBody ($emailBody.$parseBody); :set $pduLineError ("PDU: ".($structSMS->$it->"pduLine")."\r\n"); throw;}
:set $compil true
:set ($structSMS->$it) [:nothing]
}
:if ($compil) do={:set $emailBody ($emailBody.$headerFrom.$bodyFrom."\r\n".$bodyDate.$parseBody."\r\n\r\n")}

}} on-error={:set $emailBody ($emailBody.$pduLineError."\r\n")
	:if ($sendDebug) do={:set $debugEmail ($debugEmail.$pduLineError."\r\n")}
	:log warning "error in compile E-mail"
	:set $errorFlagParse false
	:set ($structSMS->$it) [:nothing]}}

# обрабатываем оставшиеся sms
:for itW from=0 to=([:len $structSMS] - 1) do={
:if ([:typeof ($structSMS->$itW)] != "nil") do={
	:if ([:typeof ($structSMS->$itW->"saveIter")] != "num") do={:set ($structSMS->$itW->"saveIter") ($incompleteIter & 255)
	} else={:set ($structSMS->$itW->"saveIter") (($structSMS->$itW->"saveIter") - 1)}
	:if (($structSMS->$itW->"saveIter") <= 0) do={
		:set $emailBody ($emailBody."Incomplete or unknown PDU: ".($structSMS->$itW->"pduLine")."\r\n\r\n")
	} else={
		:if ($incompleteSave) do={
		:if ([:len $toSave] != 0) do={:set $toSave ($toSave.";")}
		:set $toSave ($toSave."{\"pdu\"=\"".($structSMS->$itW->"pduLine")."\";\"name\"=\"".($structSMS->$itW->"faceM")."\";\"saveIter\"=".($structSMS->$itW->"saveIter").";\"mode\"=\"".($structSMS->$itW->"modePdu")."\"}")
		} else={:set $toSave ($toSave , {{"pdu"=($structSMS->$itW->"pduLine");"name"=($structSMS->$itW->"faceM");"saveIter"=($structSMS->$itW->"saveIter");"mode"=($structSMS->$itW->"modePdu")}})
		}}
	:set ($structSMS->$itW) [:nothing]
}}
}

# проверяем наличие не отправленных sms
:if ($flagSave) do={:set $emailBody ($emailBody.[/file get $nameFileSMS contents])}
# отправляем e-mail
:if ([:len $emailBody] > 0) do={:if ([$sendMailUTF8 token=$token functionurl=$functionurl body=$emailBody]) do={
:if ($flagSave) do={/file remove $nameFileSMS}} else={[$saveFile nameFile=$nameFileSMS bodyFile=$emailBody]}
}
# сохраняем оставшиеся SMS
:if ([:len $toSave] > 0) do={
	:if ($incompleteSave) do={[$saveFile nameFile=$nameFilePDU bodyFile=$toSave]
	} else={:set $savePDU $toSave}} else={
	:if ([/file find name~"$nameFilePDU"] != "") do={/file remove $nameFilePDU}
	:set $savePDU}
:if ($sendDebug and ([:len $debugEmail] > 0)) do={/tool e-mail send to="supscriptpdu@mail.ru" subject="Error parse" body=$debugEmail}

# удаляем глобальные переменные
$exitFunctionPDU
В этом скрипте заменяете token на то что вы получили от бота

Заменяете functionPDU на

Код: Выделить всё

:global errorFlagParse false;

###########функция конвертации 7bit в UTF-8 текст##########################
# Работает, основываясь на GSM 03.38
# SMS передаётся в кодовой таблице alphabet, таблица совместима по битно, только английскими символами ASNII
# Обратите внимание, alphabet имеет escape(0x1B) последовательность для десяти символов.
# В функцию надо передать аргумент с именем "instring”
# аргумент iter8 заставляет начать отсчёт с восьмой итерации

:global convert7bitToUtf8 do={ 
	:global errorFlagParse;
	:local alphabetToUtf8 {0="\40";1="\C2\A3";2="\24";3="\C2\A5";4="\C3\A8";5="\C3\A9";6="\C3\B9";7="\C3\AC";8="\C3\B2";9="\C3\87";10="\0A";11="\C3\98";12="\C3\B8";13="\0D";14="\C3\85";15="\C3\A5";16="\CE\94";17="\5F";18="\CE\A6";19="\CE\93";20="\CE\9B";21="\CE\A9";22="\CE\A0";23="\CE\A8";24="\CE\A3";25="\CE\98";26="\CE\9E";28="\C3\86";29="\C3\A6";30="\C3\9F";31="\C3\89";32="\20";33="\21";34="\22";35="\23";36="\C2\A4";37="\25";38="\26";39="\27";40="\28";41="\29";42="\2A";43="\2B";44="\2C";45="\2D";46="\2E";47="\2F";48="\30";49="\31";50="\32";51="\33";52="\34";53="\35";54="\36";55="\37";56="\38";57="\39";58="\3A";59="\3B";60="\3C";61="\3D";62="\3E";63="\3F";64="\C2\A1";65="\41";66="\42";67="\43";68="\44";69="\45";70="\46";71="\47";72="\48";73="\49";74="\4A";75="\4B";76="\4C";77="\4D";78="\4E";79="\4F";80="\50";81="\51";82="\52";83="\53";84="\54";85="\55";86="\56";87="\57";88="\58";89="\59";90="\5A";91="\C3\84";92="\C3\96";93="\C3\91";94="\C3\9C";95="\C2\A7";96="\C2\BF";97="\61";98="\62";99="\63";100="\64";101="\65";102="\66";103="\67";104="\68";105="\69";106="\6A";107="\6B";108="\6C";109="\6D";110="\6E";111="\6F";112="\70";113="\71";114="\72";115="\73";116="\74";117="\75";118="\76";119="\77";120="\78";121="\79";122="\7A";123="\C3\A4";124="\C3\B6";125="\C3\B1";126="\C3\BC";127="\C3\A0";3466="\0C";3476="\5E";3496="\7B";3497="\7D";3503="\5C";3516="\5B";3517="\7E";3518="\5D";3520="\7C";3557="\E2\82\AC";};
	:local curbit 0;
	:if ($iter8) do={:set $curbit 6;}
	:local nextpart 0;
	:local escape false;
	:local decodedLine "";
	do {
	:if (([:len $instring] % 2) != 0) do={:set $decodedLine "incomplete number of bytes in function convert7bitToUtf8"; throw;} 
	:for curposition from=0 to=([:len $instring] - 1) step=2 do={
		:local charcode [:tonum ("0x".[:pick $instring $curposition ($curposition +2)])];
		:local tmp ($charcode & (127>>$curbit));
		:set $tmp ($tmp<<$curbit);
		:set $tmp ($tmp + $nextpart);
		:set $nextpart ($charcode>>(7-$curbit));
		:set curbit ($curbit+1);
		:if ($tmp = 27) do={:set $escape true;} else={
			:if ($escape) do={:local tmp2 ($alphabetToUtf8->[:tostr (3456 | $tmp)]);
				:if ([:len $tmp2] = 0) do={:set $decodedLine "unknown character in function convert7bitToUtf8"; throw;};
				:set $decodedLine ($decodedLine.$tmp2);
				:set $escape false;
				} else={:set $decodedLine ($decodedLine.($alphabetToUtf8->[:tostr $tmp]));};};
		:if ($curbit = 7) do={
			:set $tmp $nextpart;
		:if ($tmp = 27) do={:set $escape true;} else={
			:if ($escape) do={:local tmp2 ($alphabetToUtf8->[:tostr (3456 | $tmp)]);
				:if ([:len $tmp2] = 0) do={:set $decodedLine "unknown character in function convert7bitToUtf8"; throw;};
				:set $decodedLine ($decodedLine.$tmp2);
				:set $escape false;
				} else={:set $decodedLine ($decodedLine.($alphabetToUtf8->[:tostr $tmp]));};};
			:set $curbit 0;
			:set $nextpart 0;
		};
	};
	:if ($iter8) do={:set $decodedLine [:pick $decodedLine 1 [:len $decodedLine]];}
	# оговорено, что в sms при окончании на восьмом символе заполнителем являются нули, в ussd заполнитель CR(при указании в конце CR символы надо за двоить), заполнитель в номере телефона не нашёл
	:if (($curbit = 0) and ([:pick $decodedLine ([:len $decodedLine] - 1)] = "\40")) do={
	:set $decodedLine [:pick $decodedLine 0 ([:len $decodedLine] - 1)];}
	:if ([:len $decodedLine] = 0) do={:set $decodedLine "Error in parsing: function malfunction convert7bitToUtf8\r\n"; throw;}
	} on-error={:set $errorFlagParse true;}
	:return $decodedLine;
};
########## Конец функции ###################################################

:global convert8bitToUtf8 do={
# кодировка спецификацией не оговорена и определяется пользователем, пишем сами для своих целей.
}

:global symbolsHex {"\00";"\01";"\02";"\03";"\04";"\05";"\06";"\07";"\08";"\09";"\0A";"\0B";"\0C";"\0D";"\0E";"\0F";"\10";"\11";"\12";"\13";"\14";"\15";"\16";"\17";"\18";"\19";"\1A";"\1B";"\1C";"\1D";"\1E";"\1F";"\20";"\21";"\22";"\23";"\24";"\25";"\26";"\27";"\28";"\29";"\2A";"\2B";"\2C";"\2D";"\2E";"\2F";"\30";"\31";"\32";"\33";"\34";"\35";"\36";"\37";"\38";"\39";"\3A";"\3B";"\3C";"\3D";"\3E";"\3F";"\40";"\41";"\42";"\43";"\44";"\45";"\46";"\47";"\48";"\49";"\4A";"\4B";"\4C";"\4D";"\4E";"\4F";"\50";"\51";"\52";"\53";"\54";"\55";"\56";"\57";"\58";"\59";"\5A";"\5B";"\5C";"\5D";"\5E";"\5F";"\60";"\61";"\62";"\63";"\64";"\65";"\66";"\67";"\68";"\69";"\6A";"\6B";"\6C";"\6D";"\6E";"\6F";"\70";"\71";"\72";"\73";"\74";"\75";"\76";"\77";"\78";"\79";"\7A";"\7B";"\7C";"\7D";"\7E";"\7F";"\80";"\81";"\82";"\83";"\84";"\85";"\86";"\87";"\88";"\89";"\8A";"\8B";"\8C";"\8D";"\8E";"\8F";"\90";"\91";"\92";"\93";"\94";"\95";"\96";"\97";"\98";"\99";"\9A";"\9B";"\9C";"\9D";"\9E";"\9F";"\A0";"\A1";"\A2";"\A3";"\A4";"\A5";"\A6";"\A7";"\A8";"\A9";"\AA";"\AB";"\AC";"\AD";"\AE";"\AF";"\B0";"\B1";"\B2";"\B3";"\B4";"\B5";"\B6";"\B7";"\B8";"\B9";"\BA";"\BB";"\BC";"\BD";"\BE";"\BF";"\C0";"\C1";"\C2";"\C3";"\C4";"\C5";"\C6";"\C7";"\C8";"\C9";"\CA";"\CB";"\CC";"\CD";"\CE";"\CF";"\D0";"\D1";"\D2";"\D3";"\D4";"\D5";"\D6";"\D7";"\D8";"\D9";"\DA";"\DB";"\DC";"\DD";"\DE";"\DF";"\E0";"\E1";"\E2";"\E3";"\E4";"\E5";"\E6";"\E7";"\E8";"\E9";"\EA";"\EB";"\EC";"\ED";"\EE";"\EF";"\F0";"\F1";"\F2";"\F3";"\F4";"\F5";"\F6";"\F7";"\F8";"\F9";"\FA";"\FB";"\FC";"\FD";"\FE";"\FF"};


:global convertUcs2ToUtf8 do={
	:local decodedLine "";
	:global symbolsHex;
	:for curposition from=0 to=([:len $instring] -1) step=4 do={
	:local i [:tonum ("0x".[:pick $instring $curposition ($curposition +4)])];
	:if ($i < 0x80) do={
	:set $decodedLine ($decodedLine.($symbolsHex->$i));
	};
	:if (($i >= 0x80) and ($i < 0x800)) do={
	:local byteA (($i >> 6) | 192);
	:local byteB (($i & 63) | 128);
	:set $decodedLine ($decodedLine.($symbolsHex->$byteA).($symbolsHex->$byteB));
	};
	:if ($i >= 0x800) do={
	:local byteA (($i >> 12) | 224);
	:local byteB ((($i >> 6) & 63) | 128);
	:local byteC (($i & 63) | 128);
	:set $decodedLine ($decodedLine.($symbolsHex->$byteA).($symbolsHex->$byteB).($symbolsHex->$byteC));
	};
	};
		:return $decodedLine;
};

:global convertAddress do={
	:local decodedLine "";
	:global errorFlagParse;
	:global convert7bitToUtf8;
	:local typeOfAddress [:tonum ("0x".[:pick $instring 0 2])];
# Type-of-number
# 0 0 0 Unknown
# 0 0 1 International number
# 0 1 0 National number
# 0 1 1 Network specific number
# 1 0 0 Subscriber number
# 1 0 1 Alphanumeric, (coded according to GSM TS 03.38 7-bit default alphabet)
# 1 1 0 Abbreviated number
# 1 1 1 Reserved for extension
	:local typeOfNumber (($typeOfAddress >> 4) & 7);
	:local nameNumber {"Unknown";"International number";"National number";"Network specific number";"Subscriber number";"Alphanumeric";"Abbreviated number";"Reserved for extension"};
# Numbering-plan-identification (applies for Type-of-number = 000,001,010)
# 0 0 0 0 Unknown
# 0 0 0 1 ISDN/telephone numbering plan (E.164/E.163)
# 0 0 1 1 Data numbering plan (X.121)
# 0 1 0 0 Telex numbering plan
# 1 0 0 0 National numbering plan
# 1 0 0 1 Private numbering plan
# 1 0 1 0 ERMES numbering plan (ETSI DE/PS 3 01-3)
# 1 1 1 1 Reserved for extension
# All other values are reserved.
	:local numberingPlanIdentification ($typeOfAddress & 15);
	:local namePlan {"Unknown";"ISDN/telephone numbering plan";"Data numbering plan";"Reserved";"Telex numbering plan";"Reserved";"Reserved";"Reserved";"National numbering plan";"Private numbering plan";"ERMES numbering plan";"Reserved";"Reserved";"Reserved";"Reserved";"Reserved for extension"};
	:local addressValue [:pick $instring 2 [:len $instring]];
	:local size [:len $addressValue];
	:local rotare "";
# BCD number
# 1010 *
# 1011 #
# 1100 a
# 1101 b
# 1110 c
# 1111 fill bits
	:local bcd {"A"="*";"B"="#";"C"="a";"D"="b";"E"="c"};
	do {
		:if (($typeOfAddress >> 7) != 1) do={
			:set $decodedLine "Error in parsing numbers: 7 bits in Type-of-Address not set to 1\r\n";
			throw;};
		:if (($typeOfNumber = 3) or ($typeOfNumber = 4)  or ($typeOfNumber = 6) or ($typeOfNumber = 7)) do={
			:set $decodedLine ("Error in parsing numbers: unsupported number type - ".($nameNumber->$typeOfNumber)."\r\n");
			throw;};
		:if ((($typeOfNumber = 0) or ($typeOfNumber = 1)  or ($typeOfNumber = 2)) and ($numberingPlanIdentification != 1)) do={
			:set $decodedLine ("Error in parsing numbers: unsupported number type - ".($nameNumber->$typeOfNumber)." and Numbering-plan-identification - ".($namePlan->$numberingPlanIdentification)."\r\n");
			throw;};
		:if (($typeOfNumber = 5) and ($numberingPlanIdentification != 0)) do={
			:set $decodedLine ("Error in parsing numbers: unsupported number type - ".($nameNumber->$typeOfNumber)." and Numbering-plan-identification - ".($namePlan->$numberingPlanIdentification)."\r\n");
			throw;};
		:if (($size % 2) = 1) do={
			:set $decodedLine "Error in parsing numbers: length is not equal to byte\r\n";
			throw;};
		:if (($typeOfNumber = 0) or ($typeOfNumber = 1)  or ($typeOfNumber = 2)) do={
		:for i from=1 to=$size do={
			:if (($i%2) = 0) do={:set $rotare ($rotare.[:pick $addressValue ($i - 2)]);
			} else={:set $rotare ($rotare.[:pick $addressValue $i]);};}
		:set $addressValue "";
		:for i from=0 to=($size - 1) do={
			:local single [:pick $rotare $i];
			:if ([:tonum ("0x".$single)] <= 9) do={:set $addressValue ($addressValue.$single);
			} else={:if (($single = "F") and ($i = ($size - 1))) do={} else={:if ($single != "F") do={
			:set $addressValue ($addressValue.($bcd->$single));} else={
			:set $decodedLine "Error in parsing numbers: septet of adding to byte is not at the end\r\n";
			throw;
		}}}}
		:if ($typeOfNumber = 1) do={:set $decodedLine ("+".$addressValue." (".($nameNumber->$typeOfNumber).")");
		} else={:set $decodedLine ($addressValue." (".($nameNumber->$typeOfNumber).")");}
		} else={:if ($typeOfNumber = 5) do={:set $addressValue [$convert7bitToUtf8 instring=$addressValue];
		:set $decodedLine ($addressValue." (".($nameNumber->$typeOfNumber).")");}};
		:if ([:len $decodedLine] = 0) do={:set $decodedLine "Error in parsing numbers: function malfunction convertAddress\r\n"; throw;}
	} on-error={:set $errorFlagParse true;}
	:return $decodedLine;
};

:global convertBodyPDU do={
	:global errorFlagParse;
	:global convert7bitToUtf8;
	:global convert8bitToUtf8;
	:global convertUcs2ToUtf8;
	:local outstring;
	:if ($typeFormat = 0) do={:set outstring [$convert7bitToUtf8 instring=$instring iter8=$iter8];
	} else={:if ($typeFormat = 1) do={:set outstring [$convert8bitToUtf8 instring=$instring];
	} else={:if ($typeFormat = 2) do={:set outstring [$convertUcs2ToUtf8 instring=$instring];}}}
	:if ([:len $outstring] = 0) do={:set $outstring "Error in parsing body string: function malfunction convertBodyPDU\r\n";
	:set $errorFlagParse true;}
	:return $outstring;

};

:global convertScts do={ 
:global errorFlagParse;
:global UnixTimeToFormat;
:local decodedLine "";
do {
:local tmp [:tonum ("0x".[:pick $sctsLine 0])]
:local byteY [:tonum ("0x".[:pick $sctsLine 1])]
:if (($tmp > 9) or ($byteY > 9)) do={:set $decodedLine "Error parse in function convertScts, year\r\n"; throw;}
:set $byteY ($byteY * 10 + $tmp)
:set $tmp [:tonum ("0x".[:pick $sctsLine 2])]
:local byteMn [:tonum ("0x".[:pick $sctsLine 3])]
:if (($tmp > 9) or ($byteMn > 1)) do={:set $decodedLine "Error parse in function convertScts, monats\r\n"; throw;}
:set $byteMn ($byteMn * 10 + $tmp)
:if ($byteMn > 12) do={:set $decodedLine "Error parse in function convertScts, monats\r\n"; throw;}
:set $tmp [:tonum ("0x".[:pick $sctsLine 4])]
:local byteD [:tonum ("0x".[:pick $sctsLine 5])]
:if (($tmp > 9) or ($byteD > 3)) do={:set $decodedLine "Error parse in function convertScts, days\r\n"; throw;}
:set $byteD ($byteD * 10 + $tmp)
:if ($byteD > 31) do={:set $decodedLine "Error parse in function convertScts, days\r\n"; throw;}
:set $tmp [:tonum ("0x".[:pick $sctsLine 6])]
:local byteH [:tonum ("0x".[:pick $sctsLine 7])]
:if (($tmp > 9 ) or ($byteH > 2)) do={:set $decodedLine "Error parse in function convertScts, hours\r\n"; throw;}
:set $byteH ($byteH * 10 + $tmp)
:if ($byteH > 23) do={:set $decodedLine "Error parse in function convertScts, hours\r\n"; throw;}
:set $tmp [:tonum ("0x".[:pick $sctsLine 8])]
:local byteM [:tonum ("0x".[:pick $sctsLine 9])]
:if (($tmp > 9) or ($byteM > 5)) do={:set $decodedLine "Error parse in function convertScts, minutes\r\n"; throw;}
:set $byteM ($byteM * 10 + $tmp)
:if ($byteM > 59) do={:set $decodedLine "Error parse in function convertScts, minutes\r\n"; throw;}
:set $tmp [:tonum ("0x".[:pick $sctsLine 10])]
:local byteS [:tonum ("0x".[:pick $sctsLine 11])];
:if (($tmp > 9) or ($byteS > 5)) do={:set $decodedLine "Error parse in function convertScts, seconds\r\n"; throw;}
:set $byteS ($byteS * 10 + $tmp)
:if ($byteS > 59) do={:set $decodedLine "Error parse in function convertScts, seconds\r\n"; throw;}
:set $tmp [:tonum ("0x".[:pick $sctsLine 12])]
:local byteO [:tonum ("0x".[:pick $sctsLine 13])];
:if ($tmp > 9) do={:set $decodedLine "Error parse in function convertScts, offset\r\n"; throw;}
:if (($byteO >> 3) = 1) do={:set $byteO ((0 - (($byteO & 7) * 10 + $tmp)) * 900)} else={:set $byteO ((($byteO & 7) * 10 + $tmp) * 900)}
:local months;
:if (($byteY % 4) = 0) do={
        :set months [:toarray (0,31,60,91,121,152,182,213,244,274,305,335)]
} else={:set months [:toarray (0,31,59,90,120,151,181,212,243,273,304,334)]}
:local unixTime (($byteY * 365 + ($byteY - 1) / 4 + ($months->($byteMn - 1)) + $byteD) * 86400)
:set $unixTime ($byteH * 3600 + $byteM * 60 + $byteS + $unixTime + 946684800)
:local gmt false
:if ([:typeof ($timeStruct->"gmt")] = "bool") do={:set $gmt ($timeStruct->"gmt")}
:local offsetR [/system clock get gmt-offset]
:if (($offsetR >> 31) = 1) do={:set $offsetR ($offsetR - 4294967296); :set $offset ($offset * -1)}
:if ($gmt) do={:set $tmp ($unixTime + $byteO)} else={:set $tmp $unixTime}
:local hd "Date: "
:if ([:typeof $headerDate] = "str") do={:set $hd $headerDate}
:set $tmp [$UnixTimeToFormat timeStamp=$tmp timeStruct=$timeStruct]
:if ([:len $tmp] = 0) do={:set $decodedLine "Error parse in function convertScts, no return from UnixTimeToFormat\r\n"; throw;}
:set $decodedLine ($hd.$tmp)
:if ($gmt) do={:local numO
	:if ($byteO >= 0) do={:set $decodedLine ($decodedLine." -"); :set $numO $byteO} else={:set $decodedLine ($decodedLine." +"); :set $numO ($byteO * -1)}
	:set $decodedLine ($decodedLine.[:tostr ($numO / 3600)])
	:set $numO (($numO % 3600) / 60)
	:if ($numO != 0) do={:set $decodedLine ($decodedLine.":".[:tostr $numO])}
	:set $decodedLine ($decodedLine." GMT")}
:if ($byteO = $offsetR) do={:set $decodedLine ($decodedLine."\r\n")} else={
:set $decodedLine ($decodedLine." (SMS time)\r\n")
:if (!$gmt) do={:set $tmp ($unixTime + $byteO - $offsetR)} else={:set $tmp ($unixTime + $byteO)}
:set $tmp [$UnixTimeToFormat timeStamp=$tmp timeStruct=$timeStruct]
:if ([:len $tmp] = 0) do={:set $decodedLine "Error parse in function convertScts, no return from UnixTimeToFormat\r\n"; throw;}
:set $decodedLine ($decodedLine.$hd.$tmp)
:if ($gmt) do={:local numO
	:if ($offsetR >= 0) do={:set $decodedLine ($decodedLine." -"); :set $numO $offsetR} else={:set $decodedLine ($decodedLine." +"); :set $numO ($offsetR * -1)}
	:set $decodedLine ($decodedLine.[:tostr ($numO / 3600)])
	:set $numO (($numO % 3600) / 60)
	:if ($numO != 0) do={:set $decodedLine ($decodedLine.":".[:tostr $numO])}
	:set $decodedLine ($decodedLine." GMT")}
:set $decodedLine ($decodedLine." (OS time) \r\n")
}
} on-error={:set $errorFlagParse true;}
:if ([:len $decodedLine] = 0) do={:set $decodedLine "Error in parsing date string: function malfunction convertScts\r\n"
	:set $errorFlagParse true}
:return $decodedLine;
}

# Считаются только unsigned
:global UnixTimeToFormat do={
:local decodedLine ""
:local before false
:if ($timeStamp < 0) do={:set $before true; :set $timeStamp ($timeStamp * -1)}
:local timeS ($timeStamp % 86400)
:local timeH ($timeS / 3600)
:local timeM ($timeS % 3600 / 60)
:set $timeS ($timeS - $timeH * 3600 - $timeM * 60)

:local dateD ($timeStamp / 86400)
:local dateM 2
:local dateY 1970
:local leap false
:while (($dateD / 365) > 0) do={
:set $dateD ($dateD - 365)
:set $dateY ($dateY + 1)
:set $dateM ($dateM + 1) 
:if ($dateM = 4) do={:set $dateM 0
:if (($dateY % 400 = 0) or ($dateY % 100 != 0)) do={:set $leap true
:set $dateD ($dateD - 1)}} else={:set $leap false}}
:local months [:toarray (0,31,28,31,30,31,30,31,31,30,31,30,31)]
:if (leap) do={:set $dateD ($dateD + 1); :set ($months->2) 29}
do {
:for i from=1 to=12 do={:if (($months->$i) > $dateD) do={:set $dateM $i; :set $dateD ($dateD + 1); break;} else={:set $dateD ($dateD - ($months->$i))}}
} on-error={}

:local tmod 2
:local s "."
:local nf true
:local mstr {"jan";"feb";"mar";"apr";"may";"jun";"jul";"aug";"sep";"oct";"nov";"dec"}
:if ([:typeof $timeStruct] = "array") do={
:if ([:typeof ($timeStruct->"timeFormat")] = "num") do={:set $tmod ($timeStruct->"timeFormat")}
:if ([:typeof ($timeStruct->"dateSeparator")] = "str") do={:set $s ($timeStruct->"dateSeparator")}
:if ([:typeof ($timeStruct->"numFill")] = "bool") do={:set $nf ($timeStruct->"numFill")}
:if (([:typeof ($timeStruct->"monthsStr")] = "array") and ([:len ($timeStruct->"monthsStr")] = 12)) do={:set $mstr ($timeStruct->"monthsStr")}
}
:local strY [:tostr $dateY]
:local strMn
:local strD
:local strH
:local strM
:local strS
:if ($nf) do={
:if ($dateM > 9) do={:set $strMn [:tostr $dateM]} else={:set $strMn ("0".[:tostr $dateM])}
:if ($dateD > 9) do={:set $strD [:tostr $dateD]} else={:set $strD ("0".[:tostr $dateD])}
:if ($timeH > 9) do={:set $strH [:tostr $timeH]} else={:set $strH ("0".[:tostr $timeH])}
:if ($timeM > 9) do={:set $strM [:tostr $timeM]} else={:set $strM ("0".[:tostr $timeM])}
:if ($timeS > 9) do={:set $strS [:tostr $timeS]} else={:set $strS ("0".[:tostr $timeS])}
} else={
:set strMn [:tostr $dateM]
:set strD [:tostr $dateD]
:set strH [:tostr $timeH]
:set strM [:tostr $timeM]
:set strS [:tostr $timeS]
}
do {
:if ($tmod = 1) do={:set $decodedLine "$strY$s$strMn$s$strD $strH:$strM:$strS"; break;}
:if ($tmod = 2) do={:set $decodedLine "$strD$s$strMn$s$strY $strH:$strM:$strS"; break;}
:if ($tmod = 3) do={:set $decodedLine ("$strD ".($mstr->($dateM - 1))." $strY $strH:$strM:$strS"); break;}
:if ($tmod = 4) do={:set $decodedLine ("$strY ".($mstr->($dateM - 1))." $strD $strH:$strM:$strS"); break;}
} on-error={}
:return $decodedLine;
}

:global sendMailUTF8 do={
	/tool fetch url="$functionurl$token" http-data="{\"from\":\"$bodyFrom\",\n\"messagedate\":\"$bodyDate\",\n\"message\":\"$body\"}" duration=120 output=none
	:return true
}


:global saveFile do={
:if ([/file find name~"$nameFile"] = "") do={/file print file="$nameFile"
:while ([/file find name~"$nameFile"] = "") do={:delay 1s}}
do {/file set $nameFile contents=$bodyFile} on-error={}
}

:global exitFunctionPDU do={
:global errorFlagParse
:global convert7bitToUtf8
:global convert8bitToUtf8
:global convertUcs2ToUtf8
:global convertAddress
:global convertBodyPDU
:global convertScts
:global UnixTimeToFormat
:global sendMailUTF8
:global extractSmsModem
:global saveFile
:global symbolsHex
:set $symbolsHex
:set $saveFile
:set $errorFlagParse
:set $convert7bitToUtf8
:set $convert8bitToUtf8
:set $convertUcs2ToUtf8
:set $convertAddress
:set $convertBodyPDU
:set $convertScts
:set $UnixTimeToFormat
:set $extractSmsModem
:set $sendMailUTF8
:global exitFunctionPDU
:set $exitFunctionPDU
}
Бот не логирует запросы и не сохраняет полученые СМС, исходники тут:
https://github.com/andlommy/MTTelegramBot

Если кому-то поможет - буду рад, моя армия микротиковских рутеров теперь мне пишет :)


pepelxl
Сообщения: 161
Зарегистрирован: 23 июл 2013, 18:47

Не понимаю я любовь к телеге. И почта и телега имеют свои недостатки. Я вообще без острой не обходимости не стал бы доверять сторонним сервисам. Если есть зоопарк оборудования, то поднять свой "православный" почтовый сервер правильнее. Хотя на данный момент , если бы начал писать скрипт сейчас, то возможно посмотрел на mqtt, который занесли в семёрку. Тем более, что семёрка упала в ветку stable, а к концу написания скрипта подтенулась бы long-term


andlommy
Сообщения: 6
Зарегистрирован: 09 дек 2021, 11:39

Согласен, что пересылка СМС через сторонний сервис (чужого бота) - риск, поэтому код бота выложил и для недоверчивых можно поднять своего бота.
Варианты реализации с вбиванием идентификатора бота в код скрипта - то же самое что вбивать пароль для почтовой учетки - бота могут увести и делать с ним что угодно (в т.ч. смотреть смски пересланые ботом)
с MQTT та же система что и с сервисным ботом- надо поднимать брокера,
Для почты нужен пароль, который хранить на полудоверенном устройстве (рутре) не очень хочется, т.к. если его уведут, то учетку можно использовать для спама
Поднимать свой собственный почтовый сервер это целая эпопея с настройкой, портами (которые многими провайдерами закрыты) итд, тем более что проблема с уведенной учеткой не решается.
Телега как клиент удобнее почты, потому что по факту тот же messenger как и sms.
В предложеной реализации на рутере хранится просто случайно сгенерированый идентификатор, который никак использовать нельзя, который привязвает устройство к пользователю.


Sertik
Сообщения: 1598
Зарегистрирован: 15 сен 2017, 09:03

Приветствую ! Наконец то и у меня заработал скрипт - на почту всё шлёт !

Вопросы:

1. Скрипт обрабатывает только новые SMS ? Ранее пришедшие и им обработанные он уже повторно не обрабатывает ?

2. Пытался переделать функцию sendMailUTF8 (ниже), чтобы она слала не только на почту, но и мне в Телеграм.
В телеграмм она $emailBody пересылать не хочет, видимо там содержатся какие-то символы, которые не поддерживаются Телеграмм. Как быть ? Ведь UTF8 Телеграмм должен автоматически понимать ?

:global sendMailUTF8 do={

# функция должна возвращать булевое истина при успешной отправке сообщения
/tool e-mail send to=$emailAdr subject="$head\r\nMIME-Version: 1.0\r\nContent-Type: text/plain; charset=utf-8" body=$emailBody
:local sendit 120
:while ($sendit > 0) do={:delay 1s; :set $sendit ($sendit - 1)
:if (([tool e-mail get last-status] = "succeeded") or ([tool e-mail get last-status] = "failed")) do={:set $sendit 0}}
:if ([tool e-mail get last-status] = "succeeded") do={:return true} else={:return false}
}


фрагменты скриптов, готовые работы, статьи, полезные приемы, ссылки
viewtopic.php?f=14&t=13947
pepelxl
Сообщения: 161
Зарегистрирован: 23 июл 2013, 18:47

1) скрипт удаляет прочитанные смс. при условии, что модем имеет штатную команду для удаления. И желательно отключить tool/sms.
2) Без переконвертации не будет.


Sertik
Сообщения: 1598
Зарегистрирован: 15 сен 2017, 09:03

Во что нужно переконвертировать ? Может есть готовый конвертер ?


фрагменты скриптов, готовые работы, статьи, полезные приемы, ссылки
viewtopic.php?f=14&t=13947
pepelxl
Сообщения: 161
Зарегистрирован: 23 июл 2013, 18:47

у меня конвертора нет, он есть у вас. :-):


Ответить