Скрипт для автоблокировки новых пользователей
Скрипт для автоблокировки новых пользователей — мощный скрипт, который был реализован в MediaWiki в апреле 2026 года.
Описание[править]
Сильные дятлы стали буйствовать и долбить, что не наносило особого вреда, однако отвлекало людей от работы, так как требовалось подчищать вандализм. При этом птица использовала много разных IP-адресов и на то, чтобы бан диапазонов и автономных систем заработал и накопился, требовалось время.
И вот во времена особой атаки дятла был реализован сильный скрипт, который потужно жужжит на сервере и время от времени проверяет журналычи новых регистраций, чтобы затем восраться в оныя, проверить нет ли вандальных именований, и яко таковые имеются, то и пристрелить нах такого злодеянича.
В случае если дятле долбил упорно, был режим бана вообще всех новых пользователей. Проверка делалась раз в 8-15 секунд, так что при условии наличия каптчи у вандалыча не было никакой возможности успеть навандалить, он моментально получал сильный бан от робота.
В связи с полным выжиганием гнёзд дятла скрипт вряд ли нужен, а оттого публикуется.
Запускался сей мощный борец с пернатыми как сервис, то бишь мог произвольно подрубаться да отрубаться: systemctl start mediawiki-banbot.service.
Исходный код[править]
anti-kluv.pl[править]
В @FORBIDDEN_PATTERNS первая строчка содержит вандальные паттерны, вторая же значит что будут баниться вообще все новые аккаунты (режим ЧЕРЕПАХА при натиске дятла).
#!/usr/bin/perl
use strict;
use warnings;
use MediaWiki::API;
use POSIX qw(strftime);
use Data::Dumper; # core module – always available
use utf8;
# ================== CONFIGURATION ==================
my $API_URL = 'https://lurkmore.org/w/api.php';
my $BOT_USERNAME = 'Balledur';
my $BOT_PASSWORD = 'tyzhepojedshproveyatpetyhtyebanij9000';
my @FORBIDDEN_PATTERNS = (
qr/(пидо|педик|говно|некроф|админ|сперм|дебил|дально|трак|драйв|driv|truck|антисем|евре|жид|14|88)/iu, # example words
# qr/[а-я0-9a-z]/iu
# add your patterns here
);
my $BLOCK_REASON = 'дятел';
my $BLOCK_EXPIRY = 'never'; # "never" or "infinite" = permanent block
my $LOG_FILE = '/var/log/mediawiki-banbot.log';
# ===================================================
my $mw = MediaWiki::API->new({ api_url => $API_URL });
# Login once
print "Logging in as $BOT_USERNAME...\n";
unless ($mw->login({ lgname => $BOT_USERNAME, lgpassword => $BOT_PASSWORD })) {
die "LOGIN FAILED: " . Dumper($mw->{error});
}
print "Logged in successfully.\n";
my $max_seen_logid = mw_timestamp_to_num(get_current_timestamp());
my $csrf_token;
sub get_csrf_token {
my $resp = $mw->api({ action => 'query', meta => 'tokens', type => 'csrf' });
if (!$resp) {
die "CSRF TOKEN FAILED: " . Dumper($mw->{error});
}
return $resp->{query}->{tokens}->{csrftoken};
}
sub get_current_timestamp {
my $resp = $mw->api({
action => 'query',
meta => 'info',
curtimestamp => 1,
}) or die "Failed to get current timestamp: " . Dumper($mw->{error});
return $resp->{curtimestamp}; # e.g. '2026-04-02T12:39:52Z'
}
sub mw_timestamp_to_num {
my $ts = shift || '';
$ts =~ s/[-T:Z]//g; # remove -, T, :, Z
return 0 + $ts; # convert to real number for easy > / <= comparison
}
sub log_message {
my $msg = shift;
my $timestamp = strftime("%Y-%m-%d %H:%M:%S", localtime);
my $full_msg = "[$timestamp] $msg";
print "$full_msg\n";
if ($LOG_FILE) {
open my $fh, '>>', $LOG_FILE or warn "Cannot write log: $!";
print $fh "$full_msg\n";
close $fh;
}
}
log_message("=== MediaWiki username auto-ban service STARTED (debug mode) ===");
while (1) {
eval {
log_message("--- Starting new check cycle ---");
# Refresh CSRF token
$csrf_token = get_csrf_token() unless $csrf_token;
log_message("CSRF token obtained successfully");
# Get new user registrations
my $result = $mw->api({
action => 'query',
list => 'logevents',
letype => 'newusers',
lelimit => 30,
leprop => 'id|title|timestamp|user|comment',
ledir => 'older',
});
if (!$result) {
die "LOG EVENTS QUERY FAILED: " . Dumper($mw->{error});
}
my $logs = $result->{query}->{logevents} || [];
my $new_max_id = $max_seen_logid;
log_message("Fetched " . scalar(@$logs) . " log entries");
for my $log (@$logs) {
my $logid = mw_timestamp_to_num($log->{timestamp}) or next;
next if $logid <= $max_seen_logid;
my $username = $log->{title} || $log->{user} || next;
$username =~ s/^Участник://iu;
log_message("Processing $username");
my $is_forbidden = 0;
for my $pat (@FORBIDDEN_PATTERNS) {
if ($username =~ $pat) {
$is_forbidden = 1;
last;
}
}
if ($is_forbidden) {
log_message("FORBIDDEN USERNAME DETECTED → '$username' (log id: $logid) — banning now");
my $block_params = {
action => 'block',
user => $username,
expiry => $BLOCK_EXPIRY,
reason => $BLOCK_REASON,
token => $csrf_token,
autoblock => 1,
nocreate => 1,
noemail => 1,
};
my $block_result = $mw->api($block_params);
if ($block_result) {
log_message("✓ SUCCESSFULLY BLOCKED '$username'");
} else {
die "BLOCK FAILED for '$username': " . Dumper($mw->{error});
}
}
$new_max_id = $logid if $logid > $new_max_id;
}
$max_seen_logid = $new_max_id;
log_message("Cycle completed successfully. Next check in 20 seconds.");
} or do {
my $err = $@ || 'Unknown error (no $@)';
log_message("ERROR in main loop: $err");
# also dump the raw error object if it exists
log_message("Raw MediaWiki error object: " . Dumper($mw->{error})) if $mw->{error};
};
sleep 15;
}
/etc/systemd/system/mediawiki-banbot.service[править]
[Unit]
Description=MediaWiki New User Auto-Ban Service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=clown
ExecStart=/usr/bin/perl /home/clown/anti-dyatel/anti-kluv.pl
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
NoNewPrivileges=yes
[Install]
WantedBy=multi-user.target