Структура документа: 

Описание

Вступление

Теория

Функции для пользовательских скриптов:

Команды для работы в partyline

  1. .uafs

Константы

Прочие настройки

Примеры  

Wrapper

DESCRIPTION - Описание UAFS.tcl - Universal Anti-Flood Script

Версия: beta 8

Скрипт UAFS.tcl for eggdrop представляет собой средство для контроля количества событий в определенный промежуток времени. Применительно к eggdrop'y, события, как правило, представляют собой команды пользователя, посланные боту, хотя в общем случае это могут быть и joins/parts/nick changes/all messages.

Вступление:

Рассмотрим пример:есть скрипт который выдает новости с neowin.net по запросу пользователя.Доступ к скрипту имеют все пользователи данного канала.Легко представить себе ситуацию когда кучка хакеров-полуросликов решает завалить бота запросами которые он и будет обслуживать не жалея своих сил (встроенный антифлуд не сработает - маски то разные!). В этом случае имеются как минимум 2 фактора из-за которых хотелось бы ввести ограничение на пользование скриптом по временному признаку,то есть по частоте:

  1. При обращении к ресурсу,бот принимает/посылает определенное количество информации с сервера/на сервер,что выражается в количестве потребленного траффика и нагрузке на канал [передачи данных],в некоторых случаях это может быть критично, если, к примеру, бот сидит на медленном канале [передачи данных].
  2. Если бот выдает новости на канал, то во-первых, он создает флуд на канале, во-вторых забивает свою очередь сообщений, которая, как известно, по умолчанию не резиновая ибо бот защищает себя от excess flood'a (это применимо и к приватным сообщениям/нотисам). В следствии этого остальные функции бота могут быть недоступны, грубо говоря получается маленький DoS. Несмотря на то что данный вопрос намного шире и частично решается подгонкой конфигурации ircd тем или иным образом под конкретного бота, проблему он показывает ясно.

Для предотвращения этих а также других нежелательных ситуаций и необходимо вводить ограничения,но к сожалению, по умолчанию данную функциональность eggdrop не поддерживает, поэтому на свет появился UAFS.tcl, призванный упростить жизнь другим разработчикам и избавить от изобретания колеса.

Ну и как вся эта хуйня [censored] работает? Как бля [censored] её юзать нах [censored]?  (вместо введения :)

Поразмышляв некоторое время над схожим вопросом,только вместо "работает" было - "будет работать", я пришел к выводу что необходимо реализовать внешний интерфейс, или, если угодно внешнюю функцию, которая и будет проверять есть флуд,али нет. Но как внешняя функция узнает, по какому критерию нужно проверять? Сколько событий и за какой промежуток времени можно допустить? Правильно,нужно ей об этом сообщить, то есть вызвать ее с параметром задающим легетимное кол-во событий в определенный отрезок времени. Хорошо, с этим определились,но вот незадача - пользователи идентифицируются минимум тремя параметрами в виде nick!ident@host а не только nick'om как нам бы хотелось, значит надо еще и по маске сравнивать? а если сравнивать по маске,значит уже имеющиеся данные о масках надо где-то хранить? да и получать их нужно, лишние строчки кода писать в нашей локальной функции вызванной триггером. То есть мы сами получаем,запоминаем храним, а внешняя функция только проверяет на временной критерий? Хм...ну ладно,пускай так будет, но что делать с другой проблемой - если использовать ограничение более чем в одном месте (более чем одной локальной процедуре ) да еще и с разными критериями - как внешняя функция сможет различить кто желает от нее проверки? Легче тогда уж хранить все данные в своей процедуре,в ней же и проверять, а на внешную хуй [censored] забить, нахуй [censored] она сдалась. Но получаем изобретение колеса - каждый будет все реализовывать по-своему и еще раз по-своему и еще раз по-своему...кто-то скажет - ну тык,йопт, пусь реализовывает если его чиста прет и вставляет,на что вполне резонно можно заметить -  а нахуйа [censored] ему сдался eggdrop & tcl - писал бы своего бота и свой язык, раз его так чиста прет, в общем не наш это метод Федя,не наш. Как написанно в умной книжке Dan'a Appelman'a по VB.Net - повторное использование кода - мантра программиста, что и так ясно, если вы канешна настоящий программист по убеждению - то есть обладаете достаточным запасом логики и лени. В общем, такого рода рассуждения завели меня в небольшой тупик - вроде и надо людям помоч,а вроде как хуйня [censored] выходит. К моему удивлению, выход нашелся довольно быстро - повспоминав Win32 API, я пришел к выводу что решением может послужить использование handlers ( по-русски "манипуляторов", всё из той же умной книжки ) для однозначной идентификации настроек флуд контроля...допустим, регистрируются настройки флуда, флагов игнорирования, строгости проверки, способ сравнения масок в другой внешней функции, она отдает нам какой-либо идентификатор который в дальнейшем и используется, и пусть сама она при проверке все это дело извлекает, вычисляет,проверяет, на то она и отдельная функция. Такая идея мне понравилась (не знаю как вам), поэтому все так и реализованно. Предвижу вопрос - ну и нахуй [censored] нужно было всё это расписывать да еще и на столько строчек - и,  предвидя, отвечаю - дабы объяснить кажущуюся излишнюю трудность использования скрипта, которой очень часто приходится платить за расширение функциональности и гибкости конечного решения. 

