This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

syntax checking of user input on a public trigger

Requests for complete scripts or modifications/fixes for scripts you didn't write. Response not guaranteed, and no thread bumping!
L
LoKii
Voice
Posts: 34
Joined: Wed Oct 21, 2009 3:59 am

syntax checking of user input on a public trigger

Post by LoKii »

Hi everyone.

Got something new here that is doing my head in.

I made a small script, where as the trigger is based on a few specific types of letters/numbers.

an example would be:

.test 5 10 5 n:sp

So far i got it to work all smoothly the way i want, but now what IF a user types in for example:

.test abc def ghi 10:5

I need the script to make sure that the first 3 inputs are NUMBERS ONLY, and the last part can ONLY be the LETTERS n, kb, sp, either single or combined with a : as a separator (such as n:kb:sp or just kb or even n:sp etc..).

If one of the first 3 inputs is NOT a number, i would like it to return an error, and if the last part (modes) is NOT one of the allowed letters, to have it return an error as well.

In addition to this, there are a total of 4 arguments. If the arguments are more than 4, return an error, and if the arguments are less than 4, to return an error as well. BUT, if the argument is just 1, as i have defined in the script below (-help or off), to not count this as an error.

Code: Select all

proc test {nick host hand channel text} {
    set modes [lrange [split $text] 0 end]
    set help [lindex [split $text] 0]
    set h "-help"
    set o off
    if {$modes == ""} {
        putserv "NOTICE $nick :Missing arguements. Please type \002.test -help\002 for information on how to use this command."
        return 1
    }
    if {$help == $h} {
        putserv "NOTICE $nick :Set & Activate TEST"
        putserv "NOTICE $nick :This is the help description of the command \002test\002."
### I have subsituted the meanings with num1, num2, num3 to show you that the value MUST be only numerical digits, whereas <modes> must and only be either a sp, kb, n or a combination like sp:kb or n:kb, or even sp:kb:n ###
        putserv "NOTICE $nick :usage: .test <num1> <num2> <num3> <modes>"
        putserv "NOTICE $nick :example: .test 5 20 5 n:sp"
        putserv "NOTICE $nick : "
        putserv "NOTICE $nick Valid modes are sp for sapart, kb for kickban, n for notify"
        putserv "NOTICE $nick :To dectivate TEST type: .test off"
        return 1
    }
    if {$help == $o} {
        putserv "NOTICE $nick :TEST have been de-activated for channel $channel"
        return 1
    }
    putserv "NOTICE $nick :Channel TEST features for channel $channel have been set to \002[lindex [split $text] 0] [lindex [split $text] 1] [lindex [split $text] 2] [lindex [split $text] 3]\002"
    putserv "NOTICE $nick :In english this means, <num1> = \002[lindex [split $text] 0]\002, l<num2> = \002[lindex [split $text] 1]\002, <num3> = \002[lindex [split $text] 2]\002, and finally the <modes> = \002[lindex [split $text] 3]\002"
    return 1
}
The reason for all this is, that the final version, will execute various .chansets, and for security reasons the user's input MUST be limited to the above mentioned.

Any help would be be appreciated.

Cheers. 3
d
dirty
Halfop
Posts: 40
Joined: Fri Feb 08, 2013 2:33 pm
Location: Romania
Contact:

Post by dirty »

try using [isnumber] and [regsub] for that
come to the dark side.. I have cookies!
WwW.BotZone.TK
User avatar
Get_A_Fix
Master
Posts: 206
Joined: Sat May 07, 2005 6:11 pm
Location: New Zealand

Post by Get_A_Fix »

Before there was [isnumber] we had [isnum] :P

Use this method, in the procs

Code: Select all

