<?php
// Устанавливаем часовой пояс Томск (+7) (Вы можете выбрать свой, если нужно)
date_default_timezone_set('Asia/Tomsk');
$dir = __DIR__;
require($dir . '/src/XF.php');
try {
XF::start($dir);
// Повторно устанавливаем часовой пояс (Здесь необходимо повторно установить ваш часовой пояс)
date_default_timezone_set('Asia/Tomsk');
// Установите права администратора (Укажите Ваш ID администратора на форуме где user_id', 1)
$adminUser = \XF::finder('XF:User')->where('user_id', 1)->fetchOne();
if ($adminUser) {
\XF::setVisitor($adminUser);
} else {
throw new \Exception("Не удалось установить пользователя ID=1");
}
$excludeThreadIds = [1, 2, 3, 10, 20]; // Исключайте темы здесь по ID через запятую.
// Ключевые слова для спама (Рекламные маркеры)
$spamKeywords = [
'даркнет', 'darknet', 'kra34.cc', 'kra34.at', 'krn28.click', 'kra27.at', 'kraken', 'darkweb', 'KRaKeN?!'
];
$spamPatterns = [
'/kra\d{1,2}\.(cc|at)/i',
'/\.onion/i',
'/[!?\@]{2,}/',
'/[\p{So}]/u'
];
// Ключевое слово "kraken" проверяем только с другими маркерами
$secondarySpamKeywords = ['kraken', 'KRAKEN', 'Kraken'];
$threadsFinder = \XF::finder('XF:Thread')
->with(['User', 'FirstPost'])
->where('discussion_state', ['visible', 'moderated'])
->where('thread_id', '!=', $excludeThreadIds[0])
->order('thread_id', 'DESC');
$keywordConditions = [];
foreach ($spamKeywords as $keyword) {
$keywordConditions[] = ['FirstPost.message', 'LIKE', "%$keyword%"];
$keywordConditions[] = ['title', 'LIKE', "%$keyword%"];
}
foreach ($secondarySpamKeywords as $keyword) {
foreach ($spamKeywords as $additionalKeyword) {
$keywordConditions[] = [
['FirstPost.message', 'LIKE', "%$keyword%"],
['FirstPost.message', 'LIKE', "%$additionalKeyword%"]
];
$keywordConditions[] = [
['title', 'LIKE', "%$keyword%"],
['title', 'LIKE', "%$additionalKeyword%"]
];
}
}
if (!empty($keywordConditions)) {
$threadsFinder = $threadsFinder->whereOr($keywordConditions);
}
$sqlQuery = $threadsFinder->getQuery();
$threads = $threadsFinder->fetch();
$filteredThreads = [];
foreach ($threads as $thread) {
$textToCheck = ($thread->title ?? '') . ' ' . ($thread->FirstPost->message ?? '');
$isSpam = false;
foreach ($spamPatterns as $pattern) {
if (preg_match($pattern, $textToCheck)) {
$isSpam = true;
break;
}
}
if ($isSpam) {
$filteredThreads[$thread->thread_id] = $thread;
}
}
// Подсчёт тем
$threadCount = count($filteredThreads);
// Общее количество тем и сообщений
$totalThreads = \XF::finder('XF:Thread')->total();
$totalPosts = \XF::finder('XF:Post')->total();
// Обработка удаления
$confirmDeletion = \XF::app()->request()->filter('delete_confirm', 'bool');
$deleteThreadIds = \XF::app()->request()->filter('delete_threads', 'array-uint');
$errorMessages = [];
if ($confirmDeletion && !empty($deleteThreadIds)) {
foreach ($filteredThreads as $thread) {
if (in_array($thread->thread_id, $deleteThreadIds)) {
try {
$error = '';
if ($thread->canDelete($error)) {
$thread->delete();
} else {
$errorMessages[] = "Невозможно удалить тему #{$thread->thread_id}: " . ($error ?: 'Отсутствуют права на удаление');
}
} catch (\Exception $e) {
$errorMessages[] = "Ошибка при удалении темы #{$thread->thread_id}: " . $e->getMessage();
}
}
}
if (empty($errorMessages)) {
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
}
} catch (\Exception $e) {
$errorMessage = "Критическая ошибка: " . $e->getMessage() . "\nStack trace: " . $e->getTraceAsString();
$errorMessages[] = $errorMessage;
http_response_code(500);
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Управление спам-темами</title>
<meta charset="UTF-8">
<style>
body { font-family: Georgia, serif; }
table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
th:nth-child(1), th:nth-child(2), th:nth-child(4), th:nth-child(5) {
white-space: nowrap;
width: 100px;
}
th:nth-child(3) {
width: 800px;
white-space: nowrap;
}
.delete-btn { background-color: #ff4444; color: white; padding: 5px 10px; border: none; cursor: pointer; }
.delete-btn:hover { background-color: #cc0000; }
.error { color: red; margin-top: 10px; margin-bottom: 10px; }
.info {
color: #FF0000;
margin-top: 10px;
margin-bottom: 10px;
border: 2px solid #8B0000;
border-radius: 5px;
padding: 10px;
background-color: #FFF3F3;
text-align: center;
font-weight: bold;
}
.debug { margin-top: 10px; margin-bottom: 10px; }
.debug p { margin: 0; }
.label { color: #FF4500; }
.value { color: #008000; }
.warning {
border: 2px solid #FF0000;
background-color: #FFF3F3;
padding: 15px;
margin-bottom: 20px;
border-radius: 5px;
}
.warning h3 {
color: #FF0000;
margin-top: 0;
font-size: 1.2em;
}
.warning p {
margin: 5px 0;
}
.warning ul {
margin-left: 20px;
list-style-type: disc;
}
.description { margin-bottom: 15px; }
.description .label { font-weight: bold; }
h2 {
text-align: center;
color: #333333;
}
hr {
border: 0;
border-top: 1px solid #333333;
margin: 10px 0;
}
</style>
</head>
<body>
<h2>Управление спам-темами форума</h2>
<hr>
<div class="description">
<p><span class="label">Описание:</span><br>Этот инструмент позволяет находить и удалять спам-темы, связанные с даркнет-маркетплейсом «Кракен», содержащие ключевые слова (Например: «даркнет», «darknet», «kra34.cc», «kra34.at», «krn28.click», «kra27.at», «kraken», «darkweb», «KRaKeN?!») в заголовке или первом сообщении. Используйте с осторожностью и не разглашайте доступ к скрипту.</p><hr>
<p><span class="label">Дополнительная информация:</span><br><b>За идею благодарю:</b> <a href="https://xenforo.info/members/west14.22547/" style="color: #008000;">West14</a>.<br><b>Реализовал:</b> <a href="https://xenforo.info/members/shumasick.64948/" style="color: #8B0000;">Shumasick</a></p>
</div>
<hr>
<?php if (!empty($errorMessages)): ?>
<div class="error">
<?php foreach ($errorMessages as $error): ?>
<p><?= htmlspecialchars($error) ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="warning">
<h3>ВНИМАНИЕ</h3>
<ul>
<li>Не разглашайте имя или существование этого файла. Доступ к нему должен быть строго ограничен, чтобы предотвратить несанкционированное использование и потенциальные проблемы с безопасностью форума.</li>
</ul>
</div>
<div class="debug">
<p><b><span class="label">Скрипт запущен: </span><span class="value"><?php
$date = new DateTime();
echo $date->format('d.m.Y H:i:s');
?></span></b></p>
<p><b><span class="label">Текущий часовой пояс: </span><span class="value"><?= date_default_timezone_get() ?></span></b></p>
<p><b><span class="label">Общее количество тем в базе: </span><span class="value"><?= isset($totalThreads) ? $totalThreads : 'Н/Д' ?></span></b></p>
<p><b><span class="label">Общее количество сообщений в базе: </span><span class="value"><?= isset($totalPosts) ? $totalPosts : 'Н/Д' ?></span></b></p>
<p><b><span class="label">Найдено тем по критериям: </span><span class="value"><?= $threadCount ?? 'Н/Д' ?></span></b></p>
</div>
<?php if (empty($filteredThreads)): ?>
<div class="info">
<p>Подходящие темы не найдены.</p>
</div>
<?php else: ?>
<form method="post">
<table>
<tr>
<th><input type="checkbox" id="selectAll"> Выбрать все</th>
<th>ID Темы:</th>
<th>Название Темы:</th>
<th>ID Пользователя:</th>
<th>Пользователь:</th>
<th>Статус Темы:</th>
<th>Первые 50 символов сообщения</th>
</tr>
<?php foreach ($filteredThreads as $thread): ?>
<tr>
<td><input type="checkbox" name="delete_threads[]" value="<?= $thread->thread_id ?>"></td>
<td><?= $thread->thread_id ?></td>
<td><?= htmlspecialchars($thread->title) ?></td>
<td><?= $thread->user_id ?? 'N/A' ?></td>
<td><?= htmlspecialchars($thread->User->username ?? 'Неизвестный пользователь') ?></td>
<td><?= $thread->discussion_state === 'moderated' ? 'На модерации' : ($thread->isDeleted() ? 'Удалена' : 'Активна') ?></td>
<td><?= htmlspecialchars(substr($thread->FirstPost->message ?? 'Нет сообщения', 0, 50)) . (strlen($thread->FirstPost->message ?? '') > 50 ? '...' : '') ?></td>
</tr>
<?php endforeach; ?>
</table>
<input type="hidden" name="delete_confirm" value="1">
<button type="submit" class="delete-btn">Удалить выбранные темы</button>
</form>
<p>Задействованные ID пользователей: <?= implode(', ', array_unique(array_filter(array_column($filteredThreads, 'user_id')))) ?></p>
<?php endif; ?>
<script>
document.getElementById('selectAll')?.addEventListener('change', function(e) {
document.querySelectorAll('input[name="delete_threads[]"]').forEach(checkbox => {
checkbox.checked = e.target.checked;
});
});
</script>
</body>
</html>