Антиспам Вконтакте - Powershell скрипт для сообществ

Антиспам Вконтакте

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

Избавиться от этого надоедливого дела можно при помощи простенького Powershell скрипта "Антиспам Вконтакте".

Скрипт добавляет в черный список сообщества (на 11 месяцев, то есть возможность чтения сообщества остается, блокируется только возможность оставлять комментарии):

  • комментаторов стены, которые не являются подписчиками этого сообщества
  • тех, кто оставляет идентичные комментарии
  • тех, кто добавляет в указанный фотоальбом фотографии и при этом не является подписчиком сообщества.
  • тех, комментаторов, у которых страница создана явно для рекламных целей (анализирует статус, стену, количество подписок, пытается определить дату создания страницы)

Повторюсь, так как блокировка временная, а не навсегда, то комментатор и дальше может просматривать сообщество, но при этом возможность оставлять комментарии у него блокируется.

Преимущество антиспам скрипта для Вконтакте над платными сервисами:

  • бесплатен
  • можно проанализировать код и убедится что ничего злонамеренного не совершается
  • есть возможность переделать скрипт под свои нужды и предпочтения

Скрипт время от времени дорабатывается и обновляется в этой записи.

Powershell скрипт "Антиспам Вконтакте":

# Скрипт добавляет в черный список сообщества (на 11 месяцев) комментаторов которые не являются подписчиками этого сообщества либо тех, кто оставляет идентичные комментарии и имеет явно спамные страницы
# Автор elims.org.ua

