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.

same issue with massvoice and nicks with {}

Help for those learning Tcl or writing their own scripts.
Post Reply
s
simo
Revered One
Posts: 1108
Joined: Sun Mar 22, 2015 2:41 pm

same issue with massvoice and nicks with {}

Post by simo »

simmilarly as the previous one i checked what it could be and i couldnt find any thing why it wouldnt set voice on nicks with {} in them like {somenick}

Code: Select all

bind pub n .mv voice_all
 
set excnick { 
    "chanserv" 
    "somenick" 
} 

 


proc voice_all {nick host handle chan text} {
global excnick

if {![botisop $chan]} {
      puthelp "NOTICE $nick :I'm not oped on $chan."
      return
   }

     # grab the channel members list and exclude the bot from it
   set userList [chanlist $chan]
   set pos [lsearch -nocase $userList $::botnick]
   set userList [lreplace $userList $pos $pos]
 
   # build the op list with only people that aren't op or any other channel access level
   foreach user [split $userList] {
    if {![isvoice $user $chan] && ![isop $user $chan] && ![ishalfop $user $chan] && ![matchattr [nick2hand $user] o|o $chan]  && [lsearch -nocase $excnick $user] == -1} {  lappend voiceList $user }
    }
 
   # define the max amount of modes per line
   set max 15
 
   # op all the members in the above built voice list
   if {[info exists voiceList]} {
      set len [llength $voiceList]
      while {$len > 0} {
         if {$len > $max} {
         set mode [string repeat "v" $max]
         set users [join [lrange $voiceList 0 [expr {$max - 1}]]]
         set voiceList [lrange $voiceList $max end]
         incr len -$max
         } else {
            set mode [string repeat "v" $len]
            set users [join $voiceList]
            set len 0
         }
      putnow "MODE $chan +$mode $users" 
      }
      puthelp "NOTICE $nick :Voiced all users on $chan."
   } else {
      puthelp "NOTICE $nick :Nobody needed to be Voiced on $chan."
   }
}
it adds {} to the nicks for example
a nick like {ykbeucwvi} would be seen as {{ykbeucwvi}}
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

Hi Simo,
Going to try and not make a complete teardown of the script this time...

But...
Lets start here:

Code: Select all

set userList [chanlist $chan]
set pos [lsearch -nocase $userList $::botnick
set userList [lreplace $userList $pos $pos]
Here, we treat userList as an actual list (which it is); so far so good.

But then, we get here:

Code: Select all

foreach user [split $userList] {
split is really only intended to be used on strings, not lists. Remove it, as userList already is a list.

Next;

Code: Select all

 if {... && [lsearch -nocase $excnick $user] ...
Here, we treat excnick as a list, but to be honest it's merely a list-like string;

Code: Select all

set excnick {
    "chanserv"
    "somenick"
}
You'll get away with it for as long as your exclusion-nicks does not contain list-formatting characters such as {}... Consider creating a proper list using the list command instead.

In the end, you've got some code iterating through a list of nicks, trying to build optimal MODE commands (stack multiple mode changes in one command, not unlike the KICK-stacking in your previous thread). Technically nothing obviously wrong with the code; I just wanted to point out that this functionality is already implemented in eggdrop itself using the pushmode command (which will queue all mode changes until the binding returns, and then stack them properly).
NML_375
s
simo
Revered One
Posts: 1108
Joined: Sun Mar 22, 2015 2:41 pm

Post by simo »

excellent tnx for the explanation nml375

about the list i tried searching for it in the docs couldnt find it would you have any example i can use as a template cause we might want to add nicks wich may contain list formatting chars as u mentioned we want to have it optional anyway

tnx once again nml375 much apreciated
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

Certainly; though the man-page for list do have a few good examples: https://www.tcl.tk/man/tcl8.6/TclCmd/list.htm

Code: Select all

#sampe 1
set excnick [list chanserv somenick]

#sample 2
set excnick [list \
  chanserv \
  somenick \
]

#sample 3, adding the value of the global variable ::botnick to the list.
set excnick [list "chanserv" \
  somenick $::botnick]
Be cautious though, as you still go through variable- and command-substitutions, as well as syntax checks in the tcl-interpreter:

Code: Select all

#this will not work well;
set uxcnick [list badnick{ \
badnick2 \
}badnick3 ]

#this one might surprise you
set excnick [list "chanserv" \
{somenick} \
]
#result is "chanserv somenick"

#What you probably wanted:
set excnick [list "chanserv" \
"{somenick}" \
"{othernick" \
]
#result is "chanserv {{somenick}} \{othernick"
#"lindex $excnick 0" => "chanserv"
#"lindex $excnick 1" => "{somenick}"
#"lindex $excnick 2" => "{othernick"

#Another one:
set excnick [list "chanserv" \
{$botnick} \
]
#result is "chanserv {$botnick}"
#"lindex $excnick 1" => "$botnick"
#note that there is no variable substitution done due to the use of braces
Somewhat simplified, an atomic item on the command line may be a single word (no quotes or braces), a substitutable string (enclosed in quotes), or a plain string (enclosed in braces, or {}). That's why list "chanserv" {somenick} produced a list with the strings "chanserv" and "somenick", and list "chanserv" {$botnick} created a list containing the strings "chanserv" and "$botnick".

Finally, the backspaces are needed to escape the newline character into a whitespace character, as newline otherwize would terminate the commandline early (and there's alot of more trickery that can be done with escaping).

You'll also find a decent readup on the various substitutions done by the tcl interpreter here: https://www.tcl.tk/man/tcl8.6/TclCmd/Tcl.htm
NML_375
s
simo
Revered One
Posts: 1108
Joined: Sun Mar 22, 2015 2:41 pm

Post by simo »

excellent thank you nml375 for the explanation and the provided examples much apreciated
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

You're welcome,
I tried to cover the most common mistakes I've seen over the years, though there certainly are other pitfalls. Understanding when and how substitutions and escaping occurs is really the name of the game here.

The list command does guarantee a valid list (will not break on foreach, lindex, etc), but you still need to make sure the input isn't mangled by the tcl interpreter before list gets it hands on it.
NML_375
User avatar
CrazyCat
Revered One
Posts: 1301
Joined: Sun Jan 13, 2002 8:00 pm
Location: France
Contact:

Post by CrazyCat »

I'll made a short critic about the script...

You set a "max" variable ("define the max amount of modes per line") and after various calculation and manipulation you use:

Code: Select all

putnow "MODE $chan +$mode $users"
I think that if you use the good tcl commands (pushmode & flushmode), you can really simplify your procedure:

Code: Select all

proc voice_all {nick host handle chan text} {
	global excnick
	if {![botisop $chan]} {
		puthelp "NOTICE $nick :I'm not oped on $chan."
		return 0
	}

   # build the op list with only people that aren't op or any other channel access level
	set voiced 0
	foreach user [chanlist $chan] {
		if {($user ne $::botnick) && ![isvoice $user $chan] && ![isop $user $chan] && ![ishalfop $user $chan] && ![matchattr [nick2hand $user] o|o $chan]  && [lsearch -nocase $excnick $user] == -1} {
			incr voiced
			pushmode $chan +v $user
		}
    }
	flushmode $chan
	if { $voiced == 0} {
		puthelp "NOTICE $nick :Nobody needed to be Voiced on $chan."
	} else {
		puthelp "NOTICE $nick :Voiced all users on $chan."
	}
}
s
simo
Revered One
Posts: 1108
Joined: Sun Mar 22, 2015 2:41 pm

Post by simo »

thats even better thanx CrazyCat
Post Reply