Automatically generate guest's hotspot account

genHotspot.rsc: This script creating new guest account randomly (you should have before “Hotspot server”), the script will create hotspot’s user profile with “on-login” and random hotspot account (username and password), after someone login by this your it’s will update him MAC address so no body else can use this account anymore, after that the script will create new randomly hotspot account (username and password) and send you notification by mail. When user is expired, It’s mean not login in last 30days (can be can at userExpired) the script will remove users without email assign and disable users with email assign.

# Hotspot server name
:local serverName "guests"
# User Profile
:local userProfile "generated"
# User prefix
:local userPrefix "G"
# User expired days
:local userExpired 30
# User identify length (the username will be 'userPrefix'+random chars)
:local identifyLength 4
# Password generate length
:local passwordLength 5
# After creating username and password send notification mail to (addresses seperate by ',')
:local notifyMail "your@mail.com"


:local rsc ("
  :local calcDays 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 date2month [:pick \$date2 0 3]
    :local date2day [:pick \$date2 4 6]
    :local date2year [:pick \$date2 7 11]

    # month to decimal converter - http://forum.mikrotik.com/t/compare-dates/53609/1
    :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 globalDiff 
    :local yearDiff (\$date2year - \$date1year)
    :local monthDiff (\$date2month - \$date1month)
    :local dayDiff (\$date2day - \$date1day) 

    # check if date1 is older than date2 to avoid errors in calculation
    if (\$yearDiff < 0) do={
      :return \"error : date1 should be older that date2 (year check), exiting\"
    } else={
      if (\$yearDiff = 0) do={
        if (\$monthDiff <0) do={
          :return \"error : date1 should be older that date2 (month check), exiting\"
        } else={
          if (\$monthDiff = 0) do={
            if (\$dayDiff < 0) do={
              :return \"error : date1 should be older that date2 (day check), exiting\"
            }
          }
        }
      }
    }

    # 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)
    :return \$globalDaysDiff
  }

  :local PwdGen do={
    :local random 0
    :local charStrNum 1
    :local p1
    :local var1
    :local var2
    :local var3
    :local newPassword
    :local newPassLength [:tonum \$1];
    :local charStr1 \"23456789ABCDEFGHJKLMNPQRSTUVWXYZ23456789abcdefghijkmnopqrstuvwxyz23456789\"
    :local charStr2 \"cN47iKd2bLn8sQz4JAu2PD6Vm5RjTG4UrFY78XewHE3W9gMq62v7a9Z5yf5pC3k6xt9Bh8S3o\"
    :local charStr3 \"uYswW92z6M5fJynQp6hGm5VSr4oR8k7A2bKq5U3FiZvc8gHP2tdL9E4jBT3X7xC6N4D8e7a93\"

    :for char from=1 to=\$newPassLength step=1 do={
      :set var1 ([:pick [/system clock get time] 6 8])
      :set p1 ([:len [/system resource get uptime]])
      :set var2 ([:pick [/system resource get uptime] (\$p1-2) \$p1])
      :set var3 ((\$var1 * \$var2) / 48)

      :if (\$charStrNum=1) do={
        :set newPassword (\$newPassword . [:pick \$charStr1 \$var3])
      }
      :if (\$charStrNum=2) do={
        :set newPassword (\$newPassword . [:pick \$charStr2 \$var3])
      }
      :if (\$charStrNum=3) do={
        :set newPassword (\$newPassword . [:pick \$charStr3 \$var3])
      }
      :set charStrNum (\$charStrNum + 1)
      :if (\$charStrNum = 4) do={
        :set charStrNum 1
      }
      :delay ((\$var1 + \$var2 + \$var3) / 15)
    }
    :return [:tostr \$newPassword];
  }

  :local mailBody;
  :local mailSend false;

  :foreach activeConn in=[/ip hotspot active find] do={
    :if ([:len [:find [/ip hotspot active get value-name=user \$activeConn] \"$userPrefix\"]]>0) do={
      :if ([/ip hotspot user get value-name=mac-address [/ip hotspot user find where name=[/ip hotspot active get value-name=user \$activeConn]]]!=[/ip hotspot active get value-name=mac-address \$activeConn]) do={
        /ip hotspot user set mac-address=[/ip hotspot active get value-name=mac-address \$activeConn] [/ip hotspot user find where name=[/ip hotspot active get value-name=user \$activeConn]];
        :log warning (\"MAC Address (\".[/ip hotspot active get value-name=mac-address \$activeConn].\") has been updated to '\".[/ip hotspot active get value-name=user \$activeConn].\"'\");
        :set mailSend true;
      }
      :if ([:len [:find [/ip hotspot user get value-name=comment \$activeConn] \"last connection was at\"]]>0) do={
        /ip hotspot user set comment=([:pick [/ip hotspot user get value-name=comment \$activeConn] 0 ([:find [/ip hotspot user get value-name=comment \$activeConn] \"last connection was at\"]-2)].\" (last connection was at \".[/system clock get date].\" \".[/system clock get time].\")\")  [/ip hotspot user find where name=[/ip hotspot active get value-name=user \$activeConn]];
      } else={
        /ip hotspot user set comment=([/ip dhcp-server lease get value-name=host-name [/ip dhcp-server lease find address=[/ip hotspot active get value-name=address \$activeConn]]].\" (last connection was at \".[/system clock get date].\" \".[/system clock get time].\")\")  [/ip hotspot user find where name=[/ip hotspot active get value-name=user \$activeConn]];
      }
    }
  }

  :if ([:len [/ip hotspot user find where profile=$userProfile and !mac-address~\"\"]]=0) do={
    :log warning \"Creating username and password...\";
    :local newUser (\"$userPrefix\".[\$PwdGen $identifyLength]);
    :local newPass [\$PwdGen $passwordLength];
    :while ([:len [/ip hotspot user find where name=\$newUser]]>0) do={
      :log error \"The user \$newUser is already exists, retry to create another user.\";
      :set newUser (\"$userPrefix\".[\$PwdGen $identifyLength]);
    }
    /ip hotspot user add name=\$newUser password=\$newPass server=$serverName profile=$userProfile comment=(\"Auto created at \".[/system clock get date].\" \".[/system clock get time]);
    :log warning (\"Hotspot account created successfully (\$newUser/\$newPass)\")
    :set mailSend true;
  }

  :foreach userIndex in=[/ip hotspot user find profile=$userProfile and server=$serverName] do={
    :local loginName [/ip hotspot user get value-name=name \$userIndex];
    :local loginPass [/ip hotspot user get value-name=password \$userIndex];
    :local loginMAC [/ip hotspot user get value-name=mac-address \$userIndex];
    :local loginComment [/ip hotspot user get value-name=comment \$userIndex];
    :if ([:len [:find [/ip hotspot user get value-name=comment \$userIndex] \"last connection was at\"]]>0) do={
      :local loginLastTime [:pick [/ip hotspot user get value-name=comment \$userIndex] ([:find [/ip hotspot user get value-name=comment \$userIndex] \"last connection was at\"]+23) ([:find [/ip hotspot user get value-name=comment \$userIndex] \"last connection was at\"]+23+11)];
      :local loginDaysAgo [\$calcDays \$loginLastTime [/system clock get date]];
      :set loginComment [:pick [/ip hotspot user get value-name=comment \$userIndex] 0 ([:find [/ip hotspot user get value-name=comment \$userIndex] \"last connection was at\"]-2)];
      :if (\$loginDaysAgo>$userExpired) do={
        :if ([:len [/ip hotspot user get value-name=email \$userIndex]]=0) do={
          /ip hotspot user remove \$userIndex;
          :log warning \"Hotspot's account '\$loginName' was removed. The account not being use more then \$loginDaysAgo days.\";
          :set loginComment \"<expired>\";
        } else={
          /ip hotspot user disable \$userIndex;
          :log warning (\"Hotspot's account '\$loginName' was disabled. The account not being use more then \$loginDaysAgo days.\")
          :set loginComment \"<disabled>\";
        }
        :set mailSend true;
      }
      :set loginComment (\$loginComment.\"\\nLast seen: \$loginLastTime (\$loginDaysAgo days ago)\");
    }
    :if ([:len \$loginMAC]>0) do={
      :set mailBody (\$mailBody.\"\\nUsername: \$loginName\\nPassword: \$loginPass\\nMAC Address: \$loginMAC\\nComment: \$loginComment\\n\");
    } else={
      :set loginMAC \"<unsigned>\";
      :set mailBody (\"\\nUsername: \$loginName\\nPassword: \$loginPass\\nMAC Address: \$loginMAC\\nComment: \$loginComment\\n\".\$mailBody);
    }
  }

  :if ((\$mailSend) and ([:len \"$notifyMail\"]>0)) do={
    foreach mail in=[:toarray $notifyMail] do={
      :log info \"send hotspot's account list to \$mail\";
      /tool e-mail send to=\$mail subject=([/system identity get name].\": Hotspot accounts update\") body=(\$mailBody);
    }
  }");

:if ([:len [/ip hotspot user profile find where name=$userProfile]]=0) do={
  :log warning "Creating user profile '$userProfile'..."
  /ip hotspot user profile add name=$userProfile mac-cookie-timeout=30d shared-users=1
} else={
  :log info "Update '$userProfile' (user profile) on-login action..."
  /ip hotspot user profile set on-login=$rsc [find name=$userProfile]
}

:execute script=[/ip hotspot user profile get value-name=on-login [find name=$userProfile]]