function http_request { #функция отсылки http-запросов и получения ответа 
 param([string]$uri = $null) #входящий параметр
 $uri = "https://api.vk.com/method/"+$uri 
 $i = 1 #счетчик попыток получения ответа от сервера
 while ($request -eq $null) { #пока не получим ответ от сервера 
  $request = Invoke-WebRequest -Uri $uri #посылаем запрос
  Start-Sleep -m 340 #задержка в 340 милисекунд
  if($i -gt 1) { #если пытались получить ответ от сервера более 1 раза, то выводим это на экран
   write-host 'Попытка' $i ':' $uri
  }
  $i++ #увеличиваем счетчик попыток
 } 
 $response_array = $request.content | ConvertFrom-Json #Конвертируем полученные данные из формата JSON в массив
 return $response_array #возврат результата функции
}
function ban-user {
 param($owner_id = $null,[string]$user_id = $null,$ban_comment = $null,$comment_id = $null,$message_string = $null) #входящий параметр 
 $response_array = http_request("groups.banUser?group_id="+$group_id+"&user_id="+$user_id+"&end_date="+$end_date+"&comment="+$ban_comment+"&access_token="+$token+"&v=5.27") #банним комментатора
 $response_array = http_request("wall.reportComment?owner_id="+$owner_id+"&comment_id="+$comment_id+"&access_token="+$token+"&v=5.27") #удаляем комментарий 
 $message_string | Out-File -Append $file_export -Encoding UTF8
}
function check_for_spam_in_string { #функция проверяющая есть ли спам в входящей строке
 param([string]$function_string = $null) #входящий параметр
 $spam_string = $False #пока считаем что спама нет
 $function_string = $function_string.ToLower() #переводим текст строки в нижний регистр
 $function_string = $function_string -replace '(\\)|(\/)|(\*)|(:)|(\?)|(")|(<)|(>)|(\|)|(\[)|(\])|(\n)|(_)|( )', "" #убираем всякие разделители
 foreach($spam_text in $spam_texts_array) { #пробегаемся по всем указанным спамным паттернам 
  $spam_text = $spam_text -replace '(\\)|(\/)|(\*)|(:)|(\?)|(")|(<)|(>)|(\|)|(\[)|(\])|(\n)|(_)|( )', "" #убираем всякие разделители
  $spam_text = $spam_text.ToLower() #переводим текст строки в нижний регистр
  if ($function_string.contains($spam_text)) { #если строка содержит спам
   $spam_string = $True #комментарий считается спамной 
  }
 }
 return $spam_string #возвращаем результат
} 
function vk-antispam-function { #функция антиспама
 $group_id = $owner_id * -1 
 $response_array = http_request("wall.get?owner_id="+ $owner_id + "&count=100&v=5.27") #получаем список постов
 $posts_array = $response_array.response.items
 $i = 0 
 $comments_from_id_text = @() #массив состоящий из текстов сообщений и отравителей
 $comments_comment_id = @() #массив состоящий из id комментариев 
 $end_date = [int][double]::Parse($(Get-Date -date (get-date).ToUniversalTime()-uformat %s)) + 28857600 #юникс дата разбана 
 while ( $i -lt 100 ) { #обходим полученные 100 постов
  if ($posts_array.comments[$i].count -gt 0) { #если пост содержит комментарии
   $response_array = http_request("wall.getComments?owner_id="+ $owner_id + "&post_id=" + $posts_array.id[$i] + "&count=100&v=5.27")#получаем комментарии к посту
   $comments_array = $response_array.response.items
   $comment_i = 0
   foreach($comment in $comments_array.id) { #обходим полученные коментарии 
    $time_now = Get-Date -date (get-date) #текущее время
    if(check_for_spam_in_string($comments_array[$comment_i].text)) { #если текст комментария содержит спам 
     $message_string = "" + $time_now + " Комментарий содержит спам! post: "+$owner_id+"_"+$posts_array.id[$i]+" from_id: "+$comments_array.from_id[$comment_i]+" text: "+$comments_array[$comment_i].text 
     ban-user $owner_id $comments_array.from_id[$comment_i] "antispam:spamincomment" $comments_array.id[$comment_i] $message_string #баним и удаляем текущий комментарий 
    }
    else { #если текст комментария НЕ содержит спам 
     $response_array = http_request("groups.get?user_id=" + $comments_array.from_id[$comment_i] + "&count=1000&access_token="+$token+"&v=5.29") #проверяем являеться ли комментатор подписчиком
     $groups = $response_array.response.items
     $groups_count = $response_array.response.count 
     $member = 1 
     if ($groups.contains($group_id)) { #если комментатор состоит в группе
      $member = 2
     }
     elseif ($groups_count -gt 0) {
      $response_array = http_request("groups.isMember?group_id=" + $group_id + "&user_id=" + $comments_array.from_id[$comment_i] + "&v=5.27") #проверяем являеться ли комментатор подписчиком, перепроверяем через другой запрос
      if ($response_array.response -eq 0) {
       $member = 0
      } 
     }  
     if ($member -eq 0) { #если комментатор не является подписчиком   
      $message_string = "" + $time_now + " Не подписчик! post: vk.com/wall" +$owner_id+"_"+$posts_array.id[$i]+" from_id: vk.com/id"+$comments_array.from_id[$comment_i]+" text: "+$comments_array[$comment_i].text
      ban-user $owner_id $comments_array.from_id[$comment_i] "antispam:notsubscriber" $comments_array.id[$comment_i] $message_string #баним и удаляем текущий комментарий 
     }
     else { #если комментатор является подписчиком
      $string = "from_id: " + $comments_array.from_id[$comment_i] + " text: " + $comments_array[$comment_i].text #строка содержащая id комментатора и текст комментария
      if ($comments_from_id_text -contains $string ) { #если комментатор уже оставлял такое сообщение 
       $message_string = ""+$time_now+" Дубликат! id исходного комментария: " + $comments_comment_id[$comments_from_id_text.indexOf($string)] + "id текущего комментария: " + $comments_array.id[$comment_i] + " автор и текст: " + $string
       ban-user $owner_id $comments_array.from_id[$comment_i] "antispam:identicalcomment" $comments_array.id[$comment_i] $message_string #баним и удаляем текущий комментарий 
       #удаляем исходный комментарий:
       $response_array = http_request("wall.reportComment?owner_id="+$owner_id+"&comment_id="+$comments_comment_id[$comments_from_id_text.indexOf($string)]+"&access_token="+$token+"&v=5.27") 
      }
      else { #если комментатор еще не оставлял такое сообщение
       $time_now_unix = [int][double]::Parse($(Get-Date -date (get-date).ToUniversalTime()-uformat %s)) #берем текущее время в юникс-формате и переводит в целое число
       $comments_from_id_text = $comments_from_id_text + $string #записываем в массив строку содержащую id комментатора и текст комментария
       $comments_comment_id = $comments_comment_id + $comments_array.id[$comment_i] #записываем в массив id коментария
       $response_array = http_request("wall.get?owner_id=" + $comments_array.from_id[$comment_i] + "&count=1&access_token="+$token+"&v=5.28") #получаем самую новую запись на стене 
       $post_text = $response_array.response.items.text + $response_array.response.items.copy_history.text 
       $posts_count = $response_array.response.count #количество постов на стене комментатора
       $response_array = http_request("users.get?user_ids=" + $comments_array.from_id[$comment_i] + "&fields=status&access_token="+$token+"&v=5.27") #получаем статус комментатора
       $status = $response_array.response.status #статус 
       if ($status -eq $null) { $status = "" } #если неудалось получить статус, то делаем его пустым
       if ($post_text -eq $null) { $post_text = "" } #если неудалось получить первый пост, то делаем его пустым
       if((check_for_spam_in_string($status))-or(check_for_spam_in_string($post_text))) { #если текст статуса или первый пост содержит спам 
        $message_string = ""+$time_now+" Страница комментатора спамная! post: "+$owner_id+"_"+$posts_array.id[$i]+" from_id: "+$comments_array.from_id[$comment_i]+" text: "+$comments_array[$comment_i].text+" Status: "+$status+ " First Post: "+$post_text
        ban-user $owner_id $comments_array.from_id[$comment_i] "antispam:spampage:text-status-last-post" $comments_array.id[$comment_i] $message_string
       } 
       else { 
        $response_array = http_request("wall.get?owner_id="+$comments_array.from_id[$comment_i]+"&offset="+($posts_count-1)+"&count=1&access_token="+$token+"&v=5.28") #получаем самую старую запись на стене 
        $post_first_date = $response_array.response.items.date #дата первого поста на стене комментатора 
        if ((($time_now_unix-$post_first_date) -lt 2*2592000)-or($posts_count -eq 0)) { #если самый старый пост был менее 2 месяцев назад или постов вообще нет
         $response_array = http_request("photos.getAll?owner_id="+$comments_array.from_id[$comment_i]+"&count=1&access_token="+$token+"&v=5.28") #получаем самую новую фотографию на странице
         $photos_count = $response_array.response.count #количество фотографий на странице комментатора
         $response_array = http_request("photos.getAll?owner_id="+$comments_array.from_id[$comment_i]+"&offset="+($photos_count-1)+"&count=1&access_token="+$token+"&v=5.28") #получаем самую старую фотографию на странице комментатора 
         $photo_first_date = $response_array.response.items.date #дата первой фотографии на странице комментатора
         if (($time_now_unix-$photo_first_date) -lt 2*2592000) { #если самая старая фотография была добавлена менее 2 месяцев назад
          $response_array = http_request("users.getSubscriptions?user_id=" + $comments_array.from_id[$comment_i] +"&count=1&extended=1&access_token="+$token+"&v=5.28") #получаем подписки комментатора
          $subscriptions_count = $response_array.response.count #количество подписок
          if ($subscriptions_count -ge 50) { #если комментатор имеет более 50 подписок 
           $message_string = ""+$time_now+" Страница vk.com/id"+$comments_array.from_id[$comment_i]+" была недавно создана! Фотография:"+([TimeZone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($photo_first_date)))+" Пост:"+([TimeZone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($post_first_date)))+" Подписок:"+$subscriptions_count+" Пост:vk.com/wall"+$owner_id+"_"+$posts_array.id[$i]
           ban-user $owner_id $comments_array.from_id[$comment_i] "antispam:spampage:date-of-photo-wall-subscritions" $comments_array.id[$comment_i] $message_string
          }
         }
        }
       } 
      } 
     }
    }
    $comment_i++ #счетчик комментариев
   }
  } 
  "" + $i + " of 100 post: "+$owner_id+"_"+$posts_array.id[$i]
  $i++ #счетчик постов
 }
}