proc test {nick host hand channel text} {
    set firstarg [lindex [split $text] 0]
    set secondarg [lindex [split $text] 1]
    set thirdarg [lindex [split $text] 2]
    if {![isnum $firstarg]} {putquick "NOTICE $nick :That is not a number. Please type \002.test -help\002 for information on how to use this command."; return}
    if {![isnum $secondarg]} {putquick "NOTICE $nick :That is not a number. Please type \002.test -help\002 for information on how to use this command."; return}
    if {![isnum $thirdarg]} {putquick "NOTICE $nick :That is not a number. Please type \002.test -help\002 for information on how to use this command."; return}
    set help [lindex [split $text] 0]
    set h "-help"
    set o off
    if {$modes == ""} {
        putserv "NOTICE $nick :Missing arguements. Please type \002.test -help\002 for information on how to use this command."
        return 1
    }
then add this to the last script loaded...

Code: Select all

proc isnum {string} {
  if {[regexp {^-?\d+(\.\d+)?$} $string]} {
    return 1;
  }
  return 0;
}
We explore.. and you call us criminals. We seek after knowledge.. and you call us criminals. We exist without skin color, without nationality, without religious bias.. and you call us criminals.
d
dirty
Halfop
Posts: 40
Joined: Fri Feb 08, 2013 2:33 pm
Location: Romania
Contact:

Post by dirty »

why the hell should he use [isnum] and make a proc for that since [isnumber] is already in the eggdrop
come to the dark side.. I have cookies!
WwW.BotZone.TK
User avatar
caesar
Mint Rubber
Posts: 3776
Joined: Sun Oct 14, 2001 8:00 pm
Location: Mint Factory

Post by caesar »

Where's $modes variable declared?

why not use scan instead?

something like:

Code: Select all

if {[scan $text {%d%d%d%s} fist second third last] != 4} {
# do whatever as the syntax is not valid
return
}
# do whatever as syntax is valid
or if you insist instead of:

Code: Select all

    set firstarg [lindex [split $text] 0]
    set secondarg [lindex [split $text] 1]
    set thirdarg [lindex [split $text] 2] 
a simple scan would do this:

Code: Select all

if {[scan {%s%s%s} firstarg secondarg thirdarg] != 3} {
# do whatever as the syntax is not valid
return
}
# do whatever as syntax is valid
Once the game is over, the king and the pawn go back in the same box.
L
LoKii
Voice
Posts: 34
Joined: Wed Oct 21, 2009 3:59 am

Post by LoKii »

Thank you all for your imput, sorry for my delay in responding. I will sit down today and tackle this with what all of you have proposed here, and I will let you know how it turns out :)
L
LoKii
Voice
Posts: 34
Joined: Wed Oct 21, 2009 3:59 am

Post by LoKii »

Well caesar, simply put I didn't even know of it's existence (the scan). Your solution has so far partially helped me a great deal. Whilst i also did like the solution prestented by Get_A_Fix, by using directly isnumber instead of isnum only for the fact that i could customize the response based on every argument separately, i still chose the scan method, just to keep the code a bit shorter, and also there seems to be a regex support using scan, which does bring me to my next point.

My next point was to limit the input possibilites to only certain characters & symbols, and using regex seems the right way to go. Now at first i thought of various approaches, as in letting the final argument be either settled by a separate if statement of its own using only regex in there, or if the scan's regex would also suffice. ( I guess this shows that I have no idea whatsoever about regex, and all ths stuff i read online just made me stay away from it, for as long as possible). But seeing now that I just simply NEED it whether i like it or not, i had to sit down and find a source that would explain it to me.

A friend of mine dropped of a book by O'reilly - Mastering Regular Expressions. I spent all day yesterday focusing just on that book, reading and I'm literally kicking myself in the a$$ that i did not go into regex earlier in my life. Its the MOST useful thing, and addictive as hell. So i will be solving my last issue once i understand how to perform the regex i need for my particular solution.

Below is just a small modification of the code. Changed the variable's names a bit for clarity sake, and added caesar's scan method. As you can see below, i need to implement the regext at the %s ($modes), so i hope that by tomorrow (after spending all day too to read on regex) that i will have a solution that suits my needs.

Code: Select all

