[JS] ScammerController | Расширение для XSS.IS с детектом СКАМЕРОВ!

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0
Всех приветствую, это моя четвертая статья, и возможно она с слишком кликабельным названием, но все же свою функцию оно выполняет -> расширение предупреждает пользователя в диалоге о том, что на собеседника есть открытая жалоба.
Это мой первый опыт в написании расширений, ранее я никогда этим не занимался и до сих пор не совсем обладаю опытом и нужными знаниями, эта статья нацелена для таких же как и я новичков в сфере написания браузерных расширений.

ДИСКЛЕЙМЕР: укажу все недоработки в шапке, чтобы ниже не оставалось ни у кого вопросов.
  • пароль к архиву с расширением ниже: xss.is
  • я не реализовал поиск уже закрытых жалоб, чтобы они не показывались в диалоге (я не понял как это правильно сделать, мои методы с поиском классов structItem-status или blockStatus-messag не сработали) ;
  • я не реализовал поддержку английского языка ;
  • я не совсем уверен, что эта версия расширения работает именно так, как нужно. выкладываю эту статью и эту версию с целью собрать фидбэк и доработать это расширение до идеального состояния, ибо моего опыта и знаний не хватило для этого. пожалуйста, не кидайтесь камнями.
  • я не реализовал поддержку темной темы.
TODO лист:
  1. реализовать поиск уже закрытых жалоб, чтобы они не показывались в диалоге как активный блек ;
  2. добавить поддержку английского языка (корректная работа на английской раскладке форума) ;
  3. сделать предупреждение более "ярким", чтобы оно попадало сразу в глаза. возможен перенос этого блока ;
  4. найти и исправить все (или максимально возможное количество) баги, которые присутствуют в этой версии расширения ;
  5. адаптировать расширение к темной теме форума ;
Я расположил задачи от самой важной, до менее важных соответственно. Постепенно этот TODO лист будет пополняться, а уже реализованные функции будут зачеркиваться по мере редактирования этой темы и самого расширения.

КАК РАСШИРЕНИЕ ВЫГЛЯДИТ НА ДАННЫЙ МОМЕНТ (момент написания статьи, 29.12.2024 14:18 МСК)

ЕСЛИ НА СОБЕСЕДНИКА ЕСТЬ НАПИСАННЫЕ ЖАЛОБЫ:
Спойлер: скриншот
wbAf5JC.png



ЕСЛИ НА СОБЕСЕДНИКА НЕТ НАПИСАННЫХ ЖАЛОБ:
Спойлер: скриншот
nVJ46lN.png



Приступим к написанию самого расширения.

Наверное каждый сталкивался с ситуацией, когда связывался с каким-нибудь человеком на форуме, а оказывается, на него был написан блек. Вы этого не посмотрели, либо при просмотре ветки с жалобами не заметили, и попались на недобросовестного пользователя. Это расширение поможет с этим и теперь не придется мониторить раздел с жалобами самостоятельно. Если на пользователя есть открытые жалобы - расширение нам скажет об этом. Писать наше расширение мы будем на JavaScript без использования дополнительных фреймворков.

Структура проекта:
pOzylhr.png


  • images: папка с логотипами для расширения.
  • background.js: скрипт для оповещения в консоли разработчика о том, что расширение корректно установлено.
  • contents.js: основная логика расширения.
  • popup.html: скрипт для функций при нажатии на иконку расширения в браузере
  • styles.css: стили расширения.
Начало. Manifest.json.

JSON: Скопировать в буфер обмена
Код:
{
  "manifest_version": 3,
  "name": "ScammersController",
  "version": "0.1",
  "description": "Расширение помогает наблюдать за скамерами. Написал AGN, специально для XSS.is.",
  "permissions": [
    "activeTab"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": ["https://xss.is/conversations/*"],
      "js": ["content.js"]
    }
  ],
  "icons": {
    "16": "images/icon16.png",
    "48": "images/icon48.png",
    "128": "images/icon128.png"
  }
}
  • manifest_version: указывает на использование версии.
  • name: название расширения.
  • version: версия расширения.
  • description: описание расширения.
  • permissions: разрешение activeTab позволяет расширению взаимодействовать с текущей активной вкладкой браузера.
  • background: ссылка на фоновый скрипт, который использует сервис (background.js).
  • content_scripts: указывает, что скрипт content.js будет выполняться на страницах форума, где URL включает /conversations/ (иными словами - в диалогах пользователей)
  • icons: путь к иконкам расширения для разных размеров.
background.js