function vk-antispam-photo-function { #функция антиспама в фотоальбоме
 "Удаляем фотографии от не подписчиков в альбоме " + $album_id + " сообщества " + $owner_id
 $group_id = $owner_id * -1 
 $response_array = http_request("photos.get?owner_id="+ $owner_id + "&album_id=" + $album_id + "&rev=1&count=100&v=5.27") #получаем 100 фотографий
 $photo_array = $response_array.response.items
 $i = 0 
 $photo_date = [int][double]::Parse($(Get-Date -date (get-date).ToUniversalTime()-uformat %s)) - 2*86400 #текущее время минус количество секунд за 2*сутки
 $end_date = [int][double]::Parse($(Get-Date -date (get-date).ToUniversalTime()-uformat %s)) + 28857600 #юникс дата разбана 
 while ( $i -lt 100 ) { #обходим полученные 100 фотографий 
  if($photo_array.date[$i] -gt $photo_date) { #если фотография добавлялась за последние 2 сутки 
   $response_array = http_request("groups.isMember?group_id=" + $group_id + "&user_id=" + $photo_array.user_id[$i] + "&v=5.27") #проверяем являеться ли человек подписчиком 
   $member = 2
   $member = $response_array.response 
   if ($member -eq 0) { #убеждаемся еще раз что он не подписчик, так как контакт глючит и не всегда отдает верный результат
    $response_array = http_request("groups.isMember?group_id=" + $group_id + "&user_id=" + $photo_array.user_id[$i] + "&v=5.27") #проверяем являеться ли человек подписчиком 
    $member = 2
    $member = $response_array.response 
   }
   if ($member -eq 0) { #если автор фотографии не является подписчиком 
    $response_array = http_request("groups.banUser?group_id="+$group_id+"&user_id="+$photo_array.user_id[$i]+"&end_date="+$end_date+"&comment=antispamphoto:notsubscriber&access_token="+$token+"&v=5.27") #банним человека 
    $response_array = http_request("photos.report?owner_id="+$owner_id+"&photo_id="+$photo_array.id[$i]+"&access_token="+$token+"&v=5.27") #удаляем фотографию
    $string_message = "" + (Get-Date -date (get-date)) + " Не подписчик! Обнаружена фотография: " + $owner_id+"_"+$photo_array.id[$i] + " от user_id: " + $photo_array.user_id[$i]
    $string_message 
    $string_message | Out-File -Append $file_export -Encoding UTF8 
   }
   elseif($photo_array.text[$i].lenght -gt 0 ) {
    $string_message = "" + (Get-Date -date (get-date)) + "Обнаружена фотография с описанием!" + $owner_id+"_"+$photo_array.id[$i] + " от user_id: " + $photo_array.user_id[$i] + " описание: " + $photo_array.text[$i]
    $string_message 
    $string_message | Out-File -Append $file_export -Encoding UTF8 
   }
  }
  $i++ #счетчик фотографий
 }
}

