Login and user system
A user system with accesslevels and user control.
The main idea of this script is to identify users through a login, not like mirc's default accesslevels, by nick/ident/host or some of those combinations.
Here it doesn't matter what nick/ident/host the user has, he just logs into the bot.
Commands:
- /loadusers = makes the users and login tables, and loads saved user information.
- /saveusers = saves user information to users.hsh and frees users and login tables.
- /adduser = adds a new user (/adduser <username> <password>
- /deluser = deletes an existing user /deluser <username>)
- /userlist = gives you a list of users (/userlist <-all|-active> -all = all of the users, -active = list only logged in users)
- /edituser = edits users information (/edituser <-pass|-username|-addlevel|-remlevel|-help> <username> <parameters>)
- /logoutusers = logs out all users from a given channel (/logoutusers #chan, used when you part/get kicked)
Identifiers:
- $checklevel = checks if the user has sufficient accesslevel, returns $true/$false ($checklevel($address,$chan,master))
- $getlevel = return the users level, numbered/named ($getlevel($address,$chan|Global))
- $getuser = returns username by given address ($getuser($address))
A little more explanation on how this works:
The user can have an acceslevels on a channel, and a global accesslevel, when checking for a level on certain channel, and it's insufficient, then the global level is checked.
ie. you have a command !op, which requires atleast level 10 to use it, $checklevel($address,$chan,10) is then used, if the user has accesslevel of 5 on that channel, but he has a Global level of 15, he can use the command.
You can also check only for a global level by doing $checklevel($address,global,10), and if you only want to know, if the user is logged in, you can use $checklevel($address).
If you want to compare the exact level of the user, use $getlevel($address,$chan) ($chan or "global") in an if like: if ($getlevel($address,$chan) == Master) { notice $nick yes, you are a master on $chan }
If you wish to use named levels, like Master, Owner, etc. you'll need to add those levels with corresponding numbered level into the cuser alias, see the source for more info.
I added events for user control to the examples part, so you can add the script to your bot and use the commands via query/channel with !command.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Usersystem with accesslevels and user control.
;; Contact: Cail @ QuakeNet -> #help.script
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
alias loadusers {
; check if 'users' and 'login' tables already exist, if not, create them
if (!$hget(users)) { hmake users 100 }
if (!$hget(login)) { hmake login 100 }
; check if there is a file 'users.hsh' (saved user information), if we have it, empty users-table and load the saved info
if ($is_file(users.hsh)) { hdel -w users * | hload users users.hsh }
}
alias saveusers {
; check if 'users' and 'login' tables even exist, if so, they can be saved and freed
if ($hget(users)) { hsave users users.hsh | .hfree users }
if ($hget(login)) { hfree login }
}
alias -l logoutusers {
; loop through a given channel ($1 = channel) and logout everyone who is logged in
var %x = 1
while ($nick($1,%x)) {
var %addr = $v1
; check the login table for a matching address with $ial().addr, and if he's not on any other same channels as you
if ($hget(login,$ial(%addr).addr)) && (!$comchan(%addr,2)) { hdel login %addr }
inc %x
}
}
alias cuser {
; return corresponding accesslevel for named levels, needed for comparison
if ($1 == owner) { return 200 }
elseif ($1 == master) { return 150 }
elseif ($1 == test) { return 100 }
else { return $1 }
}
alias checklevel {
; if $2 ('#channel' or 'global') isn't given, check if the user is logged in
if (!$2) { return $iif($hget(login,$1),$true,$false) }
; fetch the username to %user from the login table by $1 (given address),
; %lvl_chan and %lvl_global will get their values from the users-table, converted to a numeric with $cuser
; $iif gives them value 0 if they don't exist (comparison won't work with $null)
var %user = $hget(login,$1)
var %lvl_chan = $iif($cuser($hget(users,$+(%user,$chr(7),$2))),$v1,0)
var %lvl_global = $iif($cuser($hget(users,$+(%user,$chr(7),global))),$v1,0)
; inner $iif checks which one is greater, channel level or global level, and compares it to given level ($3)
return $iif($iif(%lvl_global > %lvl_chan,%lvl_global,%lvl_chan) >= $cuser($3),$true,$false)
}
alias getlevel {
; return the named level by given address
return $hget(users,$+($hget(login,$1),$chr(7),$2))
}
alias getuser {
; return username by given address
return $hget(login,$1)
}
alias adduser {
; errorchecking
; check if the users-table exist
if (!$hget(users)) { var %error = You need to load the usertable first: /loadusers }
; check if there's enough parameters
if (!$2) { var %error = Insufficient parameters, usage: /adduser <username> <password> }
; check if given username doesn't exist
if ($hget(users,$1)) { var %error = That username is already taken! }
; validate the username and password
if (!$regex($+($1,$2),/^([][A-Za-z\\^`{|}-][][\w\\^`{|}-]*)$/)) { var %error = Incorrect username. }
; if we encountered an error return the error msg or echo it, depending of if it was called as an identifier or not
; else say that everything's fine
if (%error) { echo -ag %error | return %error }
; everything was fine if we got here, add the user into users table and save the table
hadd users $1 $2
hsave users users.hsh
echo -ag User $1 added.
return User $1 added.
}
alias edituser {
; errorchecking
; check if valid parameters are given ($1 = switch, $2 = username, $3- = parameters)
if (!$istok(-pass -addlevel -remlevel -username -help,$1,32)) || (!$3) {
var %error Usage: /edituser <-pass|-addlevel|-remlevel|-username|-help> <username> <parameters>
}
; check if the user exists
elseif (!$hget(users,$2)) { var %error No such user. }
; echo/return errors if any, else carry on
if (%error) { echo -ag %error | return %error }
; get the address from the login-table, and check if the address is found from IAL, if so, we can msg the user with the changes made
var %nick = $ial($+(*,$hfind(login,$2,1).data),1).nick
; -pass: changes the users password
if ($1 == -pass) {
; check the validity of the password
if (!$regex($3,/^([][A-Za-z\\^`{|}-][][\w\\^`{|}-]*)$/)) { var %error Incorrect password. }
; check for errors
if (%error) { echo -ag %error | return %error }
; everything's fine, add the new password to the users table, echo, and msg %nick (if found)
hadd users $2 $3
if (%nick) { .notice $v1 Your password has been changed to $3 }
echo -ag Changed $2 $+ 's password to $3
return Changed $2 $+ 's password to $3
}
; -addlevel: add access level to user, for #channel or Global
elseif ($1 == -addlevel) {
; here we have more parameters, so we need to check that there is $4
if ($4 == $null) { var %error Usage: /edituser -addlevel username #channel|global accesslevel }
; check for errors
if (%error) { echo -ag %error | return %error }
; add a new item into users-table (username7#channel) that 7 in there is $chr(7), we use it to separate username and channel in the item name
; then again echo and msg %nick if possible
hadd users $+($2,$chr(7),$3) $4
if (%nick) { .notice $v1 Your $3 accesslevel has been changed to $4 $+ . }
echo -ag Changed $2 $+ 's level to > $3 $+ : $4
return Changed $2 $+ 's level to > $3 $+ : $4
}
; -remlevel: remove a users level
elseif ($1 == -remlevel) {
; check that the user has accesslevels in $3 (given channel/Global)
if (!$hget(users,$+($2,$chr(7),$3))) { var %error = User $2 doesn't have accesslevel in $3 }
; check for errors
if (%error) { echo -ag %error | return %error }
; hdel the item from users-table, echo, and msg %nick if possible
hdel users $+($2,$chr(7),$3)
if (%nick) { .notice $v1 Your accesslevel in $3 has been removed. }
echo -ag Removed $2 $+ 's accesslevel from $3
return Removed $2 $+ 's accesslevel from $3
}
; -username: change the username of someone (this is case-insensitive, so you cannot change ie. Cail -> cail)
elseif ($1 == -username) {
; check that the new username isn't already taken, and validate the new username
if ($hget(users,$3)) { var %error = That username is already taken! }
if (!$regex($3,/^([][A-Za-z\\^`{|}-][][\w\\^`{|}-]*)$/)) { var %error = Incorrect username. }
; check for errors
if (%error) { echo -ag %error | return %error }
; we need to store the address to a variables, 'cause we will be deleting the old item from the login-table
var %pass = $hget(users,$2)
var %address = $hfind(login,$2,1).data
; add the new username with the old password, and delete the old item
hadd users $3 $hget(users,$2)
hdel users $2
; update all the access levels for the new username, %y = the number of accesslevel items
var %y = $hfind(users,$+($2,$chr(7),*),0,w)
while (%y) {
; use $hfind to get the old item name
var %item = $hfind(users,$+($2,$chr(7),*),%y,w)
; add the new itemname with the old value, and delete the old item
hadd users $+($3,$chr(7),$gettok(%item,2-,7)) $hget(users,%item)
hdel users %item
dec %y
}
; change the username in the login-table, if the user is logged in, and msg him with the changes
if (%address) { hadd login %address $3 }
if (%nick) { .notice $v1 Your username has been changed to $3 $+ . }
echo -ag Changed $2 $+ 's username to $3
return Changed $2 $+ 's username to $3
}
; -help: echo the syntax of /edituser
elseif ($1 == -help) {
echo -ag -pass -> /edituser -pass <username> <new_password>
echo -ag -addlevel -> /edituser -addlevel <username> <#channel|global> <level>
echo -ag -remlevel -> /edituser -remlevel <username> <#channel|global>
echo -ag -username -> /edituser -username <username> <new_username>
echo -ag If level is a named level, make sure you have added it into the cuser -alias.
}
; we have made some changes to the users-table, save it now
hsave users users.hsh
}
alias deluser {
; check that given username exists
if (!$hget(users,$1)) { var %error No such user. }
; check for errors
if (%error) { echo -ag %error | return %error }
; delete the username from the userstable and remove all of his accesslevels, and echo the progress
; and if he is logged in, remove him from the login-table
hdel users $1
hdel -w users $+($1,$chr(7),*)
if ($hfind(login,12,1).data) { hdel login $v1 }
echo -ag Removed user $1
return Removed user $1
}
alias userlist {
; check that given input is correct
if (!$istok(-all -active,$1,32)) { echo -ag Usage: /userlist <-all|-active> | return }
; make variable %active $true/$false depending on if we're looking for active users
var %active = $iif($1 == -active,$true,$false)
; echo an empty line and "listing blaa blaa"
echo -ag $chr(160)
echo -ag --- Listing $remove($1,-) users:
; the regular expression in the $hfind finds all the items, that doesn't have $chr(7) in the itemname (we don't want to mix accesslevel items with username items)
var %x = 1
while ($hfind(users,/^[^\x07]+$/i,%x,r)) {
; put the username into a variable, if the user is logged in, %address will hold his address, assign %levels variable to $null at the start
var %user = $v1
var %address = $hfind(login,%user,1).data
var %levels = $null
; if we are looking for active users %active will now be $true, and if there's no %address, the user isn't logged in, so /continue and keep looking for users
if (%active) && (!%address) { inc %x | continue }
; echo username and address/"not logged", $base() here will zero-pad the number (2 becomes 02, looks better)
echo -ag $chr(31) $+ $base(%x,10,10,2) $+ : %user ( $+ $iif(%address,%address,not logged) $+ ) $chr(31)
; now that we have a user, loop through all of his accesslevels (now we want to find everyitem starting with username7, where the 7 is again $chr(7))
var %y = 1
while ($hfind(users,$+(%user,$chr(7),*),%y,w)) {
; put the levels into %levels variable, so we can echo all of them on one line (well put $chr(7) here to separate the "#channel: level" from "#channel2: level2" so we can sort them later)
var %levels = $gettok($v1,2-,7) $+ : $hget(users,$v1) $+ $chr(7) $+ %levels
inc %y
}
; so we have all the levels the user has in %levels, now $sorttok so we get the Global-level first, and replace that $chr(7) with spaces and -
; we would get a line like: Global: Master - #channel: Owner etc.
echo -ag $str($chr(160),3) Levels: $replace($sorttok(%levels,7,r),$chr(7),$+($chr(32),-,$chr(32)))
inc %x
}
; echo "end of" and an empty line
echo -ag --- End of users.
echo -ag $chr(160)
}
; EVENTS
on *:start:{ loadusers }
on *:exit:{ saveusers }
on !*:quit:{
; if the user that quit, was logged in, remove him from the login table
if ($hget(login,$address)) { hdel login $address }
}
on *:part:#:{
; if it is you, who parts the channel, logout everyone in that channel (if they're not on some other channels, where you are)
; else check if the user that quit, is logged in (and is not on any other chans with you), log him out
if ($nick == $me) { logoutusers # }
elseif ($hget(login,$address)) && (!$comchan($nick,2)) { hdel login $address }
}
on *:kick:#:{
; if it is you, who got kicked, logout everyone in that channel (if they're not on some other channels, where you are)
; else check if the user that got kicked, is logged in (and is not on any other chans with you), log him out
if ($knick == $me) { logoutusers # }
elseif ($hget(login,$address)) && (!$comchan($knick,2)) { hdel login $address }
}
on ^*:open:?:login & &:{
; check if user is already logged in, if so, tell him that
if ($hget(login,$address)) {
.notice $nick You are already logged in!
}
; check that the user is on atleast one channel with you
elseif (!$comchan($nick,0)) {
.notice $nick You need to be on atleast one same channel as i am to login.
}
; if the login information is correct (check that item $2 has the right password $3), add the user into login-table
elseif ($hget(users,$2) == $3) {
.notice $nick succesfully logged in
hadd login $address $2
}
; else the login information is incorrect
else {
.notice $nick Wrong username/password.
}
haltdef
}
Exaples of usage
on *:text:!adduser & &:*:{
if ($getlevel($address,global) == Owner) { .notice $nick $adduser($2,$3) }
else { .notice $nick You need to be an owner to use this command }
}
on *:text:!edituser & & *:*:{
if ($getlevel($address,global) == Owner) { .notice $nick $edituser($2,$3,$4,$5) }
else { .notice $nick You need to be an owner to use this command }
}
on *:text:!deluser &:*:{
if ($getlevel($address,global) == Owner) { .notice $nick $deluser($2) }
else { .notice $nick You need to be an owner to use this command }
}
on *:text:!opme:*:{
; we'll use $checklevel to determine if the user has atleast accesslevel of Master in $chan (or higher Global level)
if ($checklevel($address,$chan,master)) { mode $chan +o $nick }
else { .notice $nick You have to be atleast master to use this command. }
}
on *:text:!deopme:*:{
; and here the same, the user needs to have atleast accesslevel of 'test'
if ($checklevel($address,$chan,test)) { mode $chan -o $nick }
else { .notice $nick You have to be atleast at test level to use this command. }
}
on *:text:!whoami:*:{
; here we only check that is the user logged in, no other parameters given to $checklevel but the $address
if ($checklevel($address)) {
; $getuser returns the username of the user (username isn't the same as the nick)
; $getlevel returns the named/numbered level of the user on $chan or 'Global'
.msg $chan You are known as $getuser($address) $+ , you have accesslevel of $iif( $getlevel($address,$chan) ,$v1,0) on $chan
.msg $chan Your global level is $getlevel($address,Global)
}
else { .notice $nick You need to login to use this command. }
}