Monitor Mikrotik log by Telegram

Hi, I was long time working this script until upgrade ROS version that stop working… I´m in stable 6.48.3

Anyone has checked in this ROS version?

Regards!!

Hi. I have the 6.45.9 version on my hAP-lite
I was wery interested in this monitor, so I made some fixes in code, and got success working masterpeace. Thanks to all !!!
You may find some bugs in code below
I apologize, but I wrote the code for the first time for the RouterOS

# BEGIN SETUP Edit Here
:local myserver ([/system identity get name])
:local scheduleName "LogFilter"
:local bot "YOUR_BOT_TOKEN"
:local ChatID "YOUR_CHANEL_ID"
:local startBuf [:toarray [/log find message~" failure" || message~"loop" || message~"down" || message~"fcs" || message~"excessive"]]
:local removeThese [:toarray ""]
# END SETUP

# warn if schedule does not exist and create it 
:if ([:len [/system scheduler find name="$scheduleName"]] = 0) do={
  /log warning "[LogFilter] Alert : Schedule does not exist. Creating schedule ...."
  /system scheduler add name=$scheduleName interval=60s start-date=Jul/05/2019 start-time=startup on-event=LogFilter
  /log warning "[LogFilter] Alert : Schedule created ."
}

# get last time
:local lastTime [/system scheduler get [find name="$scheduleName"] comment]
# for checking time of each log entry
:local currentTime
# log message
:local message
# final output
:local output

:local keepOutput false
# if lastTime is empty, set keepOutput to true
:if ([:len $lastTime] = 0) do={
  :set keepOutput true
}

:local counter 0
# loop through all log entries that have been found
:foreach i in=$startBuf do={
 
# loop through all removeThese array items
  :local keepLog true
  :foreach j in=$removeThese do={
#   if this log entry contains any of them, it will be ignored
    :if ([/log get $i message] ~ "$j") do={
      :set keepLog false
    }
  }
  :if ($keepLog = true) do={
   
   :set message [/log get $i message]

#   LOG DATE
#   depending on log date/time, the format may be different. 3 known formats
#   format of jan/01/2002 00:00:00 which shows up at unknown date/time. Using as default
    :set currentTime [ /log get $i time ]
#   format of 00:00:00 which shows up on current day's logs
   :if ([:len $currentTime] = 8 ) do={
     :set currentTime ([:pick [/system clock get date] 0 11]." ".$currentTime)
    } else={
#     format of jan/01 00:00:00 which shows up on previous day's logs
     :if ([:len $currentTime] = 15 ) do={
        :set currentTime ([:pick $currentTime 0 6]."/".[:pick [/system clock get date] 7 11]." ".[:pick $currentTime 7 15])
      }
   }
    
#   if keepOutput is true, add this log entry to output
   :if ($keepOutput = true) do={
     :set output ($output.$currentTime." ".$message." %0A%0A ")
   }
    :if ($currentTime = $lastTime) do={
     :set keepOutput true
     :set output ""
   }
  }
  :if ($counter = ([:len $startBuf])-1) do={
   :if ($keepOutput = false) do={    
     :if ([:len $message] > 0) do={
        :set output ($output.$currentTime." ".$message)
      }
    }
  }
  :set counter ($counter + 1)
}
if ([:len $output] > 0) do={
  /system scheduler set [find name="$scheduleName"] comment=$currentTime
  /tool fetch url="https://api.telegram.org/bot$bot/sendMessage\?chat_id=$ChatID&text=$myserver%0A%0A$output" keep-result=no;
}

thxs a lot of rusinym, for very nice fix this code, it’s work very very very nice on crs board with ros 6.48.3, you are best :slight_smile:

Hi All,
Please kindly some advice.
I found same script as OP however it is for email.
the script as follow (Email):

# BEGIN SETUP
#Change this to the name of your schedule (the date/time stamp is saved in the schedule's comment).
:local scheduleName "LogToEmail"

#Put your email address here.
:local emailAddress "XXXXXX"

#This currently detects two strings. It can be changed to more or less strings if desired. Remove: || message~"login failure" if you only want to use one string, or if you want more strings, add this same code at the end (but before the last two end brackets).
:local startBuf [:toarray [/log find topics~"system" || message~"logged" ]]

#Edit the quoted items for strings you want to be filtered out of the results. For example, if you want all "logged in" logs found, but you do not want any of the "logged in via telnet" logs included, simply include the word "telnet" in the array and these logs will be excluded. Double quote additional strings and separate them with semi-colons. If you don't want any logs filtered, simply declare the variable :local removeThese without any curly braces. curly braces sample as follow {"testing";"whatever string you want"}
:local removeThese 

