Solution: Automatically clean expired User-Manager accounts

Finally managed to code it, after a couple of sleepless nights!

I’m sure many people will find this useful, as I’ve seen quite some requests for this “missing feature”.

The problem: User-Manager has no automated way to delete accounts which have gone past their ‘credit-till-time’ date. In a busy environment such as mine (two Hotels), where 100’s of accounts are constantly being generated, the user database can grow to biblical proportions in a short time, since old accounts are never cleaned away.

The solution: What I wanted was something that cycles through all the accounts, compares their ‘credit-till-time’ date with today’s date. If any of them has a date lesser than today, it’s deleted. After a few days of tinkering, I have managed to code it.

For clarity’s sake, I have made it in a way that if you run it manually through console, you will see the operations in progress and display a summary at the end. If you run it through WinBox, it will only display the final summary and push it to log.

Here it goes. Comments and / or feedback appreciated, as this is the first script that I have ever shared.

NB: Read the included notes!!

# Date: 		03/06/2011
# Revised:		22/02/2013
# Author: 		Alfredo Agius
# File:			cleanUserManager
# Tested on: 	RouterOS version 3.xx
#
# Description: 	Deletes Mikrotik User-Manager accounts whos credit-till-time is less than today's date.
#				Accounts which are not yet activated are skipped.

#				A summary of all accounts skipped, deleted and kept is displayed in the end of the cycle to
#				console screen and to log.
#
# Notes:		This script can be run either from WinBox or from console.
#				-- If console is used, all processing details will be shown, and a summary of operations in the end.
#				-- If WinBox is used, all processing details will be HIDDEN, and a summary of operations will be
#				   sent to log.
#
# Use:			In Winbox, go to System > Script, create a new script, name it cleanUserManager and paste
#				this whole document in it. Click Apply and OK.
#
# Syntax:		To run the script, from terminal enter "/system script run cleanUserManager" without quotes.

:if ([/system ntp client get status]="synchronized") do={

	:global iterator 0
	:global deleted 0
	:global skipped 0
	:global kept 0
	/tool user-manager user print brief without-paging
	:global counter [/tool user-manager user print count-only]

	:do {

		:local thisDate
		:local thisYear
		:local thisDay
		:local thisMonth
		:local thisCredit
		:local creditYear
		:local creditDay
		:local creditMonth
		:local creditName

		:set thisDate [/system clock get date]

		:set thisYear [:pick $thisDate 7 11]
		:set thisDay [:pick $thisDate 4 6]
		:set thisMonth [:pick $thisDate 0 3]

		:if ($thisMonth="jan") do { :set thisMonth "01"}
		:if ($thisMonth="feb") do { :set thisMonth "02"}
		:if ($thisMonth="mar") do { :set thisMonth "03"}
		:if ($thisMonth="apr") do { :set thisMonth "04"}
		:if ($thisMonth="may") do { :set thisMonth "05"}
		:if ($thisMonth="jun") do { :set thisMonth "06"}
		:if ($thisMonth="jul") do { :set thisMonth "07"}
		:if ($thisMonth="aug") do { :set thisMonth "08"}
		:if ($thisMonth="sep") do { :set thisMonth "09"}
		:if ($thisMonth="oct") do { :set thisMonth "10"}
		:if ($thisMonth="nov") do { :set thisMonth "11"}
		:if ($thisMonth="dec") do { :set thisMonth "12"}

		:local thisCredit [/tool user-manager user get $iterator credit-till-time]

		:set creditYear [:pick $thisCredit 7 11]
		:set creditDay [:pick $thisCredit 4 6]
		:set creditMonth [:pick $thisCredit 0 3]
		
		:set creditName [/tool user-manager user get $iterator name]

		:if ($creditMonth="jan") do { :set creditMonth "01"}
		:if ($creditMonth="feb") do { :set creditMonth "02"}
		:if ($creditMonth="mar") do { :set creditMonth "03"}
		:if ($creditMonth="apr") do { :set creditMonth "04"}
		:if ($creditMonth="may") do { :set creditMonth "05"}
		:if ($creditMonth="jun") do { :set creditMonth "06"}
		:if ($creditMonth="jul") do { :set creditMonth "07"}
		:if ($creditMonth="aug") do { :set creditMonth "08"}
		:if ($creditMonth="sep") do { :set creditMonth "09"}
		:if ($creditMonth="oct") do { :set creditMonth "10"}
		:if ($creditMonth="nov") do { :set creditMonth "11"}
		:if ($creditMonth="dec") do { :set creditMonth "12"}
		
		:if ([:len $thisCredit]!=0) do {
			:if ($creditYear>$thisYear) do {
				:put {"Kept username ".$creditName." which expires on ".$thisCredit }; :set kept ($kept+1)} else {:if ($creditYear<$thisYear) do {
				/tool user-manager user remove $creditName; :put {"Deleted username ". $creditName . " which expired on " . $thisCredit}; :set deleted ($deleted+1)} else {:if ($creditMonth>$thisMonth) do {
						:put {"Kept username " . $creditName . " which expires on " . $thisCredit}; :set kept ($kept+1)} else {:if ($creditMonth<$thisMonth) do {
							/tool user-manager user remove $creditName; :put {"Deleted username ". $creditName . " which expired on " . $thisCredit}; :set deleted ($deleted+1)} else {:if ($creditDay>=$thisDay) do {
								:put {"Kept username " . $creditName . " which expires on " . $thisCredit}; :set kept ($kept +1)} else {/tool user-manager user remove $creditName; :put {"Deleted username ". $creditName . " which expired on " . $thisCredit}; :set deleted ($deleted+1)}
						}
					}
				}
			}
		} else {:put {"Username ". $creditName . " is not yet activated. Skipping ..."}; :set skipped ($skipped+1)}
		:set iterator ($iterator+1)} while=($iterator!=$counter)

	:put {"\n\n\r==============================\n\n\rCleaning ready!\n\n\rPerformed these operations:\n\r\t"}
	:put {"Records processed: \t" . $counter}
	:put {"\n\rDeleted usernames: \t" . $deleted}
	:put {"Skipped usernames: \t" . $skipped}
	:put {"Kept usernames:\t\t" . $kept}
	:put {"\n\r==============================\n\r"}

	:log info ("\n\r==============================\n\n\rCleaning ready!\n\n\rPerformed these operations:\n\r\t")
	:log info ("Records processed: \t" . $counter)
	:log info ("\n\rDeleted usernames: \t" . $deleted)
	:log info ("Skipped usernames: \t" . $skipped)
	:log info ("Kept usernames: \t\t" . $kept)
	:log info ("\n\r==============================\n\r")

}

