Basic Bot framework v0.2

By skitzkid on Dec 31, 2010

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:

  • Place code in remotes section of your script editor.

Register: register password Login:login password
Setaccess: `setaccess nick accesslevel(1-4)

  • Note: Register, Setaccess and Login commands are only accessible via Private message/Query if you type it in a channel nothing will happen

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

Comments

Sign in to comment.
jaytea   -  Jan 03, 2011

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:

  • $true becomes $false
  • $false becomes $true
  • 0 becomes 1
  • any number becomes 0
  • $null becomes $true
  • any other strings becomes $null

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.

 Respond  
jaytea   -  Jan 03, 2011

what?

read the whole post - there is no old syntax being used, only incorrect syntax ;P

 Respond  
Jethro   -  Jan 02, 2011

I still don't get why the older coding approach is utilized when it's getting obsolete and not the norm these days.

 Respond  
jaytea   -  Jan 02, 2011

//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) {
 Respond  
skitzkid   -  Jan 02, 2011

//echo -a $iif(test = !$isalnum,a,b)
But I think you are right, if($2 = !$isalnum) was incorrect.

Quoted from Jethro_:
There is no doubt skitzkid's script here will result some random errors...or even an unworkable outcome. I mean, doesn't it make sense to you to start a project or work and finish it that's worth your time and effort? Don't just submit a script with a nonchalant attitude.

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

 Respond  
napa182   -  Jan 02, 2011

idk if that is an older form of coding I have never seen anyone code like that, but any ways
this just don't work

if ($2 = !$isalnum)

best to use what Jethro_ told you

if ($2 !isalnum)
 Respond  
Jethro   -  Jan 02, 2011

Well, I find some old, superseded msl coding techniques rather redundant, "wordy" and longer in a way. But I'm sure Khaled keeps them there for a purpose.

 Respond  
Jethro   -  Jan 02, 2011

There is no doubt skitzkid's script here will result some random errors...or even an unworkable outcome. I mean, doesn't it make sense to you to start a project or work and finish it that's worth your time and effort? Don't just submit a script with a nonchalant attitude.

 Respond  
skitzkid   -  Jan 02, 2011

Quoted from Jethro_:
There is no such identifier as $isalnum unless you have a custom alias for it, which I don't see it in your script.

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

 Respond  
napa182   -  Jan 02, 2011

skitzkid Said:

the current code was made in less than 15 mins without any testing.

lolwut =/
you should really test before you post it.
I don't get why people don't just take their time when making something. Then test it over an over then post it when all bugs are worked out.

 Respond  
Jethro   -  Jan 02, 2011

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.

 Respond  
skitzkid   -  Jan 02, 2011

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.

 Respond  
Known   -  Jan 01, 2011

You should just use what Jethro_ said, or you can make all commands triggered via channel or PM/query, wouldn't be that hard using goto.

 Respond  
Jethro   -  Dec 31, 2010

Wader, ensure that the command starts with !cmd and ends with it:> on $*:TEXT:/\A[!`@.]login\b/Si:#: { }You can use ^ in place of \A

Without these chars, the regex becomes as rampant as isin or iswm operator.

 Respond  
_Teen_   -  Dec 31, 2010

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

 Respond  
Wade   -  Dec 31, 2010

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

 Respond  
skitzkid   -  Dec 31, 2010

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

 Respond  
_Teen_   -  Dec 31, 2010

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

 Respond  
skitzkid   -  Dec 31, 2010

Fixed, I will make the user files save to a separate directory for tidiness in the next version.

 Respond  
_Teen_   -  Dec 31, 2010

sorry but i didnt understand where, this command will be triggered

on *:Text:`setaccess*: {
 Respond  
Are you sure you want to unfollow this person?
Are you sure you want to delete this?
Click "Unsubscribe" to stop receiving notices pertaining to this post.
Click "Subscribe" to resume notices pertaining to this post.