Разумным решением было бы переставить форум с нуля, но заказчик настоял именно на чистке. Ну что ж, Ваши деньги, любой каприз.
Решил определиться что попадает 100% под спам:
- Посты в которых 0 сообщений
- Посты не от админа
- Посты не содержащие ключевых слов
Все эти варианты можно смело сразу удалять, те которые не попадают под определенный нами фильтр, уже будут фильтроваться вручную:
- Просмотр заголовка
- Просмотр первого поста
- Просмотр последних постов
Окей, нужно приступать к модерации, но выводятся только по 25 сообщений... Значит надо увеличить этот параметр, делается это в админке в управлении форумами:
Админка > Forums > Edit (зеленая шестеренка) > Topics per page:
Максимально тут можно поставить 127. Если Вам надо больше (позволяет оперативка выделенная для скрипта, и его время выполнения), можно сделать хак, модифицировав запись непосредственно в таблице phpbb_forums, столбец: forum_topics_per_page, не забудьте так же ему поменять тип, на побольше.. Замечу, что это необходимо сделать для каждого из форумов.
Т.к. меня устраивало именно 127 на страницу, то я поставил именно это значение. Началась чистка.. Дабы ускорить процесс, я решил предварительно кильнуть пользователей спаммеров, у которых в несколько раз больше сообщений чем у самого общительного пользователя на форуме, отследить это можно по кол-ву сообщений пользователей в таблице Members list, которая находится по примерно такому пути www.forum.com/memberlist.php
Теперь непосредственно про само удаление.. это скучная и не благодарная работа вскоре я понял, что мне для визуализации нужных постов, надо раскрасить те, которые удалять, возможно, не нужно. Можно это делать разными способами, я пошел самым простым и написал javascript плагин для GreaseMonkey. Собственно этот пост, именно для того и нужен чтобы этот скриптик не писать вновь, вот он:
Код: Выделить всё
// ==UserScript==
// @name IB
// @namespace IB
// @include http://site.com/forum/
// ==/UserScript==
var aPosts = document.getElementsByClassName('posts');
for (var i=0; i<aPosts.length; i++) {
var ch = aPosts[i].innerHTML.substring(0, 1);
if (ch!='0' && ch!='R') {
aPosts[i].parentNode.style.backgroundColor = '#FF0000';
}
var stopWords = Array('impotence', 'levitra', 'cialis', 'viagra', 'nude', 'adult', 'pharma', 'bdsm', 'apotheke');
for (var j=0; j<stopWords.length; j++) {
var expr = new RegExp(stopWords[j], 'gim');
if (expr.exec(aPosts[i].parentNode.innerHTML)) {
aPosts[i].parentNode.style.backgroundColor = 'transparent';
break;
}
}
var stopWords = Array('admin', 'iHit', 'shooting', 'support', 'telepoints');
for (var j=0; j<stopWords.length; j++) {
var expr = new RegExp(stopWords[j], 'gim');
if (expr.exec(aPosts[i].parentNode.innerHTML)) {
aPosts[i].parentNode.style.backgroundColor = '#00FF00';
break;
}
}
}
Ну, а дальше Mark all > Delete
Хочу заметить, что т.к. я раньше с такой работой не сталкивался, то я довольно сильно продешевил, так что советую тем у кого есть подобное предложение, изначально попробовать сделать 15 минутный забег и засечь время которое Вам понадобиться на эту работу
Не забывайте делать бэкапы! Удачи!
Отправлено спустя 4 минуты 50 секунд:
После чистки, я писал для него защиту, которая вполне себе работает до сих пор, без использования дополнительных каптч и т.д.
При регистрации:
1) поле email делаем скрытым с помощью стилей
2) генерируем уникальное название, например хэш от ip + соль.
3) создаем поле с таким именем и располагаем его вместо скрытой формы email-а
4) при отправке формы, проверяем, если поле email-а пустое, а в нашем сгенериванном есть email, значит попросту перезаписываем оригинальное поле, что типа:
$_POST['email'] = $_POST['email_some-hash-code'];
иначе ничего не делаем.
5) соотвественно боты будут писать email в скрытое (оригинальное) поле, и таким образом будут палиться.
А комментарии, мы защитили, исходя из соображений, что у всех, важных нам, пользователей включен JS, поэтому опять же заводили скрытое поле, в которое писали, на клиенте, какой-нибудь хэш, например тот же ip. Соответственно если этого поля нет или оно не верное, значит спам.
Разумеется есть поток, ручного спама, но он составляет, только 0,2% от всех попыток оставить коммент, поэтому такой спам, легко фильтруется модератором.
Я написал javascript который после получения фокуса полем email, подменял его на другое поле. Дальше проверял пришло ли мое поле, на стороне сервера и если оно не пришло, то решал что это робот.
В файле: www/styles/prosilver/template/ucp_register.html, в конец файла, после формы, но перед
Код: Выделить всё
<!-- INCLUDE overall_footer.html -->
Код: Выделить всё
</form>
<script>
var el = document.getElementById('email');
el.onfocus = function(){
el.style.display = 'none';
var container = document.createElement('div');
container.innerHTML = '<input name="emai1" id="emai1" type="text" tabindex="2" size="25" maxlength="100" value="{EMAIL}" class="inputbox autowidth" autocomplete="off">';
el.parentNode.appendChild(container);
document.getElementById('emai1').focus();
document.forms["register"].onsubmit = function(){
el.parentNode.removeChild(el);
}
};
</script>
<!-- INCLUDE overall_footer.html -->
Далее, в файле www/ucp.php в самое начало я добавил проверку
Код: Выделить всё
//include 'disable-cache.php';
if (!empty($_GET['mode']) && $_GET['mode'] =='register' && !empty($_POST['submit'])) {
if (empty($_POST['emai1'])) {
$_POST['email'] = '';
} else {
$_POST['email'] = $_POST['emai1'];
}
}
Усложнять сильнее можно развивая эту идею, но я не стал этого делать, т.к. только это отсеяло всех авто-ботов.
В коде выше вы можете увидеть закомментированную строку с подключением файла disable-cache.php, вот его содержимое
Код: Выделить всё
<?php
//die('disabled on backend');
function rrmdir($dir) {
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
if (is_dir($dir."/".$object))
rrmdir($dir."/".$object);
else
unlink($dir."/".$object);
}
}
rmdir($dir);
}
}
rrmdir(dirname(__FILE__).'/cache/twig');
mkdir(dirname(__FILE__).'/cache/twig',0755);
foreach(glob(dirname(__FILE__).'/cache/*.php') as $file) unlink($file);
Теперь, вернемся к тому, что же делать со спамом который постят вручную. Способов достаточно много, я выбрал способ с быстрым оповещением о любой активности на форуме, чтобы моментально узнавать не только о спаме, но и том что кто-то просит поддержки. Написал такой скрипт:
Код: Выделить всё
<?php
class Cfg {
const DB_HOST = 'localhost';
const DB_USER = '<ПОЛЬЗОВАТЕЛЬ-БД>';
const DB_PASS = '<ПАРОЛЬ-БД>';
const DB_NAME = '<ИМЯ-БД>';
const EMAIL_FROM = 'It-Rem Forum <no-reply@forum.it-rem.ru>';
const EMAIL_REPLY_TO = 'no-reply@forum.it-rem.ru';
const EMAIL_TO = 'new@forum.it-rem.ru';
const EMAIL_SUBJECT = 'New posts at forum.it-rem.ru';
const POSTS_PER_MAIL_LIMIT = 50; // per email limit
const ADMIN_TIMEZONE_OFFSET = 3; // in hours from UTC
const PHPBB_TBL_PREFIX = 'phpbb_';
const PHPBB_BASE_URL = 'http://forum.it-rem.ru'; // base url of forum, ex: http://forum.site.com
const ALLOW_IGNORE_USERS = true; // that option allow ignore messages from specified users (admins, moderators, etc..)
public static function getIgnoreUsersSql() {
$ignoreUsersWithEmails = [ // specify here emails that we should ignore
'admin@forum.it-rem.ru',
];
if (!self::ALLOW_IGNORE_USERS OR empty($ignoreUsersWithEmails)) return '';
return ' AND `users`.`user_email`!="'
.implode('" AND `users`.`user_email`!="', $ignoreUsersWithEmails)
.'"'
;
}
public static function getVar($var, $default=null) {
$values = [];
$storagePath = dirname(__FILE__).'/storage.json';
if (file_exists($storagePath)) {
$values = json_decode(file_get_contents($storagePath), true);
if (!is_array($values)) $values =[];
}
return isset($values[$var]) ? $values[$var] : $default;
}
public static function setVar($var, $val) {
$values = [];
$storagePath = dirname(__FILE__).'/storage.json';
if (file_exists($storagePath)) {
$values = json_decode(file_get_contents($storagePath), true);
if (!is_array($values)) $values =[];
}
$values[$var] = $val;
file_put_contents($storagePath, json_encode($values));
}
}
class DbConnection {
private $host = '';
private $user = '';
private $pass = '';
private $dbName = '';
private $defaultCharset = 'utf8';
private $dbLink = null;
public function __construct($host, $user, $pass, $dbName) {
$this->host = $host;
$this->user = $user;
$this->pass = $pass;
$this->dbName = $dbName;
}
public function getConnection($force=false){
if (is_null($this->dbLink) || $force) {
$this->dbLink = new mysqli($this->host, $this->user, $this->pass, $this->dbName);
if ($this->dbLink->connect_errno) {
echo "MySQL Error: Failed to make a MySQL connection, here is why: \n";
echo "Errno: " . $this->dbLink->connect_errno . "\n";
echo "Error: " . $this->dbLink->connect_error . "\n";
exit;
}
}
if (!$this->dbLink->set_charset($this->defaultCharset)) {
echo "MySQL Error loading character set $charset\n";
echo "Errno: " . $this->dbLink->errno . "\n";
echo "Error: " . $this->dbLink->error . "\n";
exit;
}
return $this->dbLink;
}
public function setCharset($charset='utf8') {
$db = $this->getConnection();
if (!$db->set_charset($charset)){
echo "MySQL Error loading character set $charset\n";
echo "Errno: " . $db->errno . "\n";
echo "Error: " . $db->error . "\n";
exit;
}
}
public function query($sql){
$db = $this->getConnection();
if (!$result = $db->query($sql)) {
echo "MySQL Error: Our query failed to execute and here is why: \n";
echo "Query: " . $sql . "\n";
echo "Errno: " . $db->errno . "\n";
echo "Error: " . $db->error . "\n";
exit;
}
return $result;
}
public function select($sql){
$result = $this->query($sql);
$ret = [];
if ($result->num_rows !== 0) {
while ($row = $result->fetch_assoc()) {
$ret[] = $row;
}
}
$result->free();
return $ret;
}
public function close(){
if (!is_null($this->dbLink)) {
$this->dbLink->close();
}
}
public function __destruct(){
$this->close();
}
}
$db = new DbConnection(Cfg::DB_HOST, Cfg::DB_USER, Cfg::DB_PASS, Cfg::DB_NAME);
$lastSentPostId = Cfg::getVar('lastSentPostId',0);
$sql = 'SELECT
`posts`.`post_id` AS `post_id`,
`posts`.`forum_id` AS `forum_id`,
`posts`.`topic_id` AS `topic_id`,
`posts`.`post_time` AS `time`,
`posts`.`post_subject` AS `subject`,
`posts`.`post_text` AS `text`,
`users`.`username` AS `author`,
`users`.`user_email` AS `email`
FROM
`'.Cfg::PHPBB_TBL_PREFIX.'posts` AS `posts`
LEFT JOIN
`'.Cfg::PHPBB_TBL_PREFIX.'users` AS `users`
ON
`posts`.`poster_id` = `users`.`user_id`
WHERE
`posts`.`post_id`>'.intval($lastSentPostId).'
'.Cfg::getIgnoreUsersSql().'
ORDER BY
`posts`.`post_id` ASC
LIMIT '.Cfg::POSTS_PER_MAIL_LIMIT.'
';
$newPosts = $db->select($sql);
if (!$newPosts) {
echo 'No new posts from last check';
exit;
}
$msgTemplate = '
<html>
<head>
<style>
div.text {
background-color: #F5F5F5;
font-size:13px;
margin:15px 0px;
max-width:600px;
border: 1px solid #D5D5D5;
border-left: 2px solid silver;
border-radius:5px;
padding:15px;
}
blockquote {
background-color: #d5d5d5;
border: 1px solid #c5c5c5;
padding: 5px;
color:black;
margin:0px 0px 0px 15px;
border-radius:2px;
}
blockquote blockquote {
background-color: #e4e4e4;
margin: 0.5em 1px 0 15px;
}
blockquote blockquote blockquote {
background-color: #f4f4f4;
}
.bbcode_tags {color:silver;}
</style>
</head>
<body style="font-family:Courier; font-size:14px;">
{body}
</body>
</html>
';
$msgPostTemplate = '
<strong>User</strong>: {author} <{email}><br>
<strong>Subject</strong>: {subject}<br>
<strong>Date</strong>: {date}<br>
<strong>Text</strong>:
<div class="text">
{text}
</div>
<strong>Manage</strong>: <a href="{forum_url}/viewtopic.php?f={forum_id}&t={topic_id}">Open topic</a><br>
<br><hr><br>
';
$msgBody = '';
foreach($newPosts as $post) {
$text = $post['text'];
// replace quotes bbcode
$text = preg_replace('~\[quote[^:\]]*:[^\]]+\]~','<blockquote>',$text);
$text = preg_replace('~\[/quote:[^\]]+\]~','</blockquote>',$text);
// replace code bbcode
$text = preg_replace('~\[code[^:\]]*:[^\]]+\]~','<code>',$text);
$text = preg_replace('~\[/code:[^\]]+\]~','</code>',$text);
// replace b,i,u bbcodes
$text = preg_replace('~\[(b|u|i):[^\]]+\]~','<$1>',$text);
$text = preg_replace('~\[/(b|u|i):[^\]]+\]~','</$1>',$text);
// replace urls bbcode
$text = preg_replace('~\[url:[^\]]+\]([^\[]+)(?=\[)~','<a href="$1">$1',$text);
$text = preg_replace('~\[url=([^:\]]+):[^\]]+\]([^\[]+)(?=\[)~','<a href="$1">$2',$text);
$text = preg_replace('~\[/url:[^\]]+\]~','</a>',$text);
// replace video bbcode
$text = preg_replace('~\[\s?video:[^\]]+\]([^\[]+)(?=\[)~','<a href="$1">$1',$text);
$text = preg_replace('~\[/video:[^\]]+\]~','</a>',$text);
// replace other bbcode
$text = preg_replace('~\[([^:\]]+):[^\]]+\]~','<span class="bbcode_tags">[$1]</span>',$text);
// clean up double spaces and double line endings. Convert line ending to new lines (br)
// $text = preg_replace('~(\s)\s+~','$1',$text);
$text = str_replace("\n",'<br>', trim($text));
// Fix unclosed tags
$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="utf-8" ?>'.$text, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$text = $dom->saveHTML();
$msgBody .= str_replace(
[
'{author}',
'{email}',
'{subject}',
'{date}',
'{text}',
'{forum_url}',
'{forum_id}',
'{topic_id}',
], [
htmlspecialchars($post['author']),
$post['email'] ? htmlspecialchars($post['email']) : 'undefined@email',
$post['subject'],
date('d.m.y H:i',strtotime('+'.Cfg::ADMIN_TIMEZONE_OFFSET.' hours',$post['time'])),
$text,
Cfg::PHPBB_BASE_URL,
intval($post['forum_id']),
intval($post['topic_id']),
],
$msgPostTemplate
);
}
$emailMessage = str_replace('{body}',$msgBody, $msgTemplate);
$emailheaders = "From: " . Cfg::EMAIL_FROM . "\r\n";
$emailheaders .= "Reply-To: ". Cfg::EMAIL_REPLY_TO . "\r\n";
$emailheaders .= "MIME-Version: 1.0\r\n";
$emailheaders .= "Content-Type: text/html; charset=UTF-8\r\n";
if (mail(Cfg::EMAIL_TO, Cfg::EMAIL_SUBJECT, $emailMessage, $emailheaders)) {
Cfg::setVar('lastSentPostId', $newPosts[count($newPosts)-1]['post_id']);
echo 'Message about '.count($newPosts).' new post was succesfully sent'.PHP_EOL;
} else {
echo 'Error: Mail not sent'.PHP_EOL;
}
P.S. Весь описанный в этой статье код был актуален для phpBB 3.1.9, в новых версиях код возможно но не факт потребует актуализации.
Отправлено спустя 2 минуты 30 секунд:
Источник:
https://www.it-rem.ru/izbavlyaemsya-ot- ... phpbb.html
https://www.it-rem.ru/chistka-i-moderir ... phpbb.html