JavaScript: Скопировать в буфер обмена
Код:
chrome.runtime.onInstalled.addListener(() => {
    console.log("Extension installed!");
});
  • скрипт выполняется при установке или обновлении расширения. когда расширение устанавливается, срабатывает событие onInstalled, и выводится сообщение в консоль браузера о том, что расширение установлено.
content.js

Функция isConversationPage()

JavaScript: Скопировать в буфер обмена
Код:
function isConversationPage() {
    return window.location.href.includes('conversations');
}
  • эта функция проверяет, находимся ли мы на странице личных сообщений (переписки) на форуме..
    для этого она использует свойство window.location.href, которое содержит текущий URL страницы, и проверяет, включает ли этот URL подстроку conversations. если подстрока присутствует, то функция возвращает true, что означает, что мы на странице переписки.
Функция getOtherUsernameFromConversation()
JavaScript: Скопировать в буфер обмена
Код:
function getOtherUsernameFromConversation() {
    const participantElements = document.querySelectorAll('.block-body .contentRow-main .username');
   
    if (participantElements.length > 1) {
        const otherUsername = participantElements[1].innerText.trim();
        console.log('Other Username:', otherUsername);
        return otherUsername;
    }

    console.log('No other username found!');
    return null;
}
  • функция извлекает имя собеседника из страницы личного сообщения.
  • сначала она находит все элементы с классом .username в блоке переписки. эти элементы содержат имена участников.
  • если на странице больше одного участника (должен быть второй элемент в списке участников), функция возвращает имя второго участника (собеседника).
  • если собеседник не найден (например, мы не находим второго пользователя), выводится сообщение об ошибке, и возвращается null.
Функция fetchComplaints(username)
JavaScript: Скопировать в буфер обмена
Код:
async function fetchComplaints(username) {
    const complaints = [];
    const response = await fetch("https://xss.is/forums/82/");
    const pageText = await response.text();

    console.log('Page Text:', pageText.substring(0, 1000));

    const regex = new RegExp(`<a href="\\/threads\\/([0-9]+)\\/.*?">${username}.*?<\/a>`, "g");
    let match;

    while ((match = regex.exec(pageText)) !== null) {
        const threadId = match[1];
        const url = `https://xss.is/threads/${threadId}/`;

        const hasLockIcon = pageText.includes(`<a href="/threads/${threadId}/">`) && pageText.includes('<i class="structItem-status structItem-status--locked" aria-hidden="true" title="Закрыто"></i>');
       
        const hasClosedMessage = pageText.includes(`<a href="/threads/${threadId}/"`) && pageText.includes('Закрыто для дальнейших ответов');
       
        if (hasLockIcon || hasClosedMessage) {
            console.log(`Тема с ID ${threadId} закрыта, пропускаем...`);
            continue;
        }

        const title = match[2] || "Ссылка на блек";
        complaints.push({ url, title });
    }

    console.log('Complaints:', complaints);
    return complaints;
}
  • эта функция собирает жалобы на пользователя на форуме с помощью запросов.
  • сначала она запрашивает страницу форума с помощью fetch(), получает текст страницы и извлекает из нее все упоминания заданного пользователя.
  • для извлечения жалоб используется регулярное выражение, которое находит все ссылки, содержащие имя пользователя. регулярное выражение ищет темы форума, где упоминается имя пользователя.
  • далее проверяется, не закрыта ли тема (с помощью наличия иконки замка или сообщения о закрытой теме). если тема закрыта, она пропускается (почему-то этот метод не срабатывает)
  • в список жалоб добавляется URL темы и ее заголовок.
  • функция возвращает массив объектов с жалобами, содержащими ссылку и заголовок темы.
Функция createComplaintWarning(complaints)
JavaScript: Скопировать в буфер обмена
Код:
function createComplaintWarning(complaints) {
    if (complaints.length === 0) return;

    const warningBlock = document.createElement('div');
    warningBlock.className = 'block';

    const warningContent = `
      <div class="block-container">
        <h3 class="block-minorHeader" style="font-size: 16px; font-weight: bold; color: #34495E;">ВНИМАНИЕ! НА ПОЛЬЗОВАТЕЛЯ ОТКРЫТ БЛЕК!</h3>
        <div class="block-body block-row block-row--minor">
          <p>На пользователя, с кем вы ведете диалог, открыт блек:</p>
          <ul class="block-body">
            ${complaints.map(complaint => `
              <li class="block-row" style="padding: 5px 0;">
                <a href="${complaint.url}" class="username" style="color: #34495E; text-decoration: none;" target="_blank">${complaint.title}</a>
              </li>
            `).join('')}
          </ul>
        </div>
      </div>
    `;
    warningBlock.innerHTML = warningContent;

    const sidebar = document.querySelector('.p-body-sidebar');
    if (sidebar) {
        sidebar.appendChild(warningBlock);
    }
}
  • эта функция создает и отображает предупреждение о том, что на пользователя, с которым ведется переписка, есть жалобы на форуме. если список жалоб пуст, то функция ничего не делает.
  • если жалобы есть, она создает новый HTML-блок, который содержит заголовок и список жалоб, каждую из которых представляет собой ссылку на тему форума. этот блок вставляется в боковую панель страницы личного сообщения (элемент .p-body-sidebar), чтобы пользователь мог увидеть предупреждение.
