Code: Select all
# command should be the name of the script (default: telegram)
# botapi - telegram botapi
# reply - default message to (telegram id)
# trusted - telegram id(s) accepted
# refresh_active - change scheduler to check telegram for updates every X time after sending or receiving telegram message.
# refresh_standby - after 3 min(s) without updates change scheduler to check telegram for updates every Y time.
:local config {
"command"="telegram";
"botapi"="";
"reply"="";
"trusted"="XXXXXXXXX,BBBBBBBB";
"storage"="";
"timeout"=1;
"refresh_active"=20s;
"refresh_standby"=1h;
"start"="";
}
return $config
telegram script (call it "telegram" and put it in scripts)
Code: Select all
:local fconfig [:parse [/system script get telegram.cfg source]]
:local cfg [$fconfig]
:local scriptname ($cfg->"command")
:local onactive ($cfg->"refresh_active")
:local onstandby ($cfg->"refresh_standby")
#convert W D XX:XX:XX time to secounds
:local timeConvert do={
:local time [:tostr $1]
:local result
:local daypos
:set daypos (0);
:for pos from=0 to=([:len $time]-8) do={
# Find "w" and the weeks
:if ([:pick $time $pos] = "w") do={
:set result ([:tonum [:pick $time 0 ($pos)]]*7*24*60*60)
:set daypos ($pos+1)
}
# Find "d" and days
:if ([:pick $time $pos] = "d") do={
:set result ($result+([:tonum [:pick $time ($daypos) ($pos)]]*24*60*60))
}
}
# Recalculate hours and add :min:sec
:set result ($result+([:tonum [:pick $time ([:len $time]-8) ([:len $time]-6)]]*60*60))
:set result ($result+([:tonum [:pick $time ([:len $time]-5) ([:len $time]-3)]]*60))
:set result ($result+([:tonum [:pick $time ([:len $time]-2) ([:len $time])]]))
:return $result
}
# check last time received message
:local timeCompare do={
:local date1 [:tostr $1];
:local date2 [:tostr $2];
:local date1month [:pick $date1 0 3]
:local date1day [:pick $date1 4 6]
:local date1year [:pick $date1 7 11]
:local date1hours [:pick $date1 12 14]
:local date1minutes [:pick $date1 15 17]
:local date1seconds [:pick $date1 18 20]
:local date2month [:pick $date2 0 3]
:local date2day [:pick $date2 4 6]
:local date2year [:pick $date2 7 11]
:local date2hours [:pick $date2 12 14]
:local date2minutes [:pick $date2 15 17]
:local date2seconds [:pick $date2 18 20]
# month to decimal converter - https://forum.mikrotik.com/viewtopic.php?t=58674
:local months ("jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec");
:set date1month ([:find $months $date1month -1 ] + 1)
:set date2month ([:find $months $date2month -1 ] + 1)
:local yearDiff ($date2year - $date1year)
:local monthDiff ($date2month - $date1month)
:local dayDiff ($date2day - $date1day)
:local hoursDiff ($date2hours - $date1hours)
:local minutesDiff ($date2minutes - $date1minutes)
:local secondsDiff ($date2seconds - $date1seconds)
:local secondsGlobalDiff
:set secondsGlobalDiff (($dayDiff * 86400) + ($hoursDiff * 3600) + ($minutesDiff *60) + $secondsDiff)
:set dayDiff ($secondsGlobalDiff / 86400)
:set secondsGlobalDiff ($secondsGlobalDiff - ($dayDiff * 86400))
:set hoursDiff ($secondsGlobalDiff / 3600)
:set secondsGlobalDiff ($secondsGlobalDiff - ($hoursDiff * 3600))
:set minutesDiff ($secondsGlobalDiff / 60)
:set secondsGlobalDiff ($secondsGlobalDiff - ($minutesDiff * 60))
:set secondsDiff $secondsGlobalDiff
# check if leap years - https://wiki.mikrotik.com/wiki/AutomatedBilling/MonthEndScript
:local isYear1Leap 0
:local isYear2Leap 0
if ((($date1year / 4) * 4) = $date1year) do={
:set isYear1Leap 1
}
if ((($date2year / 4) * 4) = $date2year) do={
:set isYear2Leap 1
}
# find the right amount of days between 2 months
:local daysInEachMonth ("31","28","31","30","31","30","31","31","30","31","30","31");
:local daysInEachMonthLeapYear ("31","29","31","30","31","30","31","31","30","31","30","31");
:local totalDaysBetweenMonths
# same year; yearDiff = 0 so year1 = year2
if ($yearDiff = 0 and $monthDiff >= 1) do={
if ($isYear1Leap = 0) do={
for month from=($date1month - 1) to=($date2month - 1) step=1 do={
:set totalDaysBetweenMonths ($totalDaysBetweenMonths + [:pick $daysInEachMonth $month])
}
}
if ($isYear1Leap = 1) do={
for month from=($date1month - 1) to=(($date2month - 1) - 1) step=1 do={
:set totalDaysBetweenMonths ($totalDaysBetweenMonths + [:pick $daysInEachMonthLeapYear $month])
}
}
}
# different year, make concatenation of daysInEachMonth arrays first
:local daysInEachMonthConcatenatedYears
if ($yearDiff >= 1) do={
for year from=$date1year to=$date2year step=1 do={
# if leap year, concatenate the right daysInEachMonth array
if ((($year / 4) * 4) = $year) do={
:set daysInEachMonthConcatenatedYears ($daysInEachMonthConcatenatedYears, $daysInEachMonthLeapYear)
} else={
:set daysInEachMonthConcatenatedYears ($daysInEachMonthConcatenatedYears, $daysInEachMonth)
}
}
# must add years count
for month from=($date1month - 1) to=(($date2month - 1) + (($yearDiff * 12) - 1)) step=1 do={
:set totalDaysBetweenMonths ($totalDaysBetweenMonths + [:pick $daysInEachMonthConcatenatedYears $month])
}
}
:local globalDaysDiff ($totalDaysBetweenMonths + $dayDiff)
# add leading zeros if necessary
:if ($hoursDiff < 10) do={
:set hoursDiff ("0" . $hoursDiff)
}
:if ($minutesDiff < 10) do={
:set minutesDiff ("0" . $minutesDiff)
}
:if ($secondsDiff < 10) do={
:set secondsDiff ("0" . $secondsDiff)
}
:local inMinutes ($globalDaysDiff*24*60+$hoursDiff*60+$minutesDiff);
return $inMinutes;
}
#send message
:global tgSendMessage do={
:local fconfig [:parse [/system script get telegram.cfg source]]
:local cfg [$fconfig]
:local api ($cfg->"botapi")
:local storage ($cfg->"storage")
:local reply ($cfg->"reply")
:if ([:len $chat]>0) do={
:set reply $chat
} else={
:local upreply
:local getTelegramID do={
:if ([:type [:find $input "="]]!="nil") do={
:return [:pick $input 0 [:find $input "="]]
} else={
:return $input
}
}
:local getTelegramMAC do={
:if ([:type [:find $input "="]]!="nil") do={
:return [:pick $input ([:find $input "="]+1) [:len $input]]
} else={
:return false
}
}
:foreach tg in=[:toarray $reply] do={
:if (([$getTelegramMAC input=$tg]=false) or ([:len [/ip dhcp-server lease find mac-address=[$getTelegramMAC input=$tg]]]>0)) do={
:set upreply ([$getTelegramID input=$tg].",".$upreply)
}
}
:set reply $upreply
}
:foreach chatID in=[:toarray $reply] do={
:local fail true
:local retry 0
:local attempt 3
:local url "https://api.telegram.org/bot$api/sendmessage\?chat_id=$chatID&text=$text"
:if ($markdown!="false") do={
:set url ($url."&parse_mode=Markdown")
}
:while (($fail=true) and ($retry<$attempt)) do={
:do {
/tool fetch url=$url keep-result=no
:set fail false
} on-error={
:set fail true
:delay 3s
}
:set retry ($retry+1)
}
:if ($fail=true) do={
:log error "failed to send telegram message ('$[:pick [$text] 0 96] ...') to '$chatID'"
}
}
}
#receive message
:global tgReceiveMessage do={
:global tgSendMessage
:global tgReceiveID
:global tgReceiveUPDID
:global tgReceiveTIME
:local fconfig [:parse [/system script get telegram.cfg source]]
:local cfg [$fconfig]
:local trustedid [:toarray ($cfg->"trusted")]
:local api ($cfg->"botapi")
:local storage ($cfg->"storage")
:local rtimeout ($cfg->"timeout")
:local startmsg ($cfg->"start")
:local tgGetkey do={
:local cur 0
:local lkey [:len $key]
:local res ""
:local p
:if ([:len $block]>0) do={
:set p [:find $text $block $cur]
:if ([:type $p]="nil") do={
:return $res
}
:set cur ($p+[:len $block]+2)
}
:set p [:find $text $key $cur]
:if ([:type $p]!="nil") do={
:set cur ($p+lkey+2)
:set p [:find $text "," $cur]
:if ([:type $p]!="nil") do={
:if ([:pick $text $cur]="\"") do={
:set res [:pick $text ($cur+1) ($p-1)]
} else={
:set res [:pick $text $cur $p]
}
}
}
:return $res
}
:local http do={
:local res ($storage.$file.".fetch.txt")
:if ([:len $resfile]>0) do={:set res $resfile}
#:put $res
:local cmd "/tool fetch"
:if ([:len $mode]>0) do={:set cmd "$cmd mode=$mode"}
:if ([:len $upload]>0) do={:set cmd "$cmd upload=$upload"}
:if ([:len $user]>0) do={:set cmd "$cmd user=\"$user\""}
:if ([:len $password]>0) do={:set cmd "$cmd password=\"$password\""}
:if ([:len $address]>0) do={:set cmd "$cmd address=\"$address\""}
:if ([:len $host]>0) do={:set cmd "$cmd host=\"$host\""}
:if ([:len $"http-data"]>0) do={:set cmd "$cmd http-data=\"$"http-data"\""}
:if ([:len $"http-method"]>0) do={:set cmd "$cmd http-method=\"$"http-method"\""}
:if ([:len $"check-certificate"]>0) do={:set cmd "$cmd check-certificate=\"$"check-certificate"\""}
:if ([:len $"src-path"]>0) do={:set cmd "$cmd src-path=\"$"src-path"\""}
:if ([:len $"dst-path"]>0) do={:set cmd "$cmd dst-path=\"$"dst-path"\""}
:if ([:len $ascii]>0) do={:set cmd "$cmd ascii=\"$ascii\""}
:if ([:len $url]>0) do={:set cmd "$cmd url=\"$url\""}
:put ">> $cmd"
:global tgReceiveFETCH
:set tgReceiveFETCH "none"
:local script "\
:global tgReceiveFETCH;\
:do {\
$cmd;\
:set tgReceiveFETCH \"success\";\
} on-error={\
:set tgReceiveFETCH \"failed\";\
}\
"
:execute script=$script file=$res
:local cnt 0
#:put "$cnt -> $tgReceiveFETCH"
:while ($cnt<100 and $tgReceiveFETCH="none") do={
:delay 1s
:set $cnt ($cnt+1)
#:put "$cnt -> $tgReceiveFETCH"
}
:local content [/file get [find name=$res] content]
#:put $content
if ($content~"finished") do={:return "success"}
:return $tgReceiveFETCH
}
:put "trusted=$trustedid"
:put "botID=$api"
:put "storage=$storage"
:put "timeout=$rtimeout"
:local noupdate false;
#loop start
while (!$noupdate) do={
:local url ("https://api.telegram.org/bot".$api."/getUpdates?timeout=$rtimeout&limit=1")
:if ([:len $tgReceiveUPDID]>0) do={
:set url "$url&offset=$($tgReceiveUPDID+1)"
}
#:log info "telegram receiving updates..."
:local res [$http dst-path=($storage.$file.".receive.txt") url=$url resfile=($storage.$file.".fetch.txt")]
:if ($res!="success") do={
:log error "telegram receive updates failed"
:return "Failed get updates"
:set noupdate true;
}
#:log info "telegram receiving updates complete"
:local content [/file get [/file find name=($storage.$file.".receive.txt")] contents]
:local msgid [$tgGetkey key="message_id" text=$content]
:if ($msgid="") do={
#:log info "telegram has no new messages"
:set noupdate true;
:return 0
} else={
:set tgReceiveID $msgid
:local updid [$tgGetkey key="update_id" text=$content]
:set tgReceiveUPDID $updid
:local tgFromID [$tgGetkey block="from" key="id" text=$content]
:local tgUsername [$tgGetkey block="from" key="username" text=$content]
:local tgFirstName [$tgGetkey block="from" key="first_name" text=$content]
:local tgLastName [$tgGetkey block="from" key="last_name" text=$content]
:local tgID [$tgGetkey block="chat" key="id" text=$content]
:local tgMessage [$tgGetkey block="chat" key="text" text=$content]
:local tgName "$tgFirstName $tgLastName"
:put "message id=$msgid"
:put "update id=$updid"
:put "from id=$tgFromID"
:put "first name=$tgFirstName"
:put "last name=$tgLastName"
:put "username=$tgUsername"
:if ([:len $tgName]<2) do {
:if ([:len $tgUsername]>0) do={
:set tgName $tgUsername
} else={
:set tgName "Unknown"
}
}
:put "name=$tgName"
:put "in chat=$tgID"
:put "command=$tgMessage"
:local allowed ([:type [:find $trustedid $tgFromID]]!="nil" or [:type [:find $trustedid $tgID]]!="nil")
:if (!$allowed) do={
:log warning "telegram received content from $tgUsername ($tgFromID) has been ignored"
:if ($tgMessage="/start") do={
$tgSendMessage text=("Telegram message has been received from $tgUsername ($tgFromID), Content has been ignored, Telegram ID not allowed.") markdown=$markdown
}
:return -1
} else={
:set tgReceiveTIME ([/system clock get date]." ".[/system clock get time])
:local cmd ""
:local params ""
:local ltext [:len $tgMessage]
:local pos [:find $tgMessage " "]
:if ([:type $pos]="nil") do={
:set cmd [:pick $tgMessage 1 $ltext]
} else={
:set cmd [:pick $tgMessage 1 $pos]
:set params [:pick $tgMessage ($pos+1) $ltext]
}
:local pos [:find $cmd "@"]
:if ([:type $pos]!="nil") do={
:set cmd [:pick $cmd 0 $pos]
}
:put "cmd=<$cmd>"
:put "params=<$params>"
:if ([:len [/system script find name=$cmd]]>0) do={
:log warning "$tgName: /$cmd $params"
:global tgReceiveCMD [:parse [/system script get $cmd source]]
:execute "\$tgReceiveCMD $params tgchatid=$tgID tgname=\"$tgName\""
} else={
:if ($cmd="start") do={
:log warning ("$tgFirstName $tgLastName ($tgID) has been connected to $[/system identity get name]'s bot")
$tgSendMessage text=("*Wellcome to $[/system identity get name]'s bot*.%0A%0A$startmsg") markdown=true
}
:if ($cmd="system") do={
:if ($params="reboot") do={
/system reboot
}
}
}
}
}
}
}
:local months ("jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec");
:local tgfile ("tg.".[:pick [/system clock get date] 7 11].([:find $months [/system clock get date] -1 ] + 1).[:pick [/system clock get date] 4 6].[:pick [/system clock get time] 0 2].[:pick [/system clock get time] 3 5].[:pick [/system clock get time] 6 8])
#set/add scheduler
:if ([:len [/system scheduler find name=$scriptname]]=0) do={
:log warning "creating telegram scheduler"
/system scheduler add name=$scriptname interval=$onstandby on-event=":execute [/system script get telegram source]" start-time=00:00:00 comment="'telegram' checking for messages"
}
:local action [:tostr $1]
#run script
:if (([:len $action]=0) or ($action="receive")) do={
$tgReceiveMessage file=$tgfile
} else={
:if ($action="send") do={
:global tgCheckTIME
$tgSendMessage chat=$chat text=$text markdown=$markdown
:if (([:len $tgCheckTIME]=0) or ([$timeCompare $tgCheckTIME ([/system clock get date]." ".[/system clock get time])]>3)) do={
:set tgCheckTIME ([/system clock get date]." ".[/system clock get time])
$tgReceiveMessage file=$tgfile
}
}
:if ($action="last") do={
/put [$timeCompare $tgReceiveTIME ([/system clock get date]." ".[/system clock get time])]
}
}
:if ([/system scheduler get $scriptname disabled]=false) do={
#set last time was online
:global tgReceiveTIME
:if (([:len $tgReceiveTIME]>0) and ([$timeCompare $tgReceiveTIME ([/system clock get date]." ".[/system clock get time])]<3)) do={
:if ([$timeConvert [/system scheduler get value-name=interval [/system scheduler find name=$scriptname]]]!=$onactive) do={
/system scheduler set interval=$onactive [/system scheduler find name=$scriptname]
}
} else={
:if ([$timeConvert [/system scheduler get value-name=interval [/system scheduler find name=$scriptname]]]!=$onstandby) do={
/system scheduler set interval=$onstandby [/system scheduler find name=$scriptname]
/system script environment remove [find name~"tg"]
/file remove [find name~"tg."]
}
}
}
/file remove [find name~$tgfile]