Теперь,когда ты, уважаемый читатель, подкован в теоретическом смысле, перейдем к практической реализации сией гениальной задумки. Итого, скрипт реализует несколько функций (в квадратных скобках необязательные параметры):

Функции для пользовательских скриптов:

  1. Первая и самая главная:

    Функция registerhandle производит регистрацию уникального идентификатора ( ufh ) с настройками флуд контроля.
    registerhandle floodsettings [hostmask type] [ignore flags] [callback_function] [strict mode]

    Parameters:

    Обязательным параметром является только floodsettings в формате times:seconds то есть, сколько раз (times) в течении промежутка времени seconds (в секундах) может происходить определенное событие.

    Если
    тип хостмаски не указан, ей будет присвоено значение по умолчанию ( masks(host) ).

    ignore flags - флаги при наличии которых функция isflood будет игнорировать пользователя и возвращать 0.Флаги имеют стандартный формат eggdrop'a. Если флаги пользователя совпадают с ignore flags функция не заполняет массив временных меток и не создает запись о пользовательской маске

    Callback function
    , тип маски хоста, флаги игнорирования как и strict mode являются необязательными.
    Функция обратного вызова представляет собой имя функции, которая будет вызвана в случае возникновения флуда.
    Если
    функция обратного вызова не указана, она не будет вызвана при возникновении флуда. В зависимости от настройки параметра checkforcallback функция может возвращать ошибку если callback function не существует.

    параметр strict mode определяет, как функция isflood будет обрабатывать проверки на флуд,после того как флуд обнаружен
    . По умолчанию выставляется в 1,то есть после того как функция определила флуд, требуется чтобы истек интервал времени заданный в floodsettings. 
    Например:
    floodsettings = 5:30, пользователь превышает этот лимит, был kick'нут с канала и зашел обратно и инициировал проверку на флуд (например использовал команду бота), функция isflood проверит истекли ли 30 секунд после того как данный пользователь был помечен как флудер. Если этот промежуток времени еще не истек, функция isflood возвратит 1,то есть флуд обнаружен.  На внутреннем уровне это реализованно через заполнение массива временных меток ( timestamps ), т.е. по умолчанию после обнаружения флуда данный массив полностью забивается временем когда произошел флуд.
    Если strictmode установлен в 0, массив содержит штампы последовательных срабатываний и интервал времени считается как разность между временем  последнего события и первого.

    Возвращаемые значения:
    Значения больше 0 - идентификатор ufh, функция зарегистрировала манипулятор успешно.

    Отрицательные значения возвращаются в случае каких-либо ошибок.

    Расшифровка кодов ошибок:
     -1 - общая ошибка,в текущей версии не используется
     -2 - ошибка в функции обратного вызова - не существует ( возвращается если checkforcallback выставлено в 1 )
     -3 - ошибка в формате floodsettings
     -4 - ошибка в формате hostmask type - такой тип не определен
     -5 - ошибка в формате strictmode

    примеры внизу
  2. unregisterhandle

    Функция unregisterhandle уничтожает заданный манипулятор ( ufh ), удаляя все временные метки и маски пользоватей связанные с этим ufh .

    unregisterhandle ufh

    Parameters: ufh - идентификатор настроек полученный из registerhandle

    Возвращаемые значения:

    1 - идентификатор успешно удален.
    -11 - ошибка, такой идентификатор не существует.

  3. isflood
    Функция isflood производит проверку на флуд

    isflood
    ufh nick uhost [chan] [ignore flags]

    Parameters:
    ufh  - идентификатор настроек полученный из registerhandle 

    nick -
    псевдоним пользователя

    uhost -
    хост пользователя в формате ident@hostname

    chan -
    имя канала. Опциональный параметр, кроме того chan не обязательно должен быть именем реального канала, т.к. функция никаких действий по отношению к пользователю и каналу не предпринимает (если не задан параметр ignore flags). Данный параметр является своего рода идентификатором. По умолчанию равен all,  то есть если проверяются команды/private query/notices и параметр chan опущен,  события будут регистрироваться на виртуальный канал all и при вызове callback function передается так же all !

    ignore flags -
    флаги при наличии которых функция будет игнорировать проверку и возвращать 0.Флаги имеют стандартный формат eggdrop'a. Если флаги пользователя совпадают с ignore flags функция не заполняет массив временных меток и не создает запись о пользовательской маске. Обратите внимание, что флаги переданные при вызове isflood имеют больший приоритет чем флаги указанные в registerhandle !

    Возвращаемые значения:
    1 если флуд обнаружен
    0 если флуд не обнаружен

    В случае ошибки возвращаются отрицательные значения

    Расшифровка кодов ошибок:
     -21 - такой ufh не существует

    -22 - ошибка при создании маски - неверный формат uhost  либо nick
    примеры внизу

  4. peacetime
    Функция peacetime определяет интервал времени ( в секундах ) по истечении которого isflood вызванная с теми же параметрами ( ufh nick uhost chan ) вернет 0, т.е. с помощью этой функции можно определить через какой промежуток времени событие должно произойти чтобы не быть расцененным как флуд, полезно для уведомления пользователей о том сколько нужно подождать чтобы снова воспользоваться ботом.

    peacetime
    ufh nick uhost [chan]

    Parameters:
    ufh  - идентификатор настроек полученный из registerhandle 

    nick -
    псевдоним пользователя

    uhost -
    хост пользователя в формате ident@hostname

    chan -
    имя канала. Опциональный параметр, кроме того chan не обязательно должен быть именем реального канала, т.к. функция никаких действий по отношению к пользователю и каналу не предпринимает (если не задан параметр ignore flags). Данный параметр является своего рода идентификатором. По умолчанию равен all,  то есть если проверяются команды/private query/notices и параметр chan опущен,  события будут отнесенны к виртуальному каналу all.

    Возвращаемые значения:
    Время в секундах

    В случае ошибки возвращаются отрицательные значения

    Расшифровка кодов ошибок:
     -31 - такой ufh не существует

    -32 - ошибка при создании маски - неверный формат uhost  либо nick

    примеры внизу
  5. formatmessage
    Функция возвращает описание ошибки по ее числовому коду

    formatmessage code
    Parameters:

    code -
    код ошибки возвращенный одной из предыдущих функций, если такого кода не определенно, возвращается 
    "there is no message for such error code"

  6. gethandleinfo
    Функция возвращает набор данных о манипуляторе ufh которые были использованны при регистрации манипулятора


    gethandleinfo ufh [type]

    Parameters:
    ufh - идентификатор манипулятора
    type - тип получаемых данных, может быть одним из:

    В случае ошибки возвращаются отрицательные значения

    Примечание:
    Проверку возвращаемых значений рекомендуется делать следующим образом
    (если, конечно, вы вообще делаете обработку ошибок) :
       set hinfo [::uafs::gethandleinfo $ufh]
       if {!([string is integer $hinfo] && $hinfo < 0)} {
            #info was recieved successfully
            #some actions here
       } else {
            putlog "error: can't get info for handle \"$ufh\""
            putlog "error code is \"$hinfo\", message is:[::uafs::formatmessage $hinfo]"
       }

    Расшифровка кодов ошибок:
     -
    51 - такой ufh не существует

  7. createmask

    Функция создаёт маску из nick и uhost используя masktype для определения вида маски.

    createmask  nick uhost masktype

    Parameters:
    nick - псевдоним пользователя
    uhost - хост пользователя в формате ident@hostname
    masktype - тип хостмаски

    В случае ошибки возвращаются отрицательные значения

    Расшифровка кодов ошибок:
     -41 - такого типа маски (masktype) не существует

    -42 - неверный формат uhost
    -43 - неверный формат nick

    Примечание:
    Проверку возвращаемых значений рекомендуется делать следующим образом
    (если, конечно, вы вообще делаете обработку ошибок) :
       set ufhmask [::uafs::createmask $nick $uhost $::uafs::masks(host)]
       if {![string is integer $ufhmask]} {
            #mask was created ok
            #some actions here
       } else {
            putlog "error: can't create hostmask"
            putlog "error code is \"$ufhmask\", message is:[::uafs::formatmessage $ufhmask]"
       }
  8. checksoftcounter

    Функция возвращает значение счетчика "мягких" (soft) флудов
    Под "мягким" флудом подразумевается серия из последовательных срабатываний функции isflood, которая при отсутствии флуда будет обнулена, то есть этакие штрафные очки, которые при непрерывном флуде увеличиваются, но если следующие событие  флудом не является, будут обнулены. Проще всего это пояснить на примере:
    Допустим, есть скрипт который банит тех кто много  слапает (slaps), к примеру больше 3х раз в минуту, и, допустим, мы не хотим банить после первого же слапа, а хотим сначала вывести предупреждение, потом кик, а затем уже бан. То есть по сути, пользователь должен нафлудить 3 раза чтобы быть забаненым,  нужно считать количество флудов относительно данного пользователя, и при этом если слап не был флудом,(пользоваталь следующий раз слапал через 5 минут), сбрасывать счетчик. Это и есть "мягкий" флуд, который сбрасывается после серии событий в 0. Если же не сбрасывается, то это уже "hard" flood  то есть общее количество раз когда событие было расцененно как флуд.

    Внимание!
    На результат работы этой функции, так же как и isflood, может значительно влиять параметр strict mode, учитывайте это при регистрации манипулятора!

    checksoftcounter
    ufh nick uhost  [ chan ]

    Parameters:

    ufh  - идентификатор настроек полученный из registerhandle 

    nick -
    псевдоним пользователя

    uhost -
    хост пользователя в формате ident@hostname

    chan - имя канала, правила те же самые что и для isflood

    Возвращаемые значения:
    Количество непрерывных последовательностей флуда на текущий момент. Как правило следует употреблять после проверки на флуд функцией isflood, хотя может быть вызвана в любой момент.

    Небольшой пример из скрипта wtf.uafs.tcl:

    set counter [::uafs::checksoftcounter $ufh $nick $uhost $chan]
    if {$counter<0} {
       #some error occured
       putlog "error while getting counter value, error code \"$counter\", description:[::uafs::formatmessage $counter]"
    } elseif {$counter > 0} {
       switch  -exact -- $counter {
           "1" {
               #1st time - warning
               putserv "NOTICE $nick : You are flooder $nick, calm down and wait for [duration [::uafs::peacetime $ufh $nick $uhost $chan]]"
           }
           "2" {
               #2nd time - kicking
                #является ли бот опом или халфопом?
                if {[botisop $chan] || [botishalfop $chan]} {
                    #раз да,то кикаем.
                    putserv "kick $chan $nick :Stop flooding me with \"$lastbind\""
                }
           }
           default {
               #3rd & other times - banning
               set mask [::uafs::createmask $nick $uhost [::uafs::gethandleinfo $ufh "hostmask"]]
               newchanban $chan $mask ${botnet-nick} "flooded me with \"$lastbind\"" $bantime
           }
       }
    }

    В случае ошибки возвращаются отрицательные значения
    Расшифровка кодов ошибок:
    -61 - такой манипулятор не зарегистрирован.

    Остальные коды ошибок возвращаются другими функциями, например createmask или isflood поэтому formatmessage может возвращать описание ошибок из других функций.

  9. checkhardcounter

    Функция возвращает значение счетчика "твердых" (hard) флудов - описание soft & hard floood

    checkhardcounter
    ufh nick uhost  [ chan ]

    Parameters:

    ufh  - идентификатор настроек полученный из registerhandle 

    nick -
    псевдоним пользователя

    uhost -
    хост пользователя в формате ident@hostname

    chan - имя канала, правила те же самые что и для isflood

    Возвращаемые значения:
    Общее количество срабатываний функции 
    isflood на текущий момент. С течением времени только увеличивается.
    Как правило следует употреблять после проверки на флуд функцией isflood, хотя может быть вызванна в любой момент.

    В случае ошибки возвращаются отрицательные значения
    Расшифровка кодов ошибок:
    -71 - такой манипулятор не зарегистрирован.

    Остальные коды ошибок возвращаются другими функциями, например createmask или isflood поэтому formatmessage может возвращать описание ошибок из других функций.