$token = "ioaudysaiuofasidufsdaifudsiofuyviuyzcviouxvysioudfgyaiufy"
$file_export = "C:\temp\antispam-report.txt" #файл экспорта
$spam_texts_array = "добавляйтесь в друзья","возбуждающая жвачка","ВОЗБУЖДАЮЩИЕ ЖВАЧКИ"
$owner_id = -5994278
$album_id = 15765487 #id-альбома в который пользователи добавляют свои фотографии
vk-antispam-function
vk-antispam-photo-function
$owner_id = -13665595
vk-antispam-function

В самом конце скрипта указываются следующие входящие параметры:

$token - что такое токен и как его сгенерировать я уже писал вот тут, если кратко - то это Ваш уникальный код, благодаря которому Вконтакте будет понимать что скрипт выполняется от Вашего имени (поэтому свой токен не рекомендуется сообщать сторонним лицам). Для работы этого скрипта нужно иметь право на удаления комментариев, фотографий в сообществе и добавление пользователей в черный список сообщества. Пример ссылки для получения такого токена:

https://oauth.vk.com/authorize?client_id=тут_id_вашего_приложения&scope=groups,wall,photos,offline&redirect_uri=https://oauth.vk.com/blank.html&display=page&v=5.24&response_type=token

$file_export - адрес текстового файла, куда будет записываться информация о выполнении срипта