Функция if (isConversationPage()) (основная логика скрипта)
JavaScript: Скопировать в буфер обмена
Код:
if (isConversationPage()) {
    const otherUsername = getOtherUsernameFromConversation();
    if (otherUsername) {
        fetchComplaints(otherUsername).then(complaints => {
            if (complaints.length > 0) {
                createComplaintWarning(complaints);
            } else {
                console.log('Нет открытых жалоб на этого пользователя.');
            }
        }).catch((error) => {
            console.error('Ошибка при получении жалоб:', error);
        });
    }
}
  • этот фрагмент кода выполняется, если мы находимся на странице личного сообщения (проверка через isConversationPage()).
  • извлекается имя собеседника через getOtherUsernameFromConversation().
  • если имя собеседника найдено, выполняется запрос к форуму через fetchComplaints() для получения жалоб.
  • если жалобы найдены, вызывается функция createComplaintWarning() для отображения предупреждения.
  • в случае ошибки при запросе жалоб выводится сообщение об ошибке в консоль.
styles.css

CSS: Скопировать в буфер обмена
Код:
.block {
    background-color: #fff3cd;
    border: 1px solid #ffeeba;
    padding: 10px;
    margin-top: 20px;
    border-radius: 5px;
}

.block-container {
    padding: 15px;
}

.block-minorHeader {
    font-size: 18px;
    font-weight: bold;
    color: #856404;
}

.block-body {
    padding: 15px;
}

.block-row {
    padding: 5px 0;
}

.username {
    color: #007aff;
    text-decoration: none;
}

.username:hover {
    text-decoration: underline;
}
  • .block — отвечает за внешний вид контейнера с предупреждением, включая фон, границу, отступы.
  • .block-minorHeader — стили для заголовка предупреждения.
  • .block-body — стили для основного содержимого блока.
  • .username — стили для ссылок на темы жалоб.
popup.html
этот HTML-код создает простой интерфейс для попапа, который открывается при нажатии на иконку расширения.
HTML: Скопировать в буфер обмена
Код:
<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Forum Complaints Notifier</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      padding: 10px;
      max-width: 200px;
    }
    button {
      padding: 10px;
      font-size: 14px;
      background-color: #007aff;
      color: white;
      border: none;
      border-radius: 5px;
      cursor: pointer;
    }
    button:hover {
      background-color: #005bb5;
    }
  </style>
</head>
<body>
  <h3>Forum Complaints Notifier</h3>
  <button id="toggleNotifications">Toggle Notifications</button>
  <script>
    document.getElementById('toggleNotifications').addEventListener('click', () => {
      alert("This feature will be implemented soon.");
    });
  </script>
</body>
</html>



Установка расширения:

  1. Пишем расширение сами основываясь на эту статью либо скачиваем уже готовый проект ниже.
  2. Переходим по ссылке chrome://extensions/ в вашем браузере и включаем режим разработчика.
    Спойлер: скриншот
    5Yrodrd.png
  3. Нажимаем на кнопку "Загрузить распакованное расширение"
    Спойлер: скриншот
    VGUeAOx.png
  4. Выбираем папку с нашим расширением и загружаем ее.
  5. Обновляем страницу с форумом и проверяем, работает ли расширение. Для этого достаточно перейти в диалог с пользователем, на которого есть открытая жалоба.


Заключение:
Как я ранее сказал, то это мой первый опыт в написании расширений, и как мне кажется, получилось довольно неплохо. Если эта статья вызовет интерес у читателей, то я с великим удовольствием буду ждать фидбека о том, что можно улучшить и доработать, а так же решение моей проблемы с невозможностью исправить поиск закрытых жалоб и их отображения в баннере (чтобы закрытые блеки не отображались в ЛС). На данный момент я параллельно работаю над еще одним расширениям по заметкам для пользователей, но на данный момент оно дается сложней к написанию, чем этот проект. Всем читателям заранее большое спасибо, надеюсь, эта статья была для вас интересна.

Авторство: AGN
Специально для форума https://xss.is/
 
Сверху Снизу