Script triggered by SMS: can I use the phone number in the script

I’m writing a script with can is meant to be triggered by an SMS. The script can perform all kind of tasks like reboot, backup, restore, restart the lte interface, etc.
Each command also creates a feeback e-mail to confirm the action. For security reasons I’d like to add the phone number that triggered the script in the confirmation mail, but I’ve not been able to find a short how to catch this number.
I could try to extract it from the log system where it is given by the gsm,info topic, but it would be more convenient to catch it in an easier way?
Is this possible?

The phone number of a message can be read like this:

 :put [/tool/sms/inbox/get 0 phone ]

Long story short, unfortunately you can’t use digits as indexes in scripts, only in the terminal. Instead, you’ll need to use indexes like “id,” as shown below, where “id” is just a variable that can be named anything
/tool sms
:foreach id in=[inbox find where message~“^*.”] do={
# Get SMS data using the index $id and store the result in the array named “msg”
:local msg [inbox get $id]

:put ($msg->".id")
:put ($msg->"message")
:put ($msg->"phone")
:put ($msg->"timestamp")

}

Thanks for your input, however both approaches uses the list of messages in the SMS inbox. My question is if the script which is triggered by the SMS can be aware of the number from which the SMS is sent. Theoretically, after the script is triggered, a new SMS could be received which makes both approaches unreliable.

