Викии Вики
Advertisement
Викии Вики
1860
страниц
Abusefilter syntax

Разметка одного из фильтров на Dev Wiki

Разметка фильтра злоупотреблений — специальный синтаксис, позволяющий указывать условия срабатывания фильтра злоупотреблений.

Описание[]

Фильтр злоупотреблений — расширение, позволяющее проверять действия участников на соответствие определенным условиям и таким образом защищать вики от потенциально вредоносного вклада. Администрация википроекта может создать сколь угодно много фильтров, а действия будут проверяться последовательно с каждым из активных условий.

Сами условия указываются на служебной странице «Настройка фильтра злоупотреблений» (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".

Порядок действий[]

Еще один важный аспект работы с разметкой фильтра злоупотреблений — это порядок выполнения действий. Ниже представлен приоритет операций в порядке по убыванию:

  1. Выражения в скобках ( ) обрабатываются в первую очередь;
  2. Подстановка соответствующих данных вместо названий переменных (например, action "edit");
  3. Вызов функций (например, upper("foo") "FOO");
  4. Выполнение унарных операторов + и -, определяющих знак числа;
  5. Обработка ключевых слов (например, "a" in "bar" true);
  6. Логическая инверсия (!x);
  7. Возведение в степень (2 ** 6 64);
  8. Умножение и деление (операторы * / %);
  9. Сложение и вычитание, в том числе конкатенация (операторы + -);
  10. Сравнение (операторы < > ==);
  11. Бинарные операции (операторы & | ^).

Кроме того, фильтр проверяет условия по стратегии вычислений Маккарти, то есть до первого 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 сравнение их результатов) — если ранее упомянутый участник прошел все предварительные проверки и удалил со страницы шаблоны, этот тест будет истинным, а фильтр сработает. В противном случае все правило фильтра будет считаться ложным, и он промолчит.

На практике получается, что расширение будет завершать вычисления еще на первом тесте, поскольку большинство правок приходится на основное пространство имен. Именно поэтому рекомендуется более общие проверки выносить вперед — очередь выполнения просто не дойдет до тяжеловесных условий, а фильтр будет работать быстрее.

Ссылки[]

Advertisement