Community discussions

MikroTik App
 
osamahfarhan
just joined
Topic Author
Posts: 10
Joined: Sat Mar 14, 2020 2:35 am
Contact:

mikrotik events script New abroach

Mon May 27, 2024 11:56 pm

Hi
I came up with a new abroach of scripting 8)
what is your opinion on script

this function runs when
1- new user added
2- user change
on /ip hotspot user

:global EventHandler do={
/log info "New Event username=$Name changed";
:return 0;
};

:execute {
:global EventHandler;
/ip hotspot user print follow-only where [$EventHandler Name=$name];
};

:delay 2s;

/ip hotspot user add name=123321 

more details on

https://github.com/osamahfarhan/mikrotik-events
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12438
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: mikrotik events script New abroach

Tue May 28, 2024 12:57 am

BRAVO.

Finally something innovative, instead of the usual bullshit.

But try to write better scripts anyway...

I use layer7-protocol instead of the hotspot, for the example. Work on both v6 and v7

example code

:global EventHandler do={
    /log info "On $1 is added a new item $2"
    :return 0
}

:execute {
    :global EventHandler
    /ip firewall layer7-protocol print follow-only where [$EventHandler "layer7-protocol" $name]
}

:delay 2s

/ip firewall layer7-protocol add name=123321


Some script like this:
https://help.mikrotik.com/docs/display/ ... ewlogentry
are completely wrong.
Just one example: if the last log have "01:38:31" time and previous "05-26 17:08:05" time,
the ":totime" do not convert "05-26 17:08:05" to some useful to compare the values...

This is one more correct way to detect if some logs are added...
viewtopic.php?p=1074126&hilit=lastLog#p1074126
 
osamahfarhan
just joined
Topic Author
Posts: 10
Joined: Sat Mar 14, 2020 2:35 am
Contact:

Re: mikrotik events script New abroach

Tue May 28, 2024 1:38 am


Some script like this:
https://help.mikrotik.com/docs/display/ ... ewlogentry
are completely wrong.
Just one example: if the last log have "01:38:31" time and previous "05-26 17:08:05" time,
the ":totime" do not convert "05-26 17:08:05" to some useful to compare the values...

This is one more correct way to detect if some logs are added...
viewtopic.php?p=1074126&hilit=lastLog#p1074126
:global lastLog
:if ([:typeof $lastLog] != "num") do={:set lastLog 0}

{
:local id2num do={:return [:tonum "0x$[:pick $1 1 [:len $1]]"]}
/log
:foreach item in=[find where (([$id2num $".id"] > $lastLog) and (message="FCC: Attemtped to send a frame with no bonded connections established"))] do={
    :set lastLog [$id2num $item]
    # do something here..................
}}
my simple code
{
:global lastLog
:local lastids [/log find where message="FCC: Attemtped to send a frame with no bonded connections established"];
:local length ([:len $lastids]-1);
:if ($length >= 0) do={
:local lastid ($lastids->$length);
:if ($lastid!=$lastLog) do={
:set $lastLog $lastid;
# do something here..................
}}}
my events code

:global eventsHandler [:toarray ""];
:set ($eventsHandler->("FCC: Attemtped to send a frame with no bonded connections established")) do={
:global lastLog;
:if ($lastid!=$lastLog) do={
:set $lastLog $lastid;
# do something here..................
};
:return (false);
};
:execute {:global eventsHandler;/log print follow-only where [($eventsHandler->("$message")) lastid=$".id"];}
or

:global eventsHandler [:toarray ""];
:set ($eventsHandler->("FCC: Attemtped to send a frame with no bonded connections established")) do={
# do something here..................
:return (false);
};
:execute {:global eventsHandler;/log print follow-only where [($eventsHandler->("$message"))];}
run the events code just once ,
then remove the script job

you are welcome
 
osamahfarhan
just joined
Topic Author
Posts: 10
Joined: Sat Mar 14, 2020 2:35 am
Contact:

Re: mikrotik events script New abroach

Tue May 28, 2024 1:49 am


Some script like this:
https://help.mikrotik.com/docs/display/ ... ewlogentry
are completely wrong.
Just one example: if the last log have "01:38:31" time and previous "05-26 17:08:05" time,
the ":totime" do not convert "05-26 17:08:05" to some useful to compare the values...



you can use my events script with follow-only instead of follow
then save the date and time
:set ($eventHistory->$length) ({"time"=$eventTime;"message"=$eventMessage;"date"=[/system clock get date]});
 
osamahfarhan
just joined
Topic Author
Posts: 10
Joined: Sat Mar 14, 2020 2:35 am
Contact:

Re: mikrotik events script New abroach

Tue May 28, 2024 1:57 am