Команды для работы в partyline
  1. uafs


    Константы:
  1. Типы хостмасок

    masks(fullmask) -
    полная маска вида nick!ident@hostname.
    masks(nickhost) - маска вида nick!*@hostname.
    masks(identhost) - маска вида *!ident@hostname.
    masks(host) - маска вида *!*@hostname.

    masks(all) - маска вида *!*@*.


  2. Коды ошибок
            "-1" "Register handle: general errror"
            "-2" "Register handle: callback function doesn't exist"
            "-3" "Register handle: floodsettings have wrong format"
            "-4" "Register handle: such hostmask type is not supported or not defined"
            "-5" "Register handle: strict mode setting format error"
            "-11" "Unregister handle: such ufh doesn't exist"
            "-21" "isflood: no such ufh exists"
            "-22" "isflood: error while creating usermask,check nick & uhost values"
            "-31" "peacetime: no such ufh exists"
            "-32" "peacetime: error while creating usermask,check nick & uhost values"
            "-41" "CreateMask: there is no such hostmask type"
            "-42" "CreateMask: uhost variable is not valid"
            "-43" "CreateMask: nick variable is not valid"
            "-51" "gethandleinfo: no such ufh exists"
            "-61" "checksoftcounter: no such ufh exists"
            "-71" "checkhardcounter: no such ufh exists"