# END SETUP
# warn if schedule does not exist
:if ([:len [/system scheduler find name="$scheduleName"]] = 0) do={
  /log warning "[LOGMON] ERROR: Schedule does not exist. Create schedule and edit script to match name"
}
# get last time
:local lastTime [/system scheduler get [find name="$scheduleName"] comment]
# for checking time of each log entry
:local currentTime
# log message
:local message
# final output
:local output
:local keepOutput false
# if lastTime is empty, set keepOutput to true
:if ([:len $lastTime] = 0) do={
  :set keepOutput true
}
:local counter 0
# loop through all log entries that have been found
:foreach i in=$startBuf do={
# loop through all removeThese array items
  :local keepLog true
  :foreach j in=$removeThese do={
#   if this log entry contains any of them, it will be ignored
    :if ([/log get $i message] ~ "$j") do={
      :set keepLog false
    }
  }
  :if ($keepLog = true) do={
   :set message [/log get $i message]
#   LOG DATE
#   depending on log date/time, the format may be different. 3 known formats
#   format of jan/01/2002 00:00:00 which shows up at unknown date/time. Using as default
    :set currentTime [ /log get $i time ]
#   format of 00:00:00 which shows up on current day's logs
   :if ([:len $currentTime] = 8 ) do={
     :set currentTime ([:pick [/system clock get date] 0 11]." ".$currentTime)
    } else={
#     format of jan/01 00:00:00 which shows up on previous day's logs
     :if ([:len $currentTime] = 15 ) do={
        :set currentTime ([:pick $currentTime 0 6]."/".[:pick [/system clock get date] 7 11]." ".[:pick $currentTime 7 15])
      }
   }
#   if keepOutput is true, add this log entry to output
   :if ($keepOutput = true) do={
     :set output ($output.$currentTime." ".$message."\r")
   }
#   if currentTime = lastTime, set keepOutput so any further logs found will be added to output
#   reset output in the case we have multiple identical date/time entries in a row as the last matching logs
#   otherwise, it would stop at the first found matching log, thus all following logs would be output
    :if ($currentTime = $lastTime) do={
     :set keepOutput true
     :set output ""
   }
  }
#   if this is last log entry
  :if ($counter = ([:len $startBuf]-1)) do={
#   If keepOutput is still false after loop, this means lastTime has a value, but a matching currentTime was never found.
#   This can happen if 1) The router was rebooted and matching logs stored in memory were wiped, or 2) An item is added
#   to the removeThese array that then ignores the last log that determined the lastTime variable.
#   This resets the comment to nothing. The next run will be like the first time, and you will get all matching logs
   :if ($keepOutput = false) do={
#     if previous log was found, this will be our new lastTime entry      
     :if ([:len $message] > 0) do={
        :set output ($output.$currentTime." ".$message."\r")
      }
    }
  }
  :set counter ($counter + 1)
}
# If we have output, save new date/time, and send email
if ([:len $output] > 0) do={
  /system scheduler set [find name="$scheduleName"] comment=$currentTime
  /tool e-mail send to="$emailAddress" subject="MikroTik alert $currentTime" body="$output"
  /log info "[LOGMON] New logs found, send email"
}
#Other Notes, If you would rather run a script or whatever (instead of sending email), simply remove the email config line at the top, and change the "/tool email" line near the bottom to do whatever you want.

and i have modified it for telegram as follow:

# BEGIN SETUP
#Change this to the name of your schedule (the date/time stamp is saved in the schedule's comment).
:local scheduleName "LogToTelegram"

#put telegram ID
:local BotToken "XXXXXX";
:local ChatID "XXXXXX";
:local DeviceName [/system identity get name];

#This currently detects two strings. It can be changed to more or less strings if desired. Remove: || message~"login failure" if you only want to use one string, or if you want more strings, add this same code at the end (but before the last two end brackets).
:local startBuf [:toarray [/log find topics~"system" || message~"logged" ]]

#Edit the quoted items for strings you want to be filtered out of the results. For example, if you want all "logged in" logs found, but you do not want any of the "logged in via telnet" logs included, simply include the word "telnet" in the array and these logs will be excluded. Double quote additional strings and separate them with semi-colons. If you don't want any logs filtered, simply declare the variable :local removeThese without any curly braces. curly braces sample as follow {"testing";"whatever string you want"}
:local removeThese 

