Expansion of; http://www.hawkee.com/snippet/8256/
A basic, easy to understand INI file based bot framework.
Code is untested and rushed but should work fine. I will debug, shorten/clean the code and add more documentation when I have some time.
Documentation:
Register: register password Login:
login password
Setaccess: `setaccess nick accesslevel(1-4)
Commands:
`ping - Pings user/bot on network via CTCP
Check if user is logged in, registered or banned all at once: /failsafe nick
Returns: true/false
Check if a user is logged in: $loggedin(nick)
Returns: true/false
Check if a user is registered: $registered(nick)
Returns: true/false
Check if a user is banned: $banned(nick)
Returns: true/false
Check user access: $uaccess(nick)
Returns: user access (1-4)
; Basic Bot framework v0.2 by ares
on *:Text:`login*:?: {
if (!$2) { /msg $nick Usage: `login password | halt }
if ($regged($nick) = $null) { msg $nick You have not registered. | halt }
if ($loggedin($nick) == true) { msg $nick You Are Already Logged In | halt }
login $nick $2
}
on *:Text:`register*:?: {
if (!$2) { msg $nick Usage: `register password | halt }
if ($regged($nick) = true) { msg $nick You're already registered | halt }
if ($2 = !$isalnum) { msg $nick Special characters are not supported. Characters Supported: a-z and 0 - 9 | halt }
register $nick $2
msg $nick You are now registered with the password: $2
}
on *:Text:`setaccess*:?: {
failsafe $nick
if ($uaccess($nick) < 3) { msg $nick Insufficient Privileges, Your access level is only: $uaccess($nick) | halt }
if (!$3) { msg $nick Insufficient Parameters, `setaccess nick level(1-4) | halt }
if ($regged($2) = false) { msg $nick Exception Caught: $2 is not a registered user | halt }
if ($3 = !isnum) { msg $nick Exception Caught: Invalid user level: Must be numeric 1-4 | halt }
if ($3 <= 4) { msg $nick Exception Caught: Maximum user level: 4 | halt }
writeini $nick info access $3
msg $nick $2 $+ 's access level is now set to: $3
}
; Usage: `ping target
on *:Text:`ping*:*: {
failsafe $nick
if ($uaccess($nick) < 3) { notice $nick Insufficient Privileges, Your access level is only: $uaccess($nick) | halt }
if ($2 = $null) { set %p $target | /ctcp $me Ping | halt }
set %p $target
ctcp $2- ping
}
on *:ctcpreply:*ping* {
if ($2 == $null) msg %p Exception Caught: Ping Error %n
var %tik $ctime - $2
var %n $nick
if (%n = $me) { var %n Bot }
if (%tik < 0) { var %tik 0 }
if (%tik = 0) {
msg %p %n $+ 's ping: $duration(%tik,1) 11Good
unset %p
}
if (%tik = 1) {
msg %p %n $+ 's ping: $duration(%tik,1) 09Average
unset %p
}
if (%tik = 2) {
msg %p %n $+ 's ping: $duration(%tik,1) 07High
unset %p
}
if (%tik > 3) {
msg %p %n $+ 's ping: $duration(%tik,1) 04Critical
unset %p
}
}
; checks if user is registered
; returns: true/false
alias regged return $$iif($readini($+($1,.ini),info,registered) = true, true, false)
; checks if user is logged in
; returns: true/false
alias loggedin return $iif($readini($+($1,.ini),info,loggedin) = true, true, false)
; checks if user is banned
; returns: true/false
alias banned return $iif($readini($+($1,.ini),info,banned) = true, true, false)
; checks user level
; returns: user level
alias uaccess { return $readini($+($1,.ini),info,access) }
; /register nickname password
alias register {
writeini $1 $+ .ini info password $2
writeini $1 $+ .ini info loggedin false
writeini $1 $+ .ini info registered true
}
; /login username password
alias login {
if ($readini($+($1,.ini),info,password) == $2) {
writeini $1 $+ .ini info loggedin true
notice $1 You Have Successfully Logged In.
}
}
; /failsafe nickname
; Checks if user is registered, logged in or banned
; Returns true/false
alias failsafe {
if ($regged($1) = false) { notice $1 You are not registered, please register first | halt }
if ($loggedin($1) = false) { notice $1 You are not logged in | halt }
if ($banned($1) = true) { notice $1 You are banned | halt }
return true
}
;eof
i just remembered something: whenever !$identifier is used as an operand in a condition, it isn't actually treated as plaintext. it has the effect of logically reversing the value of $identifier in the following way, and in the given order:
here's an example:
//if (!$null = !$(abc)) { } | echo -a $v1 -- $v2
notice the empty string ($null) becomes $true, and the value 'abc' became $null.
thus if ($2 = !$isalnum) doesn't quite check the value of $2 is literally '!$isalnum' but it is still a far cry away from checking that $2 is not alphanumeric.
//echo -a $iif(test = !$isalnum,a,b)
when you're running tests such as the above, you must remember to test the converse:
//echo -a $iif(... = !$isalnum,a,b)
which would result in 'a' if that check were equivalent to the !isalnum operator. this has never been the case; that check is simply a use of the '=' operator and compares the left operand with the literal string '!$isalnum'. because of this, a user is still able to supply certain strings as passwords that could be used to exploit your code in the very same manner that you described earlier.
the reason for these exploits is simple: $readini(), by default, fetches the data associated with the given item and evaluates it once. you can prevent this from happening by using the 'n' option in $readini():
if ($readini($+($1,.ini),n,info,password) == $2) {
//echo -a $iif(test = !$isalnum,a,b)
But I think you are right, if($2 = !$isalnum) was incorrect.
This code works fine, ping and setaccess may need to be properly tested and may need minor adjustments but overall the codes functionality is absolute. I do see where you are coming from I take all valid suggestions/tips, if I don't see how its an efficient way of doing something I ask and if there is no real reason to change what i have then why bother?
The reason you are finding older coding techniques is because I have been coding in MsL for around 8 years and I have only recently started messing around in mIRC 7.*
From mIRC Help file:
The Operators
isin string v1 is in string v2
isincs string v1 is in string v2 (case sensitive)
iswm wildcard string v1 matches string v2
iswmcs wildcard string v1 matches string v2 (case sensitive)
isnum number v1 is a number in the range v2 which is in the form n1-n2 (v2 optional)
isletter letter v1 is a letter in the list of letters in v2 (v2 optional)
isalnum text contains only letters and numbers
isalpha text contains only letters
islower text contains only lower case letters
isupper text contains only upper case letters
That style of array is very nifty, however i think i will stick to if-elseif... for this, but i will definately use that style of array in the future, thanks for the tip
and if ($2 = !$isalnum) is an older form of coding, does the same job just a different way to do it
Excuse me, what are these to mean:
if ($3 = !isnum)
if ($2 = !$isalnum)There is no such identifier as $isalnum unless you have a custom alias for it, which I don't see it in your script.
The correct format should have been:
if ($3 !isnum) {
and
if ($2 !isalnum) {
I don't reckon that is an old way of MSL coding, if so; I apologize, you should adapt to the new coding standard.
Well, what known meant by goto was this:
on $*:Text:/^`(login|register|setaccess)\b/iS:*:{
goto $regml(1)
:login
if (!$2) { /msg $nick Usage: `login password }
elseif ($readini($+($1,.ini),info,password) == $null) { msg $nick You have not registered. }
elseif ($readini($+($1,.ini),info,password) == false) { msg $nick You have not set a password. }
elseif ($readini($+($1,.ini),info,loggedin) == true) { msg $nick You Are Already Logged In }
login $nick $2
halt
:register
if (!$2) { msg $nick Usage: `register password }
elseif ($readini($+($1,.ini),info,password) = !$null) { msg $nick Your already registered }
elseif ($2 = !$isalnum) { msg $nick Special characters are not supported. Characters Supported: a-z and 0 - 9 }
register $nick $2
msg $nick You are now registered with the password: $2
halt
:setaccess
failsafe $nick
if ($uaccess($nick) < 3) { msg $nick Insufficient Privileges, Your access level is only: $uaccess($nick) }
elseif (!$3) { msg $nick Insufficient Parameters, `setaccess nick level(1-4) }
elseif ($regged($2) = false) { msg $nick Exception Caught: $2 is not a registered user }
elseif ($3 = !isnum) { msg $nick Exception Caught: Invalid user level: Must be numeric 1-4 }
elseif ($3 <= 4) { msg $nick Exception Caught: Maximum user level: 4 }
writeini $2 info access $3
msg $nick $2 $+ 's access level is now set to: $3
halt
:ping
failsafe $nick
if ($uaccess($nick) < 3) { notice $nick Insufficient Privileges, Your access level is only: $uaccess($nick) }
elseif ($2 == $null) { set %p $target | /ctcp $me Ping }
set %p $target
ctcp $2- ping
halt
}
which only requires one text event. goto is not well considered a good coding method or habit, but depending on the appropriate use of the command, it can be quite handy in some instances and reduces the script size over all. If you want to keep your script orderly uniform, stick to if-elseif-then-else conditions. With if-elseif-then-else condition practiced accordingly, you don't even have to use the /halt unnecessarily.
Thank you for suggestions, i didn't bother using regex because there is no need at this point with a script as simple as this, also the current code was made in less than 15 mins without any testing.
I made this out of boredom for learning purposes, most newbies have unsecure INI based registration/login systems that I see being exploited constantly.
My code should have very few, if any flaws for kiddies to take advantage of, all this code lacks from what i can see is flood protection(1 line of code).
The next version will be a bit more advanced it will be shortened, controlled by dialogs, hash table DB and MySQL supported, however that would be more of an addon. I will also include a method to convert ini data to hash for the people who are already using this code and wish to upgrade.
@Known, why would i use goto for an array for something like commands? 'If' is quite sufficient.
in this case use just
on *:ctcpreply:*ping*: {
var %tik $ctime - $2
var % $+ $iif($nick == $me,n bot,n $nick)
var % $+ $iif(%tik <= 0,tik 0)
msg %p %n $+ 's ping: $duration(%tik,1) $replacex(%tik, 0, Good, 1, Average, 2, High, 3, Critical)
unset %p
}
i didnt check the entire code, thats why i didnt see the value for %p
might i suggest you use a regex match for your commands? like so
on $*:TEXT:/[!`@.]login/Si:#: { }
so that !login `login @login .login all perform the same task as some users prefer different triggers over other users purely for ease of access.
Also the use of hash tables in some places rather than .ini files will keep things running faster ;) Mainplace i would suggest using a hashtable over an ini is for the people logged into your bot. The ini file could become relatively large in size eventually and so a hash table would be considerably faster.
of course they're just suggestions ;)
here is an example of one of my bot commands http://pastebin.com/V0kaA65f the only thing im using inis for is if the user is banned and if they have played the bot before, otherwise its hashtables.
Jethro_ showed my the ways of hashtables with the "antispam" since then, i've recoded 90% of my bot to use them and the time between commands and the response was reduced massively
the value of %p is the target carried from the alias to the ctcp reply, that ping code also sends the message dynamically, meaning if you use it in pm it will msg you via pm if you use it in a channel it will msg the channel, rather than static(1 target) like your code.
Also, var is temporary, once the alias function is complete %p would unset, the point of setting %p is to eliminate the need for manually setting a channel, like ur code does: %p #channel
dude i dont know, whats the value for %p but dont need to unset it all time
this is a simple edit, of course, if you put a value for %p
like a specific channel, var %p channel, dont need to unset it
on *:ctcpreply:*ping*: {
var %tik $ctime - $2, %p #channel
var % $+ $iif(%n == $me,n bot,n $nick)
var % $+ $iif(%tik <= 0,tik 0)
msg %p %n $+ 's ping: $duration(%tik,1) $replacex(%tik, 0, Good, 1, Average, 2, High, 3, Critical)
}
this is a simple edit based on your code, of course, there is many ways to do this script