javascript

Автоматически скрываем лишние истории в VK: простой браузерный скрипт

  • воскресенье, 23 ноября 2025 г. в 00:00:03
https://habr.com/ru/articles/969048/

Зачем вообще что-то делать с историями во VK

Со временем во VK у меня набралось несколько тысяч друзей. Отчасти это осознанная стратегия: расширение сети контактов помогает продвигать свои проекты, находить коллаборации и тестировать идеи на более широкой аудитории.

Но есть обратная сторона. Лента историй превращается в хаотичную карусель:

  • люди, с которыми вы давно не общаетесь;

  • рабочие контакты;

  • случайные добавления «на всякий случай»;

  • тестовые или заброшенные аккаунты.

При этом истории для меня — про личный контекст: что происходит у близких друзей, семьи, небольшой группы людей, с которыми действительно хочется оставаться на связи. Удалять всех подряд из друзей не вариант, сеть всё ещё полезна. Хотелось другого решения:

Оставить большую сеть контактов для работы и при этом видеть истории только от небольшого круга людей.

В итоге я написал небольшой скрипт, который автоматизирует ту рутину, на которую руками я бы никогда не потратил столько времени.

Исходники и инструкция:
https://github.com/AleksPetrakov/vk-hide-stories-script/


Идея решения

Задача звучит просто:

  • скрывать истории почти от всех;

  • не трогать истории ограниченного набора людей:

    • по имени (как оно отображается в шапке истории);

    • по ID (из ссылки вида /id123456 или /nickname).

Это типичная работа для автоматизации:

  1. Открыт просмотрщик историй VK, текущая история по центру.

  2. Скрипт смотрит, кто автор текущей истории.

  3. Если автор в списке исключений — историю пропускаем, переходим к следующей.

  4. Если автора нет в списке — открываем меню истории и нажимаем «Скрыть из историй» с подтверждением.

Скрипт запускается один раз и дальше сам крутит цикл заданное количество итераций. После старта он продолжает работать даже в неактивной вкладке: можно переключиться в другие вкладки или приложения, а через какое-то время вернуться к VK уже с очищенной лентой историй.


Как выглядит скрипт с точки зрения пользователя

Скрипт живёт в файле vk-stories-auto-hider.js. Это не расширение браузера, никаких установок не требуется. Достаточно один раз вставить код в консоль и настроить списки исключений.

На верхнем уровне внутри файла есть две ключевые части:

  1. Настройки исключений.

  2. Функция запуска цикла.

Настройки исключений

В начале файла находятся два массива:

// Список имён друзей, чьи истории НЕ нужно скрывать.
// Формат: 'Имя Фамилия', регистр не важен.
const EXCLUDED_NAMES = [
  // 'Имя Фамилия',
];

// Список id друзей (из href="/idилиник"), чьи истории НЕ нужно скрывать.
const EXCLUDED_IDS = [
  // 'id123456',
];

Идея простая:

  • EXCLUDED_NAMES — удобно для близких людей, где имя очевидно и редко меняется.

  • EXCLUDED_IDS — для точного контроля, когда важен именно ID профиля, а имя может меняться.

Строки сравниваются без учёта регистра, перед этим нормализуются и обрезаются пробелы.

Запуск скрипта

Основная функция выглядит примерно так:

async function hideVkStoriesWithExclusions(cyclesCount = 200) {
  // ...
}

Вызов:

hideVkStoriesWithExclusions(200);

означает «попробовать обработать до 200 историй подряд», где под «обработать» понимается либо скрыть историю, либо пропустить её, если автор в списке исключений.


Как это работает внутри

Ниже — упрощённый разбор логики. Полная версия со всеми проверками и логами есть в репозитории на GitHub.

Поиск текущей активной истории

VK помечает текущий элемент истории классами вроде:

.stories_item.multi_stories.active

или, как запасной вариант:

.stories_item.active

Скрипт ищет активный элемент:

function getActiveStoryElement() {
  return (
    document.querySelector('.stories_item.multi_stories.active') ||
    document.querySelector('.stories_item.active')
  );
}

Получение имени и ID автора

Дальше нужно понять, кто автор текущей истории. Для этого в шапке истории берётся:

  • имя — из элемента с классами StoryInfo__title StoryInfo__title--author;

  • ID — из href:

    • либо этой же ссылки с именем;

    • либо ссылки на аватарку автора (a.stories_author_avatar[href]).

Примерно так:

function getAuthorInfoFromActiveStory(activeStoryEl) {
  if (!activeStoryEl) return { name: null, id: null };

  const nameLink =
    activeStoryEl.querySelector('.StoryInfo__title.StoryInfo__title--author') ||
    activeStoryEl.querySelector('.StoryInfo__title--author');

  const rawName = nameLink ? nameLink.textContent || '' : '';
  const name = rawName.trim();

  let rawHref = null;

  if (nameLink && nameLink.getAttribute('href')) {
    rawHref = nameLink.getAttribute('href');
  } else {
    const avatarLink = activeStoryEl.querySelector(
      'a.stories_author_avatar[href]'
    );
    if (avatarLink) rawHref = avatarLink.getAttribute('href');
  }

  let id = null;
  if (rawHref) {
    const href = rawHref.trim();
    if (href.startsWith('/')) {
      const withoutSlash = href.slice(1);
      const withoutQuery = withoutSlash.split('?')[0].split('#')[0];
      const firstPart = withoutQuery.split('/')[0];
      id = firstPart || null;
    }
  }

  return { name, id };
}

Полученные name и id дальше нормализуются и сравниваются с массивами EXCLUDED_NAMES и EXCLUDED_IDS.

Логика исключений

Проверка вынесена в отдельную функцию:

function normalizeString(str) {
  return (str || '').normalize('NFC').trim().toLowerCase();
}

function isAuthorExcluded(authorInfo) {
  const { name, id } = authorInfo;

  const normalizedName = normalizeString(name);
  const normalizedId = normalizeString(id);

  const excludedNamesNormalized = EXCLUDED_NAMES.map(normalizeString);
  const excludedIdsNormalized = EXCLUDED_IDS.map(normalizeString);

  const byName =
    !!normalizedName &&
    excludedNamesNormalized.includes(normalizedName);

  const byId =
    !!normalizedId &&
    excludedIdsNormalized.includes(normalizedId);

  return byName || byId;
}

Если автор в исключениях, история не скрывается. Вместо этого нужно аккуратно перейти к следующей истории.


Как пропустить историю из списка исключений

Здесь важный момент: я специально не привязывался к конкретным кнопкам внутри интерфейса VK (например, к стрелкам). Вместо этого скрипт имитирует «человеческий» клик по области экрана чуть правее текущей истории.

Алгоритм:

  1. Находим контейнер текущей истории .stories_item_cont.

  2. Берём его размер через getBoundingClientRect().

  3. Рассчитываем координату клика:

    • x — правая граница истории + 100 пикселей (но не дальше правого края окна);

    • y — вертикальный центр (window.innerHeight / 2).

  4. Находим элемент под этой точкой через document.elementFromPoint(x, y).

  5. Отправляем по этому элементу события mousedown, mouseup, click.

Код:

function goToNextStoryByScreenClick(activeStoryEl) {
  if (!activeStoryEl) return false;

  const cont = activeStoryEl.querySelector('.stories_item_cont');
  if (!cont) return false;

  const rect = cont.getBoundingClientRect();

  let x = rect.right + 100;
  const maxX = window.innerWidth - 10;
  if (x > maxX) x = maxX;
  if (x < 0) x = 0;

  let y = window.innerHeight / 2;
  if (y < 0) y = 0;
  const maxY = window.innerHeight - 10;
  if (y > maxY) y = maxY;

  const target = document.elementFromPoint(x, y) || document.body;

  const eventInit = {
    bubbles: true,
    cancelable: true,
    view: window,
    clientX: x,
    clientY: y,
  };

  ['mousedown', 'mouseup', 'click'].forEach((type) => {
    const ev = new MouseEvent(type, eventInit);
    target.dispatchEvent(ev);
  });

  return true;
}

