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. } }