Cum sa scrii un script pentru eggdrop care nu se blocheaza la caracterele speciale

de catre "Peterre"


Scripturi pentru eggdrop care se blocheaza
Multe scripturi pentru eggdrop se blocheaza la nickname, username, sau la text, care contin caractere ce au un rol special in Tcl, si anume: [, ], {, }, ", \, si $. Din acest motiv, unele canale de IRC baneaza nickname-uri care contin [ sau {.

Totusi, problema poate fi evitata complet prin scrierea corecta a codului Tcl. Sunt doua reguli de aur care trebuiesc respectate.

Prima regula de aur a Tcl-ului pentru eggdrop
De a include toate split-urile si join-urile necesare pentru a efectua convertirea intre list-uri si string-uri.

Ar fi bine de a privi list-ele si string-urile ca doua tipuri (de date) separate, plasand in codul nostru toate split-urile si join-urile care sunt necesare pentru a trece de la un tip la altul. Daca facem acest lucru, interpretorul va lua masurile speciale pentru orice caracter special al Tcl-ului care ar putea fi prezent. Castigam un mare avantaj pentru ca nu trebuie sa intelegem cum list-ele sunt reprezentate - in schimb, pur si simplu lasam pe interpretor sa aiba grija de acest aspect pentru noi.

De exemplu, un script pentru eggdrop ce mi-a fost dat continea urmatoarele trei linii similare cu urmatoarele:

bind dcc o tell do_dcc_tell
proc do_dcc_tell { hand idx arg } {
set arg [lrange $arg 0 0]

Cand procedura este chemata, primul cunvant al arg este un nickname. Scriptul s-a blocat la nickname-uri ce contineau [ sau {. A treia linie a fost cea care a cauzat problema.

Autorul scriptului putea scrie in schimb
set arg [lindex $arg 0]
dar si aceasta poate produce deasemenea un esec daca nickname-ul contine caractere speciale ale Tcl.

Pentru a vedea de ce codul de mai sus este incorect, puteti citi in tcl-commands.doc ca valoarea lui arg va fi "string-ul argumentului". Totusi lrange si lindex ar trebui folosite numai la liste. Totusi, inainte sa aplicam lrange sau lindex, trebuie sa convertim string-ul intr-o lista, ceea ce putem face folosind
set arg [lrange [split $arg] 0 0]

In orice caz, aceasta e tot incorect. Restul procedurii asteapta ca arg sa fie un string, nu o lista, chiar daca lrange returneaza o lista (in acest caz formata dintr-un element). Totusi trebuie sa folosim join pentru a o converti intr-un string. Versiunea finala si corecta este
set arg [join [lrange [split $arg] 0 0]]

Alternativ (si ca rezultat mai frunos), putem corecta urmatoarea versiune, in alte cuvinte
set arg [lindex $arg 0]
in aceeasi modalitate, rezultand
set arg [lindex [split $arg] 0]

In acest caz nu avem nevoie de join, in timp ce lindex returneaza primul argument, care in acest caz este deja string.

Aici exista o exceptie, in cazul cand ultimul argument al do_dcc_tell s-ar fi numit args in loc de arg, atunci situatia ar fost diferita. Daca o procedura a unui Tcl are un argument cu numele special args, acel argument se comporta diferit fata de cele cu oricare alt nume.

Si acum alt exemplu de script pentru eggdrop. Acesta este scris corect.

bind pub B|B !orderfor pub_orderfor
proc pub_orderfor {nick uhost hand chan rest} {
  global botnick
  set rest [split $rest]
  if {[llength $rest] < 2} {
    putnotc $nick "Syntax: !orderfor\
    <nick to order something for>\
    <what to order>"
    return 0
  }
  putchan $chan "\001ACTION sets\
  [join [lrange $rest 1 end]] in front of\
  [lindex $rest 0], compliments of $nick.\001"
  return 0
}

Nota, putnotc si putchan sunt definite in alltools.tcl care este distribuit cu eggdrop.

Versiunea originala (cea pe care am download-at-o de la o librarie cu scripturi) nu continea
set rest [split $rest]
dar continea
set cmd [string tolower [lindex $rest 0]]
de unde este clar ca facem greseala de a aplica lindex la un string.

Versiunea originala downloada-ta va da cateodata raspunsuri gresite daca input-ul ar contine caractere speciale de Tcl.

A doua regula de aur a Tcl-ului pentru eggdrop
Cand executarea unei comenzi trebuie amanata in timp, asigurati-va ca are o forma corecta.

S-ar putea sa dorim sa facem o comanda care trebuie evaluata mai tarziu, ca exemplu cand un timer expira, sau cand il initiem cu comanda eval. Comanda list ofera o cale convenabila de a formata comanda intr-o forma corecta. Aceasta introduce backslash-uri si acolade adecvat, pentru a face fata la orice caracter special de Tcl care ar putea fi prezent.

Iata o procedura unde a doua regula de aur a fost incalcata. Aceasta da un autogreet pentru cei care intra pe canal, cu conditia ca ei nu primisera deja autogreet-ul in ultimele 3 minute.

bind join - * do_jn_msg
proc do_jn_msg {nick uhost hand chan} {
  global botnick jn_msg_done
  if {$nick == "X" || $nick == $botnick} {
    return 0
  }
  if {[info exists jn_msg_done($nick:$chan)]} {
    return 0
  }
  set jn_msg_done($nick:$chan) 1
  timer 3 "unset jn_msg_done($nick:$chan)"
  puthelp "NOTICE $nick :Welcome to $chan"
  return 0
}

Sa presupunem ca nick are valoarea abc si chan are valoarea #room. Intre ghilimelele duble vor inlocuite variabilele $nick si $chan in asa fel incat comanda data la timer va fi
unset jn_msg_done(abc:#room)

Cand timer-ul expira, comanda unset va lucra fara nici o problema.

Dar sa presupunem ca in loc de valoarea abc, nick are valoarea a[b]c. Intre ghilimelele duble vor inlocuite variabilelr $nick si $chan in asa fel incat comanda data la timer va fi
unset jn_msg_done(a[b]c:#room)

Cand timer-ul expira, interpretul Tcl-ului va incerca sa execute o serie de inlocuiri in jn_msg_done(a[b]c:#room), primul pas in evaluarea oricarei comenzi este executarea unei serii de inlocuiri la cuvintele comenzii.

Deci, interpretorul va incerca sa execute substituirea de comanda [b]. El va da un mesaj de eroare din cauza ca nu exista nici o comanda numita b. Presupunand ca nick-ul ar fi fost a[die]c, comanda die ar fi fost executata, avand ca rezultat inchiderea bot-ului.

Pentru a corecta scriptul, inlocuim comanda timer cu ceea ce urmeaza.

timer 3 [list unset jn_msg_done($nick:$chan)]

In cazul in care sunt prezente caractere speciale Tcl, list adauga backslash-uri si/sau acolade in asa fel incat sa dea o forma corecta comenzii. Noi nu avem nevoie sa stim sau chiar sa ne gandim care este acea forma. Comanda unset, acum in forma corecta, este preluata de timer si functioneaza fara nici o problema atunci cand timer-ul expira.

De fapt, in cazul comenzii a[b]c, comanda list doar insereaza cateva acolade in plus. Daca nick-ul ar fi fost a[b]c{, s-ar fi inserat cateva caractere backslash. Dar nu este necesar ca noi sa cunoastem aceste lucruri. Pur si simplu folosim comanda list si lasam interpretorul sa aiba grija de aceste detalii.

Oricand dorim sa construim o comanda care urmeaza sa fie evaluata mai tarziu, comanda list poate fi folosita pentru a construi comanda in forma sa corecta.

De obicei, in scripturile pe care le-am vazut care sunt similare cu exemplul anterior, este folosit mai des $uhost decat $nick. Dar, userhost-ul poate contine de asemenea unele caractere speciale Tcl, astfel ivindu-se aceeasi problema, care poate fi rezolvata in acelasi mod ca mai sus, folosind comanda list.

De fapt, se pare a fi mai bine de folosit $uhost decat $nick. Eu am folosit $nick in exemplul anterior deoarece e mai simplu pentru cititor de a experimenta cu scriptul, coppind-ul intr-un fisier Tcl sursa.

Alte metode de a rezolva problema
Exista alte metode care sunt folosite uneori pentru a evita problemele care pot aparea in legatura cu caracterele speciale Tcl.

De exemplu unele scripturi filtreaza input-urile sale cu ceva asemanator codului ce urmeaza:

proc filt {data} {
regsub -all -- \\\\ $data \\\\\\\\ data
regsub -all -- \\\[ $data \\\\\[ data
regsub -all -- \\\] $data \\\\\] data
regsub -all -- \\\} $data \\\\\} data
regsub -all -- \\\{ $data \\\\\{ data
regsub -all -- \\\" $data \\\\\" data
return $data
}

Un asemenea filtru poate intr-adevar uneori remedia problema caracterelor speciale Tcl ce pot aparea odata cu un script eggdrop incorect scris. Dar daca e posibil sau nu, aceasta depinde numai de circumstantele scriptului. Poate sa nu rezolve, sau poate sa rezolve problema numai in cateva cazuri. Adaugarea unui asemenea filtru poate, de asemenea, cauza unele probleme in plus.

Daca un script esueaza din cauza unor caractere speciale Tcl, atunci este mult mai bine sa se corecteze codul astfel incat cele doua reguli de aur sa fie indeplinite, decat sa se aplice solutia cu filtrul prezentat mai sus, a carei functionare nu este garantata in toate circumstantele si care poate crea probleme suplimentare.

Un lucru de luat in considerare despre args
Plasarea args ca ultim argument al procedurii Tcl - ne permite folosirea unui numar variabil de argumente de input. Dar exista o potentiala sursa ce poate cauza confuzie daca procedura este una chemata de o comanda eggdrop bind.

Intr-un script pe care l-am downloadat am vazut un cod similar cu urmatorul:

bind dcc - m2f dcc_calc_m2f
proc dcc_calc_m2f {hand idx args} {
  if {[llength $args] == "1"} {

Autorul se gandea, fara indoiala, ca daca user-ul tasta
.m2f aaa bbb ccc
atunci valoarea argumentelor ar fi fost o lista de trei elemente, aaa, bbb and ccc, si ca, in consecinta, valoarea comenzii [llength $args] ar fi 3.

De fapt, valoarea comenzii args ar fi o lista de un singur element, acel element fiind string-ul aaa bbb ccc. Orice string ar tasta user-ul, valoarea comenzii [llength $args] va fi intotdeauna 1.

In mod similar,valoarea comenzii [lindex $args 0] nu va fi string-ul aaa, ci string-ul aaa bbb ccc.

Bind-urile eggdrop pub and msg se comporta in aceeasi maniera.

Proprietatile de mai sus ale comenzii de eggdrop bind au fost confirmate prin testarea cu versiunea 1.6.6 de eggdrop.

Peterre
paperclip442 at ntlworld dot com
(adresa email special camuflata to
pentru a impedica sa fie colectata de spameri)


Versiunea originala @ http://www.peterre.info/characters.html
Varianta engleza
Varianta rusa

Traducere catre Erised pentru translate.botlending.com @ botlending.com
Verificata catre UniversaliA
Redactare catre UniversaliA