Такой подход менее хрупкий по отношению к внутренней разметке: даже если VK немного поменяет классы у кнопок, сам факт «клик справа от истории» с высокой вероятностью останется рабочим.


Как скрывается обычная история

Если автор не в списке исключений, скрипт проделывает за пользователя три клика:

  1. Открывает меню истории (иконка с тремя точками в шапке).

  2. Нажимает пункт «Скрыть из историй».

  3. Подтверждает действие в модальном окне.

Опорные элементы:

  • кнопка меню — по data-testid="story_header_menu_button";

  • пункт меню — по data-testid="story_header_menu_action_add_blacklist";

  • кнопка подтверждения — button.FlatButton.FlatButton--primary.FlatButton--size-m с текстом Скрыть из историй.

На практике это выглядит как последовательность mousedownmouseupclick по каждому из элементов с небольшими задержками между шагами.


Работа в неактивной вкладке

Один из приятных побочных эффектов: скрипт прекрасно живёт в фоне.

После запуска, например:

hideVkStoriesWithExclusions(200);

можно:

  • оставить вкладку VK открытой;

  • переключиться на другую вкладку или вообще в другое приложение;

  • через какое-то время вернуться и увидеть, что часть историй уже скрыта.

Браузеры по-разному оптимизируют работу вкладок в фоне, но для такого типа задач этого достаточно: даже если таймеры чуть замедлятся, скрипт всё равно отработает до конца, пока вкладка не будет полностью выгружена или страница не перезагружена.


Как использовать скрипт шаг за шагом

Краткая инструкция:

  1. Открыть VK в браузере.

  2. Открыть любую историю так, чтобы просмотрщик историй был активен и история была по центру.

  3. Открыть консоль разработчика:

    • Chrome:

      • Windows / Linux: Ctrl + Shift + I → вкладка Console;

      • macOS: Cmd + Option + I → вкладка Console.

  4. Открыть файл vk-stories-auto-hider.js из репозитория:
    https://github.com/AleksPetrakov/vk-hide-stories-script/

  5. Скопировать весь код и вставить его в консоль, нажать Enter.

  6. При необходимости отредактировать массивы EXCLUDED_NAMES и EXCLUDED_IDS в начале файла.

  7. Запустить:

    hideVkStoriesWithExclusions(200);
  8. Оставить вкладку VK открытой и заняться своими делами. Спустя какое-то время часть историй исчезнет из ленты, а истории людей из списков исключений останутся и будут продолжать показываться.


Почему не расширение

Логичный вопрос: почему это скрипт в консоли, а не полноценное расширение с UI.

На текущем этапе плюсы такого формата для меня перевешивают:

  • минимальный порог входа — никакой установки, один файл;

  • легко прочитать и проверить, что именно делает код;

  • удобнее экспериментировать с селекторами и таймингами;

  • нет лишних разрешений и фона, который живёт в браузере постоянно.

Если эта утилита приживётся и будет полезна не только мне, вариант с расширением никуда не исчезает. Там уже можно думать про UI для управления списками, сохранение настроек, статистику и так далее.


Планы дальше

Этот скрипт решает одну конкретную задачу: очистить ленту историй, не ломая при этом большую сеть контактов.

Очевидные следующие шаги:

  • сделать инструменты для более системного расширения сети контактов во VK (но без спама и агрессивной автоматизации);

  • попробовать перенести похожие идеи на другие социальные сети, где есть схожая проблема «шума»;

  • собрать небольшой набор утилит вокруг темы «гигиена социальной графа»: где-то чистим, где-то аккуратно растим.

Исходники, актуальная версия скрипта и документация — в репозитории:
https://github.com/AleksPetrakov/vk-hide-stories-script/