$album_id - id-альбома в который пользователи добавляют свои фотографии

$owner_id - id-сообщества в котором нужно чистить стену от спама. Если у Вас таких сообществ несколько, то присваиваете эту переменную несколько раз чередуя с вызовом функции vk-antispam-function, у меня в скрипте указано два сообщества.

$spam_texts_array - список спамных словосочетаний, на которые реагирует скрипт, перечисляются в кавычках, через запятую, в моем примере указано три спамных словосочетания: "добавляйтесь в друзья", "возбуждающая жвачка", "ВОЗБУЖДАЮЩИЕ ЖВАЧКИ".

Для большей наглядности предположим я хочу чистить стену от спама в 5 сообществах и у них следующие id:

  • -12345
  • -453254
  • -52452
  • -4525
  • -464576

Тогда я должен в скрипте заменить код:

$owner_id = -5994278
vk-antispam-function
$owner_id = -13665595
vk-antispam-function

На код:

$owner_id = -12345
vk-antispam-function
$owner_id = -453254
vk-antispam-function
$owner_id = -52452
vk-antispam-function
$owner_id = -4525
vk-antispam-function
$owner_id = -464576
vk-antispam-function

Осталось этот скрипт сохранить в файл с расширением ps1 и прописать в планировщике заданий  его периодический запуск, после чего стена у Вас будет в чиста от спама =)

UPD: для работы скрипта нужен Powershell не ниже 4-й версии

Понравилось? =) Поделись с друзьями:

Обсуждение записи “Антиспам Вконтакте - Powershell скрипт для сообществ”

  1. Антон says:

    Здравствуйте. Понравилась Ваша статься, задумка актуальна, но у меня не получается запустить этот скрипт или руки у меня кривые.
    Сделал все как было описано. Помогите пожалуйста.
    Скрин.
    s35-temporary-files.radikal.ru/3b892c1e10104b5eb248735a61e733ac/-88693455.jpg

  2. Владимир Демянович (elims.org.ua) says:

    Антон, здравствуйте. Скрин не отображается — «404 — File or directory not found.». Попробуйте еще куда-то выложить. Возможно у Вас старая версия Powershell.

  3. Антон says:

    Перезалил. Установил Powershell на XP по вашей ссылке в статье. Может это быть из за того, что у меня посты от имени группы?
    uploads.ru/JEhtb.jpg

  4. Владимир Демянович (elims.org.ua) says:

    Мои посты также от имени группы. Судя по ошибке массив с постами пустой, нужно смотреть первые ошибки, а не последние. Лучше просто их скопируйте текстом и вставьте куда-либо, например вот сюда: pastebin.com/

  5. Антон says:

    Я бы это сделал, но консоль после быстрого прогона красного текста закрывается. Как это можно решить?

  6. Владимир Демянович (elims.org.ua) says:

    Я сначала запускаю консоль: cmd
    Потом в ней запускаю повершел: Powershell
    А потом уже в ней запускаю скрипт: antispam.ps1

    И у меня ничего не закрывается.

  7. Антон says:

    pastebin.com/WxG2ukGm Вот, вроде бы скопировал все.

  8. Владимир Демянович (elims.org.ua) says:

    Ошибка та же: массив с комментариями пуст. owner_id какой указан?

  9. Антон says:

    мой
    $owner_id = -2286932

  10. Владимир Демянович (elims.org.ua) says:

    Указан корректно. А версия powershell какая? команда «$host.version» в консоли говорит какая версия.

    консоль: cmd
    Потом повершел: Powershell
    А потом: $host.version

    Интересует столбик Major

  11. Антон says:

    Major
    ——
    2

  12. Владимир Демянович (elims.org.ua) says:

    Думаю именно в этом проблема, так как сам специально ставил более новую версию — там был набор необходимых мне команд. У меня 4-я версия.

    Возможно под XP нет 4-й версии.

  13. Антон says:

    Да. Действительно, дело было в старой версии. Сейчас запустил на w7 с обновленной 4й версией шелла и все работает. Спасибо за помощь и пардон за потраченное время). Но Если реализуете это творение под 2ю версию шелла, то буду очень благодарен.

  14. Владимир Демянович (elims.org.ua) says:

    На здоровье =) Под 2-ю версию, увы, переписывать не планирую.

  15. Владимир Демянович (elims.org.ua) says:

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

  16. Антон says:

    Здравствуйте Владимир. Старался Вас не беспокоить, но все таки очень нужен скрипт под XP, хоть криком кричи). Можно ли с Вами как-нибудь договориться?

  17. Владимир Демянович (elims.org.ua) says:

    Антон, здравствуйте. Как говорится «любой каприз за Ваши деньги», все таки на переписывание скрипта придется уделять время, которое у меня распланировано, + ОС на виртуальной машине поднимать, но с необходимой мотивацией могу за это взяться. Пишите по этому поводу на мою почту, она указана на странице About, в конце текста =)

  18. Владимир Демянович (elims.org.ua) says:

    Скрипт существенно доработан. Исправлены ошибки с ложным срабатыванием, добавлены новые проверки на выявление спама и спамных страниц.

  19. Евгений says:

    Здравствуйте!

    Его нужно всегда вручную запускать? Нельзя сделать, чтобы 24 часа работало?

  20. Алексей says:

    Привет!

    Пытаюсь использовать скрипт. Во-первых из-за русского текста он запускаться не хочет. Во-вторых после выполнения скрипта (русский язык я из скрипта удалил) спам не удаляется. В лог вываливается:

    10/09/2015 17:13:20 Comment with SPAM! post: -104125447_1 from_id: 262525396 text: test

  21. Владимир Демянович (elims.org.ua) says:

    Можно, если немного изменить скрипт. Но я предпочел прописать запуск в планировщике задач. То есть скрипт не запускается вручную, он запускается автоматически через планировщик задач.

  22. Сергей says:

    Еще актуально ?

  23. Сергей says:

    Спасибо большое Владимиру , очень помог — все установил и сделал за минимальную сумму денег)

  24. Владимир Демянович (elims.org.ua) says:

    Сергей, пожалуйста =)

  25. Владимир says:

    У меня удаляет комменты вроде, а в ЧС не заносит. Вот скрин: https://vk.com/albums105089913?z=photo105089913_434620781%2Falbum105089913_00
    Помогите мне пожалуйста!
    Спасибо!

  26. Владимир says:

    и по пути вопрос, как уменьшить или убрать время бана?
    Спасибо!

  27. Владимир Демянович (elims.org.ua) says:

    Скорее всего комментатор не состоит ни в одной группе, либо скрывает их список.

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

    #юникс дата разбана — строка которое задает время разбана.28857600 — время в секундах которое плюсуется к текущему времени

  28. Владимир says:

    Владимир, извините, ещё вопрос:
    Есть дубли комментариев, авторов от заносит в ЧС, а вот комменты их не удаляет. Не подскажите где копать?

  29. Владимир Демянович (elims.org.ua) says:

    Рядышком с кодом, где есть что-то вроде дубли или dublicates

  30. Smit says:

    Привет! Что-то я не так делаю походу.
    Выставил в Notepad++ кодировку utf-8. Скопировал скрипт. Сохранил в ps1.
    Залил на хост Jino. Выставил в файле: utf-8, windows, powershell.

    Запускаю — просто вижу в браузере весь текст файла и ничего не срабатывает…

  31. Владимир Демянович (elims.org.ua) says:

    Привет. Да, не то. Скрипт никакого отношения к браузеру не имеет. Ничего в браузере запускать не нужно. И хостинг обычный для него не подойдет.

Обсудить