The script which is triggered accepts additional variables, which can be added to the SMS command (like [var1=val1 [var2=val2] .. ]

It would be convenient if also eg the command line in the SMS and the number of the phone which sent the SMS would be available within the script

The SMS data contains the phone number of the sender who initiated the script with the ':cmd' syntax.

Maybe I'm misunderstanding what you're trying to achieve, but we're using scripts with MT LTE CPEs to perform actions like checking status, reboots, etc, as a last resort if our normal out-of-band management stops working for some reason.

The docs are a bit sparse on the how-to, so here’s a quick summary: You need to enable the phone numbers that are allowed to run scripts. Basically, you send an SMS to the LTE device with the syntax as below, where ':cmd' and 'script' are mandatory and must be in exactly those positions. PASSWORD must match the secret defined in the receiving settings. The script SCRIPTNAME is case sensitive and must be stored in "/system script"
:cmd PASSWORD script SCRIPTNAME [ VAR1 VAR2 ... ]
Additionally, you should consider auto-deleting or manually deleting SMS messages from within the script that is called. For more details, see the documentation here: Mikrotik Help: RouterOS SMS Receiving

Here’s an example script that sends a ping response back to the caller. It’s recursive because of an old bug (which might not exist anymore) that used to block the script from running, so it also handles unmanaged requests too.

System SMS script: Ping

:log info "SMS Script: Ping responder (START)"

/tool sms
:foreach i in=[inbox find where message~"^:cmdPing.*"] do={
:local msg [inbox get $i]
:log info "SMS Script: Ping responder msg=<$[:tostr $msg]>"
/tool sms send phone-number=($msg->"phone") message="Ping: OK!"

Uncomment this line to automatically delete SMS after processing

/tool sms inbox remove ($msg->".id")

}

:log info "SMS Script: Ping responder (DONE)"
And here is how you call the Ping script using SMS text:

:cmd mypassword script Ping

Hi Larsa, thanks again for your reply. Below is a part of the script I’m working on. This part only triggers a reboot command but before sends an e-mail confirming that the reboot will take place. In this confirmation mail I want to put the phone number which triggered the script. I understand I can retrieve this from the SMS inbox, and also from the logging system, but again it would be better and safer if the script would be aware of the phone number, and maybe other parameters, which triggered the script.

This is a snippet from the full script. It also contains sections to handle backups, reinitiate lte, tidy up the SMS inbox, etc. All variables used in this snippet are defined elswhere in the script.

# triggered by sms ":cmd SECRET script systemcommand command=reboot"
# reboot the router
if ($command = "reboot") do={
  :log info "Reboot initiated by SMS command"
  :local emailSubject "SMS response of $routerName to command $command"
  :local emailBody "$routerName reboot at $systemDate on $systemTime"
  :log info "$emailSubject"
  /tool e-mail send to="$emailTo" body="$emailBody" subject="$emailSubject"
  delay 10s
  /system/reboot
}

Aha, now I get what you're asking about! We had the exact same thoughts when we first started testing this feature.

It’s really an unfortunate combination of poor documentation and a design flaw in the SMS script execution functionality. You don’t get any info about which number triggered the scripts as preset variables or anything like you do with DHCP scripts, so you have to reverse-engineer the SMS content to match the script command and then extract the phone number from it.

In this case, you need to look for SMS messages with the exact phrase ':cmd SECRET script systemcommand command=reboot', like in the example below. Of course, you can use other regex combinations to match the command. IMO, it's a somewhat dirty solution, but it works!

/tool sms inbox
:local msg [get [([find where message~"^:cmd SECRET script systemcommand command=reboot.*"]->0)]]
:local phone ($msg->"phone")

Find the message matching the command (returns an array):

:local arr [find where message~"^:cmd SECRET script systemcommand command=reboot.*"]

Get the index of the first element (like picking the first match):

:local idx ($arr->0)

Retrieve the actual SMS content:

:local msg [get $idx]

Extract the phone number from the SMS:

:local phone ($msg->"phone")
Hope this clears it up for you!

Thanks, this exactly is my question. It would be convenient if Mikrotik indeed could add the phone number, date, time etc as a property of the trigger which calls the SMS script. In you example I’d first have to sort the messages and pick the last one sent as there are more than one in the inbox.

I’ll work on the script and post it here when I have a working version…

Cheers!

This snippet can be used to collect the phone number of the last SMS which triggered a script. In most cases it will return the phone number of the currently running script, as long as the script itself is triggered by an SMS. There is a theoretical chance that between triggering the scirpt and running this code a new SMS arrives which also triggers a(nother) script. It could be made more reliable when more details are added to the second script line, however this also makes the script more vulnerable for variants in the spelling (nr of used spaces) of the SMS cmd line. Anyway it could be good to add the name of the cript in line 2 at the end of the regexp (like " … script{1,}scriptName{1,}.*"

#collect the SMS secret from the SMS settings
:local SECRET ( [/tool sms print as-value]->"secret")

#store the SMS inbox in an array as long as the message passes the regex (~) criteria
:local inboxArr ([/tool sms inbox print as-value where message~"^:cmd[ ]{1,}$SECRET[ ]{1,}script[ ]{1,}.*"])
:local lastSMStime 0
:local triggeringPhoneNr ""

#walk through the SMS inbox array
:foreach msg in=$inboxArr do={
  #from each message store the date and time as an integer in the variable $smsTime
  :local msgTime ( [:pick ($msg->"timestamp") 0 4] .   [:pick ($msg->"timestamp") 5 7] .   [:pick ($msg->"timestamp") 8 10] .   [:pick ($msg->"timestamp") 11 13] .   [:pick ($msg->"timestamp") 14 16] .   [:pick ($msg->"timestamp") 17 19] )
  #compare the current message smsTime with the smsTime of the previous stored smsTime
  :if ($msgTime > $lastSMStime) do={
    #if the current smsTime is larger than the stored smsTime, replace the stored smsTime
    :set lastSMStime $msgTime;
    #store the phone number in triggeringPhoneNr. This is by definition the phone number associated with the message with the highest smsTime
    :set triggeringPhoneNr ($msg->"phone")  
  }
}

#by the end of the foreach-loop the phone number of the message which is sent the latest is stored in triggeringPhoneNr
:log info "The last sms triggering this script was sent by phone number: $triggeringPhoneNr"

Please comment on this post if the code needs to be revised or improved