#END OF FILE

thanks i needed this

This script really looks great! I am going to test it to night on version 4.17, You have done great job in deed from the look of it.

Nice job! And useful as well.

I just tried to use this on v5.4 and the user credit-xx functions are gone. ?

Thanks for the comments.

I have only tested it on version 3.xx, so I have no idea what changes were done to the user-manager in version 5.

I’ll try to install version 5 on a spare Mikrotik when I have a free moment and have a peek at it, however I wouldn’t hold my breath as my hands are pretty full right now!

Nice script! it Works perfect with v4.17, :sunglasses:

I think much safer that warning in comment is:

:if ([/system ntp client get status]="synchronized") do={"original script here"}

Everybody not only ISP,WISP should sync their clock.

Is script is make system time sync first before do anything ?

Pls forgive my stupid question. I am a newbie.

Thank.

No. Time syncing is something that you have to do yourself with an NTP time server.

If i want system time sync first after reboot router and don’t do anything if sync time is not. What should i do ?
Because I have a problem that some user login after RB reboot and the ntp time is not sync. Pls suggest to me.

Thank you.

Anyway to adjust this script so that it will clear out accounts that expired a month ago and not just today. Some users let their account expire then the next day fill it up again. Want to clean out old accounts that haven’t been used for a month? Any pointers would be great as I’m not the best at scripting.

Not sure if I understood what you mean.

This script deletes any account whose expiry date is before today’s date. Being yesterday, a month, or a year ago does not matter.

This script in a nutshell is: if expiry_date < today, then delete. Else skip.

I see. What I was looking for is to limit that.
Example, normally a user would buy time on the hotspot, for say 1 month. They use 1 month and it expires, the script is running daily, when it finds this expired user it wipe out the expired account, but they may want to log back in and refill it the next day. So now they need to recreate their account. It would be good to be able to give it a window and delete only accounts that have been expired for 30+ days.
If the script looked for accounts with 30 days after the expiry date, then removed only those that have been expired for a month, this would be good for those users that let their accounts expire, but then top them up a few days later.

I guess the simplest and crudest way around it, is to +1 on the variable named creditMonth, unless it’s 12 (December) where it should change it to 1 (January).

That way you’ll be offsetting the expiry that it’s checking my 1 month.

Thanks I will play around with it and give that a shot!

Don’t forget to +1 the year too if you switch from December to January :smiley:

thx!

Hello!
I have this error when trying to run your script :frowning:

[admin@MikroTik] > /system script run cleanUserManager
Flags: X - disabled, A - active, I - incomplete

NAME LOCATION IP-ADDRESS UPTIME-USED

0 test 3m23s

1
interrupted
input does not match any value of value-name

Is the username “test” created in user-manager, or hotspot?