2006-01-19 17:17:03 +00:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* An aggressive spam cleanup script.
|
2018-04-06 17:52:42 +00:00
|
|
|
* Searches the database for matching pages, and reverts them to
|
|
|
|
* the last non-spammed revision.
|
|
|
|
* If all revisions contain spam, blanks the page
|
2006-01-19 17:17:03 +00:00
|
|
|
*/
|
|
|
|
|
2024-10-20 09:55:49 +00:00
|
|
|
use MediaWiki\Content\ContentHandler;
|
|
|
|
use MediaWiki\Content\TextContent;
|
2022-04-08 13:05:02 +00:00
|
|
|
use MediaWiki\Extension\SpamBlacklist\BaseBlacklist;
|
2024-10-20 09:55:49 +00:00
|
|
|
use MediaWiki\Maintenance\Maintenance;
|
2020-03-04 00:33:41 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2022-06-24 17:44:29 +00:00
|
|
|
use MediaWiki\Page\WikiPageFactory;
|
2020-03-04 00:33:41 +00:00
|
|
|
use MediaWiki\Revision\RevisionLookup;
|
|
|
|
use MediaWiki\Revision\RevisionRecord;
|
|
|
|
use MediaWiki\Revision\SlotRecord;
|
2023-08-19 04:19:23 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2024-01-05 18:28:12 +00:00
|
|
|
use MediaWiki\Title\TitleFormatter;
|
|
|
|
use MediaWiki\User\User;
|
2020-03-04 00:33:41 +00:00
|
|
|
|
2018-04-06 17:52:42 +00:00
|
|
|
$IP = getenv( 'MW_INSTALL_PATH' );
|
|
|
|
if ( $IP === false ) {
|
|
|
|
$IP = __DIR__ . '/../../..';
|
2006-01-19 17:17:03 +00:00
|
|
|
}
|
2018-04-06 17:52:42 +00:00
|
|
|
require_once "$IP/maintenance/Maintenance.php";
|
2006-01-19 17:17:03 +00:00
|
|
|
|
2018-04-06 17:52:42 +00:00
|
|
|
class Cleanup extends Maintenance {
|
2020-03-04 00:33:41 +00:00
|
|
|
/** @var RevisionLookup */
|
|
|
|
private $revisionLookup;
|
|
|
|
/** @var TitleFormatter */
|
|
|
|
private $titleFormatter;
|
2022-06-24 17:44:29 +00:00
|
|
|
/** @var WikiPageFactory */
|
|
|
|
private $wikiPageFactory;
|
2020-03-04 00:33:41 +00:00
|
|
|
|
2018-04-06 17:52:42 +00:00
|
|
|
public function __construct() {
|
|
|
|
parent::__construct();
|
2020-03-04 00:33:41 +00:00
|
|
|
$this->revisionLookup = MediaWikiServices::getInstance()->getRevisionLookup();
|
|
|
|
$this->titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
|
2022-06-24 17:44:29 +00:00
|
|
|
$this->wikiPageFactory = MediaWikiServices::getInstance()->getWikiPageFactory();
|
2020-03-04 00:33:41 +00:00
|
|
|
|
2018-04-06 17:52:42 +00:00
|
|
|
$this->requireExtension( 'SpamBlacklist' );
|
|
|
|
$this->addOption( 'dry-run', 'Only do a dry run' );
|
2012-10-08 23:33:43 +00:00
|
|
|
}
|
2006-04-02 03:50:06 +00:00
|
|
|
|
2018-04-06 17:52:42 +00:00
|
|
|
public function execute() {
|
|
|
|
$user = User::newSystemUser( 'Spam cleanup script', [ 'steal' => true ] );
|
2006-01-19 17:17:03 +00:00
|
|
|
|
2018-04-06 17:52:42 +00:00
|
|
|
$sb = BaseBlacklist::getSpamBlacklist();
|
|
|
|
$regexes = $sb->getBlacklists();
|
|
|
|
if ( !$regexes ) {
|
|
|
|
$this->fatalError( "Invalid regex, can't clean up spam" );
|
|
|
|
}
|
|
|
|
$dryRun = $this->hasOption( 'dry-run' );
|
2006-01-19 17:17:03 +00:00
|
|
|
|
2024-03-18 20:34:56 +00:00
|
|
|
$dbr = $this->getReplicaDB();
|
2024-04-20 19:20:29 +00:00
|
|
|
$maxID = (int)$dbr->newSelectQueryBuilder()
|
|
|
|
->select( 'MAX(page_id)' )
|
|
|
|
->from( 'page' )
|
|
|
|
->caller( __METHOD__ )
|
|
|
|
->fetchField();
|
2018-04-06 17:52:42 +00:00
|
|
|
$reportingInterval = 100;
|
2006-01-19 17:17:03 +00:00
|
|
|
|
2022-09-29 11:56:43 +00:00
|
|
|
$this->output( "Regexes are " . implode( ', ', array_map( 'strlen', $regexes ) ) . " bytes\n" );
|
2018-04-06 17:52:42 +00:00
|
|
|
$this->output( "Searching for spam in $maxID pages...\n" );
|
|
|
|
if ( $dryRun ) {
|
|
|
|
$this->output( "Dry run only\n" );
|
|
|
|
}
|
2006-01-19 17:17:03 +00:00
|
|
|
|
2018-04-06 17:52:42 +00:00
|
|
|
for ( $id = 1; $id <= $maxID; $id++ ) {
|
|
|
|
if ( $id % $reportingInterval == 0 ) {
|
|
|
|
printf( "%-8d %-5.2f%%\r", $id, $id / $maxID * 100 );
|
|
|
|
}
|
2020-03-04 00:33:41 +00:00
|
|
|
$revision = $this->revisionLookup->getRevisionByPageId( $id );
|
2018-04-06 17:52:42 +00:00
|
|
|
if ( $revision ) {
|
2021-05-17 22:30:59 +00:00
|
|
|
$content = $revision->getContent( SlotRecord::MAIN );
|
|
|
|
$text = ( $content instanceof TextContent ) ? $content->getText() : null;
|
2018-04-06 17:52:42 +00:00
|
|
|
if ( $text ) {
|
|
|
|
foreach ( $regexes as $regex ) {
|
|
|
|
if ( preg_match( $regex, $text, $matches ) ) {
|
2020-03-04 00:33:41 +00:00
|
|
|
$titleText = $this->titleFormatter->getPrefixedText( $revision->getPageAsLinkTarget() );
|
2018-04-06 17:52:42 +00:00
|
|
|
if ( $dryRun ) {
|
|
|
|
$this->output( "Found spam in [[$titleText]]\n" );
|
|
|
|
} else {
|
|
|
|
$this->output( "Cleaning up links to {$matches[0]} in [[$titleText]]\n" );
|
|
|
|
$match = str_replace( 'http://', '', $matches[0] );
|
|
|
|
$this->cleanupArticle( $revision, $regexes, $match, $user );
|
|
|
|
}
|
|
|
|
}
|
2006-09-18 09:56:57 +00:00
|
|
|
}
|
2006-01-21 23:27:39 +00:00
|
|
|
}
|
2006-01-19 17:17:03 +00:00
|
|
|
}
|
|
|
|
}
|
2018-04-06 17:52:42 +00:00
|
|
|
// Just for satisfaction
|
|
|
|
printf( "%-8d %-5.2f%%\n", $id - 1, ( $id - 1 ) / $maxID * 100 );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find the latest revision of the article that does not contain spam and revert to it
|
2020-03-04 00:33:41 +00:00
|
|
|
* @param RevisionRecord $rev
|
2018-04-06 17:52:42 +00:00
|
|
|
* @param array $regexes
|
2019-03-02 12:28:18 +00:00
|
|
|
* @param string $match
|
2018-04-06 17:52:42 +00:00
|
|
|
* @param User $user
|
|
|
|
*/
|
2020-03-04 00:33:41 +00:00
|
|
|
private function cleanupArticle( RevisionRecord $rev, $regexes, $match, User $user ) {
|
|
|
|
$title = Title::newFromLinkTarget( $rev->getPageAsLinkTarget() );
|
2018-04-06 17:52:42 +00:00
|
|
|
while ( $rev ) {
|
|
|
|
$matches = false;
|
2021-05-17 22:30:59 +00:00
|
|
|
$content = $rev->getContent( SlotRecord::MAIN );
|
2018-04-06 17:52:42 +00:00
|
|
|
foreach ( $regexes as $regex ) {
|
|
|
|
$matches = $matches
|
|
|
|
|| preg_match(
|
|
|
|
$regex,
|
2021-05-17 22:30:59 +00:00
|
|
|
( $content instanceof TextContent ) ? $content->getText() : null
|
2018-04-06 17:52:42 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
if ( !$matches ) {
|
|
|
|
// Didn't find any spam
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-03-04 00:33:41 +00:00
|
|
|
$rev = $this->revisionLookup->getPreviousRevision( $rev );
|
2018-04-06 17:52:42 +00:00
|
|
|
}
|
|
|
|
if ( !$rev ) {
|
|
|
|
// Didn't find a non-spammy revision, blank the page
|
|
|
|
$this->output( "All revisions are spam, blanking...\n" );
|
2019-12-22 05:21:52 +00:00
|
|
|
$content = ContentHandler::makeContent( '', $title );
|
2018-04-06 17:52:42 +00:00
|
|
|
$comment = "All revisions matched the spam blacklist ($match), blanking";
|
|
|
|
} else {
|
|
|
|
// Revert to this revision
|
2020-03-04 00:33:41 +00:00
|
|
|
$content = $rev->getContent( SlotRecord::MAIN ) ?:
|
|
|
|
ContentHandler::makeContent( '', $title );
|
2018-04-06 17:52:42 +00:00
|
|
|
$comment = "Cleaning up links to $match";
|
|
|
|
}
|
2022-06-24 17:44:29 +00:00
|
|
|
$wikiPage = $this->wikiPageFactory->newFromTitle( $title );
|
2021-06-24 04:36:18 +00:00
|
|
|
$wikiPage->doUserEditContent( $content, $user, $comment );
|
2006-01-19 17:17:03 +00:00
|
|
|
}
|
|
|
|
}
|
2018-04-06 17:52:42 +00:00
|
|
|
|
|
|
|
$maintClass = Cleanup::class;
|
|
|
|
require_once RUN_MAINTENANCE_IF_MAIN;
|