Разметка фильтра злоупотреблений — специальный синтаксис, позволяющий указывать условия срабатывания фильтра злоупотреблений.
Описание[]
Фильтр злоупотреблений — расширение, позволяющее проверять действия участников на соответствие определенным условиям и таким образом защищать вики от потенциально вредоносного вклада. Администрация википроекта может создать сколь угодно много фильтров, а действия будут проверяться последовательно с каждым из активных условий.
Сами условия указываются на служебной странице «Настройка фильтра злоупотреблений» (Special:AbuseFilter) в специальном поле. В целом синтаксис условий напоминает соответствующие конструкции в языках программирования, подобных C, Java и Perl.
Синтаксис[]
Типы данных[]
Так же, как и во многих других языках, в разметке фильтра злоупотреблений используются обычные типы данных:
- Строки, ограниченные одинарными или двойными кавычками. Могут содержать escape-последовательности и некоторые спецсимволы (например, перенос строки
\n
и символ табуляции\t
); - Целые и дробные числа, как положительные, так и отрицательные. Дробная часть отделяется от целой символом точки;
null
, обозначающий отсутствующее значение определенной переменной;- Логические значения, принимающие только состояние истины (
true
) и лжи (false
). Ложными значениями помимоfalse
считаются ноль, пустой массив, пустая строка иnull
; - Массивы, которые могут содержать сразу несколько значений. О них будет отдельно упомянуто позже.
Вот несколько примеров с разными типами данных:
/* строки */
"Обычная строка"
'Строка\nс переносом'
'That\'s a string with escape'
165 -99 34.81 -0.02 /* числа */
true false /* логические значения */
/* массивы */
['abc', 'def', 'ghi']
[13, 6, 0, -4]
Как видно из примеров выше, в коде фильтра можно писать комментарии, заключая поясняющий текст в сочетание /* */
.
Массивы[]
Все массивы в разметке фильтра индексируются с нуля. У них нет никаких методов, а все операции совершаются с помощью определенных синтаксических конструкций.
Получение и изменение значения определенного элемента можно совершить, обратившись к нему по индексу: array[0]
возвратит первый элемент массива, а array[0] = 6
присвоит ему значение 6. Добавить новое значение в конец можно следующим образом: array[] := 6
. Здесь используется не знак равенства, а оператор присваивания :=
, подобный тому, что есть в языке Pascal.
Функции length()
и int()
определяют длину массива в виде целого числа, а float()
— в виде дробного. Например, int( [1, 3, 5, 7] ) == 4
. string()
превращает массив в строку, разделенную символами переноса (на конце по ряду причин также будет такой символ): string( [1, 2, 3] ) == "1\n2\n3\n"
.
Оператор in
позволяет проверить, есть ли данный элемент в массиве: 4 in [1, 4, 8] == true
. Однако при работе с массивами необходимо помнить, что в синтаксисе фильтра злоупотреблений все массивы сводятся к строкам, где каждый элемент разделен переносом строки \n
, то есть фактически применяется функция string()
. Вследствие этого рассматриваемый оператор может давать неверный результат:
namespaces := [4, 11, 15]; /* представим проверку пространств имен */
4 in namespaces == true /* здесь все правильно */
"4" in namespaces == true /* здесь тоже, т. к. подстрока "4" есть в строке "4\n11\n15\n" */
"4\n11" in namespaces == true /* такая подстрока также существует, но по нашей логике это уже неверно */
1 in namespaces == true /* неверно, но выдает true, потому что подстрока "1" в строке "4\n11\n15\n" присутствует */
В данном случае рекомендуется использовать функцию equals_to_any(a, b, c)
, действие которой идентично записи a === b | a === c
.
Переменные[]
Внутри условий допускается создавать пользовательские переменные. Имя переменной может быть любым, но должно состоять из букв латинского алфавита, цифр (но не может с них начинаться) или символов нижнего подчеркивания; регистр значения не имеет. Содержимое записывается в переменную оператором :=
, а в конце строки обязательно должна стоять точка с запятой. Будучи объявленной в одном условии, переменная становится доступной и в остальных тоже.
В качестве примера можно привести переменную, содержащую регулярное выражение и использующуюся в одном из фильтров английской Википедии:
(
line1:="(\{\{(r|R)еflist|\{\{(r|R)efs|<rеferences\s?/>|</rеferences\s?>)";
rcount(line1, removed_lines)
) > (
rcount(line1, added_lines)
)
Операторы[]
Синтаксис условий фильтра злоупотреблений предоставляет три группы операторов для действий с данными. Первая группа — арифметические действия:
- сложение чисел и конкатенация строк (
+
); - вычитание (
-
); - умножение (
*
); - возведение в степень (
**
); - деление (
/
); - остаток от деления (
%
).
/* сложение и конкатенация */
16 + 4 == 20
34.22 + 16.09 == 50.31
"Lorem" + "ipsum" == "Loremipsum"
/* умножение и возведение в степень */
6 * 4 == 24
29 * 0.1 == 2.9
4 ** 3 == 64
/* деление */
16 / 5 == 3.2
13 % 10 == 3
Следующая группа — бинарные операторы. Их всего четыре:
- AND или конъюнкция (
&
) — истинна, когда истинны оба аргумента; - OR или дизъюнкция (
|
) — истинна, если истинен хотя бы один аргумент; - XOR или строгая дизъюнкция (
^
) — истинна, если истинен только один аргумент; - NOT или инверсия (
!
) — инвертирует значение: истина становится ложью и наоборот.
/* таблица истинности NOT */
x !x
0 1
1 0
/* таблица истинности AND, OR и XOR */
x y x & y x | y x ^ y
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0
И, наконец, группа операторов сравнения, которые в совокупностями с действиями булевой алгебры образуют логику условий фильтра:
- строгое сравнение (
<
,>
) — истинно, когда левый аргумент соответственно меньше или больше, чем правый; - нестрогое сравнение (
<=
,>=
) — истинно, когда левый аргумент соответственно меньше или больше, чем правый, или равен ему; - равенство (
=
,==
) и неравенство (!=
) — истинны тогда, когда все левые аргменты соответственно равны или не равны всем правым. При необходимости производится приведение типов; - строгое равенство (
===
) и неравенство (!==
) — истинны тогда, когда все левые аргументы соответственно равны или не равны всем правым и при этом имеют один и тот же тип данных.
Обработчик разметки фильтра злоупотреблений предоставляет одну очень важную и в некоторой степени уникальную возможность — сравнение массивов. За это отвечают операторы строгого и нестрогого равенства:
[1, 2] == [1, 2] /* true */
[1, 2] == ["1", 2] /* true */
[1, 2] === ["1", 2] /* false, разные типы у первых элементов */
["1", 2] === ["1", 2] /* true, типы совпадают */
[1, 2, 3] == [1, 2] /* false, разная длина массивов */
Ключевые слова[]
Также в синтаксис фильтра злоупотреблений входят несколько операторов, которые в документации называются ключевыми словами. Отдельно стоит упомянуть, что данные конструкции приводят аргументы к строкам.
like
(илиmatches
) проверяет вхождение левой части выражения в правое. Можно использовать маску поиска (glob pattern).rlike
(илиregex
) делает абсолютно то же самое, но вместо маски используется регулярное выражение в формате PCRE. Ключевое словоirlike
делает паттерн нечувствительным к регистру.in
иcontains
выполняют одинаковые функции — проверка наличия одной строки в другой. Разница лишь в порядке:in
пытается найти левый операнд в правом, аcontains
— наоборот.
Разметка фильтра поддерживает и конструкции ветвления if ... then ... end
и if ... then ... else ... end
вместе с тернарным оператором condition ? true : false
, однако в контексте написания условий они вряд ли могут быть использованы.
Стандартные переменные[]
Расширение AbuseFilter добавляет достаточно большое число переменных, содержащих полезные данные, которые можно будет проанализировать в условиях фильтра. Часть из них доступна всегда, а другая часть — только при соблюдении определенных условий.
Переменные, доступные всегда | ||
---|---|---|
Переменная | Тип данных | Описание |
action
|
строка | Текущее действие, производимое со страницей. Возможные значения: edit , move , delete , upload , stashupload , createaccount , autocreateaccount .
|
timestamp
|
строка | UNIX-время в момент совершения действия. Рекомендуется переводить в число функцией int() .
|
wiki_name
|
строка | Кодовое название данного википроекта в базе данных Фэндома. Например, для Викии Вики это будет ruwikies .
|
wiki_language
|
строка | Языковой код данной вики в базе данных Фэндома. Для Викии Вики это будет ru .
|
user_name
|
строка | Имя пользователя, совершившего действие. |
user_editcount
|
число или null
|
Количество правок пользователя. Для анонимов равно null .
|
user_emailconfirm
|
строка или null
|
Дата подтверждения адреса электронной почты в формате YYYYMMDDHHMMSS либо null , если это аноним.
|
user_age
|
число | Время, прошедшее с момента создания аккаунта пользователя, в секундах. Для анонимов равно 0. |
user_blocked
|
логический | Если аккаунт участника заблокирован (в том числе и глобально) или его IP-адрес входит в заблокированный диапазон, будет равно true .
|
user_rights и user_groups
|
массив строк | Перечисляет все права и группы прав пользователя соответственно. |
page_id
|
число | Идентификатор данной страницы. Можно использовать для проверки создания страницы (page_id == 0 ), но этот способ не полностью надежен.
|
page_title и page_prefixedtitle
|
строка | Название страницы без префикса пространства имен и полное название соответственно. |
page_namespace
|
число | Номер пространства имен, в котором расположена данная страница. |
page_age
|
число | Количество секунд, прошедших с первой правки страницы (при создании равно 0). Используется для точного определения создания страницы, но работает медленнее, чем page_id
|
Переменные, доступные при некоторых обстоятельствах | ||
Переменная | Тип данных | Описание |
summary
|
строка | Описание выполненной правки. Нужно помнить, что автоматически генерируемые движком описания вроде «Полностью удалено содержимое страницы» создаются после проверки фильтром. |
old_size и new_size
|
число | Размер страницы до и после правки соответственно. |
edit_delta
|
число | Разница между размерами страницы до и после правки. Соответствует выражению new_size - old_size .
|
added_lines и removed_lines
|
массив строк | Строки, которые были соответственно добавлены и удалены во время правки. |
added_links и removed_links
|
массив строк | Содержит списки всех внешних ссылок, добавленных и удаленных во время правки. Это медленные переменные, поэтому для повышения производительности лучше сначала проверять added_lines и removed_lines .
|
file_size
|
число | Вес загружаемого файла в байтах. |
file_width и file_height
|
число | Геометрические размеры загружаемого файла в пикселях — ширина и высота. |
file_mime
|
строка | MIME-тип загружаемого файла. Например, image/jpeg .
|
old_wikitext и new_wikitext
|
строка | Полный код страницы до правки и после. Содержимое может достигать огромных размеров, поэтому лучше сначала проверять added_lines и removed_lines .
|
new_text и new_html
|
строка | Текст страницы после правки, избавленный от любого вида разметки, и полный HTML-код нового контента. Могут содержать очень много данных, поэтому лучше использовать другие переменные. |
Функции[]
Помимо переменных расширение добавляет функции. Они предназначены для изменения входных данных и упрощения проверок.
Функция | Описание |
---|---|
bool(arg) , int(arg) , float(arg) и string(arg)
|
Приводят тип аргумента arg к логическому, целочисленному, дробному и строковому соответственно.
|
ccnorm(arg)
|
Нормализует похожие символы в данной строке arg и возвращает результат в верхнем регистре. Полный список замен при нормализации можно найти на Phabricator Wikimedia.
|
contains_all(example, arg1, arg2, ...)
|
Возвращает true , если подстрока example входит в каждый из данных аргументов arg .
|
contains_any(example, arg1, arg2, ...)
|
Возвращает true , если подстрока example входит в любой данный аргумент arg .
|
count(needle, haystack)
|
Подсчитывает количество вхождений подстроки needle в строку haystack .
|
rcount(needle, haystack)
|
Действует аналогично предыдущей функции, но в качестве needle принимает регулярное выражение.
|
equals_to_any(left, right1, right2, ...)
|
Возвращает true , если аргумент left строго равен любому аргументу right . Запись equals_to_any(a, b, c) эквивалентна a === b | a === c .
|
get_matches(needle, haystack)
|
Находит все вхождения захватывающих скобок (capture groups) регулярного выражения needle в строку haystack и возвращает их в качестве массива строк. Первый элемент такого массива будет содержать полное вхождение.
|
ip_in_range(ip, range)
|
Проверяет, находится ли данный IP-адрес ip в диапазоне range согласно адресации CIDR.
|
lcase(arg) и ucase(arg)
|
Переводит данную строку arg в нижний и верхний регистр соответственно.
|
length(arg)
|
Возвращает количество символов в данной строке или количество элементов в данном массиве arg .
|
rescape(arg)
|
Экранирует спецсимволы в данной строке arg с помощью обратного слеша. Преобразованная строка может быть использована в регулярном выражении буквально.
|
rmdoubles(arg) , rmspecials(arg) и rmwhitespaces(arg)
|
Подчищают строку arg соответственно от повторяющихся символов, спецсимволов и пустых мест (пробелы, табы и переносы строк).
|
norm(arg)
|
Полностью нормализует данную строку arg и возвращает результат. Эквивалентна комбинации функций rmwhitespace(rmspecials(rmdoubles(ccnorm(arg)))) .
|
set(name, value)
|
Записывает значение value в переменную name . Эквивалентна записи name := value; .
|
strpos(haystack, needle, offset?)
|
Находит позицию первого вхождения подстроки needle в строку haystack . Также можно указать минимальный индекс offset , с которого начнется поиск.
|
substr(arg, offset, length?)
|
Удаляет содержимое строки arg до позиции offset и возвращает оставшееся. Можно указать длину отреза length , то есть запись substr("foobar", 2, 3) вернет "oba" .
|
Порядок действий[]
Еще один важный аспект работы с разметкой фильтра злоупотреблений — это порядок выполнения действий. Ниже представлен приоритет операций в порядке по убыванию:
- Выражения в скобках
( )
обрабатываются в первую очередь; - Подстановка соответствующих данных вместо названий переменных (например,
action → "edit"
); - Вызов функций (например,
upper("foo") → "FOO"
); - Выполнение унарных операторов
+
и-
, определяющих знак числа; - Обработка ключевых слов (например,
"a" in "bar" → true
); - Логическая инверсия (
!x
); - Возведение в степень (
2 ** 6 → 64
); - Умножение и деление (операторы
* / %
); - Сложение и вычитание, в том числе конкатенация (операторы
+ -
); - Сравнение (операторы
< > ==
); - Бинарные операции (операторы
& | ^
).
Кроме того, фильтр проверяет условия по стратегии вычислений Маккарти, то есть до первого true
для логического OR и до первого false
для логического AND. Вот несколько примеров, где подчеркнута та часть условия, после которой дальнейшее вычисление прекращается:
false | true | false | false → true
;false & true & true & false → false
;(true | false) & false & (true & true) → false
(стратегия работает внутри каждой группы).
Подсчет условий[]
Правила срабатывания каждого созданного фильтра имеют ограничение на количество используемых условий. Эти лимиты налагаются системой с целью сохранения высокой производительности расширения. По умолчанию максимальное количество условий — 1000, фильтры с большим числом работать не будут.
При работе с фильтром злоупотреблений крайне рекомендуется составлять правила так, чтобы используемых условий было как можно меньше. Однако для этого нужно знать, как именно происходит подсчет, а таблица ниже это проиллюстрирует:
Правило подсчета | Пример | Количество условий |
---|---|---|
Простые сравнения и проверки считаются за одно условие. | "foo" == "bar"
|
1 |
"pine" in "pineapple" & 4 < 8
|
2 | |
Считается каждое условие, которое выполняется в стратегии вычсилений Маккарти. Таким образом, в первом примере вычисляется только первое условие, равное false , а во втором — первые два, равные false и true соответственно.
|
"bar" == "bas" & 3 + 4 == 7
|
1 |
4 < 3 | 5 == "5" | "foo" in "bar"
|
2 | |
Каждый вызов функции считается за одно условие. | lcase("EXAMPLE") == "example"
|
2 |
Повторный вызов функции с идентичными аргументами не считается. | lcase("EXAMPLE") contains "ex" &
lcase("EXAMPLE") == "example"
|
3 |
lcase("EXAMPLE") contains "ex" &
lcase("FOO") == "foo"
|
4 |
Дополнительно можно рассмотреть пример фильтра, который используется на английской Википедии и сейчас:
page_namespace == 6
& !("autoconfirmed" in user_groups)
& !(user_name in page_recent_contributors)
& rcount ("\{\{.*\}\}", removed_lines) > rcount ("\{\{.*\}\}", added_lines)
Можно упростить представление: A & !B & !C & fun1 > fun2
. Теперь оценить количество используемых условий становится проще.
- 1 условие (1 сравнение) используется, когда номер пространства имен отличается от 6 (пространство «Файл»). Первый тест ложный, все остальное по принципу коротких вычислений не обрабатывается.
- 2 условия (2 сравнения) — правка происходит в пространстве файлов, но пользователь не имеет статуса автоподтвержденного. Первый тест истинный, второй ложный — остальные условия не вычисляются.
- 3 условия (3 сравнения) — правка делается подтвержденным участником в пространстве файлов, но его имени нет в списке недавних вкладчиков этой страницы. Первые два теста истинны, а этот ложен — остаток игнорируется.
- 6 условий (3 сравнения + 2 вызова функций + 1 сравнение их результатов) — если ранее упомянутый участник прошел все предварительные проверки и удалил со страницы шаблоны, этот тест будет истинным, а фильтр сработает. В противном случае все правило фильтра будет считаться ложным, и он промолчит.
На практике получается, что расширение будет завершать вычисления еще на первом тесте, поскольку большинство правок приходится на основное пространство имен. Именно поэтому рекомендуется более общие проверки выносить вперед — очередь выполнения просто не дойдет до тяжеловесных условий, а фильтр будет работать быстрее.
Ссылки[]
- Страница расширения AbuseFilter на MediaWiki.org
- Описание синтаксиса фильтра на MediaWiki.org
- Как считается число условий фильтра злоупотреблений (MediaWiki.org)
- Тесты условий фильтра на Wikimedia Phabricator
Языки разметки и программирования | ||
---|---|---|
Основные | HTML • Викиразметка (Теги) | |
Специализированные | CSS • JavaScript • Lua • TeX • Разметка фильтра злоупотреблений | |
Для работы с ботами | Bash • C/C Sharp/C++ • Go • Haskell • Java • JScript • Lisp • Microsoft .NET • Perl • PHP • Python • Ruby | |
См. также: устройство и технология википроектов и участников |