# END SETUP
# warn if schedule does not exist
:if ([:len [/system scheduler find name="$scheduleName"]] = 0) do={
  /log warning "[LOGMON] ERROR: Schedule does not exist. Create schedule and edit script to match name"
}
# get last time
:local lastTime [/system scheduler get [find name="$scheduleName"] comment]
# for checking time of each log entry
:local currentTime
# log message
:local message
# final output
:local output
:local keepOutput false
# if lastTime is empty, set keepOutput to true
:if ([:len $lastTime] = 0) do={
  :set keepOutput true
}
:local counter 0
# loop through all log entries that have been found
:foreach i in=$startBuf do={
# loop through all removeThese array items
  :local keepLog true
  :foreach j in=$removeThese do={
#   if this log entry contains any of them, it will be ignored
    :if ([/log get $i message] ~ "$j") do={
      :set keepLog false
    }
  }
  :if ($keepLog = true) do={
   :set message [/log get $i message]
#   LOG DATE
#   depending on log date/time, the format may be different. 3 known formats
#   format of jan/01/2002 00:00:00 which shows up at unknown date/time. Using as default
    :set currentTime [ /log get $i time ]
#   format of 00:00:00 which shows up on current day's logs
   :if ([:len $currentTime] = 8 ) do={
     :set currentTime ([:pick [/system clock get date] 0 11]." ".$currentTime)
    } else={
#     format of jan/01 00:00:00 which shows up on previous day's logs
     :if ([:len $currentTime] = 15 ) do={
        :set currentTime ([:pick $currentTime 0 6]."/".[:pick [/system clock get date] 7 11]." ".[:pick $currentTime 7 15])
      }
   }
#   if keepOutput is true, add this log entry to output
   :if ($keepOutput = true) do={
     :set output ($output.$currentTime." ".$message."\r")
   }
#   if currentTime = lastTime, set keepOutput so any further logs found will be added to output
#   reset output in the case we have multiple identical date/time entries in a row as the last matching logs
#   otherwise, it would stop at the first found matching log, thus all following logs would be output
    :if ($currentTime = $lastTime) do={
     :set keepOutput true
     :set output ""
   }
  }
#   if this is last log entry
  :if ($counter = ([:len $startBuf]-1)) do={
#   If keepOutput is still false after loop, this means lastTime has a value, but a matching currentTime was never found.
#   This can happen if 1) The router was rebooted and matching logs stored in memory were wiped, or 2) An item is added
#   to the removeThese array that then ignores the last log that determined the lastTime variable.
#   This resets the comment to nothing. The next run will be like the first time, and you will get all matching logs
   :if ($keepOutput = false) do={
#     if previous log was found, this will be our new lastTime entry      
     :if ([:len $message] > 0) do={
        :set output ($output.$currentTime." ".$message."\r")
      }
    }
  }
  :set counter ($counter + 1)
}
# If we have output, save new date/time, and send email
if ([:len $output] > 0) do={
  /system scheduler set [find name="$scheduleName"] comment=$currentTime
  /tool fetch url="https://api.telegram.org/bot$BotToken/sendMessage?chat_id=$ChatID&text=%E2%9D%97$DeviceName:$output" keep-result=no;
  /log info "[LOGMON] New logs found, send Telegram Message"
}
#Other Notes, If you would rather run a script or whatever (instead of sending email), simply remove the email config line at the top, and change the "/tool email" line near the bottom to do whatever you want.

But it doesn’t work. i keep getting
“failure: closing connection: <400 Bad Request> 149.154.167.220:443 (4)”

but here’s the strange part
if i were to lose the $ for the ‘output’ on the /tool fetch url such as below

 /tool fetch url="https://api.telegram.org/bot$BotToken/sendMessage\?chat_id=$ChatID&text=%E2%9D%97$DeviceName:output" keep-result=no;

the script runs, and able to send msg to telegram. but it is rather useless as i only receive “output”

anyone can help to spot what is issue with this ?

Never mind
after comparing the email script with the OP script.

replace “\r” in this part :