proc test {nick host hand channel text} {
    set syntax [lrange [split $text] 0 end]
    set 1 [lindex [split $text] 0]
    set 2 [lindex [split $text] 1]
    set 3 [lindex [split $text] 2]
    set modes [lindex [split $text] 3]
    set h "-help"
    set o off
    if {$syntax == ""} {
        putserv "NOTICE $nick :Missing arguements. Please type \002.test -help\002 for information on how to use this command."
        return 1
    }
    if {$1 == $h} {
        putserv "NOTICE $nick :Set & Activate TEST"
        putserv "NOTICE $nick :This is the help description of the command \002test\002."
        putserv "NOTICE $nick :usage: .test <num1> <num2> <num3> <modes>"
        putserv "NOTICE $nick :example: .test 5 20 5 n:sp"
        putserv "NOTICE $nick : "
        putserv "NOTICE $nick Valid modes are sp for sapart, kb for kickban, n for notify"
        putserv "NOTICE $nick :To dectivate TEST type: .test off"
        return 1
    }
    if {$1 == $o} {
        putserv "NOTICE $nick :TEST have been de-activated for channel $channel"
        return 1
    }
    if {[scan $syntax {%d%d%d%s} $1 $2 $3 $modes] != 4} {
		putserv "NOTICE $nick :Invalid arguements. Please type \002.test -help\002 for information on how to use this command."
		return 1
	}
    putserv "NOTICE $nick :Channel TEST features for channel $channel have been set to \002$1 $2 $3 $modes\002"
    putserv "NOTICE $nick :In english this means, <num1> = \002$1\002, <num2> = \002$2\002, <num3> = \002$3\002, and finally the <modes> = \002$modes\002"
    return 1
}
Many thanks to everyone here for their great help and support.

I will post the final code once it works.

In the mean time, maybe someone could give me an opinion on performance issues based on how I wrote that code in general? I don't like that i use so many lines for set. Maybe there is a more elegant way to go about this? (Would be nice to adapt more elegant approaches from now to avoid bad habits, rather than having to cut bad habits later on.

Cheers everyone :)
User avatar
caesar
Mint Rubber
Posts: 3776
Joined: Sun Oct 14, 2001 8:00 pm
Location: Mint Factory

Post by caesar »

Code: Select all

proc test {nick host hand channel text} {
	set syntax [lrange [split $text] 0 end]
	scan $syntax {%s%s%s%s} 1 2 3 modes
	set h "-help"
	set o "off"
	if {[string match -nocase $1 $h]} {
		puthelp "NOTICE $nick :Set & Activate TEST"
		puthelp "NOTICE $nick :This is the help description of the command \002test\002."
        puthelp "NOTICE $nick :usage: .test <num1> <num2> <num3> <modes>"
        puthelp "NOTICE $nick :example: .test 5 20 5 n:sp"
        puthelp "NOTICE $nick Valid modes are sp for sapart, kb for kickban, n for notify"
        puthelp "NOTICE $nick :To dectivate TEST type: .test off"
        return
	}
	if {[string match -nocase $1 $o]} {
		puthelp "NOTICE $nick :TEST have been de-activated for channel $channel"
		return
	}
	if {![info exists $modes]} {
		puthelp "NOTICE $nick :Missing arguements. Please type \002.test -help\002 for information on how to use this command."
	} else {
		puthelp "NOTICE $nick :Channel TEST features for channel $channel have been set to \002$1 $2 $3 $modes\002"
		puthelp "NOTICE $nick :In english this means, <num1> = \002$1\002, <num2> = \002$2\002, <num3> = \002$3\002, and finally the <modes> = \002$modes\002"
	}
}
Haven't tested. :P
Once the game is over, the king and the pawn go back in the same box.
User avatar
Get_A_Fix
Master
Posts: 206
Joined: Sat May 07, 2005 6:11 pm
Location: New Zealand

Post by Get_A_Fix »

dirty wrote:why the hell should he use [isnum] and make a proc for that since [isnumber] is already in the eggdrop
Did you notice where I said "Before there was [isnumber] ..." ???

It was merely an example of how it 'was' achieved in my instance.
We explore.. and you call us criminals. We seek after knowledge.. and you call us criminals. We exist without skin color, without nationality, without religious bias.. and you call us criminals.
L
LoKii
Voice
Posts: 34
Joined: Wed Oct 21, 2009 3:59 am

Post by LoKii »

Hello everyone again,

Hey, no need for arguing about why this method, and not that. All point of views are of interest, since non of your suggestions here have been known to me in the first place. Its nice to see all the possibilities even if some are obsolete or over complicated.

Ok, back on track. I have read up on various regex solutions. In some extra test code i have tried various approaches using regexp, and others. However, i would like to stick with the scan solution.

When it comes to the scan solution, non of the regex seems to be applied or work for me (based on what i have read about regex).