:global lastLog
:set $lastLog [:local done (false);/log find where [:if ([:len [:find $lastLog $".id"]]=0) do={:if ($message="FCC: Attemtped to send a frame with no bonded connections established" && !($done)) do={
:set $done (true);
# do something here..................
};};(true)]];
:D

https://github.com/osamahfarhan/mikrotik-new-scripting
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 4100
Joined: Sun May 01, 2016 7:12 pm
Location: California
Contact:

Re: mikrotik events script New abroach

Wed May 29, 2024 6:48 am

It's like the brother of the "op" type (>[:put $0]). This does highlight the general scripting rule to: don't use same name for :local variables as any attributes= for the command.

I use something similar for /container to keep them started, and watch the state "live":
/container/print interval=1s proplist=tag,status where tag~"*" or [:if ($status="stopped") do={start $".id"}]            
This comes up in the /container stuff since "start"/"stop"/"add" etc. are all asynchronous, that it make them tricky to script.

But good explanation. I personally call [print follow-only] a "generators" and when find use like here an "iterators, since that's closest analog in other languages.

What I've never been able to do with the syntax here is exit out of the "print follow-only" – return true/false just effects what's in put/as-value, but doesn't stop the loop. Even using stuff like [:error] does not exit a "print follow-only". That the main limitation using [/.../print follow-only] as a "generator"
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12438
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: mikrotik events script New abroach

Wed May 29, 2024 6:47 pm

What I've never been able to do with the syntax here is exit out of the "print follow-only"...

Why did not you ask the Cat?

example code

/ip firewall layer7-protocol remove [find where name=123321 or name=654654]

:global jobID *0

:global EventHandler do={
    /log info "On $1 is added a new item $2"
    :return 0
}

:set jobID [:execute ":global EventHandler; /ip firewall layer7-protocol print follow-only where [\$EventHandler \"layer7-protocol\" \$name]"]

:delay 2s

/ip firewall layer7-protocol add name=123321

:delay 2s

/log info "kill the EventHandler"
/system script job remove [find where .id=$jobID]

/ip firewall layer7-protocol add name=654654

One hint:
Is possible to create a global array with event handler names and relatve job IDs
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 4100
Joined: Sun May 01, 2016 7:12 pm
Location: California
Contact:

Re: mikrotik events script New abroach

Wed May 29, 2024 8:26 pm

What I've never been able to do with the syntax here is exit out of the "print follow-only"...
Why did not you ask the Cat?
Is that going to be the title of your book on RouterOS scripting?

It is these /containers that always give me troubles in scripting, since commands trigger events (i.e. "/container start" == request a start) - but there are no on-status-change= handlers built-in. And the "print follow-only" is pretty useful when messing with these – so more an example of slight more practical usage of they scripting tricks here. Some :while/:retry approach for /container works. But I could never figure out to simple way to exit if using "print follow-only where" described here... without more code and/or jobs/:execute.

Perhaps a more simple example help others here...


Example: Using "find" as an iterator, instead of :foreach

A more basic example of using this technique is getting a list of /ip/address. Here the "[find] as iterator" is way shorter than the alternative expressions:
/ip/address find [:put $address]
172.16.174.254/24
172.16.174.1/32
192.168.216.1/24
192.168.101.173/24
172.28.8.61/24
10.88.100.113/24

