HOWTO Write TCL Script/ru
Contents |
Начинаем писать скрипты на TCL
...Вы установили себе бота, настроили конфиг, запустили, бот увидел вас как хозяина и сидит, красуется на канале.
Что же теперь? Как же "научить" его выполнять ваши команды или например просто приветствовать всех при их входе на канал?
Вы думаете это очень сложно и непонятно? Эта статья создана что бы развеять эти убеждения. Начнем с простого.
Написание простейшего скрипта на TCL
Команда bind. Вы конечно спросите - "Что же она делает и зачем нужна?" - вот ответ: Она сообщает боту что нужно ожидать определенное событие и как на него реагировать.
Синтаксис: bind <тип события> <флаги пользователя> <текст> <процедура, которую вызывать при данном событии>
Теперь поясним каждый пункт:
<тип события> - тип события, произошедшего на IRC (в чате) или в ботнете (ботнет - 2 или более слинкованных вместе ботов, об этом
чуть позже).
Например bind pub будет означать что ожидать определенной строки на канале. (о типах событий будет в продолжении)
<флаги пользорвателя> - у каждого пользователя на боте есть определенные внутренние флаги. (о флагах поговорим немного позже)
<текст> - на канале это может быть например !op или !kick, в некоторых событиях (напр. join или part) в этом месте идет хостмаска вида: "#канал ник!юзер@хост".
<процедура, которую вызывать при данном событии> - процедура (функция), которую вызывать при происхождении описанного события.
Пример:
bind pub - !beer pub:beer
Эта команда будет ожидать строку, в начале которой идет !beer и вызовет функцию pub:beer
Теперь мы создадим процедуру pub:beer. Как? - Все очень просто - командой proc.
Команда: proc
Синтакис: proc {<параметры>} { <тело процедуры> }
Поясним:
<параметры> - это параметры, которые нужны этой процедуре для нормальной работы <тело> - это тело самой процедуры
Пример:
proc pub:beer {nick uhost hand chan text} {<тело>}
Почему именно эти параметры? - спросите вы. Отвечу - именно эти параметры возвращает bind с типом pub. Сейчас коротко опишем каждый:
nick - ник человека, который произнес данную фразу uhost (userhost) - хост пользователя вида "user@host.domain.com" hand - имя, под которым пользователь записан на боте chan - канал, на котором была произнесена фраза text - текст, который был написан после строки, указанной в bind (напр. !beer всем)
Пишем тело процедуры
proc pub:beer {nick uhost hand chan text} {
putserv "privmsg $chan : Держи пиво $nick!"
}
Команда: putserv Cинтаксис: putserv "текст, отправляемый на сервер"
Пример:
putserv "privmsg $chan : Держи пиво $nick!"
Данная команда отправит на сервер строку "privmsg $chan :Держи пиво $nick"
Где $chan - переменная, содержащая имя канала, а $nick - переменная, содержащая ник.
Что же мы получим, соединив все что тут описано?
Код на TCL:
bind pub - !beer pub:beer
proc pub:beer {nick uhost hand chan text} {
putserv "privmsg $chan :Держи пиво $nick!"
}
Событие на IRC:
<юзер_Вася> !beer
<бот> Держи пиво юзер_Вася!
Как подключить скрипт к боту?
Подключение готового скрипта к боту:
Команда: source Синтаксис: source
Пример:
source scripts/beer.tcl
Сохраним наш скрипт в папку scripts в папке с ботом и этой командой подключим его к боту.
Файлы скриптов должны быть написаны в формате text-only, т.е. не должны содержать никакой
разметки как в MS Word и т.п. Создавать их лучше всего блокнотом (notepad) или vim под UNIX.
Несколько полезных советов:
Создайте файл scripts.conf в папке с ботом и прописывайте все нужные скрипты оттуда. Не забудьте подключить его командой source scripts.conf в конфиге бота (в самом низу) Для чего? - А для того чтобы не листать целиком здоровый конфиг с кучей коментариев (при медленной связи по ssh или ftp, и просто неудобно).
Усложнение нашего скрипта
Возьмем наш скрипт:
bind pub - !beer pub:beer
proc pub:beer {nick uhost hand chan text} {
putserv "privmsg $chan :Держи пиво $nick!"
}
И научим его молчать на каналах, где скрипт не нужен. Сделаем мы это с помощью оператора if
Синтаксис: if {<условие>} {<выполняемое действие>}
Пример:
if {1==1} { putlog "верно" }
Что за putlog? Это команда, которая пишет в лог бота указанный текст. Этот текст также идет в DCC (т.н. патилайн бота). Используется в целях отладки.
Синтаксис: putlog "text"
Пример:
putlog "текст, который пойдет в лог"
Операторы условий:
== - строго равно
!= - строго НЕ равно
>= - больше, либо равно
<= - меньше, либо равно
В одной строке if можно указывать несколько условий, объединяя их знаками:
&& - И
|| - ИЛИ
Пример:
if {{$var1==$var2} && {$var3==$var1}} { <действие> }
Вернемся к нашему скрипту:
Мы допишем в него строку if {$chan=="#MyChannel"} {}
Получим:
bind pub - !beer pub:beer
proc pub:beer {nick uhost hand chan text} {
if {$chan=="#MyChan" } {
putserv "privmsg $chan :Держи пиво $nick!"
}
}
Теперь бот будет откликаться на !beer только на канале #MyChan
Для принятия изменений в силу достаточно команды rehash
(В патилайне наберите .rehash)
Пишем скрипт приветствия на канале.
Как нам обработать событие захода на канал? Незнаете? А bind нам зачем по-вашему? Вот опять мы и встетились с этой командой (она будет встречаться нам очень часто).
bind join <флаги пользователя> <маска> <процедура>
<флаги пользователя> - флаги пользователя на боте
<маска> - маска вида "#канал ник!юзер@хост.домен" и может содержать знаки маски (как в бане * или ?) например
"#MyChan *@*" будет означать "любой юзер с любым хостом, который зашел на канал #MyChan" <процедура> - процедура, которая будет вызвана при происхождении данного события
Переходим к написанию скрипта:
bind join - "#MyChan *@*" join:autohi
proc join:autohi {nick uhost hand chan} {
global botnick
if {$nick != $botnick} {
putserv "notice $nick :$nick привет! Добро пожаловать на $chan!"
}
}
Заметели незнакомую команду? Правильно. Команда global. Что она делает? И что за botnick?
Объясним:
Команда: global
Синтаксис: global <переменная1> [<переменная2> <переменная3> <переменнаяN>]
Что же она делает? - Объявляет данной процедуре что эти переменные надо брать из глобальной области
Теперь про botnick: Это переменная глобальной области, содержащая ник бота.
Теперь, когда все вроде ясно, вернемся к нашему скрипту.
Строка bind join - "#MyChan *@*" join:autohi говорит боту что надо ожидать заход на канал #MyChan любого пользователя
и передавать управление процедуре join:autohi.
Строка global botnick сообщает боту что для этой процедуры надо взять переменную botnick из глобальной области.
Усложнение данного скрипта.
Все конечно хорошо. Бот приветствует всех входящих, все работает, НО приветствие однообразное и немного
надоедает потом. Чтоже делать? Менять почаще строку с приветствиями? - Нет, это плохое решение этой проблемы.
Мы пойдем другим путем.
Команда: switch
Синтаксис: switch <переменная> {
<возможное значение переменной(1)> { <выполняемое действие> }
<возможное значение переменной(2)> { <выполняемое действие> }
default { <выполняемое действие если выше описано небыло> }
}
Пример:
switch $somevar {
1 { putlog "Переменная somevar равна 1" }
2 { putlog "Переменная somevar равна 2" }
default { putlog "Переменная somevar не равна ни 1 ни 2" }
}
Можно сказать что команда switch заменяет многочисленные if (если вариантов 5 или более, то количество if при неправильном использовании может шокировать) И возьмем для нашего скрипта функцию rand. Из названия понятно (может не всем, но это не смертельно) что функция будет возвращать случайное число (random)
Команда: rand
Синтаксис: rand <макс. значение>
Пример:
set myvar [rand 10]
В переменную myvar будет положено число от 0 до 10
Вернемся к нашему скрипту и немного преобразуем его:
bind join - "#MyChan *@*" join:autohi
proc join:autohi {nick uhost hand chan} {
global botnick
if {$nick!=$botnick} {
switch [rand 5] {
0 { set message "Привет $nick! Добро пожаловать на $chan!" }
1 { set message "Привет $nick!" }
2 { set message "Добро пожаловать на $chan!" }
default { set message "Wenlcome on $chan! $nick!" }
}
putserv "notice $nick :$message"
}
}
Эта часть скрипта готова.Бот приветствует юзверей и наливает им пива.Теперь разберёмся с флагами пользователей.
Работаем с флагами пользователей.
...Вот сидите вы спокойно и пьете пиво, тут влетает какой-то урод и начинает рекламить и флудить, или просто всех оскорблять. Все решается просто. - !ban Ник_чела Причина Вроде все просто, только как узнать можно ли данному пользователю использовать эту команду? Для этого есть флаги доступа. Ознакомиться с ними можно тут.
Набросаем небольшой скрипт:
bind pub - !ban pub:ban
proc pub:ban {nick uhost hand chan text} {
if [[matchattr $hand |o $chan] || [matchattr $hand o]]
set bannednick [lindex $text 0]
set reason [lreplace $text 0 0]
newchanban $chan *![getchanhost $bannednick] $hand $reason
putserv "kick $chan $bannednick :$reason"
}
}
Команда: matchattr
Синтаксис: matchattr <пользователь> <флаги> [канал]
Эта функция возвращает 1 если проверяемые флаги у пользователя есть и 0 - если нет.
Пример:
matchattr User |o #test - проверяем есть ли у пользователя канальный флаг +o для канала #test matchattr User o - проверяем есть ли у пользователя глобальный флаг +o
Команда: lindex
Синтаксис: lindex "текстовая строка" <какое слово возвратить>
Функция возвращает N-ое слово из строки. Отсчет ведется с нуля и с левого края
Пример:
lindex "всякие разные слова" 0 - возвратит "всякие" lindex "всякие разные слова" 1 - возвратит "разные"
Команда: lreplace
Синтаксис: lreplace "текстовая строка" <первый> <последний> [еще слова]
Вот несколько примеров:
.tcl lreplace "q w e r t y" 0 0 Tcl: w e r t y .tcl lreplace "q w e r t y" 0 0 w Tcl: w w e r t y .tcl lreplace "q w e r t y" 0 0 a s d Tcl: a s d w e r t y .tcl lreplace "q w e r t y" 1 0 a s d Tcl: q a s d w e r t y (.tcl - ввели команду; Tcl: вернула команда)
Команда: newchanban
Синтаксис: newchanban <#канал> <маска бана> <создатель> <причина> [время жизни] [опции]
Команда создает бан на канале и кикает с причиной бана. Параметр [время жизни] указывается в секундах. Если параметр не указан, время будет взято из переменной ban-time В параметре [опции] можно указать "stickly" - эта опция означает что бан будет удерживаться на канале если канальная опция dynamicbans включена. (+dynamicbans)
Пример:
newchanban #mychannel *!*@*.microsoft.comBart-wht- "Я нИнАвИжУ мЕкРАсОФт :)"
Вот сидит теперь бот на канале, всех приветствует, желающим - пива наливает, за порядком следит. Вот мирное общение. Сидите, общаетесь... Захотелось пошутить - поговорить от имени бота. в DCC лезть не хочется... Что делать? А вот сейчас и узнаем :)
Работаем с приватами.
Набросаем небольшой скрипт:
bind msg n !say msg:say
proc msg:say {nick uhost hand text} {
putlog "$nick!$uhost ($hand) !say $text"
set to [lindex $text 0]
set text [lreplace $text 0 0]
putserv "privmsg $to :$text"
}
Вы в привате говорите боту !say <куда говорить> <что говорить>
<куда говорить> - ник или канал, куда бот должет сказать данную фразу. Если будет ник, то бот напишет в приват,
если канал - бот скажет на канал.
<что говорить> - тест, который бот должен сказать
Пример1:
Приват бота: <владелец> !say Вася Привет Васек! Приват Васи: <Бот> Привет Васек!
Пример2:
Приват бота: <владелец> !say #канал Всем привет! Канал #канал: <Бот> Всем привет!
Вообще скрипт можно написать в пару строк :)
bind msg n !say msg:say
proc msg:say {nick uhost hand text} { putlog "$nick!$uhost ($hand) !say $text"; set to [lindex $text 0]; set text
[lreplace $text 0 0]; putserv "privmsg $to :$text" }
Можно и короче, но это будет полный изврат :)
bind msg n !say msg:say; proc msg:say {nick uhost hand text} { putlog "$nick!$uhost ($hand) !say $text"; set to
[lindex $text 0]; set text [lreplace $text 0 0]; putserv "privmsg $to :$text" }
Но так менее понятно что куда и зачем. Так стоит делать в очень редких случаях, а лучше не делать так :)
Мутим обратную связь.
...Вот поболтали вы от бота, посмеялись над всеми, потомучто только глобальный владелец (global owner) (См. описание флага +n) может пользоваться этим. Одно только неудобно - не видно что боту отвечают в приват. Набросаем еще один скрипт посложнее:
# *!*!*!*: Для работы этого скрипта необходим EggDrop/WinDrop с опцией HANDLEN 32 и пользователь dynamicvariables_ :*!*!*!* #
bind msg n !say msg:say
proc msg:say {nick uhost hand text} {
putlog "$nick!$uhost ($hand) !say $text"
set to [lindex $text 0]
set text [lreplace $text 0 0]
putserv "privmsg $to :$text"
}
- Функция, которая будет возвращать кому редиректить. Можно было и не создавать ее но так ИМХО удобнее.
proc get:redirect:msg {nick} { return [getuser dynamicvariables_ xtra "redirmsg_from_$nick"] }
bind msgm - * msgm:redirect
proc msgm:redirect {nick uhost hand text} {
if {[tolower [lindex $text 0]]!="pass" && [tolower [lindex $text 0]]!="auth" && [tolower [lindex $text 0]]!="die" &&
[tolower [lindex $text 0]]!="ident"} {
set to [get:redirect:msg $nick]
if {[matchattr $to QB]} { putserv "privmsg [hand2nick $to] :<$nick> $text" }
}
}
bind msg n !redirect set:msg:redirect
proc set:msg:redirect {nick uhost hand text} {
putlog "!regirect $text from $nick!$uhost ($hand)"
set from [lindex $text 0]
set set [lindex $text 1]
if {$set=="on"} { setuser dynamicvariables_ xtra redirmsg_from_$from $hand; putserv "privmsg $nick OK, redirect privmsg from
$from set to $hand" }
if {$set=="off"} { setuser dynamicvariables_ xtra redirmsg_from_$from ""; }
}
Совсем другое дело .Видно что отвечают боту, можно даже общаться через него. На самом деле писать скрипты просто, главное - это желание и капелька фантазии. Оригинал статьи можно почитать кликнув по этой ссылке