--[[ Антиреклама для PtokaX. Авторы: Damaks и nd Версия: 1.7 Совместимость: PtokaX 0.5, Lua 5.2, API2 - Наиболее эффективный и понятный скрипт антирекламы для PtokaX. - Сканирует чат и личку. - Запрещённые слова ищутся по универсальным шаблонам, что во много раз уменьшает количество примеров рекламных фраз. - Регистр не важен, www и wWw фильтруются одинаково (самый эффективный и компактный на сегодняшний день алгоритм преобразования кодировки cp1251 в нижний регистр). - Не существует проблемы с обходом защиты благодаря оптимальному поисковому алгоритму (распространённая проблема в большинстве скриптов, случается при нахождении в одном сообщении заперещённой фразы и исключения). - Ложные срабатывания сведены к минимуму и практически исключены. - Раздельные функции сканирования чата и лички (выбрать нужный режим можно изменением одной цифры в скрипте). - Приличный выбор самых необходимых режимов наказания: возможность бана на заданное время (задаётся в скрипте), разъединения (сообщение юзеру о рекламе и disconnect), просто замены запрещённой фразы (сообщение с рекламой заменяется информационным, его видит только отправитель), тихого фильтрования (отправитель думает, что всё нормально и сообщение доставлено, в личке и общем чате же оно не появляется) или просто информирование операторов о поступаемых ссылках. - Возможность информирования операторов о попытке рекламы с показом запрещённой фразы (если хотите знать, кто что отсылает). - При рекламе в личку показывает получателя сообщения, что даёт больше информации. - При отправлении сообщения профилю-исключению (tProfiles) оно не фильтруется. - Минимальный размер сообщения для проверки снижает нагрузку. В скрипте есть подробные пояснения, которые помогут разобраться даже новичку в lua. ]] -------------------------------[ Preference ]------------------------------- local sBot = 'АнтирекламА' -- Имя бота local bot_reg = 1 -- Показывать бота в списке пользователей (1 - да, 0 - нет) local ban_time = 10 -- Время бана (мин) local op_info = 1 -- Информировать о попытке рекламы операторов с показом запрещённой фразы (1 - да, 0 - нет) local min_size = 8 -- Минимальная длина сообщения, с которого начинается фильтрование local tProfiles = { -- Профили пользователей, которым разрешена реклама (1 - разрешена; 0 - запрещена) [0] = 1, -- Мастер [1] = 1, -- Оператор [2] = 1, -- VIP [3] = 0, -- Зарегистрированный пользователь [-1] = 0, -- Незарегистрированный пользователь } local block_all_mode = 2 --[[ Наказание за рекламу в главном чате: 0 - временный бан на время, указанное в переменной BanTime; 1 - разъединение; 2 - замена сообщением block_message[1]; 3 - тихое фильтрование (сообщение видит только отправитель). 4 - без фильтрования (если включено информирование ОПов, то им просто будут отправляться ссылки. ]] local block_pm_mode = 2 --[[ Наказание за рекламу в личке: 0 - временный бан на время, указанное в переменной BanTime; 1 - разъединение; 2 - фильтрование и сообщение block_message[1] в личку от бота; 3 - тихое фильтрование (сообщение видит только отправитель). 4 - без фильтрования (если включено информирование ОПов, то им просто будут отправляться ссылки). ]] local forbidden_patterns = { -- Шаблоны запрещённых выражений 'd.?c.?h.?u.?b.?:.?/.?/', 'd%p*c%p*h%p*u%p*b%p*:%p*/%p*/', 'd%s*c%s*h%s*u%s*b%s*:%s*/%s*/', -- Фильтрует "dchub://" и модификации 'h.?t.?t.?p.?:.?/.?/', 'h%p*t%p*t%p*p%p*:%p*/%p*/', 'h%s*t%s*t%s*p%s*:%s*/%s*/', -- Фильтрует "http://" и модификации 'w.?w.?w.?%.', -- Фильтрует "www." и модификации 'no%s?%-%s?ip', '%-%s?hub%s?%.%s?org', 'dyndns%s?%.%s?tv', '%.%s?ucoz%s?%.', -- Фильтрует наиболее "спамные" адреса без префиксов (*no-ip*, *-hub.org*, *dyndns.tv, *.ucoz.*) '%d+%.%d+%.%d+%.%d+', -- Фильтрует ip-адреса '%a%.ro%:?%d*%s+', '%a%.ro%:?%d*$', '%a%.ro%:?%d*/', -- Фильтрует .ro '%a%.ru%:?%d*%s+', '%a%.ru%:?%d*$', '%a%.ru%:?%d*/', -- Фильтрует .ru '%a%.ua%:?%d*%s+', '%a%.ua%:?%d*$', '%a%.ua%:?%d*/', -- Фильтрует .ua '%a%.net%:?%d*%s+', '%a%.net%:?%d*$', '%a%.net%:?%d*/', -- Фильтрует .net '%a%.com%:?%d*%s+', '%a%.com%:?%d*$', '%a%.com%:?%d*/', -- Фильтрует .com } local allowed_patterns = { -- Шаблоны разрешённых выражений 'magnet:%?xt%S*', -- Не проверять рекламу магнет-ссылки '%S*wikipedia%.org%S*%s*', '%S*youtube%.com%S*%s*', '%S*rutube%.ru%S*%s*', '%S*smotri%.com%S*%s*', '%S*radikal%.ru%S*%s*', '%S*fastpic%.ru%S*%s*', '%S*rghost%.ru%S*%s*', -- Шаблоны для сайтов '%S*rsload%.net%S*%s*', '%S*forum%.rsload%.net%S*%s*', '%S*95%.141%.193%.17%S*%s*', '%S*goldhub%.mydc.ru%S*%s*', '%S*89%.222%.185%.103%S*%s*', '%S*10.170.161.189%S*%s*', -- Шаблоны для хабов и ip } local block_message = { -- Стандартные сообщения 'Внимание! Реклама посторонних ресурсов на хабе категорически запрещена!', 'Внимание! Реклама посторонних ресурсов на хабе категорически запрещена! Вы были забанены на ', 'Внимание! Реклама посторонних ресурсов на хабе категорически запрещена! Вы были отключены.', ' реклама в общий чат: ', ' реклама в личку ', } ------------------------------[ Code Section ]------------------------------ function OnStartup() if bot_reg==1 then Core.RegBot(sBot,'','',true) end end -- Преобразует строку в кодировке cp1251 в нижний регистр function string.lower2(message) return message:gsub('([А-Я])', function(str) return string.char(str:byte()+32) end):gsub('Ё', 'ё'):lower() end -- Возвращает итератор, который, при каждом вызове, возвращает индексы начала и конца совпадения с шаблоном и захваченные значения (captures) local function gfind_patterns(string_, patterns) return coroutine.wrap(function () for _, pattern in ipairs(patterns) do local i = 1 while true do local results = {string_:find(pattern, i)} if not results[1] then break end i = results[2] + 1 coroutine.yield(table.unpack(results)) end end end) end -- "Поглощает" итератор и возвращает все полученные значения в виде массива local function collect_results(iterator) local all_results = {} while true do local results = {iterator()} if not results[1] then break end table.insert(all_results, results) end return all_results end -- Возвращает true если сообщение содержит части совпадающие с forbidden_patterns и не совпадающие с allowed_patterns, иначе возвращает false local function is_forbidden(message, forbidden_patterns, allowed_patterns) local allowed_indexes for start, end_ in gfind_patterns(message, forbidden_patterns) do if not allowed_indexes then allowed_indexes = collect_results(gfind_patterns(message, allowed_patterns)) end local forbid = true for _, indexes in ipairs(allowed_indexes) do if start >= indexes[1] and end_ <= indexes[2] then forbid = false break end end if forbid then return true end end return false end -- Блокирует запрещенные сообщения в чате function ChatArrival(user,data) local message = data:match('%b<>%s*(.+)|$') if message and tProfiles[user.iProfile]==0 then if #message >= min_size then message = string.lower2(message) if is_forbidden(message, forbidden_patterns, allowed_patterns) then if op_info==1 then Core.SendPmToOps(sBot, ('*** ['..user.sIP ..'] '..user.sNick..block_message[4]..message)) end if block_all_mode==0 then Core.SendToUser(user,'<'..sBot..'> *** '..block_message[2]..ban_time..' мин.') BanMan.TempBan(user,ban_time,'','',false) return true end if block_all_mode==1 then Core.SendToUser(user,'<'..sBot..'> *** '..block_message[3]) Core.Disconnect(user) return true end if block_all_mode==2 then Core.SendToUser(user, '<'..sBot..'> *** '..block_message[1]) return true end if block_all_mode==3 then Core.SendToUser(user, ('<%s> %s'):format(user.sNick, message)) return true end if block_all_mode==4 then end end end end return false end -- Блокирует запрещенные сообщения в личке function ToArrival(user,data) local message = data:match('%b<>%s*(.+)|$') local s,e,to,from,sNick,message = string.find(data, '%$To:%s(%S+)%sFrom:%s(%S+)%s$<(%S+)%>%s(.*)$') local tToUser = Core.GetUser(to) if tToUser and message and tProfiles[user.iProfile]==0 and tProfiles[tToUser.iProfile]==0 then message = string.lower2(message) if is_forbidden(message, forbidden_patterns, allowed_patterns) then if op_info==1 then Core.SendPmToOps(sBot, ('*** ['..user.sIP ..'] '..user.sNick..block_message[5]..'['..tToUser.sIP ..'] '..to..' : '..message)) end if block_pm_mode==0 then Core.SendToUser(user,'<'..sBot..'>*** '..block_message[2]..ban_time..' мин.') BanMan.TempBan(user,ban_time,'','',false) return true end if block_pm_mode==1 then Core.SendToUser(user,'<'..sBot..'>*** '..block_message[3]) Core.Disconnect(user) return true end if block_pm_mode==2 then Core.SendPmToUser(user, sBot,'*** '..block_message[1]) return true end if block_pm_mode==3 then return true end if block_pm_mode==4 then end end end return false end