Printing same with a :foreach, it's much longer:
 /ip/address { :foreach i in=[find] do={ :put [get $i address] } 
    # or same using "fully-qualified" commands
:foreach i in=[/ip/address/find] do={ :put [/ip/address/get $i address] }
172.16.174.254/24
172.16.174.1/32
192.168.216.1/24
192.168.101.173/24
172.28.8.61/24
10.88.100.113/24

And the normal "print" includes the headers which are unavoidable:
/ip/address/print proplist=address 
Flags: X - DISABLED, D - DYNAMIC
Columns: ADDRESS
# ADDRESS
0 172.16.174.254/24
1 172.16.174.1/32
...

While "print as-value", using a :foreach is possible... you need to deference the array provided by "print as-value", so also longer:
:foreach i in=[/ip/address/print as-value] do={:put ($i->"address")}
172.16.174.254/24
172.16.174.1/32
...

On the which one is "faster", I don't know. With 100 IPs, all were in the 8-12ms range, and all varied within that. But you can use the :time function to check (which is how I got those numbers):
:put [:time {/ip/address find [:put $address]}]
172.16.174.254/24
172.16.174.1/32
...
00:00:00.005610


All do the same things, pick you poison. But the "find" technique is useful one... And while I use :put, it could be your own function too (by passing the find variables to the function).
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 4100
Joined: Sun May 01, 2016 7:12 pm
Location: California
Contact:

Re: mikrotik events script New abroach

Thu May 30, 2024 7:32 am

Since this was fresh in my mind. Another fun one here with "print follow-only as-value where" & /container...

Example: Using "print as-value follow-only where [...]" to print logs with colorized ANSI codes

I use the Traefik /container on a few routers to add CORS support to REST API. But its logging includes all the timestamp and rather long $message too. And importantly for the example it uses ANSI colors. So some "normal" print get this kinda output when monitoring a container using "follow-only" (which I do regularly use):
/log print proplist=message follow-only where topics~"container" $message~"(ERR|INF|DEBUG|WARN)"
\1B[90m2024-05-30T03:51:02Z\1B[0m \1B[32mINF\1B[0m Traefik version 3.0.1 built on 2024-05-22T13:12:16Z \1B[36mversion=\1B[0m3.0.1
\1B[90m2024-05-30T03:51:02Z\1B[0m \1B[32mINF\1B[0m Starting provider aggregator aggregator.ProviderAggregator
\1B[90m2024-05-30T03:51:02Z\1B[0m \1B[32mINF\1B[0m Starting provider *file.Provider
\1B[90m2024-05-30T03:51:02Z\1B[0m \1B[32mINF\1B[0m Starting provider *traefik.Provider
\1B[90m2024-05-30T03:51:02Z\1B[0m \1B[32mINF\1B[0m Starting provider *acme.ChallengeTLSALPN
\1B[90m2024-05-30T03:51:02Z\1B[0m \1B[32mINF\1B[0m Starting provider *acme.Provider
\1B[90m2024-05-30T03:51:02Z\1B[0m \1B[32mINF\1B[0m Testing certificate renew... \1B[36macmeCA=\1B[0mhttps://acme-v02.api.letsencrypt.org/directory \1B[36mproviderName=\1B
[0mletsencrypt.acme
\1B[90m2024-05-30T03:51:23Z\1B[0m \1B[32mINF\1B[0m I have to go...
\1B[90m2024-05-30T03:51:23Z\1B[0m \1B[32mINF\1B[0m Stopping server gracefully
\1B[90m2024-05-30T03:51:23Z\1B[0m \1B[1m\1B[31mERR\1B[0m\1B[0m \1B[36merror=\1B[0m\1B[31m"accept tcp [::]:80: use of closed network connection"\1B[0m\1B[36mentryPointName=\1B[0mweb
\1B[90m2024-05-30T03:51:23Z\1B[0m \1B[1m\1B[31mERR\1B[0m\1B[0m \1B[36merror=\1B[0m\1B[31m"accept tcp [::]:8080: use of closed network connection"\1B[0m\1B[36mentryPointName=\1B[0mtraefik
\1B[90m2024-05-30T03:51:23Z\1B[0m \1B[1m\1B[31mERR\1B[0m\1B[0m \1B[36merror=\1B[0m\1B[31m"accept tcp [::]:443: use of closed network connection"\1B[0m\1B[36mentryPointName=\1B[0mwebsecure
\1B[90m2024-05-30T03:51:23Z\1B[0m \1B[32mINF\1B[0m Server stopped
\1B[90m2024-05-30T03:51:23Z\1B[0m \1B[32mINF\1B[0m Shutting down
-- Ctrl-C to quit. Space prints separator. New entries will appear at bottom.

In fairness, the colors can be disabled (LOG_NOCOLOR=true) in Traefik config since just a plain "/log print" will not render ANSI codes in its output. But since a ":put" can deal with ANSI colors, this works well with the "print follow-only where" shown above in the thread:
/log print proplist=message as-value follow-only where topics~"container" [:if ($message~"(ERR|INF|DEBUG|WARN)") do={:put [:pick "$message\1B[0K" 25 999]}] 
Screenshot 2024-05-29 at 9.16.10 PM.png

Now have to throw the whole kitchen sink of scripting tricks to get the nicely formatted colorized output from that garbage shown of "/log print", but possible.

Notes:
- The "\1B[0K" is added ANSI code I use to "clear to end of line" - since the "follow-only" put prints headers (with /terminal/cuu to re-wrtie same line) which conflict with :put.
- The "as-value" is used to suppress the actual output of "print" to console — since the "where" always runs the code for ALL elements... we use :if and :put to print to the console.
You do not have the required permissions to view the files attached to this post.
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12438
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: mikrotik events script New abroach

Thu May 30, 2024 12:54 pm

Imagine, if RouterOS were open source, what four or five users, who are already here on the forum, could do with it...
 
optio
Forum Veteran
Forum Veteran
Posts: 872
Joined: Mon Dec 26, 2022 2:57 pm

Re: mikrotik events script New abroach

Sun Jun 02, 2024 10:35 pm

This approach can be used for various forum script requests how to parse log records to do some logic, no need to track last processed record by timestamp called with some interval scheduler, instead :execute script which runs in background that calls event global function with log properites in arguments when log record occurs. Such script can be added in startup scheduler to catch all needed log records and process by event function. Very handy for such need.
 
User avatar
Sertik
Member
Member
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: mikrotik events script New abroach

Wed Aug 28, 2024 11:54 am

:global EventHandler do={
/log info "Connect new wifi-client=$Name";
:return true;
};

:execute {
:global EventHandler;
/ip dhcp-client print follow-only where [$EventHandler Name=$name]
};

And in this form, will your approach work when connecting a new wifi client?
 
User avatar
Sertik
Member
Member
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: mikrotik events script New abroach

Wed Aug 28, 2024 12:10 pm

Or is :return 0 required in the function?
 
User avatar
Sertik
Member
Member
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: mikrotik events script New abroach

Wed Aug 28, 2024 2:25 pm

I 'll answer myself ... Messages arrive, but both when connecting and when disconnecting the wifi client. This is a minus ...
 
optio
Forum Veteran
Forum Veteran
Posts: 872
Joined: Mon Dec 26, 2022 2:57 pm

Re: mikrotik events script New abroach

Wed Aug 28, 2024 3:36 pm

It calls EventHandler function on any change, pass status (or other config property if needed) as function argument and check its value inside function...
 
User avatar
Sertik
Member
Member
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: mikrotik events script New abroach

Thu Aug 29, 2024 12:35 pm

Got it, thanks. Nice welcome.
 
User avatar
Sertik
Member
Member
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: mikrotik events script New abroach

Fri Aug 30, 2024 9:37 am

By the way, can someone explain why :execute is used here, since print follow already starts a separate process? Can I use it without :execute?
 
optio
Forum Veteran
Forum Veteran
Posts: 872
Joined: Mon Dec 26, 2022 2:57 pm

Re: mikrotik events script New abroach

Fri Aug 30, 2024 1:32 pm

Depends how it's run and needs, without :execute, if you executing commands directly from CLI it will remain interactive or if you run from saved script commands after print follow-only where ... will not be executed since it is interactive and waits. :execute runs commands in background and console will not be locked for executing other commands after.
 
User avatar
Sertik
Member
Member
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: mikrotik events script New abroach

Thu Sep 12, 2024 12:56 pm

It turned out that the method works, but not always...
:global funcEventHandler do={
        :delay 1s
        :log warning $1
        :if (([/system script environment get [/system script environment find name=$1] value]="(code)") \
                                or ([:len [:find [/system script environment get [/system script environment find name=$1] value] "(eval"]]>0)) do={
    /log warning "On $1 is added a new item"}
    :return 0
}

:execute {
    :global funcEventHandler
    /system script environment print follow-only where [$funcEventHandler $name]

}
At the command:
    /system script environment print follow-only where
it does not respond at all to the appearance/modification/deletion of new records...
 
osamahfarhan
just joined
Topic Author
Posts: 10
Joined: Sat Mar 14, 2020 2:35 am
Contact:

Re: mikrotik events script New abroach

Fri Oct 04, 2024 10:59 pm

It turned out that the method works, but not always...
:global funcEventHandler do={
        :delay 1s
        :log warning $1
        :if (([/system script environment get [/system script environment find name=$1] value]="(code)") \
                                or ([:len [:find [/system script environment get [/system script environment find name=$1] value] "(eval"]]>0)) do={
    /log warning "On $1 is added a new item"}
    :return 0
}

:execute {
:global funcEventHandler
:local IDS;
/system script environment print count-only  interval=1 as-value  where [:if ([:len [:find $IDS $".id"]]=0) do={:set $IDS ($IDS,$".id");[$funcEventHandler $name]}];

}
At the command:
    /system script environment print follow-only where
it does not respond at all to the appearance/modification/deletion of new records...
 
osamahfarhan
just joined
Topic Author
Posts: 10
Joined: Sat Mar 14, 2020 2:35 am
Contact:

Re: mikrotik events script New abroach

Fri Oct 04, 2024 11:16 pm

:global funcEventHandler do={
        :if (($2="(code)") or ([:len [:find $2 ("(eval")]]>0)) do={/log warning "On $1 is added a new item"}
        :return 0
}

:execute {
:global funcEventHandler
:local IDS;
/system script environment print count-only  interval=1 as-value  where [:if ([:len [:find $IDS $".id"]]=0) do={:set $IDS ($IDS,$".id");[$funcEventHandler $name [:tostr $value]]}];

}

or
  :execute {
    :local IDS;
    /system script environment print count-only  interval=1 as-value  where [
    :if ([:len [:find $IDS $".id"]]=0) do={
      :set $IDS ($IDS,$".id");
      :if (([:tostr $value]="(code)") or ([:len [:find [:tostr $value] ("(eval")]]>0)) do={
        /log warning "On $name is added a new item"
        }
      }
    ];
    }

Who is online

Users browsing this forum: ansh, FurfangosFrigyes, soltanpour and 12 guests