Wer kennt das nicht? Gerade erst ist der neue Beitrag draußen und schon kommen die ersten Spam-Bots an und möchten dich ärgern. Heutzutage gibt es unzählige Plugins um Spam zu verhindern und die gute Arbeit leisten. Allerdings kommt es immer mal wieder vor, dass sich dennoch ein unerwünschter Kommentar durchschleicht.
Auch WordPress bietet schon seit der Version 1.2.0 intern mit der Funktion check_comments
eine Überprüfung auf Spam. Diese Funktion überprüft anhand einer sogenannten “Blacklist” ob Teile von den Kommentarfeldern Name, E-Mail, URL, Kommentartext, IP und User-Agent mit dieser Liste übereinstimmen. Wenn dies der Fall ist, wird er Kommentar direkt als “Spam” markiert.
Doch wer pflegt heutzutage diese Liste noch von Hand? Wäre es dann nicht schön, wenn diese Liste automatisiert dazulernt? Hier kommt unser heutiges Mini-Plugin ins Spiel!
Aktivierung Mini-Plugin
Unser Mini-Plugin befüllt nach der Aktivierung erst einmal die Blacklist mit vorhanden Spam-Kommentaren. Dazu registrieren wir einen Callback in den Aktivierungs-Hook von Plugins:
register_activation_hook( __FILE__, 'chrico_comment_blacklist_activation' );
Unsere Funktion holt sich nun alle Kommentare die als “Spam” markiert sind und ruft intern unsere “Füge zur Blacklist hinzu”-Funktion auf (mehr dazu unten).
/**
* Callback for Plugin-Activation
*
* @return Void
*/
function chrico_comment_blacklist_activation() {
$comment_args = array( 'status' => 'spam' );
$comments = get_comments( $comment_args );
foreach( $comments as $comment ){
chrico_comment_blacklist( 'spam', '', $comment );
}
}
Achtung: Es kann durchaus sein, wenn Ihr viele Spam-Kommentare habt, dass die Aktivierung eine zu lange Laufzeit hat und ins Timeout läuft.
Blacklist up to date halten
Als nächstes wollen wir, dass unsere Blacklist dazu lernt. Hierfür bietet uns WordPress einen sehr nüztlich Hook: transition_comment_status
. Dieser wird jedesmal aufgerufen, wenn sich ein Kommentar-Status ändern und übergibt uns den alten Status, neuen Status und Kommentar selbst. Da wir alle 3 Parameter intern benötigen, müssen wir das auch mit angeben:
add_action( 'transition_comment_status', 'chrico_comment_blacklist', 10, 3 );
Unser Callback sieht dann wie folgt aus:
/**
* Callback to add or remove a IP to/from blacklist
*
* @wp-hook transition_comment_status
* @uses get_option, update_option
* @param String $new_status
* @param String $old_status
* @param $comment
* return Void
*/
function chrico_comment_blacklist( $new_status, $old_status, $comment ){
$orig_blacklist = get_option( 'blacklist_keys', array() );
if( !is_array( $orig_blacklist ) ){
$orig_blacklist = explode( "n" , trim( $orig_blacklist ) );
}
$new_blacklist = $orig_blacklist;
$the_ip = $comment->comment_author_IP;
if ( $old_status === 'spam' && !in_array( $new_status, array( 'trash', 'delete' ) ) ){
// comment is approved/unapproved, not trashed
if( in_array( $the_ip, $orig_blacklist ) ){
// ...and the ip does exists in blacklist
$new_blacklist = array_diff( $new_blacklist, array( $the_ip ) );
}
$new_blacklist = apply_filters( 'chrico_comment_blacklist_remove', $new_blacklist, $new_status, $old_status, $comment );
}
else if ( $new_status === 'spam' ) {
// comment is now spam
if( !in_array( $the_ip, $orig_blacklist ) ){
// ...and the ip does not exists in blacklist
$new_blacklist[] = $the_ip;
}
$new_blacklist = apply_filters( 'chrico_comment_blacklist_add', $new_blacklist, $new_status, $old_status, $comment );
}
// do we have an update?
if( count( $new_blacklist ) !== count( $orig_blacklist ) ) {
$new_blacklist = implode( "n", $new_blacklist );
update_option( 'blacklist_keys', $new_blacklist );
}
}
Blacklist laden
Zuerst laden wir unsere Blacklist, welche in der Options-Tabelle gespeichert ist. Diese Liste beinhaltet in jeder Zeile ein Keyword, welches unerwünscht ist. Um damit vernünftig arbeiten zu können, benutzen wir die PHP-Funktion explode()
um die Zeilenumbrüche (n) zu splitten und in ein Array zu konvertieren. Intern kopieren wir die Original-Blacklist um später überprüfen zu können, ob diese sich von der Neuen unterscheidet. Somit können wir verhindern, dass unnötige Queries an die Datenbank abgesetzt werden.
Blacklist erweitern
Um die Blacklist zu erweitern, überpüfen wir ob der neue gesetzte Status “Spam” und ob die IP-Adresse noch nicht in der Blacklist vorhanden ist. Wenn dies zutrifft, fügen wir die Adresse in die Liste hinzu. Zusätzlich habe ich einen Filter chrico_comment_blacklist_add
eingefügt, welcher die Blacklist, den alte und neuen Status, sowie den Kommentar übergeben bekommt. Somit verfügt das Plugin über eine einfache Schnittstelle um die Blacklist um noch weitere Felder zu erweitern.
Blacklist bereinigen
Da es durchaus einmal vorkommen kann, dass mal ein Kommentar als Spam gekennzeichnet wird, aber gar keiner ist, müssen wir natürlich auch das ganz Spiel noch einmal umdrehen. Hier habe ich den Filter chrico_comment_blacklist_add
hinzugefügt um das Plugin flexibel zu halten.
Die Überprüfung hierfür ist allerdings etwas aufwändiger:
- Zuerst schauen wir, ob der alte Status “spam” war.
- Wenn dies zutrifft schauen wir, ob der Kommentar nicht endgültig gelöscht oder in den Papierkorb verschoben wird.
- Zu guter Letzte prüfen wir, ob es dieses Adresse in der Blacklist existiert und nicht schon manuell gelöscht worden ist.
Blacklist speichern
Wenn eine der Überprüfungen (Erweitern/Bereinigen) ausgeführt worden ist, unterscheidet sich die Größe der Original-Blacklist zu neuen Blacklist führen auch anschließend das Update aus. Hierzu konvertieren wir unser Array wieder zurück in die ursprüngliche Liste und speichern diese ab.
Fazit
Mit diesen Mini-Plugin haben wir eine einfache Möglichkeit geschaffen, dass unsere Blacklist dazulernt und zukünftig noch effektiver Spam von unseren Kommentaren fern hält. Ich habe mich bewusst nur auf die IP-Adresse beschränkt, es wäre aber durchaus aus möglich, dass man z.B. noch den Host oder die E-Mail mit in die Blacklist mit aufnimmt.
Das Mini-Plugin findet Ihr auf Gihtub: Comment Blacklist.
Dankeschön für dieses Geschenk. Werde ich gleich in meine Installation mit aufnehmen 🙂
Vielen Dank auch von mir zurück! Es freut mich zu hören, dass du das Mini-Plugin einsetzen möchtest 🙂