To simplify the process of getting regex to work inside a scan, lets dumb down the 4th argument to just a single character k. Later on I can continue to expand the regex rules, once i get this basic one running.

What i have tried so far, but with no luck whatsoever is:

Code: Select all

if {[scan $syntax {%d%d%d%[k]} $1 $2 $3 $modes] != 4} {

if {[scan $syntax {%d%d%d%[[k]+ $]} $1 $2 $3 $modes] != 4} {

if {[scan $syntax {%d%d%d%[(k)+ $]} $1 $2 $3 $modes] != 4} {

if {[scan $syntax {%d%d%d%[^[k]+ $]} $1 $2 $3 $modes] != 4} {

if {[scan $syntax {%d%d%d%\[k\]+ $} $1 $2 $3 $modes] != 4} {

if {[scan $syntax {%d%d%d%[^[k+] ]} $1 $2 $3 $modes] != 4} {
and many other combinations that I have now forgotten whilst writing this post here.

Some of the methods I have tried would either return various errors, others would just simply ignore the 4th argument of code totally.

From my understanding, at least the 1st line should work? %[k].

From my examples here, what i wish to achieve is to ONLY accept the k as a valid entry for the 4th argument of the code, and nothing else. I solved more complicated regex quizzes whilst reading about them, but i can not get any regex working with this tcl scan.

What am i missing? I would like to keep the idea of using scan rather than using regexp (which seems to have a performance difference in speed, and most people recommend scan for this type of situation).


caesar, thank you for your code above, I have not got about to trying it yet, as i want to solve the regex issue first, but once that is sorted, i will look into the modifications you have made for the performance issues.

Cheers everyone :)
User avatar
SpiKe^^
Owner
Posts: 831
Joined: Fri May 12, 2006 10:20 pm
Location: Tennessee, USA
Contact:

Post by SpiKe^^ »

First off,
$1 $2 $3 $modes
should be...
1 2 3 modes
without all the $'s
SpiKe^^

Get BogusTrivia 2.06.4.7 at www.mytclscripts.com
or visit the New Tcl Acrhive at www.tclarchive.org
.
L
LoKii
Voice
Posts: 34
Joined: Wed Oct 21, 2009 3:59 am

Post by LoKii »

SpiKe^^ wrote:First off,
$1 $2 $3 $modes
should be...
1 2 3 modes
without all the $'s
Thank you for pointing that out. Thanks to that, the error messages changed in to something that made more sense to me, and i finally figured out how to go about the regex issues i had.

So, now i managed to get the regex to actually 'do' something.

To sort out the issue i have now, let us let the regex filter out the following characters: a b cd
My regex so far looks like:

Code: Select all

"^\[ab(cd)\]?$"
My issue is, that the a or the b are identified correctly. However, the cd is still messy due to the ? (limiting it to a single char). if i were to use a + instead of a ?, then the problem is that i can use cd / ccdd / cddd / cccd etc....

In my attempt to seperate the a and the b from the cd, i tried the following (just to give me some play-space for now):

Code: Select all

"^\[ab\]?$\|^\[cd\]+$"
In this version, i can limit the a and b to a single character, however the cd can still be fooled by ccdd and such variants.

In the traditional regex (not tcl), a way to fix this would be:

Code: Select all

"^\[ab(cd)\]?$"
meaning that I would group the cd using (), but this does not seem to work in TCL for me because the ? still limits my input to 1 character only, and the + allows a surplus of characters.

Any tips on what I am looking for to fix this?

Cheers.
L
LoKii
Voice
Posts: 34
Joined: Wed Oct 21, 2009 3:59 am

Post by LoKii »

Small update. Managed to get rid of the problem when repeating some of the chars, such as ccdd ccd cdd and so on.

Code: Select all

"^\[ab\]?$\|^\[cd\]{2}$"
However now, i still have 1 last headache left. I am still able to use cc or dd. Any suggestions?


//edit

Ok, i found a full working solution.

Code: Select all

"^\[ab\]?$\|^(c)\[\1d\]$"
This version works the way i need it to.

Now then, is there maybe any recommended cosmetic change to this? Maybe something that makes a difference in performance, or security?

Below is the entire code that i have so far (with a few changes based on caesar's rewrite).

Code: Select all

proc test {nick host hand channel text} {
    set syntax [lrange [split $text] 0 end]
    foreach {1 2 3 4} [split $syntax] {break}
    set h "-help"
    set o off
    set m "^\[ab\]?$\|^(c)\[\1d\]$"
    if {$syntax == ""} {
        putserv "NOTICE $nick :Missing arguments. Please type \002.test -help\002 for information on how to use this command."
        return 1
    }
    if {[string match -nocase $1 $h]} {
        putserv "NOTICE $nick :Set & Activate TEST"
        putserv "NOTICE $nick :This is the help description of the command \002test\002."
        putserv "NOTICE $nick :usage: .test <num1> <num2> <num3> <modes>"
        putserv "NOTICE $nick :example: .test 5 20 5 <a | b | cd>"
        putserv "NOTICE $nick : "
        putserv "NOTICE $nick :To de-activate TEST type: .test off"
        return 1
    }
    if {[string match -nocase $1 $o]} {
        putserv "NOTICE $nick :TEST have been de-activated for channel $channel"
        return 1
    }
    if {([scan $syntax {%d%d%d%s} a b c prefs] != 4) || (![regexp $m $prefs])} {
		putserv "NOTICE $nick :Invalid or missing arguments. Please type \002.test -help\002 for information on how to use this command."
		return 1
	} 
	putserv "NOTICE $nick :Channel TEST features for channel $channel have been set to \002$a $b $c $prefs\002"
	putserv "NOTICE $nick :In English this means, <num1> = \002$a\002, <num2> = \002$b\002, <num3> = \002$c\002, and finally the <modes> = \002$prefs\002"
	return 1
}
Please do criticize and make recommendations to what could be better.

Cheers everyone and thank you all for your help.
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

One hint when working with regular expressions, if you use curly brackets instead of quotes, you don't have to bother with escaping the string from the tcl parser..

As for your pattern, I find it somewhat overworked. If you merely would like to match either a, b, or cd, this would suffice:

Code: Select all

set m {^(a|b|cd)$}
NML_375
L
LoKii
Voice
Posts: 34
Joined: Wed Oct 21, 2009 3:59 am

Post by LoKii »

nml375 wrote:One hint when working with regular expressions, if you use curly brackets instead of quotes, you don't have to bother with escaping the string from the tcl parser..

As for your pattern, I find it somewhat overworked. If you merely would like to match either a, b, or cd, this would suffice:

Code: Select all

set m {^(a|b|cd)$}
I was wandering, why I am the only one on the forums here who actually had to escape almost everything in the string to get it to work, whilst looking at other people's syntax.

I tried your code, but it doesn't work. As for the error, I'm not sure why not (since the error returned is my custom error). I simply replaced your set m with my set m.

How would i go about getting an error message as to why your code wont work?

I sure as hell would not mind to get a shorter version of it working.

I have applied this test code (the one that I have made) to also work for AllProtection script, as to make some aspects of it public.

The version I used for the AllProtection was:

Code: Select all

set pmeth "^\[wk\]?$\|^(k)\[\1b\]$\|^\[w\]:?\[k\]?$\|^\[w\]:?(k)\[\1b\]$\|^\[k\]:?(k)\[\1b\]$\|^\[w\]:?\[k\]:?(k)\[\1b\]$"
And yes, I would LOVE to find a more elegant/shorter way of getting the same results.

The goal of this whole regex issue is to make sure that any input which would not be recognized by the AllProtection script, should be invalid. This is just to make sure that if someone does not understand the syntax that Allprotection wants, that he could not enter something invalid, or even maybe something potentially dangerous, since at the end of the day, it does end up doing a .chanset.

I just dont get, why whenever i try to avoid escaping characters, nothing works for me :(

But hey, this is day 3 for me on learning regex. I definately want to get much more involved with regex in general, since it is highly addictive.

In addition to this, i have also read many opinions here on the forums to maybe try to use string map instead of regexp for performance. And initially i wanted to avoid using regexp in the first place, thinking i could just add the regex to the scan. Didnt work out for me like i had hoped, so i used the regexp solution.

I definitely will be checking for the string map too for other solutions that i am still in the process of working on.

Cheers.
Post Reply