Прочие настройки:

  1. checkforcallback
    В случае если checkforcallback выставлено в 1 и callback function переданная в registerhandle не существует, registerhandle возвратит ошибку. По умолчанию стоит в 0, изменяется путем правки скрипта.

  2. debugmode
    Определяет, будет ли скрипт работать в режиме отладки или нет, возможные варинаты значений: 1 и 0

  3. debuglevel
    Определяет уровень выдачи отладочной информации - чем больше уровень, тем больше информации будет выдавать скрипт.
    Возможные значения на текущий момент 1 и 2

Примеры:

  1. Самый простой
    #example tcl for uafs
    namespace eval beer {

    #registering handle 
    set beer_ufh [::uafs::registerhandle "3:40"]
    #is all okay?
    if {$beer_ufh<0} { putlog "unable to register handler";die "unable to register handler beer_ufh"}
    #putlog $beer_ufh

    proc beerproc { nick uhost hand chan rest } {
    variable beer_ufh;global lastbind
    set bla [::uafs::isflood $beer_ufh $nick $uhost $chan]
    if {$bla=="1"} {
    set piu [::uafs::peacetime $beer_ufh $nick $uhost $chan]
    putserv "NOTICE $nick :You are flooder! next time u may use $lastbind after [duration $piu]"
    } else {
    putserv "PRIVMSG $chan :here is your beer $nick"
    }
    }
    }

    bind pub - "!beer" ::beer::beerproc
    putlog "example tcl for uafs loaded"
  2. example4.tcl for uafs, updated for uafs beta 6
    задача:                                                                   
    Привет! Можешь помочь с одним скриптом, пожалуйста.                       
    Когда кто-нибудь ввёл воторой раз в течение 5 минут "!кручу", бот говорил,
    чтобы подождал 5 минут, если он начинает флудить: предпреждение, 2 кика,  
    потом бан на 1 минуту!                                                    
    uafs.example4.tcl
  3. wtf.uafs.tcl 
    version 1.0
    works with wtf tool from bsdgames package & shows acronyms meanings
    показывает значения акронимов, например AFAIK IMHO BRB и так далее. Существует возможность задавать каналы где будет работать при помощи .chanset #chan +wtf.

    wtf.uafs.tcl
  4. antislap.uafs.tcl version 1.1
    Смысл данного скрипта в том, чтобы ограничить использование популярного
    action (с применением форели) до, скажем, двух раз в минуту, с
    предупредительным киком и баном злоупотребляемых лиц (c) Red Pepper
    antislap.uafs.tcl