#   if keepOutput is true, add this log entry to output
   :if ($keepOutput = true) do={
     :set output ($output.$currentTime." ".$message."\r")

with “%0A%0A”

#   if keepOutput is true, add this log entry to output
   :if ($keepOutput = true) do={
     :set output ($output.$currentTime." ".$message."%0A%0A")

seems to do the trick.

Hi All,

anyone having issue with this script ?
after update to 6.49.2 the script now working intermittently.
it seems now when $output have too many lines, it somehow unable to send.
but if only consist 1 line, the script successfully sends to telegram.
is there a function to log the $output to a file ? i only able to save it in log and viewing the content in log is really difficult to ready (very long)

Thank you

This is a sample of msg to telegram that is successfully sent
%F0%9F%A4%96 Homer:%0A %F0%9F%8C%A1 Temp = 34 %E2%84%83 %0A %F0%9F%90%8F Free Ram = 211 / 256 MB %0A %E2%8F%B0 Uptime = 21:11:22 %0A %E2%8C%9B CPU = 2 % %0A%F0%9F%93%80 Free Space = 4 / 16

The others failed one as follow:
%0D%0A 17:56:49 - user XXXX logged out from 192.168.0.212 via winbox; %0D%0A 17:56:52 - user XXXX logged in from 192.168.0.212 via winbox;

i’m not sure what msg content on the below part that causes it to failed.

after fiddling
with one of the script that is still working

:local DeviceName [/system identity get name];
:local systemTemp [:tonum [/system health get temperature]];
:local freemem ([/system resource get free-memory] / 1024 / 1024);
:local totmem ([/system resource get total-memory] / 1024 / 1024);
:local freehddspace ([/system resource get free-hdd-space] / 1024 / 1024);
:local totalhddspace ([/system resource get total-hdd-space] / 1024 / 1024);
:local cpuload ([/system resource get cpu-load]);
:local up ([/system resource get uptime]);

:local MessageText "%F0%9F%A4%96  <b>$DeviceName:%0A</b> %F0%9F%8C%A1  <b>Temp = <i>$systemTemp %E2%84%83</i> </b> %0A %F0%9F%90%8F  <b>Free Ram =  <i>$freemem / $totmem MB</i></b> %0A %E2%8F%B0  <b>Uptime = <i>$up</i></b> %0A %E2%8C%9B <b>CPU = <i>$cpuload % </i> </b>%0A%F0%9F%93%80  <b>Free Space = <i>$freehddspace / $totalhddspace</i></b> %0A%0A [b]howdy[/b]"

#get TGBotSenderScript to send
:local SendTelegramMessage [:parse [/system script  get TGBotSenderScript source]];
$SendTelegramMessage MessageText=$MessageText;

when add the word “howdy” it’s ok, but if i change to “Howdy” the script is not sending.
which is odd.
is there something against capital H in mikrotik script ? but the strange thing is my device name is Homer and it is ok, although the placement is in front ($DeviceName).

Capital should work fine as in “Uptime”

yes I, agree, not sure why this is the case in the capital H.
i’ve changed the username with capital H to other name and the script now works again.

Looks like I have not yet shared the link to my scripts, no? You could try Forward log messages via notification.
You can configure the filters and it supports to send notifications via e-mail, Telegram and Matrix.

Thanks for this, will give it a read and try it.

just an update so I have another script to be called when sending telegram msg (credit to someone in the www) the script is as follow:

:local BotToken "XXXXX";
:local ChatID "XXXX";
:local ParseMode "html";
:local DisableWebPagePreview True;
:local SendText $MessageText;

:local tgUrl "https://api.telegram.org/bot$BotToken/sendMessage\?chat_id=$ChatID&text=$SendText&parse_mode=$ParseMode&disable_web_page_preview=$DisableWebPagePreview";

/tool fetch http-method=get url=$tgUrl output=none;

I remove the"&parse_mode=$ParseMode&disable_web_page_preview=$DisableWebPagePreview" and run the telegram msg status, and it will send to telegram chat id without html stuff. and then i re add the “&parse_mode=$ParseMode&disable_web_page_preview=$DisableWebPagePreview” and it works again. capital H or other stuff. so not sure what’s the problem is but. if there is an odd issue with the HTML parse, but try to re run it without the parse and re try again.

Hello,

The script works (from katem07), I receive telegram messages immediately. But I receive the same messages again at 00:00 and 02:00 next day. Does anyone experience the same behaviors?
I’m on hAP ac^2, fw 6.49.2

I have the same issue.

I had a problem when the text to telegram started with a space then capital H, as " H" Telegram doesnt like that the text string starts with space for some reason. not sure if this helps.

This is helpful, thanks a lot. to overcome the space i can give space before enclosing the capital H with tag or italic. something like this:
"abcd H "
it’s a workaround for now.
thanks again!

Can u share new script ?

Sorry to refloat this post, but I have detected this problem.

On a RB4011 (v7.6) it works perfectly like this:

:local startBuf [:toarray [/log find message~"login failure" || message~"loop" || message~"down" || message~"fcs" || message~"excessive" || message~"system" || message~"rebooted" || message~"ipsec" || topics~"error" || topics~"critical" || message~"ike2"]]
:local removeThese {"link";"telnet"}

But on a hAP ac2 (v7.6) I had to remove the “topics” part because otherwise it doesn’t work:

:local startBuf [:toarray [/log find message~"login failure" || message~"loop" || message~"down" || message~"fcs" || message~"excessive" || message~"system" || message~"rebooted" || message~"ipsec" || message~"ike2"]]
:local removeThese {"link";"telnet"}

Complete script:

# BEGIN SETUP Edit Here
:local bot "xxxxxxxxxxx:xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxx"
:local ChatID "xxxxxxxxxx"
:local myserver ("\E2\84\B9"." "."MikroTik"." ".[/system identity get name]." ".[/system resource get board-name])
:local scheduleName "LogFilter"
:local startBuf [:toarray [/log find message~"login failure" || message~"loop" || message~"down" || message~"fcs" || message~"excessive" || message~"system" || message~"rebooted" || message~"ipsec" || topics~"error" || topics~"critical" || message~"ike2"]]
:local removeThese {"link";"telnet"}
# :local removeThese [:toarray ""]
# END SETUP

# Convert 10/oct/2021 to numeric date: 10/10/2021 -> $mesnum
:global mesnum
:local date [/system clock get date]

# warn if schedule does not exist and create it
:if ([:len [/system scheduler find name="$scheduleName"]] = 0) do={
    /log warning "[LogFilter] Alert : Schedule does not exist. Creating schedule ...."
    /system scheduler add name=$scheduleName interval=60s start-date=dec/12/2022 start-time=12:00:00 on-event=LogFilter
    /log warning "[LogFilter] Alert : Schedule created!"
}

# get last time from scheduler's comment
:local lastTime [/system scheduler get [find name="$scheduleName"] comment]
# for checking time of each log entry
:local currentTime
# log message
:local message
# final output
:local output

:local keepOutput false
# if lastTime is empty, set keepOutput to true
:if ([:len $lastTime] = 0) do={
  :set keepOutput true
}

:local counter 0
# loop through all log entries that have been found

:foreach i in=$startBuf do={

    # loop through all removeThese array items
    :local keepLog true
    :foreach j in=$removeThese do={
    # if this log entry contains any of them, it will be ignored
        :if ([/log get $i message] ~ "$j") do={
        :set keepLog false
        }
    }

    :if ($keepLog = true) do={
        :set message [/log get $i message]
        # LOG DATE
        # depending on log date/time, the format may be different. 3 known formats
        
        # format of jan/01/2002 00:00:00 which shows up at unknown date/time. Using as default
        :set currentTime [ /log get $i time ]

        # format of 00:00:00 which shows up on current day's logs
        :if ([:len $currentTime] = 8 ) do={
        :set currentTime ([:pick $date 4 6]."/".[$mesnum [:pick $date 0 3]]."/".[:pick $date 7 11]." ".$currentTime)
        } else={
        
        # format of jan/01 00:00:00 which shows up on previous day's logs
            :if ([:len $currentTime] = 15 ) do={
                :set currentTime ([:pick $currentTime 4 6]."/".[$mesnum [:pick $currentTime 0 3]]."/".[:pick $date 7 11]." ".[:pick $currentTime 7 15])
            }
        }

        # if keepOutput is true, add this log entry to output
        :if ($keepOutput = true) do={
            :set output ($output.$currentTime." ".$message." %0A%0A ")
        }
        :if ($currentTime = $lastTime) do={
            :set keepOutput true
            :set output ""
        }
    }                                                                                                                            

    :if ($counter = ([:len $startBuf])-1) do={
        :if ($keepOutput = false) do={
            :if ([:len $message] > 0) do={
            :set output ($output.$currentTime." ".$message)
            }
        }
    }

    :set counter ($counter + 1)
}

if ([:len $output] > 0) do={
    /system scheduler set [find name="$scheduleName"] comment=$currentTime
    /tool fetch url="https://api.telegram.org/bot$bot/sendMessage\?chat_id=$ChatID&text=$myserver%0A%0A$output" keep-result=no;
}

Any ideas?

Thanks.

BR.

Try to use right syntax first:

:local startBuf [:toarray [/log find where message~"(login failure|loop|down|fcs|excessive|system|rebooted|ipsec|ike2)" \
                                        or topics~"(error|critical)"
                          ]
                ]

Indeed, that was the problem! Thank you!

What I don’t understand is how on one router (RB4011) it swallowed it with the wrong syntax and on the other (ac2) it didn’t accept it.

BR.